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/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" 22 23 namespace cloud_print { 24 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); 31 } 32 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. 41 } 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; 48 } 49 return true; 50 } 51 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)); 58 } 59 60 void CloudPrintConnector::ReportStats() { 61 PrinterJobHandler::ReportsStats(); 62 ScheduleStatsReport(); 63 } 64 65 bool CloudPrintConnector::Start() { 66 VLOG(1) << "CP_CONNECTOR: Starting connector" 67 << ", proxy id: " << settings_.proxy_id(); 68 69 pending_tasks_.clear(); 70 71 if (!InitPrintSystem()) 72 return false; 73 74 ScheduleStatsReport(); 75 76 // Start watching for updates from the print system. 77 print_server_watcher_ = print_system_->CreatePrintServerWatcher(); 78 print_server_watcher_->StartWatching(this); 79 80 // Get list of registered printers. 81 AddPendingAvailableTask(); 82 return true; 83 } 84 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; 94 } 95 96 bool CloudPrintConnector::IsRunning() { 97 return print_server_watcher_.get() != NULL; 98 } 99 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); 106 } 107 } 108 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); 117 } 118 } 119 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); 135 } 136 } else { 137 for (JobHandlerMap::iterator index = job_handler_map_.begin(); 138 index != job_handler_map_.end(); index++) { 139 index->second->CheckForJobs(reason); 140 } 141 } 142 } 143 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); 153 } 154 155 void CloudPrintConnector::OnPrinterAdded() { 156 AddPendingAvailableTask(); 157 } 158 159 void CloudPrintConnector::OnPrinterDeleted(const std::string& printer_id) { 160 AddPendingDeleteTask(printer_id); 161 } 162 163 void CloudPrintConnector::OnAuthError() { 164 client_->OnAuthFailed(); 165 } 166 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; 178 } 179 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; 187 188 DCHECK(next_response_handler_); 189 return (this->*next_response_handler_)(source, url, json_data, succeeded); 190 } 191 192 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::OnRequestAuthError() { 193 OnAuthError(); 194 return CloudPrintURLFetcher::STOP_PROCESSING; 195 } 196 197 std::string CloudPrintConnector::GetAuthHeader() { 198 return GetCloudPrintAuthHeaderFromStore(); 199 } 200 201 CloudPrintConnector::~CloudPrintConnector() {} 202 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; 212 213 UpdateSettingsFromPrintersList(json_data); 214 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 220 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); 233 } 234 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); 246 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."; 269 } 270 } 271 } else { 272 NOTREACHED(); 273 } 274 } 275 } 276 277 request_ = NULL; 278 279 RegisterPrinters(local_printers); 280 ContinuePendingTaskProcessing(); // Continue processing background tasks. 281 return CloudPrintURLFetcher::STOP_PROCESSING; 282 } 283 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; 293 294 UpdateSettingsFromPrintersList(json_data); 295 return CloudPrintURLFetcher::STOP_PROCESSING; 296 } 297 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; 309 } 310 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); 327 } 328 } 329 ContinuePendingTaskProcessing(); // Continue processing background tasks. 330 return CloudPrintURLFetcher::STOP_PROCESSING; 331 } 332 333 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()); 341 } 342 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()); 354 } 355 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()); 373 } 374 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; 383 } 384 } 385 return false; 386 } 387 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. 400 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); 410 } 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]; 424 } 425 } 426 } 427 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; 432 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(); 441 } 442 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); 456 } 457 } 458 } 459 } 460 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); 465 } 466 } 467 468 469 void CloudPrintConnector::AddPendingAvailableTask() { 470 PendingTask task; 471 task.type = PENDING_PRINTERS_AVAILABLE; 472 AddPendingTask(task); 473 } 474 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); 480 } 481 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); 488 } 489 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)); 496 } 497 } 498 499 void CloudPrintConnector::ProcessPendingTask() { 500 if (!IsRunning()) 501 return; // Orphant call. 502 if (pending_tasks_.size() == 0) 503 return; // No peding tasks. 504 505 PendingTask task = pending_tasks_.front(); 506 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(); 519 } 520 } 521 522 void CloudPrintConnector::ContinuePendingTaskProcessing() { 523 if (pending_tasks_.size() == 0) 524 return; // No pending tasks. 525 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)); 531 } 532 } 533 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); 540 } 541 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; 550 } 551 } 552 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))); 559 } 560 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); 567 } 568 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); 577 } 578 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); 587 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); 598 599 ContinuePendingTaskProcessing(); // Skip this printer registration. 600 return; 601 } 602 603 const printing::PrinterBasicInfo& info = pending_tasks_.front().printer_info; 604 DCHECK(IsSamePrinter(info.printer_name, printer_name)); 605 606 std::string mime_boundary; 607 CreateMimeBoundaryForUpload(&mime_boundary); 608 std::string post_data; 609 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 if (caps_and_defaults.caps_mime_type == kContentTypeJSON) { 626 net::AddMultipartValueForUpload(kUseCDD, "true", mime_boundary, 627 std::string(), &post_data); 628 } 629 net::AddMultipartValueForUpload(kPrinterCapsValue, 630 caps_and_defaults.printer_capabilities, mime_boundary, 631 caps_and_defaults.caps_mime_type, &post_data); 632 net::AddMultipartValueForUpload(kPrinterDefaultsValue, 633 caps_and_defaults.printer_defaults, mime_boundary, 634 caps_and_defaults.defaults_mime_type, &post_data); 635 // Send a hash of the printer capabilities to the server. We will use this 636 // later to check if the capabilities have changed 637 net::AddMultipartValueForUpload(kPrinterCapsHashValue, 638 base::MD5String(caps_and_defaults.printer_capabilities), 639 mime_boundary, std::string(), &post_data); 640 net::AddMultipartFinalDelimiterForUpload(mime_boundary, &post_data); 641 std::string mime_type("multipart/form-data; boundary="); 642 mime_type += mime_boundary; 643 644 GURL post_url = GetUrlForPrinterRegistration(settings_.server_url()); 645 StartPostRequest(CloudPrintURLFetcher::REQUEST_REGISTER, post_url, 646 kCloudPrintAPIMaxRetryCount, mime_type, post_data, 647 &CloudPrintConnector::HandleRegisterPrinterResponse); 648 } 649 650 bool CloudPrintConnector::IsSamePrinter(const std::string& name1, 651 const std::string& name2) const { 652 return (0 == base::strcasecmp(name1.c_str(), name2.c_str())); 653 } 654 655 } // namespace cloud_print 656