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