Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / service / cloud_print / cloud_print_connector.cc
blob03687d8dd1960afc476e84d6e1fb25d584a6e179
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.
5 #include "chrome/service/cloud_print/cloud_print_connector.h"
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"
23 namespace cloud_print {
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);
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.
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;
49 return true;
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));
60 void CloudPrintConnector::ReportStats() {
61 PrinterJobHandler::ReportsStats();
62 ScheduleStatsReport();
65 bool CloudPrintConnector::Start() {
66 VLOG(1) << "CP_CONNECTOR: Starting connector"
67 << ", proxy id: " << settings_.proxy_id();
69 pending_tasks_.clear();
71 if (!InitPrintSystem())
72 return false;
74 ScheduleStatsReport();
76 // Start watching for updates from the print system.
77 print_server_watcher_ = print_system_->CreatePrintServerWatcher();
78 print_server_watcher_->StartWatching(this);
80 // Get list of registered printers.
81 AddPendingAvailableTask();
82 return true;
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;
96 bool CloudPrintConnector::IsRunning() {
97 return print_server_watcher_.get() != NULL;
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);
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);
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);
136 } else {
137 for (JobHandlerMap::iterator index = job_handler_map_.begin();
138 index != job_handler_map_.end(); index++) {
139 index->second->CheckForJobs(reason);
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);
155 void CloudPrintConnector::OnPrinterAdded() {
156 AddPendingAvailableTask();
159 void CloudPrintConnector::OnPrinterDeleted(const std::string& printer_id) {
160 AddPendingDeleteTask(printer_id);
163 void CloudPrintConnector::OnAuthError() {
164 client_->OnAuthFailed();
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;
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;
188 DCHECK(next_response_handler_);
189 return (this->*next_response_handler_)(source, url, json_data, succeeded);
192 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::OnRequestAuthError() {
193 OnAuthError();
194 return CloudPrintURLFetcher::STOP_PROCESSING;
197 std::string CloudPrintConnector::GetAuthHeader() {
198 return GetCloudPrintAuthHeaderFromStore();
201 CloudPrintConnector::~CloudPrintConnector() {}
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;
213 UpdateSettingsFromPrintersList(json_data);
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
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);
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);
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.";
271 } else {
272 NOTREACHED();
277 request_ = NULL;
279 RegisterPrinters(local_printers);
280 ContinuePendingTaskProcessing(); // Continue processing background tasks.
281 return CloudPrintURLFetcher::STOP_PROCESSING;
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;
294 UpdateSettingsFromPrintersList(json_data);
295 return CloudPrintURLFetcher::STOP_PROCESSING;
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;
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);
329 ContinuePendingTaskProcessing(); // Continue processing background tasks.
330 return CloudPrintURLFetcher::STOP_PROCESSING;
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());
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());
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());
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;
385 return false;
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.
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);
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];
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;
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();
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);
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);
469 void CloudPrintConnector::AddPendingAvailableTask() {
470 PendingTask task;
471 task.type = PENDING_PRINTERS_AVAILABLE;
472 AddPendingTask(task);
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);
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);
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));
499 void CloudPrintConnector::ProcessPendingTask() {
500 if (!IsRunning())
501 return; // Orphant call.
502 if (pending_tasks_.size() == 0)
503 return; // No peding tasks.
505 PendingTask task = pending_tasks_.front();
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();
522 void CloudPrintConnector::ContinuePendingTaskProcessing() {
523 if (pending_tasks_.size() == 0)
524 return; // No pending tasks.
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));
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);
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;
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)));
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);
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);
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);
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);
599 ContinuePendingTaskProcessing(); // Skip this printer registration.
600 return;
603 const printing::PrinterBasicInfo& info = pending_tasks_.front().printer_info;
604 DCHECK(IsSamePrinter(info.printer_name, printer_name));
606 std::string mime_boundary;
607 CreateMimeBoundaryForUpload(&mime_boundary);
608 std::string post_data;
610 net::AddMultipartValueForUpload(kProxyIdValue,
611 settings_.proxy_id(), mime_boundary, std::string(), &post_data);
612 net::AddMultipartValueForUpload(kPrinterNameValue,
613 info.printer_name, mime_boundary, std::string(), &post_data);
614 net::AddMultipartValueForUpload(kPrinterDescValue,
615 info.printer_description, mime_boundary, std::string(), &post_data);
616 net::AddMultipartValueForUpload(kPrinterStatusValue,
617 base::StringPrintf("%d", info.printer_status),
618 mime_boundary, std::string(), &post_data);
619 // Add local_settings with a current XMPP ping interval.
620 net::AddMultipartValueForUpload(kPrinterLocalSettingsValue,
621 base::StringPrintf(kCreateLocalSettingsXmppPingFormat,
622 settings_.xmpp_ping_timeout_sec()),
623 mime_boundary, std::string(), &post_data);
624 post_data += GetPostDataForPrinterInfo(info, mime_boundary);
625 net::AddMultipartValueForUpload(kPrinterCapsValue,
626 caps_and_defaults.printer_capabilities, mime_boundary,
627 caps_and_defaults.caps_mime_type, &post_data);
628 net::AddMultipartValueForUpload(kPrinterDefaultsValue,
629 caps_and_defaults.printer_defaults, mime_boundary,
630 caps_and_defaults.defaults_mime_type, &post_data);
631 // Send a hash of the printer capabilities to the server. We will use this
632 // later to check if the capabilities have changed
633 net::AddMultipartValueForUpload(kPrinterCapsHashValue,
634 base::MD5String(caps_and_defaults.printer_capabilities),
635 mime_boundary, std::string(), &post_data);
636 net::AddMultipartFinalDelimiterForUpload(mime_boundary, &post_data);
637 std::string mime_type("multipart/form-data; boundary=");
638 mime_type += mime_boundary;
640 GURL post_url = GetUrlForPrinterRegistration(settings_.server_url());
641 StartPostRequest(CloudPrintURLFetcher::REQUEST_REGISTER, post_url,
642 kCloudPrintAPIMaxRetryCount, mime_type, post_data,
643 &CloudPrintConnector::HandleRegisterPrinterResponse);
646 bool CloudPrintConnector::IsSamePrinter(const std::string& name1,
647 const std::string& name2) const {
648 return (0 == base::strcasecmp(name1.c_str(), name2.c_str()));
651 } // namespace cloud_print