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 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 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 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 61 void CloudPrintConnector::ReportStats() { 62 PrinterJobHandler::ReportsStats(); 63 ScheduleStatsReport(); 64 } 65 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 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 97 bool CloudPrintConnector::IsRunning() { 98 return print_server_watcher_.get() != NULL; 99 } 100 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 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 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 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 156 void CloudPrintConnector::OnPrinterAdded() { 157 AddPendingAvailableTask(); 158 } 159 160 void CloudPrintConnector::OnPrinterDeleted(const std::string& printer_id) { 161 AddPendingDeleteTask(printer_id); 162 } 163 164 void CloudPrintConnector::OnAuthError() { 165 client_->OnAuthFailed(); 166 } 167 168 // CloudPrintURLFetcher::Delegate implementation. 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 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 193 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::OnRequestAuthError() { 194 OnAuthError(); 195 return CloudPrintURLFetcher::STOP_PROCESSING; 196 } 197 198 std::string CloudPrintConnector::GetAuthHeader() { 199 return GetCloudPrintAuthHeaderFromStore(); 200 } 201 202 CloudPrintConnector::~CloudPrintConnector() {} 203 204 CloudPrintURLFetcher::ResponseAction 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 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 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 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 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 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 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 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 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 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 470 void CloudPrintConnector::AddPendingAvailableTask() { 471 PendingTask task; 472 task.type = PENDING_PRINTERS_AVAILABLE; 473 AddPendingTask(task); 474 } 475 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 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 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 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 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 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 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 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 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 651 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