Home | History | Annotate | Download | only in cloud_print
      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 
     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     base::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     base::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   base::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       base::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     base::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     base::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     base::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     base::ListValue* printer_list = NULL;
    322     // There should be a "printers" value in the JSON
    323     if (json_data->GetList(kPrinterListValue, &printer_list)) {
    324       base::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     base::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   base::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     base::DictionaryValue* json_data) {
    445   base::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       base::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     base::string16 printer_name_utf16 = base::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   if (caps_and_defaults.caps_mime_type == kContentTypeJSON) {
    626     net::AddMultipartValueForUpload(kUseCDD, "true", mime_boundary,
    627                                     std::string(), &post_data);
    628   }
    629   net::AddMultipartValueForUpload(kPrinterCapsValue,
    630       caps_and_defaults.printer_capabilities, mime_boundary,
    631       caps_and_defaults.caps_mime_type, &post_data);
    632   net::AddMultipartValueForUpload(kPrinterDefaultsValue,
    633       caps_and_defaults.printer_defaults, mime_boundary,
    634       caps_and_defaults.defaults_mime_type, &post_data);
    635   // Send a hash of the printer capabilities to the server. We will use this
    636   // later to check if the capabilities have changed
    637   net::AddMultipartValueForUpload(kPrinterCapsHashValue,
    638       base::MD5String(caps_and_defaults.printer_capabilities),
    639       mime_boundary, std::string(), &post_data);
    640   net::AddMultipartFinalDelimiterForUpload(mime_boundary, &post_data);
    641   std::string mime_type("multipart/form-data; boundary=");
    642   mime_type += mime_boundary;
    643 
    644   GURL post_url = GetUrlForPrinterRegistration(settings_.server_url());
    645   StartPostRequest(CloudPrintURLFetcher::REQUEST_REGISTER, post_url,
    646                    kCloudPrintAPIMaxRetryCount, mime_type, post_data,
    647                    &CloudPrintConnector::HandleRegisterPrinterResponse);
    648 }
    649 
    650 bool CloudPrintConnector::IsSamePrinter(const std::string& name1,
    651                                         const std::string& name2) const {
    652   return (0 == base::strcasecmp(name1.c_str(), name2.c_str()));
    653 }
    654 
    655 }  // namespace cloud_print
    656