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