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/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 
     24 namespace cloud_print {
     25 
     26 CloudPrintConnector::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 
     34 bool 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 
     53 void 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 
     61 void CloudPrintConnector::ReportStats() {
     62   PrinterJobHandler::ReportsStats();
     63   ScheduleStatsReport();
     64 }
     65 
     66 bool 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 
     86 void 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 
     97 bool CloudPrintConnector::IsRunning() {
     98   return print_server_watcher_.get() != NULL;
     99 }
    100 
    101 void 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 
    110 void 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
    122 void 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 
    145 void 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 
    156 void CloudPrintConnector::OnPrinterAdded() {
    157   AddPendingAvailableTask();
    158 }
    159 
    160 void CloudPrintConnector::OnPrinterDeleted(const std::string& printer_id) {
    161   AddPendingDeleteTask(printer_id);
    162 }
    163 
    164 void CloudPrintConnector::OnAuthError() {
    165   client_->OnAuthFailed();
    166 }
    167 
    168 // CloudPrintURLFetcher::Delegate implementation.
    169 CloudPrintURLFetcher::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 
    181 CloudPrintURLFetcher::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 
    193 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::OnRequestAuthError() {
    194   OnAuthError();
    195   return CloudPrintURLFetcher::STOP_PROCESSING;
    196 }
    197 
    198 std::string CloudPrintConnector::GetAuthHeader() {
    199   return GetCloudPrintAuthHeaderFromStore();
    200 }
    201 
    202 CloudPrintConnector::~CloudPrintConnector() {}
    203 
    204 CloudPrintURLFetcher::ResponseAction
    205 CloudPrintConnector::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 
    285 CloudPrintURLFetcher::ResponseAction
    286 CloudPrintConnector::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 
    299 CloudPrintURLFetcher::ResponseAction
    300 CloudPrintConnector::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 
    312 CloudPrintURLFetcher::ResponseAction
    313 CloudPrintConnector::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 
    335 void 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 
    344 void 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 
    357 void 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 
    376 bool 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 
    389 void 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 
    444 void 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 
    470 void CloudPrintConnector::AddPendingAvailableTask() {
    471   PendingTask task;
    472   task.type = PENDING_PRINTERS_AVAILABLE;
    473   AddPendingTask(task);
    474 }
    475 
    476 void 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 
    483 void 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 
    491 void 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 
    500 void 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 
    523 void 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 
    535 void 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 
    543 void 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 
    562 void 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 
    580 void 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 
    651 bool 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