1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/service/cloud_print/cloud_print_connector.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/md5.h"
10#include "base/rand_util.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/string_split.h"
13#include "base/strings/string_util.h"
14#include "base/strings/stringprintf.h"
15#include "base/strings/utf_string_conversions.h"
16#include "base/values.h"
17#include "chrome/common/cloud_print/cloud_print_constants.h"
18#include "chrome/common/cloud_print/cloud_print_helpers.h"
19#include "chrome/grit/generated_resources.h"
20#include "chrome/service/cloud_print/cloud_print_service_helpers.h"
21#include "net/base/mime_util.h"
22#include "ui/base/l10n/l10n_util.h"
23
24namespace cloud_print {
25
26CloudPrintConnector::CloudPrintConnector(Client* client,
27                                         const ConnectorSettings& settings)
28  : client_(client),
29    next_response_handler_(NULL),
30    stats_ptr_factory_(this) {
31  settings_.CopyFrom(settings);
32}
33
34bool CloudPrintConnector::InitPrintSystem() {
35  if (print_system_.get())
36    return true;
37  print_system_ = PrintSystem::CreateInstance(
38      settings_.print_system_settings());
39  if (!print_system_.get()) {
40    NOTREACHED();
41    return false;  // No memory.
42  }
43  PrintSystem::PrintSystemResult result = print_system_->Init();
44  if (!result.succeeded()) {
45    print_system_ = NULL;
46    // We could not initialize the print system. We need to notify the server.
47    ReportUserMessage(kPrintSystemFailedMessageId, result.message());
48    return false;
49  }
50  return true;
51}
52
53void CloudPrintConnector::ScheduleStatsReport() {
54  base::MessageLoop::current()->PostDelayedTask(
55      FROM_HERE,
56      base::Bind(&CloudPrintConnector::ReportStats,
57                 stats_ptr_factory_.GetWeakPtr()),
58      base::TimeDelta::FromHours(1));
59}
60
61void CloudPrintConnector::ReportStats() {
62  PrinterJobHandler::ReportsStats();
63  ScheduleStatsReport();
64}
65
66bool CloudPrintConnector::Start() {
67  VLOG(1) << "CP_CONNECTOR: Starting connector"
68          << ", proxy id: " << settings_.proxy_id();
69
70  pending_tasks_.clear();
71
72  if (!InitPrintSystem())
73    return false;
74
75  ScheduleStatsReport();
76
77  // Start watching for updates from the print system.
78  print_server_watcher_ = print_system_->CreatePrintServerWatcher();
79  print_server_watcher_->StartWatching(this);
80
81  // Get list of registered printers.
82  AddPendingAvailableTask();
83  return true;
84}
85
86void CloudPrintConnector::Stop() {
87  VLOG(1) << "CP_CONNECTOR: Stopping connector"
88          << ", proxy id: " << settings_.proxy_id();
89  DCHECK(IsRunning());
90  // Do uninitialization here.
91  stats_ptr_factory_.InvalidateWeakPtrs();
92  pending_tasks_.clear();
93  print_server_watcher_ = NULL;
94  request_ = NULL;
95}
96
97bool CloudPrintConnector::IsRunning() {
98  return print_server_watcher_.get() != NULL;
99}
100
101void CloudPrintConnector::GetPrinterIds(std::list<std::string>* printer_ids) {
102  DCHECK(printer_ids);
103  printer_ids->clear();
104  for (JobHandlerMap::const_iterator iter = job_handler_map_.begin();
105       iter != job_handler_map_.end(); ++iter) {
106    printer_ids->push_back(iter->first);
107  }
108}
109
110void CloudPrintConnector::RegisterPrinters(
111    const printing::PrinterList& printers) {
112  if (!IsRunning())
113    return;
114  printing::PrinterList::const_iterator it;
115  for (it = printers.begin(); it != printers.end(); ++it) {
116    if (settings_.ShouldConnect(it->printer_name))
117      AddPendingRegisterTask(*it);
118  }
119}
120
121// Check for jobs for specific printer
122void CloudPrintConnector::CheckForJobs(const std::string& reason,
123                                       const std::string& printer_id) {
124  if (!IsRunning())
125    return;
126  if (!printer_id.empty()) {
127    JobHandlerMap::iterator index = job_handler_map_.find(printer_id);
128    if (index != job_handler_map_.end()) {
129      index->second->CheckForJobs(reason);
130    } else {
131      std::string status_message = l10n_util::GetStringUTF8(
132          IDS_CLOUD_PRINT_ZOMBIE_PRINTER);
133      LOG(ERROR) << "CP_CONNECTOR: " << status_message <<
134          " Printer_id: " << printer_id;
135      ReportUserMessage(kZombiePrinterMessageId, status_message);
136    }
137  } else {
138    for (JobHandlerMap::iterator index = job_handler_map_.begin();
139         index != job_handler_map_.end(); index++) {
140      index->second->CheckForJobs(reason);
141    }
142  }
143}
144
145void CloudPrintConnector::UpdatePrinterSettings(const std::string& printer_id) {
146  // Since connector is managing many printers we need to go through all of them
147  // to select the correct settings.
148  GURL printer_list_url = GetUrlForPrinterList(
149      settings_.server_url(), settings_.proxy_id());
150  StartGetRequest(
151      printer_list_url,
152      kCloudPrintRegisterMaxRetryCount,
153      &CloudPrintConnector::HandlePrinterListResponseSettingsUpdate);
154}
155
156void CloudPrintConnector::OnPrinterAdded() {
157  AddPendingAvailableTask();
158}
159
160void CloudPrintConnector::OnPrinterDeleted(const std::string& printer_id) {
161  AddPendingDeleteTask(printer_id);
162}
163
164void CloudPrintConnector::OnAuthError() {
165  client_->OnAuthFailed();
166}
167
168// CloudPrintURLFetcher::Delegate implementation.
169CloudPrintURLFetcher::ResponseAction CloudPrintConnector::HandleRawData(
170    const net::URLFetcher* source,
171    const GURL& url,
172    const std::string& data) {
173  // If this notification came as a result of user message call, stop it.
174  // Otherwise proceed continue processing.
175  if (user_message_request_.get() &&
176      user_message_request_->IsSameRequest(source))
177    return CloudPrintURLFetcher::STOP_PROCESSING;
178  return CloudPrintURLFetcher::CONTINUE_PROCESSING;
179}
180
181CloudPrintURLFetcher::ResponseAction CloudPrintConnector::HandleJSONData(
182    const net::URLFetcher* source,
183    const GURL& url,
184    base::DictionaryValue* json_data,
185    bool succeeded) {
186  if (!IsRunning())  // Orphant response. Connector has been stopped already.
187    return CloudPrintURLFetcher::STOP_PROCESSING;
188
189  DCHECK(next_response_handler_);
190  return (this->*next_response_handler_)(source, url, json_data, succeeded);
191}
192
193CloudPrintURLFetcher::ResponseAction CloudPrintConnector::OnRequestAuthError() {
194  OnAuthError();
195  return CloudPrintURLFetcher::STOP_PROCESSING;
196}
197
198std::string CloudPrintConnector::GetAuthHeader() {
199  return GetCloudPrintAuthHeaderFromStore();
200}
201
202CloudPrintConnector::~CloudPrintConnector() {}
203
204CloudPrintURLFetcher::ResponseAction
205CloudPrintConnector::HandlePrinterListResponse(
206    const net::URLFetcher* source,
207    const GURL& url,
208    base::DictionaryValue* json_data,
209    bool succeeded) {
210  DCHECK(succeeded);
211  if (!succeeded)
212    return CloudPrintURLFetcher::RETRY_REQUEST;
213
214  UpdateSettingsFromPrintersList(json_data);
215
216  // Now we need to get the list of printers from the print system
217  // and split printers into 3 categories:
218  // - existing and registered printers
219  // - new printers
220  // - deleted printers
221
222  // Get list of the printers from the print system.
223  printing::PrinterList local_printers;
224  PrintSystem::PrintSystemResult result =
225      print_system_->EnumeratePrinters(&local_printers);
226  bool full_list = result.succeeded();
227  if (!full_list) {
228    std::string message = result.message();
229    if (message.empty())
230      message = l10n_util::GetStringFUTF8(IDS_CLOUD_PRINT_ENUM_FAILED,
231          l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT));
232    // There was a failure enumerating printers. Send a message to the server.
233    ReportUserMessage(kEnumPrintersFailedMessageId, message);
234  }
235
236  // Go through the list of the cloud printers and init print job handlers.
237  base::ListValue* printer_list = NULL;
238  // There may be no "printers" value in the JSON
239  if (json_data->GetList(kPrinterListValue, &printer_list) && printer_list) {
240    for (size_t index = 0; index < printer_list->GetSize(); index++) {
241      base::DictionaryValue* printer_data = NULL;
242      if (printer_list->GetDictionary(index, &printer_data)) {
243        std::string printer_name;
244        printer_data->GetString(kNameValue, &printer_name);
245        std::string printer_id;
246        printer_data->GetString(kIdValue, &printer_id);
247
248        if (!settings_.ShouldConnect(printer_name)) {
249          VLOG(1) << "CP_CONNECTOR: Deleting " << printer_name <<
250              " id: " << printer_id << " as blacklisted";
251          AddPendingDeleteTask(printer_id);
252        } else if (RemovePrinterFromList(printer_name, &local_printers)) {
253          InitJobHandlerForPrinter(printer_data);
254        } else {
255          // Cloud printer is not found on the local system.
256          if (full_list || settings_.delete_on_enum_fail()) {
257            // Delete if we get the full list of printers or
258            // |delete_on_enum_fail_| is set.
259            VLOG(1) << "CP_CONNECTOR: Deleting " << printer_name <<
260                " id: " << printer_id <<
261                " full_list: " << full_list <<
262                " delete_on_enum_fail: " << settings_.delete_on_enum_fail();
263            AddPendingDeleteTask(printer_id);
264          } else {
265            LOG(ERROR) << "CP_CONNECTOR: Printer: " << printer_name <<
266                " id: " << printer_id <<
267                " not found in print system and full printer list was" <<
268                " not received.  Printer will not be able to process" <<
269                " jobs.";
270          }
271        }
272      } else {
273        NOTREACHED();
274      }
275    }
276  }
277
278  request_ = NULL;
279
280  RegisterPrinters(local_printers);
281  ContinuePendingTaskProcessing();  // Continue processing background tasks.
282  return CloudPrintURLFetcher::STOP_PROCESSING;
283}
284
285CloudPrintURLFetcher::ResponseAction
286CloudPrintConnector::HandlePrinterListResponseSettingsUpdate(
287    const net::URLFetcher* source,
288    const GURL& url,
289    base::DictionaryValue* json_data,
290    bool succeeded) {
291  DCHECK(succeeded);
292  if (!succeeded)
293    return CloudPrintURLFetcher::RETRY_REQUEST;
294
295  UpdateSettingsFromPrintersList(json_data);
296  return CloudPrintURLFetcher::STOP_PROCESSING;
297}
298
299CloudPrintURLFetcher::ResponseAction
300CloudPrintConnector::HandlePrinterDeleteResponse(
301    const net::URLFetcher* source,
302    const GURL& url,
303    base::DictionaryValue* json_data,
304    bool succeeded) {
305  VLOG(1) << "CP_CONNECTOR: Handler printer delete response"
306          << ", succeeded: " << succeeded
307          << ", url: " << url;
308  ContinuePendingTaskProcessing();  // Continue processing background tasks.
309  return CloudPrintURLFetcher::STOP_PROCESSING;
310}
311
312CloudPrintURLFetcher::ResponseAction
313CloudPrintConnector::HandleRegisterPrinterResponse(
314    const net::URLFetcher* source,
315    const GURL& url,
316    base::DictionaryValue* json_data,
317    bool succeeded) {
318  VLOG(1) << "CP_CONNECTOR: Handler printer register response"
319          << ", succeeded: " << succeeded
320          << ", url: " << url;
321  if (succeeded) {
322    base::ListValue* printer_list = NULL;
323    // There should be a "printers" value in the JSON
324    if (json_data->GetList(kPrinterListValue, &printer_list)) {
325      base::DictionaryValue* printer_data = NULL;
326      if (printer_list->GetDictionary(0, &printer_data))
327        InitJobHandlerForPrinter(printer_data);
328    }
329  }
330  ContinuePendingTaskProcessing();  // Continue processing background tasks.
331  return CloudPrintURLFetcher::STOP_PROCESSING;
332}
333
334
335void CloudPrintConnector::StartGetRequest(const GURL& url,
336                                          int max_retries,
337                                          ResponseHandler handler) {
338  next_response_handler_ = handler;
339  request_ = CloudPrintURLFetcher::Create();
340  request_->StartGetRequest(CloudPrintURLFetcher::REQUEST_UPDATE_JOB,
341                            url, this, max_retries, std::string());
342}
343
344void CloudPrintConnector::StartPostRequest(
345    CloudPrintURLFetcher::RequestType type,
346    const GURL& url,
347    int max_retries,
348    const std::string& mime_type,
349    const std::string& post_data,
350    ResponseHandler handler) {
351  next_response_handler_ = handler;
352  request_ = CloudPrintURLFetcher::Create();
353  request_->StartPostRequest(
354      type, url, this, max_retries, mime_type, post_data, std::string());
355}
356
357void CloudPrintConnector::ReportUserMessage(const std::string& message_id,
358                                            const std::string& failure_msg) {
359  // This is a fire and forget type of function.
360  // Result of this request will be ignored.
361  std::string mime_boundary;
362  CreateMimeBoundaryForUpload(&mime_boundary);
363  GURL url = GetUrlForUserMessage(settings_.server_url(), message_id);
364  std::string post_data;
365  net::AddMultipartValueForUpload(kMessageTextValue, failure_msg, mime_boundary,
366                                  std::string(), &post_data);
367  net::AddMultipartFinalDelimiterForUpload(mime_boundary, &post_data);
368  std::string mime_type("multipart/form-data; boundary=");
369  mime_type += mime_boundary;
370  user_message_request_ = CloudPrintURLFetcher::Create();
371  user_message_request_->StartPostRequest(
372      CloudPrintURLFetcher::REQUEST_USER_MESSAGE, url, this, 1, mime_type,
373      post_data, std::string());
374}
375
376bool CloudPrintConnector::RemovePrinterFromList(
377    const std::string& printer_name,
378    printing::PrinterList* printer_list) {
379  for (printing::PrinterList::iterator index = printer_list->begin();
380       index != printer_list->end(); index++) {
381    if (IsSamePrinter(index->printer_name, printer_name)) {
382      index = printer_list->erase(index);
383      return true;
384    }
385  }
386  return false;
387}
388
389void CloudPrintConnector::InitJobHandlerForPrinter(
390    base::DictionaryValue* printer_data) {
391  DCHECK(printer_data);
392  PrinterJobHandler::PrinterInfoFromCloud printer_info_cloud;
393  printer_data->GetString(kIdValue, &printer_info_cloud.printer_id);
394  DCHECK(!printer_info_cloud.printer_id.empty());
395  VLOG(1) << "CP_CONNECTOR: Init job handler"
396          << ", printer id: " << printer_info_cloud.printer_id;
397  JobHandlerMap::iterator index = job_handler_map_.find(
398      printer_info_cloud.printer_id);
399  if (index != job_handler_map_.end())
400    return;  // Nothing to do if we already have a job handler for this printer.
401
402  printing::PrinterBasicInfo printer_info;
403  printer_data->GetString(kNameValue, &printer_info.printer_name);
404  DCHECK(!printer_info.printer_name.empty());
405  printer_data->GetString(kPrinterDescValue,
406                          &printer_info.printer_description);
407  // Printer status is a string value which actually contains an integer.
408  std::string printer_status;
409  if (printer_data->GetString(kPrinterStatusValue, &printer_status)) {
410    base::StringToInt(printer_status, &printer_info.printer_status);
411  }
412  printer_data->GetString(kPrinterCapsHashValue,
413      &printer_info_cloud.caps_hash);
414  base::ListValue* tags_list = NULL;
415  if (printer_data->GetList(kTagsValue, &tags_list) && tags_list) {
416    for (size_t index = 0; index < tags_list->GetSize(); index++) {
417      std::string tag;
418      if (tags_list->GetString(index, &tag) &&
419          StartsWithASCII(tag, kCloudPrintServiceTagsHashTagName, false)) {
420        std::vector<std::string> tag_parts;
421        base::SplitStringDontTrim(tag, '=', &tag_parts);
422        DCHECK_EQ(tag_parts.size(), 2U);
423        if (tag_parts.size() == 2)
424          printer_info_cloud.tags_hash = tag_parts[1];
425      }
426    }
427  }
428
429  int xmpp_timeout = 0;
430  printer_data->GetInteger(kLocalSettingsPendingXmppValue, &xmpp_timeout);
431  printer_info_cloud.current_xmpp_timeout = settings_.xmpp_ping_timeout_sec();
432  printer_info_cloud.pending_xmpp_timeout = xmpp_timeout;
433
434  scoped_refptr<PrinterJobHandler> job_handler;
435  job_handler = new PrinterJobHandler(printer_info,
436                                      printer_info_cloud,
437                                      settings_.server_url(),
438                                      print_system_.get(),
439                                      this);
440  job_handler_map_[printer_info_cloud.printer_id] = job_handler;
441  job_handler->Initialize();
442}
443
444void CloudPrintConnector::UpdateSettingsFromPrintersList(
445    base::DictionaryValue* json_data) {
446  base::ListValue* printer_list = NULL;
447  int min_xmpp_timeout = std::numeric_limits<int>::max();
448  // There may be no "printers" value in the JSON
449  if (json_data->GetList(kPrinterListValue, &printer_list) && printer_list) {
450    for (size_t index = 0; index < printer_list->GetSize(); index++) {
451      base::DictionaryValue* printer_data = NULL;
452      if (printer_list->GetDictionary(index, &printer_data)) {
453        int xmpp_timeout = 0;
454        if (printer_data->GetInteger(kLocalSettingsPendingXmppValue,
455                                     &xmpp_timeout)) {
456          min_xmpp_timeout = std::min(xmpp_timeout, min_xmpp_timeout);
457        }
458      }
459    }
460  }
461
462  if (min_xmpp_timeout != std::numeric_limits<int>::max()) {
463    DCHECK(min_xmpp_timeout >= kMinXmppPingTimeoutSecs);
464    settings_.SetXmppPingTimeoutSec(min_xmpp_timeout);
465    client_->OnXmppPingUpdated(min_xmpp_timeout);
466  }
467}
468
469
470void CloudPrintConnector::AddPendingAvailableTask() {
471  PendingTask task;
472  task.type = PENDING_PRINTERS_AVAILABLE;
473  AddPendingTask(task);
474}
475
476void CloudPrintConnector::AddPendingDeleteTask(const std::string& id) {
477  PendingTask task;
478  task.type = PENDING_PRINTER_DELETE;
479  task.printer_id = id;
480  AddPendingTask(task);
481}
482
483void CloudPrintConnector::AddPendingRegisterTask(
484    const printing::PrinterBasicInfo& info) {
485  PendingTask task;
486  task.type = PENDING_PRINTER_REGISTER;
487  task.printer_info = info;
488  AddPendingTask(task);
489}
490
491void CloudPrintConnector::AddPendingTask(const PendingTask& task) {
492  pending_tasks_.push_back(task);
493  // If this is the only pending task, we need to start the process.
494  if (pending_tasks_.size() == 1) {
495    base::MessageLoop::current()->PostTask(
496        FROM_HERE, base::Bind(&CloudPrintConnector::ProcessPendingTask, this));
497  }
498}
499
500void CloudPrintConnector::ProcessPendingTask() {
501  if (!IsRunning())
502    return;  // Orphant call.
503  if (pending_tasks_.size() == 0)
504    return;  // No peding tasks.
505
506  PendingTask task = pending_tasks_.front();
507
508  switch (task.type) {
509    case PENDING_PRINTERS_AVAILABLE :
510      OnPrintersAvailable();
511      break;
512    case PENDING_PRINTER_REGISTER :
513      OnPrinterRegister(task.printer_info);
514      break;
515    case PENDING_PRINTER_DELETE :
516      OnPrinterDelete(task.printer_id);
517      break;
518    default:
519      NOTREACHED();
520  }
521}
522
523void CloudPrintConnector::ContinuePendingTaskProcessing() {
524  if (pending_tasks_.size() == 0)
525    return;  // No pending tasks.
526
527  // Delete current task and repost if we have more task available.
528  pending_tasks_.pop_front();
529  if (pending_tasks_.size() != 0) {
530    base::MessageLoop::current()->PostTask(
531        FROM_HERE, base::Bind(&CloudPrintConnector::ProcessPendingTask, this));
532  }
533}
534
535void CloudPrintConnector::OnPrintersAvailable() {
536  GURL printer_list_url = GetUrlForPrinterList(
537      settings_.server_url(), settings_.proxy_id());
538  StartGetRequest(printer_list_url,
539                  kCloudPrintRegisterMaxRetryCount,
540                  &CloudPrintConnector::HandlePrinterListResponse);
541}
542
543void CloudPrintConnector::OnPrinterRegister(
544    const printing::PrinterBasicInfo& info) {
545  for (JobHandlerMap::iterator it = job_handler_map_.begin();
546       it != job_handler_map_.end(); ++it) {
547    if (IsSamePrinter(it->second->GetPrinterName(), info.printer_name)) {
548      // Printer already registered, continue to the next task.
549      ContinuePendingTaskProcessing();
550      return;
551    }
552  }
553
554  // Asynchronously fetch the printer caps and defaults. The story will
555  // continue in OnReceivePrinterCaps.
556  print_system_->GetPrinterCapsAndDefaults(
557      info.printer_name.c_str(),
558      base::Bind(&CloudPrintConnector::OnReceivePrinterCaps,
559                 base::Unretained(this)));
560}
561
562void CloudPrintConnector::OnPrinterDelete(const std::string& printer_id) {
563  // Remove corresponding printer job handler.
564  JobHandlerMap::iterator it = job_handler_map_.find(printer_id);
565  if (it != job_handler_map_.end()) {
566    it->second->Shutdown();
567    job_handler_map_.erase(it);
568  }
569
570  // TODO(gene): We probably should not try indefinitely here. Just once or
571  // twice should be enough.
572  // Bug: http://code.google.com/p/chromium/issues/detail?id=101850
573  GURL url = GetUrlForPrinterDelete(
574      settings_.server_url(), printer_id, "printer_deleted");
575  StartGetRequest(url,
576                  kCloudPrintAPIMaxRetryCount,
577                  &CloudPrintConnector::HandlePrinterDeleteResponse);
578}
579
580void CloudPrintConnector::OnReceivePrinterCaps(
581    bool succeeded,
582    const std::string& printer_name,
583    const printing::PrinterCapsAndDefaults& caps_and_defaults) {
584  if (!IsRunning())
585    return;  // Orphant call.
586  DCHECK(pending_tasks_.size() > 0 &&
587         pending_tasks_.front().type == PENDING_PRINTER_REGISTER);
588
589  if (!succeeded) {
590    LOG(ERROR) << "CP_CONNECTOR: Failed to get printer info"
591               << ", printer name: " << printer_name;
592    // This printer failed to register, notify the server of this failure.
593    base::string16 printer_name_utf16 = base::UTF8ToUTF16(printer_name);
594    std::string status_message = l10n_util::GetStringFUTF8(
595        IDS_CLOUD_PRINT_REGISTER_PRINTER_FAILED,
596        printer_name_utf16,
597        l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT));
598    ReportUserMessage(kGetPrinterCapsFailedMessageId, status_message);
599
600    ContinuePendingTaskProcessing();  // Skip this printer registration.
601    return;
602  }
603
604  const printing::PrinterBasicInfo& info = pending_tasks_.front().printer_info;
605  DCHECK(IsSamePrinter(info.printer_name, printer_name));
606
607  std::string mime_boundary;
608  CreateMimeBoundaryForUpload(&mime_boundary);
609  std::string post_data;
610
611  net::AddMultipartValueForUpload(kProxyIdValue,
612      settings_.proxy_id(), mime_boundary, std::string(), &post_data);
613  net::AddMultipartValueForUpload(kPrinterNameValue,
614      info.printer_name, mime_boundary, std::string(), &post_data);
615  net::AddMultipartValueForUpload(kPrinterDescValue,
616      info.printer_description, mime_boundary, std::string(), &post_data);
617  net::AddMultipartValueForUpload(kPrinterStatusValue,
618      base::StringPrintf("%d", info.printer_status),
619      mime_boundary, std::string(), &post_data);
620  // Add local_settings with a current XMPP ping interval.
621  net::AddMultipartValueForUpload(kPrinterLocalSettingsValue,
622      base::StringPrintf(kCreateLocalSettingsXmppPingFormat,
623          settings_.xmpp_ping_timeout_sec()),
624      mime_boundary, std::string(), &post_data);
625  post_data += GetPostDataForPrinterInfo(info, mime_boundary);
626  if (caps_and_defaults.caps_mime_type == kContentTypeJSON) {
627    net::AddMultipartValueForUpload(kUseCDD, "true", mime_boundary,
628                                    std::string(), &post_data);
629  }
630  net::AddMultipartValueForUpload(kPrinterCapsValue,
631      caps_and_defaults.printer_capabilities, mime_boundary,
632      caps_and_defaults.caps_mime_type, &post_data);
633  net::AddMultipartValueForUpload(kPrinterDefaultsValue,
634      caps_and_defaults.printer_defaults, mime_boundary,
635      caps_and_defaults.defaults_mime_type, &post_data);
636  // Send a hash of the printer capabilities to the server. We will use this
637  // later to check if the capabilities have changed
638  net::AddMultipartValueForUpload(kPrinterCapsHashValue,
639      base::MD5String(caps_and_defaults.printer_capabilities),
640      mime_boundary, std::string(), &post_data);
641  net::AddMultipartFinalDelimiterForUpload(mime_boundary, &post_data);
642  std::string mime_type("multipart/form-data; boundary=");
643  mime_type += mime_boundary;
644
645  GURL post_url = GetUrlForPrinterRegistration(settings_.server_url());
646  StartPostRequest(CloudPrintURLFetcher::REQUEST_REGISTER, post_url,
647                   kCloudPrintAPIMaxRetryCount, mime_type, post_data,
648                   &CloudPrintConnector::HandleRegisterPrinterResponse);
649}
650
651bool CloudPrintConnector::IsSamePrinter(const std::string& name1,
652                                        const std::string& name2) const {
653  return (0 == base::strcasecmp(name1.c_str(), name2.c_str()));
654}
655
656}  // namespace cloud_print
657