• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CloudPrintConnector(Client * client,const ConnectorSettings & settings)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 
InitPrintSystem()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 
ScheduleStatsReport()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 
ReportStats()61 void CloudPrintConnector::ReportStats() {
62   PrinterJobHandler::ReportsStats();
63   ScheduleStatsReport();
64 }
65 
Start()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 
Stop()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 
IsRunning()97 bool CloudPrintConnector::IsRunning() {
98   return print_server_watcher_.get() != NULL;
99 }
100 
GetPrinterIds(std::list<std::string> * printer_ids)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 
RegisterPrinters(const printing::PrinterList & printers)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
CheckForJobs(const std::string & reason,const std::string & printer_id)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 
UpdatePrinterSettings(const std::string & printer_id)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 
OnPrinterAdded()156 void CloudPrintConnector::OnPrinterAdded() {
157   AddPendingAvailableTask();
158 }
159 
OnPrinterDeleted(const std::string & printer_id)160 void CloudPrintConnector::OnPrinterDeleted(const std::string& printer_id) {
161   AddPendingDeleteTask(printer_id);
162 }
163 
OnAuthError()164 void CloudPrintConnector::OnAuthError() {
165   client_->OnAuthFailed();
166 }
167 
168 // CloudPrintURLFetcher::Delegate implementation.
HandleRawData(const net::URLFetcher * source,const GURL & url,const std::string & data)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 
HandleJSONData(const net::URLFetcher * source,const GURL & url,base::DictionaryValue * json_data,bool succeeded)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 
OnRequestAuthError()193 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::OnRequestAuthError() {
194   OnAuthError();
195   return CloudPrintURLFetcher::STOP_PROCESSING;
196 }
197 
GetAuthHeader()198 std::string CloudPrintConnector::GetAuthHeader() {
199   return GetCloudPrintAuthHeaderFromStore();
200 }
201 
~CloudPrintConnector()202 CloudPrintConnector::~CloudPrintConnector() {}
203 
204 CloudPrintURLFetcher::ResponseAction
HandlePrinterListResponse(const net::URLFetcher * source,const GURL & url,base::DictionaryValue * json_data,bool succeeded)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
HandlePrinterListResponseSettingsUpdate(const net::URLFetcher * source,const GURL & url,base::DictionaryValue * json_data,bool succeeded)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
HandlePrinterDeleteResponse(const net::URLFetcher * source,const GURL & url,base::DictionaryValue * json_data,bool succeeded)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
HandleRegisterPrinterResponse(const net::URLFetcher * source,const GURL & url,base::DictionaryValue * json_data,bool succeeded)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 
StartGetRequest(const GURL & url,int max_retries,ResponseHandler handler)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 
StartPostRequest(CloudPrintURLFetcher::RequestType type,const GURL & url,int max_retries,const std::string & mime_type,const std::string & post_data,ResponseHandler handler)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 
ReportUserMessage(const std::string & message_id,const std::string & failure_msg)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 
RemovePrinterFromList(const std::string & printer_name,printing::PrinterList * printer_list)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 
InitJobHandlerForPrinter(base::DictionaryValue * printer_data)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 
UpdateSettingsFromPrintersList(base::DictionaryValue * json_data)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 
AddPendingAvailableTask()470 void CloudPrintConnector::AddPendingAvailableTask() {
471   PendingTask task;
472   task.type = PENDING_PRINTERS_AVAILABLE;
473   AddPendingTask(task);
474 }
475 
AddPendingDeleteTask(const std::string & id)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 
AddPendingRegisterTask(const printing::PrinterBasicInfo & info)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 
AddPendingTask(const PendingTask & task)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 
ProcessPendingTask()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 
ContinuePendingTaskProcessing()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 
OnPrintersAvailable()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 
OnPrinterRegister(const printing::PrinterBasicInfo & info)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 
OnPrinterDelete(const std::string & printer_id)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 
OnReceivePrinterCaps(bool succeeded,const std::string & printer_name,const printing::PrinterCapsAndDefaults & caps_and_defaults)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 
IsSamePrinter(const std::string & name1,const std::string & name2) const651 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