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"
8 #include "base/bind_helpers.h"
9 #include "base/location.h"
11 #include "base/rand_util.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "base/values.h"
20 #include "chrome/common/cloud_print/cloud_print_constants.h"
21 #include "chrome/common/cloud_print/cloud_print_helpers.h"
22 #include "chrome/grit/generated_resources.h"
23 #include "chrome/service/cloud_print/cloud_print_service_helpers.h"
24 #include "net/base/mime_util.h"
25 #include "ui/base/l10n/l10n_util.h"
27 namespace cloud_print
{
29 CloudPrintConnector::CloudPrintConnector(Client
* client
,
30 const ConnectorSettings
& settings
)
32 next_response_handler_(NULL
),
33 stats_ptr_factory_(this) {
34 settings_
.CopyFrom(settings
);
37 bool CloudPrintConnector::InitPrintSystem() {
38 if (print_system_
.get())
40 print_system_
= PrintSystem::CreateInstance(
41 settings_
.print_system_settings());
42 if (!print_system_
.get()) {
44 return false; // No memory.
46 PrintSystem::PrintSystemResult result
= print_system_
->Init();
47 if (!result
.succeeded()) {
49 // We could not initialize the print system. We need to notify the server.
50 ReportUserMessage(kPrintSystemFailedMessageId
, result
.message());
56 void CloudPrintConnector::ScheduleStatsReport() {
57 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
58 FROM_HERE
, base::Bind(&CloudPrintConnector::ReportStats
,
59 stats_ptr_factory_
.GetWeakPtr()),
60 base::TimeDelta::FromHours(1));
63 void CloudPrintConnector::ReportStats() {
64 PrinterJobHandler::ReportsStats();
65 ScheduleStatsReport();
68 bool CloudPrintConnector::Start() {
69 VLOG(1) << "CP_CONNECTOR: Starting connector"
70 << ", proxy id: " << settings_
.proxy_id();
72 pending_tasks_
.clear();
74 if (!InitPrintSystem())
77 ScheduleStatsReport();
79 // Start watching for updates from the print system.
80 print_server_watcher_
= print_system_
->CreatePrintServerWatcher();
81 print_server_watcher_
->StartWatching(this);
83 // Get list of registered printers.
84 AddPendingAvailableTask();
88 void CloudPrintConnector::Stop() {
89 VLOG(1) << "CP_CONNECTOR: Stopping connector"
90 << ", proxy id: " << settings_
.proxy_id();
92 // Do uninitialization here.
93 stats_ptr_factory_
.InvalidateWeakPtrs();
94 pending_tasks_
.clear();
95 print_server_watcher_
= NULL
;
99 bool CloudPrintConnector::IsRunning() {
100 return print_server_watcher_
.get() != NULL
;
103 void CloudPrintConnector::GetPrinterIds(std::list
<std::string
>* printer_ids
) {
105 printer_ids
->clear();
106 for (JobHandlerMap::const_iterator iter
= job_handler_map_
.begin();
107 iter
!= job_handler_map_
.end(); ++iter
) {
108 printer_ids
->push_back(iter
->first
);
112 void CloudPrintConnector::RegisterPrinters(
113 const printing::PrinterList
& printers
) {
116 printing::PrinterList::const_iterator it
;
117 for (it
= printers
.begin(); it
!= printers
.end(); ++it
) {
118 if (settings_
.ShouldConnect(it
->printer_name
))
119 AddPendingRegisterTask(*it
);
123 // Check for jobs for specific printer
124 void CloudPrintConnector::CheckForJobs(const std::string
& reason
,
125 const std::string
& printer_id
) {
128 if (!printer_id
.empty()) {
129 JobHandlerMap::iterator index
= job_handler_map_
.find(printer_id
);
130 if (index
!= job_handler_map_
.end()) {
131 index
->second
->CheckForJobs(reason
);
133 std::string status_message
= l10n_util::GetStringUTF8(
134 IDS_CLOUD_PRINT_ZOMBIE_PRINTER
);
135 LOG(ERROR
) << "CP_CONNECTOR: " << status_message
<<
136 " Printer_id: " << printer_id
;
137 ReportUserMessage(kZombiePrinterMessageId
, status_message
);
140 for (JobHandlerMap::iterator index
= job_handler_map_
.begin();
141 index
!= job_handler_map_
.end(); index
++) {
142 index
->second
->CheckForJobs(reason
);
147 void CloudPrintConnector::UpdatePrinterSettings(const std::string
& printer_id
) {
148 // Since connector is managing many printers we need to go through all of them
149 // to select the correct settings.
150 GURL printer_list_url
= GetUrlForPrinterList(
151 settings_
.server_url(), settings_
.proxy_id());
154 kCloudPrintRegisterMaxRetryCount
,
155 &CloudPrintConnector::HandlePrinterListResponseSettingsUpdate
);
158 void CloudPrintConnector::OnPrinterAdded() {
159 AddPendingAvailableTask();
162 void CloudPrintConnector::OnPrinterDeleted(const std::string
& printer_id
) {
163 AddPendingDeleteTask(printer_id
);
166 void CloudPrintConnector::OnAuthError() {
167 client_
->OnAuthFailed();
170 // CloudPrintURLFetcher::Delegate implementation.
171 CloudPrintURLFetcher::ResponseAction
CloudPrintConnector::HandleRawData(
172 const net::URLFetcher
* source
,
174 const std::string
& data
) {
175 // If this notification came as a result of user message call, stop it.
176 // Otherwise proceed continue processing.
177 if (user_message_request_
.get() &&
178 user_message_request_
->IsSameRequest(source
))
179 return CloudPrintURLFetcher::STOP_PROCESSING
;
180 return CloudPrintURLFetcher::CONTINUE_PROCESSING
;
183 CloudPrintURLFetcher::ResponseAction
CloudPrintConnector::HandleJSONData(
184 const net::URLFetcher
* source
,
186 base::DictionaryValue
* json_data
,
188 if (!IsRunning()) // Orphant response. Connector has been stopped already.
189 return CloudPrintURLFetcher::STOP_PROCESSING
;
191 DCHECK(next_response_handler_
);
192 return (this->*next_response_handler_
)(source
, url
, json_data
, succeeded
);
195 CloudPrintURLFetcher::ResponseAction
CloudPrintConnector::OnRequestAuthError() {
197 return CloudPrintURLFetcher::STOP_PROCESSING
;
200 std::string
CloudPrintConnector::GetAuthHeader() {
201 return GetCloudPrintAuthHeaderFromStore();
204 CloudPrintConnector::~CloudPrintConnector() {}
206 CloudPrintURLFetcher::ResponseAction
207 CloudPrintConnector::HandlePrinterListResponse(
208 const net::URLFetcher
* source
,
210 base::DictionaryValue
* json_data
,
214 return CloudPrintURLFetcher::RETRY_REQUEST
;
216 UpdateSettingsFromPrintersList(json_data
);
218 // Now we need to get the list of printers from the print system
219 // and split printers into 3 categories:
220 // - existing and registered printers
222 // - deleted printers
224 // Get list of the printers from the print system.
225 printing::PrinterList local_printers
;
226 PrintSystem::PrintSystemResult result
=
227 print_system_
->EnumeratePrinters(&local_printers
);
228 bool full_list
= result
.succeeded();
230 std::string message
= result
.message();
232 message
= l10n_util::GetStringFUTF8(IDS_CLOUD_PRINT_ENUM_FAILED
,
233 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT
));
234 // There was a failure enumerating printers. Send a message to the server.
235 ReportUserMessage(kEnumPrintersFailedMessageId
, message
);
238 // Go through the list of the cloud printers and init print job handlers.
239 base::ListValue
* printer_list
= NULL
;
240 // There may be no "printers" value in the JSON
241 if (json_data
->GetList(kPrinterListValue
, &printer_list
) && printer_list
) {
242 for (size_t index
= 0; index
< printer_list
->GetSize(); index
++) {
243 base::DictionaryValue
* printer_data
= NULL
;
244 if (printer_list
->GetDictionary(index
, &printer_data
)) {
245 std::string printer_name
;
246 printer_data
->GetString(kNameValue
, &printer_name
);
247 std::string printer_id
;
248 printer_data
->GetString(kIdValue
, &printer_id
);
250 if (!settings_
.ShouldConnect(printer_name
)) {
251 VLOG(1) << "CP_CONNECTOR: Deleting " << printer_name
<<
252 " id: " << printer_id
<< " as blacklisted";
253 AddPendingDeleteTask(printer_id
);
254 } else if (RemovePrinterFromList(printer_name
, &local_printers
)) {
255 InitJobHandlerForPrinter(printer_data
);
257 // Cloud printer is not found on the local system.
258 if (full_list
|| settings_
.delete_on_enum_fail()) {
259 // Delete if we get the full list of printers or
260 // |delete_on_enum_fail_| is set.
261 VLOG(1) << "CP_CONNECTOR: Deleting " << printer_name
<<
262 " id: " << printer_id
<<
263 " full_list: " << full_list
<<
264 " delete_on_enum_fail: " << settings_
.delete_on_enum_fail();
265 AddPendingDeleteTask(printer_id
);
267 LOG(ERROR
) << "CP_CONNECTOR: Printer: " << printer_name
<<
268 " id: " << printer_id
<<
269 " not found in print system and full printer list was" <<
270 " not received. Printer will not be able to process" <<
282 RegisterPrinters(local_printers
);
283 ContinuePendingTaskProcessing(); // Continue processing background tasks.
284 return CloudPrintURLFetcher::STOP_PROCESSING
;
287 CloudPrintURLFetcher::ResponseAction
288 CloudPrintConnector::HandlePrinterListResponseSettingsUpdate(
289 const net::URLFetcher
* source
,
291 base::DictionaryValue
* json_data
,
295 return CloudPrintURLFetcher::RETRY_REQUEST
;
297 UpdateSettingsFromPrintersList(json_data
);
298 return CloudPrintURLFetcher::STOP_PROCESSING
;
301 CloudPrintURLFetcher::ResponseAction
302 CloudPrintConnector::HandlePrinterDeleteResponse(
303 const net::URLFetcher
* source
,
305 base::DictionaryValue
* json_data
,
307 VLOG(1) << "CP_CONNECTOR: Handler printer delete response"
308 << ", succeeded: " << succeeded
310 ContinuePendingTaskProcessing(); // Continue processing background tasks.
311 return CloudPrintURLFetcher::STOP_PROCESSING
;
314 CloudPrintURLFetcher::ResponseAction
315 CloudPrintConnector::HandleRegisterPrinterResponse(
316 const net::URLFetcher
* source
,
318 base::DictionaryValue
* json_data
,
320 VLOG(1) << "CP_CONNECTOR: Handler printer register response"
321 << ", succeeded: " << succeeded
324 base::ListValue
* printer_list
= NULL
;
325 // There should be a "printers" value in the JSON
326 if (json_data
->GetList(kPrinterListValue
, &printer_list
)) {
327 base::DictionaryValue
* printer_data
= NULL
;
328 if (printer_list
->GetDictionary(0, &printer_data
))
329 InitJobHandlerForPrinter(printer_data
);
332 ContinuePendingTaskProcessing(); // Continue processing background tasks.
333 return CloudPrintURLFetcher::STOP_PROCESSING
;
337 void CloudPrintConnector::StartGetRequest(const GURL
& url
,
339 ResponseHandler handler
) {
340 next_response_handler_
= handler
;
341 request_
= CloudPrintURLFetcher::Create();
342 request_
->StartGetRequest(CloudPrintURLFetcher::REQUEST_UPDATE_JOB
,
343 url
, this, max_retries
, std::string());
346 void CloudPrintConnector::StartPostRequest(
347 CloudPrintURLFetcher::RequestType type
,
350 const std::string
& mime_type
,
351 const std::string
& post_data
,
352 ResponseHandler handler
) {
353 next_response_handler_
= handler
;
354 request_
= CloudPrintURLFetcher::Create();
355 request_
->StartPostRequest(
356 type
, url
, this, max_retries
, mime_type
, post_data
, std::string());
359 void CloudPrintConnector::ReportUserMessage(const std::string
& message_id
,
360 const std::string
& failure_msg
) {
361 // This is a fire and forget type of function.
362 // Result of this request will be ignored.
363 std::string mime_boundary
;
364 CreateMimeBoundaryForUpload(&mime_boundary
);
365 GURL url
= GetUrlForUserMessage(settings_
.server_url(), message_id
);
366 std::string post_data
;
367 net::AddMultipartValueForUpload(kMessageTextValue
, failure_msg
, mime_boundary
,
368 std::string(), &post_data
);
369 net::AddMultipartFinalDelimiterForUpload(mime_boundary
, &post_data
);
370 std::string
mime_type("multipart/form-data; boundary=");
371 mime_type
+= mime_boundary
;
372 user_message_request_
= CloudPrintURLFetcher::Create();
373 user_message_request_
->StartPostRequest(
374 CloudPrintURLFetcher::REQUEST_USER_MESSAGE
, url
, this, 1, mime_type
,
375 post_data
, std::string());
378 bool CloudPrintConnector::RemovePrinterFromList(
379 const std::string
& printer_name
,
380 printing::PrinterList
* printer_list
) {
381 for (printing::PrinterList::iterator index
= printer_list
->begin();
382 index
!= printer_list
->end(); index
++) {
383 if (IsSamePrinter(index
->printer_name
, printer_name
)) {
384 index
= printer_list
->erase(index
);
391 void CloudPrintConnector::InitJobHandlerForPrinter(
392 base::DictionaryValue
* printer_data
) {
393 DCHECK(printer_data
);
394 PrinterJobHandler::PrinterInfoFromCloud printer_info_cloud
;
395 printer_data
->GetString(kIdValue
, &printer_info_cloud
.printer_id
);
396 DCHECK(!printer_info_cloud
.printer_id
.empty());
397 VLOG(1) << "CP_CONNECTOR: Init job handler"
398 << ", printer id: " << printer_info_cloud
.printer_id
;
399 JobHandlerMap::iterator index
= job_handler_map_
.find(
400 printer_info_cloud
.printer_id
);
401 if (index
!= job_handler_map_
.end())
402 return; // Nothing to do if we already have a job handler for this printer.
404 printing::PrinterBasicInfo printer_info
;
405 printer_data
->GetString(kNameValue
, &printer_info
.printer_name
);
406 DCHECK(!printer_info
.printer_name
.empty());
407 printer_data
->GetString(kPrinterDescValue
,
408 &printer_info
.printer_description
);
409 // Printer status is a string value which actually contains an integer.
410 std::string printer_status
;
411 if (printer_data
->GetString(kPrinterStatusValue
, &printer_status
)) {
412 base::StringToInt(printer_status
, &printer_info
.printer_status
);
414 printer_data
->GetString(kPrinterCapsHashValue
,
415 &printer_info_cloud
.caps_hash
);
416 base::ListValue
* tags_list
= NULL
;
417 if (printer_data
->GetList(kTagsValue
, &tags_list
) && tags_list
) {
418 for (size_t index
= 0; index
< tags_list
->GetSize(); index
++) {
420 if (tags_list
->GetString(index
, &tag
) &&
421 base::StartsWith(tag
, kCloudPrintServiceTagsHashTagName
,
422 base::CompareCase::INSENSITIVE_ASCII
)) {
423 std::vector
<std::string
> tag_parts
= base::SplitString(
424 tag
, "=", base::KEEP_WHITESPACE
, base::SPLIT_WANT_ALL
);
425 DCHECK_EQ(tag_parts
.size(), 2U);
426 if (tag_parts
.size() == 2)
427 printer_info_cloud
.tags_hash
= tag_parts
[1];
432 int xmpp_timeout
= 0;
433 printer_data
->GetInteger(kLocalSettingsPendingXmppValue
, &xmpp_timeout
);
434 printer_info_cloud
.current_xmpp_timeout
= settings_
.xmpp_ping_timeout_sec();
435 printer_info_cloud
.pending_xmpp_timeout
= xmpp_timeout
;
437 scoped_refptr
<PrinterJobHandler
> job_handler
;
438 job_handler
= new PrinterJobHandler(printer_info
,
440 settings_
.server_url(),
443 job_handler_map_
[printer_info_cloud
.printer_id
] = job_handler
;
444 job_handler
->Initialize();
447 void CloudPrintConnector::UpdateSettingsFromPrintersList(
448 base::DictionaryValue
* json_data
) {
449 base::ListValue
* printer_list
= NULL
;
450 int min_xmpp_timeout
= std::numeric_limits
<int>::max();
451 // There may be no "printers" value in the JSON
452 if (json_data
->GetList(kPrinterListValue
, &printer_list
) && printer_list
) {
453 for (size_t index
= 0; index
< printer_list
->GetSize(); index
++) {
454 base::DictionaryValue
* printer_data
= NULL
;
455 if (printer_list
->GetDictionary(index
, &printer_data
)) {
456 int xmpp_timeout
= 0;
457 if (printer_data
->GetInteger(kLocalSettingsPendingXmppValue
,
459 min_xmpp_timeout
= std::min(xmpp_timeout
, min_xmpp_timeout
);
465 if (min_xmpp_timeout
!= std::numeric_limits
<int>::max()) {
466 DCHECK(min_xmpp_timeout
>= kMinXmppPingTimeoutSecs
);
467 settings_
.SetXmppPingTimeoutSec(min_xmpp_timeout
);
468 client_
->OnXmppPingUpdated(min_xmpp_timeout
);
473 void CloudPrintConnector::AddPendingAvailableTask() {
475 task
.type
= PENDING_PRINTERS_AVAILABLE
;
476 AddPendingTask(task
);
479 void CloudPrintConnector::AddPendingDeleteTask(const std::string
& id
) {
481 task
.type
= PENDING_PRINTER_DELETE
;
482 task
.printer_id
= id
;
483 AddPendingTask(task
);
486 void CloudPrintConnector::AddPendingRegisterTask(
487 const printing::PrinterBasicInfo
& info
) {
489 task
.type
= PENDING_PRINTER_REGISTER
;
490 task
.printer_info
= info
;
491 AddPendingTask(task
);
494 void CloudPrintConnector::AddPendingTask(const PendingTask
& task
) {
495 pending_tasks_
.push_back(task
);
496 // If this is the only pending task, we need to start the process.
497 if (pending_tasks_
.size() == 1) {
498 base::ThreadTaskRunnerHandle::Get()->PostTask(
499 FROM_HERE
, base::Bind(&CloudPrintConnector::ProcessPendingTask
, this));
503 void CloudPrintConnector::ProcessPendingTask() {
505 return; // Orphant call.
506 if (pending_tasks_
.size() == 0)
507 return; // No peding tasks.
509 PendingTask task
= pending_tasks_
.front();
512 case PENDING_PRINTERS_AVAILABLE
:
513 OnPrintersAvailable();
515 case PENDING_PRINTER_REGISTER
:
516 OnPrinterRegister(task
.printer_info
);
518 case PENDING_PRINTER_DELETE
:
519 OnPrinterDelete(task
.printer_id
);
526 void CloudPrintConnector::ContinuePendingTaskProcessing() {
527 if (pending_tasks_
.size() == 0)
528 return; // No pending tasks.
530 // Delete current task and repost if we have more task available.
531 pending_tasks_
.pop_front();
532 if (pending_tasks_
.size() != 0) {
533 base::ThreadTaskRunnerHandle::Get()->PostTask(
534 FROM_HERE
, base::Bind(&CloudPrintConnector::ProcessPendingTask
, this));
538 void CloudPrintConnector::OnPrintersAvailable() {
539 GURL printer_list_url
= GetUrlForPrinterList(
540 settings_
.server_url(), settings_
.proxy_id());
541 StartGetRequest(printer_list_url
,
542 kCloudPrintRegisterMaxRetryCount
,
543 &CloudPrintConnector::HandlePrinterListResponse
);
546 void CloudPrintConnector::OnPrinterRegister(
547 const printing::PrinterBasicInfo
& info
) {
548 for (JobHandlerMap::iterator it
= job_handler_map_
.begin();
549 it
!= job_handler_map_
.end(); ++it
) {
550 if (IsSamePrinter(it
->second
->GetPrinterName(), info
.printer_name
)) {
551 // Printer already registered, continue to the next task.
552 ContinuePendingTaskProcessing();
557 // Asynchronously fetch the printer caps and defaults. The story will
558 // continue in OnReceivePrinterCaps.
559 print_system_
->GetPrinterCapsAndDefaults(
560 info
.printer_name
.c_str(),
561 base::Bind(&CloudPrintConnector::OnReceivePrinterCaps
,
562 base::Unretained(this)));
565 void CloudPrintConnector::OnPrinterDelete(const std::string
& printer_id
) {
566 // Remove corresponding printer job handler.
567 JobHandlerMap::iterator it
= job_handler_map_
.find(printer_id
);
568 if (it
!= job_handler_map_
.end()) {
569 it
->second
->Shutdown();
570 job_handler_map_
.erase(it
);
573 // TODO(gene): We probably should not try indefinitely here. Just once or
574 // twice should be enough.
575 // Bug: http://code.google.com/p/chromium/issues/detail?id=101850
576 GURL url
= GetUrlForPrinterDelete(
577 settings_
.server_url(), printer_id
, "printer_deleted");
579 kCloudPrintAPIMaxRetryCount
,
580 &CloudPrintConnector::HandlePrinterDeleteResponse
);
583 void CloudPrintConnector::OnReceivePrinterCaps(
585 const std::string
& printer_name
,
586 const printing::PrinterCapsAndDefaults
& caps_and_defaults
) {
588 return; // Orphant call.
589 DCHECK(pending_tasks_
.size() > 0 &&
590 pending_tasks_
.front().type
== PENDING_PRINTER_REGISTER
);
593 LOG(ERROR
) << "CP_CONNECTOR: Failed to get printer info"
594 << ", printer name: " << printer_name
;
595 // This printer failed to register, notify the server of this failure.
596 base::string16 printer_name_utf16
= base::UTF8ToUTF16(printer_name
);
597 std::string status_message
= l10n_util::GetStringFUTF8(
598 IDS_CLOUD_PRINT_REGISTER_PRINTER_FAILED
,
600 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT
));
601 ReportUserMessage(kGetPrinterCapsFailedMessageId
, status_message
);
603 ContinuePendingTaskProcessing(); // Skip this printer registration.
607 const printing::PrinterBasicInfo
& info
= pending_tasks_
.front().printer_info
;
608 DCHECK(IsSamePrinter(info
.printer_name
, printer_name
));
610 std::string mime_boundary
;
611 CreateMimeBoundaryForUpload(&mime_boundary
);
612 std::string post_data
;
614 net::AddMultipartValueForUpload(kProxyIdValue
,
615 settings_
.proxy_id(), mime_boundary
, std::string(), &post_data
);
616 net::AddMultipartValueForUpload(kPrinterNameValue
,
617 info
.printer_name
, mime_boundary
, std::string(), &post_data
);
618 net::AddMultipartValueForUpload(kPrinterDescValue
,
619 info
.printer_description
, mime_boundary
, std::string(), &post_data
);
620 net::AddMultipartValueForUpload(kPrinterStatusValue
,
621 base::IntToString(info
.printer_status
),
622 mime_boundary
, std::string(), &post_data
);
623 // Add local_settings with a current XMPP ping interval.
624 net::AddMultipartValueForUpload(kPrinterLocalSettingsValue
,
625 base::StringPrintf(kCreateLocalSettingsXmppPingFormat
,
626 settings_
.xmpp_ping_timeout_sec()),
627 mime_boundary
, std::string(), &post_data
);
628 post_data
+= GetPostDataForPrinterInfo(info
, mime_boundary
);
629 if (caps_and_defaults
.caps_mime_type
== kContentTypeJSON
) {
630 net::AddMultipartValueForUpload(kUseCDD
, "true", mime_boundary
,
631 std::string(), &post_data
);
633 net::AddMultipartValueForUpload(kPrinterCapsValue
,
634 caps_and_defaults
.printer_capabilities
, mime_boundary
,
635 caps_and_defaults
.caps_mime_type
, &post_data
);
636 net::AddMultipartValueForUpload(kPrinterDefaultsValue
,
637 caps_and_defaults
.printer_defaults
, mime_boundary
,
638 caps_and_defaults
.defaults_mime_type
, &post_data
);
639 // Send a hash of the printer capabilities to the server. We will use this
640 // later to check if the capabilities have changed
641 net::AddMultipartValueForUpload(kPrinterCapsHashValue
,
642 base::MD5String(caps_and_defaults
.printer_capabilities
),
643 mime_boundary
, std::string(), &post_data
);
644 net::AddMultipartFinalDelimiterForUpload(mime_boundary
, &post_data
);
645 std::string
mime_type("multipart/form-data; boundary=");
646 mime_type
+= mime_boundary
;
648 GURL post_url
= GetUrlForPrinterRegistration(settings_
.server_url());
649 StartPostRequest(CloudPrintURLFetcher::REQUEST_REGISTER
, post_url
,
650 kCloudPrintAPIMaxRetryCount
, mime_type
, post_data
,
651 &CloudPrintConnector::HandleRegisterPrinterResponse
);
654 bool CloudPrintConnector::IsSamePrinter(const std::string
& name1
,
655 const std::string
& name2
) const {
656 return base::EqualsCaseInsensitiveASCII(name1
, name2
);
659 } // namespace cloud_print