- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / service / cloud_print / cloud_print_connector.cc
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_helpers.h"
19 #include "grit/generated_resources.h"
20 #include "net/base/mime_util.h"
21 #include "ui/base/l10n/l10n_util.h"
22
23 namespace cloud_print {
24
25 CloudPrintConnector::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
33 bool 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
52 void 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
60 void CloudPrintConnector::ReportStats() {
61   PrinterJobHandler::ReportsStats();
62   ScheduleStatsReport();
63 }
64
65 bool 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
85 void 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
96 bool CloudPrintConnector::IsRunning() {
97   return print_server_watcher_.get() != NULL;
98 }
99
100 void 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
109 void 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
121 void 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
144 void 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
155 void CloudPrintConnector::OnPrinterAdded() {
156   AddPendingAvailableTask();
157 }
158
159 void CloudPrintConnector::OnPrinterDeleted(const std::string& printer_id) {
160   AddPendingDeleteTask(printer_id);
161 }
162
163 void CloudPrintConnector::OnAuthError() {
164   client_->OnAuthFailed();
165 }
166
167 // CloudPrintURLFetcher::Delegate implementation.
168 CloudPrintURLFetcher::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
180 CloudPrintURLFetcher::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
192 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::OnRequestAuthError() {
193   OnAuthError();
194   return CloudPrintURLFetcher::STOP_PROCESSING;
195 }
196
197 std::string CloudPrintConnector::GetAuthHeader() {
198   return GetCloudPrintAuthHeaderFromStore();
199 }
200
201 CloudPrintConnector::~CloudPrintConnector() {}
202
203 CloudPrintURLFetcher::ResponseAction
204 CloudPrintConnector::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
284 CloudPrintURLFetcher::ResponseAction
285 CloudPrintConnector::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
298 CloudPrintURLFetcher::ResponseAction
299 CloudPrintConnector::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
311 CloudPrintURLFetcher::ResponseAction
312 CloudPrintConnector::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
334 void 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
343 void 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
356 void 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
375 bool 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
388 void 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
443 void 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
469 void CloudPrintConnector::AddPendingAvailableTask() {
470   PendingTask task;
471   task.type = PENDING_PRINTERS_AVAILABLE;
472   AddPendingTask(task);
473 }
474
475 void 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
482 void 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
490 void 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
499 void 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
522 void 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
534 void 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
542 void 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
561 void 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
579 void 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
646 bool 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