printer_job_handler.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/printer_job_handler.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/file_util.h" 10#include "base/json/json_reader.h" 11#include "base/md5.h" 12#include "base/stringprintf.h" 13#include "base/utf_string_conversions.h" 14#include "base/values.h" 15#include "chrome/common/cloud_print/cloud_print_constants.h" 16#include "chrome/common/cloud_print/cloud_print_helpers.h" 17#include "chrome/service/cloud_print/cloud_print_helpers.h" 18#include "chrome/service/cloud_print/job_status_updater.h" 19#include "googleurl/src/gurl.h" 20#include "grit/generated_resources.h" 21#include "net/http/http_response_headers.h" 22#include "net/http/http_status_code.h" 23#include "printing/backend/print_backend.h" 24#include "ui/base/l10n/l10n_util.h" 25 26namespace cloud_print { 27 28PrinterJobHandler::PrinterJobHandler( 29 const printing::PrinterBasicInfo& printer_info, 30 const PrinterInfoFromCloud& printer_info_cloud, 31 const GURL& cloud_print_server_url, 32 PrintSystem* print_system, 33 Delegate* delegate) 34 : print_system_(print_system), 35 printer_info_(printer_info), 36 printer_info_cloud_(printer_info_cloud), 37 cloud_print_server_url_(cloud_print_server_url), 38 delegate_(delegate), 39 local_job_id_(-1), 40 next_json_data_handler_(NULL), 41 next_data_handler_(NULL), 42 server_error_count_(0), 43 print_thread_("Chrome_CloudPrintJobPrintThread"), 44 job_handler_message_loop_proxy_( 45 base::MessageLoopProxy::current()), 46 shutting_down_(false), 47 job_check_pending_(false), 48 printer_update_pending_(true), 49 task_in_progress_(false), 50 weak_ptr_factory_(this) { 51} 52 53bool PrinterJobHandler::Initialize() { 54 if (!print_system_->IsValidPrinter(printer_info_.printer_name)) 55 return false; 56 57 printer_watcher_ = print_system_->CreatePrinterWatcher( 58 printer_info_.printer_name); 59 printer_watcher_->StartWatching(this); 60 CheckForJobs(kJobFetchReasonStartup); 61 return true; 62} 63 64std::string PrinterJobHandler::GetPrinterName() const { 65 return printer_info_.printer_name; 66} 67 68void PrinterJobHandler::CheckForJobs(const std::string& reason) { 69 VLOG(1) << "CP_CONNECTOR: Checking for jobs" 70 << ", printer id: " << printer_info_cloud_.printer_id 71 << ", reason: " << reason 72 << ", task in progress: " << task_in_progress_; 73 job_fetch_reason_ = reason; 74 job_check_pending_ = true; 75 if (!task_in_progress_) { 76 MessageLoop::current()->PostTask( 77 FROM_HERE, base::Bind(&PrinterJobHandler::Start, this)); 78 } 79} 80 81void PrinterJobHandler::Shutdown() { 82 VLOG(1) << "CP_CONNECTOR: Shutting down printer job handler" 83 << ", printer id: " << printer_info_cloud_.printer_id; 84 Reset(); 85 shutting_down_ = true; 86 while (!job_status_updater_list_.empty()) { 87 // Calling Stop() will cause the OnJobCompleted to be called which will 88 // remove the updater object from the list. 89 job_status_updater_list_.front()->Stop(); 90 } 91} 92 93// CloudPrintURLFetcher::Delegate implementation. 94CloudPrintURLFetcher::ResponseAction PrinterJobHandler::HandleRawResponse( 95 const net::URLFetcher* source, 96 const GURL& url, 97 const net::URLRequestStatus& status, 98 int response_code, 99 const net::ResponseCookies& cookies, 100 const std::string& data) { 101 // 415 (Unsupported media type) error while fetching data from the server 102 // means data conversion error. Stop fetching process and mark job as error. 103 if (next_data_handler_ == (&PrinterJobHandler::HandlePrintDataResponse) && 104 response_code == net::HTTP_UNSUPPORTED_MEDIA_TYPE) { 105 VLOG(1) << "CP_CONNECTOR: Job failed (unsupported media type)"; 106 MessageLoop::current()->PostTask( 107 FROM_HERE, 108 base::Bind(&PrinterJobHandler::JobFailed, this, JOB_DOWNLOAD_FAILED)); 109 return CloudPrintURLFetcher::STOP_PROCESSING; 110 } 111 return CloudPrintURLFetcher::CONTINUE_PROCESSING; 112} 113 114CloudPrintURLFetcher::ResponseAction PrinterJobHandler::HandleRawData( 115 const net::URLFetcher* source, 116 const GURL& url, 117 const std::string& data) { 118 if (!next_data_handler_) 119 return CloudPrintURLFetcher::CONTINUE_PROCESSING; 120 return (this->*next_data_handler_)(source, url, data); 121} 122 123CloudPrintURLFetcher::ResponseAction PrinterJobHandler::HandleJSONData( 124 const net::URLFetcher* source, 125 const GURL& url, 126 DictionaryValue* json_data, 127 bool succeeded) { 128 DCHECK(next_json_data_handler_); 129 return (this->*next_json_data_handler_)(source, url, json_data, succeeded); 130} 131 132// Mark the job fetch as failed and check if other jobs can be printed 133void PrinterJobHandler::OnRequestGiveUp() { 134 if (job_queue_handler_.JobFetchFailed(job_details_.job_id_)) { 135 VLOG(1) << "CP_CONNECTOR: Job failed to load (scheduling retry)"; 136 CheckForJobs(kJobFetchReasonFailure); 137 MessageLoop::current()->PostTask( 138 FROM_HERE, base::Bind(&PrinterJobHandler::Stop, this)); 139 } else { 140 VLOG(1) << "CP_CONNECTOR: Job failed (giving up after " << 141 kNumRetriesBeforeAbandonJob << " retries)"; 142 MessageLoop::current()->PostTask( 143 FROM_HERE, base::Bind(&PrinterJobHandler::JobFailed, this, 144 JOB_DOWNLOAD_FAILED)); 145 } 146} 147 148CloudPrintURLFetcher::ResponseAction PrinterJobHandler::OnRequestAuthError() { 149 // We got an Auth error and have no idea how long it will take to refresh 150 // auth information (may take forever). We'll drop current request and 151 // propagate this error to the upper level. After auth issues will be 152 // resolved, GCP connector will restart. 153 OnAuthError(); 154 return CloudPrintURLFetcher::STOP_PROCESSING; 155} 156 157std::string PrinterJobHandler::GetAuthHeader() { 158 return GetCloudPrintAuthHeaderFromStore(); 159} 160 161// JobStatusUpdater::Delegate implementation 162bool PrinterJobHandler::OnJobCompleted(JobStatusUpdater* updater) { 163 bool ret = false; 164 165 job_queue_handler_.JobDone(job_details_.job_id_); 166 167 for (JobStatusUpdaterList::iterator index = job_status_updater_list_.begin(); 168 index != job_status_updater_list_.end(); index++) { 169 if (index->get() == updater) { 170 job_status_updater_list_.erase(index); 171 ret = true; 172 break; 173 } 174 } 175 return ret; 176} 177 178void PrinterJobHandler::OnAuthError() { 179 MessageLoop::current()->PostTask( 180 FROM_HERE, base::Bind(&PrinterJobHandler::Stop, this)); 181 if (delegate_) 182 delegate_->OnAuthError(); 183} 184 185void PrinterJobHandler::OnPrinterDeleted() { 186 if (delegate_) 187 delegate_->OnPrinterDeleted(printer_info_cloud_.printer_id); 188} 189 190void PrinterJobHandler::OnPrinterChanged() { 191 printer_update_pending_ = true; 192 if (!task_in_progress_) { 193 MessageLoop::current()->PostTask( 194 FROM_HERE, base::Bind(&PrinterJobHandler::Start, this)); 195 } 196} 197 198void PrinterJobHandler::OnJobChanged() { 199 // Some job on the printer changed. Loop through all our JobStatusUpdaters 200 // and have them check for updates. 201 for (JobStatusUpdaterList::iterator index = job_status_updater_list_.begin(); 202 index != job_status_updater_list_.end(); index++) { 203 MessageLoop::current()->PostTask( 204 FROM_HERE, base::Bind(&JobStatusUpdater::UpdateStatus, index->get())); 205 } 206} 207 208void PrinterJobHandler::OnJobSpoolSucceeded(const PlatformJobId& job_id) { 209 DCHECK(MessageLoop::current() == print_thread_.message_loop()); 210 job_spooler_ = NULL; 211 job_handler_message_loop_proxy_->PostTask( 212 FROM_HERE, base::Bind(&PrinterJobHandler::JobSpooled, this, job_id)); 213} 214 215void PrinterJobHandler::OnJobSpoolFailed() { 216 DCHECK(MessageLoop::current() == print_thread_.message_loop()); 217 job_spooler_ = NULL; 218 VLOG(1) << "CP_CONNECTOR: Job failed (spool failed)"; 219 job_handler_message_loop_proxy_->PostTask( 220 FROM_HERE, base::Bind(&PrinterJobHandler::JobFailed, this, PRINT_FAILED)); 221} 222 223PrinterJobHandler::~PrinterJobHandler() { 224 if (printer_watcher_) 225 printer_watcher_->StopWatching(); 226} 227 228// Begin Response handlers 229CloudPrintURLFetcher::ResponseAction 230PrinterJobHandler::HandlePrinterUpdateResponse( 231 const net::URLFetcher* source, 232 const GURL& url, 233 DictionaryValue* json_data, 234 bool succeeded) { 235 VLOG(1) << "CP_CONNECTOR: Handling printer update response" 236 << ", printer id: " << printer_info_cloud_.printer_id; 237 // We are done here. Go to the Stop state 238 VLOG(1) << "CP_CONNECTOR: Stopping printer job handler" 239 << ", printer id: " << printer_info_cloud_.printer_id; 240 MessageLoop::current()->PostTask( 241 FROM_HERE, base::Bind(&PrinterJobHandler::Stop, this)); 242 return CloudPrintURLFetcher::STOP_PROCESSING; 243} 244 245CloudPrintURLFetcher::ResponseAction 246PrinterJobHandler::HandleJobMetadataResponse( 247 const net::URLFetcher* source, 248 const GURL& url, 249 DictionaryValue* json_data, 250 bool succeeded) { 251 VLOG(1) << "CP_CONNECTOR: Handling job metadata response" 252 << ", printer id: " << printer_info_cloud_.printer_id; 253 bool job_available = false; 254 if (succeeded) { 255 std::vector<JobDetails> jobs; 256 job_queue_handler_.GetJobsFromQueue(json_data, &jobs); 257 if (!jobs.empty()) { 258 if (jobs[0].time_remaining_ == base::TimeDelta()) { 259 job_available = true; 260 job_details_ = jobs[0]; 261 262 SetNextDataHandler(&PrinterJobHandler::HandlePrintTicketResponse); 263 request_ = CloudPrintURLFetcher::Create(); 264 request_->StartGetRequest(GURL(job_details_.print_ticket_url_), 265 this, 266 kJobDataMaxRetryCount, 267 std::string()); 268 } else { 269 job_available = false; 270 MessageLoop::current()->PostDelayedTask( 271 FROM_HERE, 272 base::Bind(&PrinterJobHandler::RunScheduledJobCheck, this), 273 jobs[0].time_remaining_); 274 } 275 } 276 } 277 278 if (!job_available) { 279 // If no jobs are available, go to the Stop state. 280 VLOG(1) << "CP_CONNECTOR: Stopping printer job handler" 281 << ", printer id: " << printer_info_cloud_.printer_id; 282 MessageLoop::current()->PostTask( 283 FROM_HERE, base::Bind(&PrinterJobHandler::Stop, this)); 284 } 285 return CloudPrintURLFetcher::STOP_PROCESSING; 286} 287 288CloudPrintURLFetcher::ResponseAction 289PrinterJobHandler::HandlePrintTicketResponse(const net::URLFetcher* source, 290 const GURL& url, 291 const std::string& data) { 292 VLOG(1) << "CP_CONNECTOR: Handling print ticket response" 293 << ", printer id: " << printer_info_cloud_.printer_id; 294 if (print_system_->ValidatePrintTicket(printer_info_.printer_name, data)) { 295 job_details_.print_ticket_ = data; 296 SetNextDataHandler(&PrinterJobHandler::HandlePrintDataResponse); 297 request_ = CloudPrintURLFetcher::Create(); 298 std::string accept_headers = "Accept: "; 299 accept_headers += print_system_->GetSupportedMimeTypes(); 300 request_->StartGetRequest(GURL(job_details_.print_data_url_), 301 this, 302 kJobDataMaxRetryCount, 303 accept_headers); 304 } else { 305 // The print ticket was not valid. We are done here. 306 FailedFetchingJobData(); 307 } 308 return CloudPrintURLFetcher::STOP_PROCESSING; 309} 310 311CloudPrintURLFetcher::ResponseAction 312PrinterJobHandler::HandlePrintDataResponse(const net::URLFetcher* source, 313 const GURL& url, 314 const std::string& data) { 315 VLOG(1) << "CP_CONNECTOR: Handling print data response" 316 << ", printer id: " << printer_info_cloud_.printer_id; 317 base::Closure next_task; 318 if (file_util::CreateTemporaryFile(&job_details_.print_data_file_path_)) { 319 int ret = file_util::WriteFile(job_details_.print_data_file_path_, 320 data.c_str(), 321 data.length()); 322 source->GetResponseHeaders()->GetMimeType( 323 &job_details_.print_data_mime_type_); 324 DCHECK(ret == static_cast<int>(data.length())); 325 if (ret == static_cast<int>(data.length())) { 326 next_task = base::Bind(&PrinterJobHandler::StartPrinting, this); 327 } 328 } 329 // If there was no task allocated above, then there was an error in 330 // saving the print data, bail out here. 331 if (next_task.is_null()) { 332 VLOG(1) << "CP_CONNECTOR: Error saving print data" 333 << ", printer id: " << printer_info_cloud_.printer_id; 334 next_task = base::Bind(&PrinterJobHandler::JobFailed, this, 335 JOB_DOWNLOAD_FAILED); 336 } 337 MessageLoop::current()->PostTask(FROM_HERE, next_task); 338 return CloudPrintURLFetcher::STOP_PROCESSING; 339} 340 341CloudPrintURLFetcher::ResponseAction 342PrinterJobHandler::HandleSuccessStatusUpdateResponse( 343 const net::URLFetcher* source, 344 const GURL& url, 345 DictionaryValue* json_data, 346 bool succeeded) { 347 VLOG(1) << "CP_CONNECTOR: Handling success status update response" 348 << ", printer id: " << printer_info_cloud_.printer_id; 349 // The print job has been spooled locally. We now need to create an object 350 // that monitors the status of the job and updates the server. 351 scoped_refptr<JobStatusUpdater> job_status_updater( 352 new JobStatusUpdater(printer_info_.printer_name, job_details_.job_id_, 353 local_job_id_, cloud_print_server_url_, 354 print_system_.get(), this)); 355 job_status_updater_list_.push_back(job_status_updater); 356 MessageLoop::current()->PostTask( 357 FROM_HERE, base::Bind(&JobStatusUpdater::UpdateStatus, 358 job_status_updater.get())); 359 if (succeeded) { 360 // Since we just printed successfully, we want to look for more jobs. 361 CheckForJobs(kJobFetchReasonQueryMore); 362 } 363 VLOG(1) << "CP_CONNECTOR: Stopping printer job handler" 364 << ", printer id: " << printer_info_cloud_.printer_id; 365 MessageLoop::current()->PostTask( 366 FROM_HERE, base::Bind(&PrinterJobHandler::Stop, this)); 367 return CloudPrintURLFetcher::STOP_PROCESSING; 368} 369 370CloudPrintURLFetcher::ResponseAction 371PrinterJobHandler::HandleFailureStatusUpdateResponse( 372 const net::URLFetcher* source, 373 const GURL& url, 374 DictionaryValue* json_data, 375 bool succeeded) { 376 VLOG(1) << "CP_CONNECTOR: Handling failure status update response" 377 << ", printer id: " << printer_info_cloud_.printer_id; 378 MessageLoop::current()->PostTask( 379 FROM_HERE, base::Bind(&PrinterJobHandler::Stop, this)); 380 return CloudPrintURLFetcher::STOP_PROCESSING; 381} 382 383void PrinterJobHandler::Start() { 384 VLOG(1) << "CP_CONNECTOR: Starting printer job handler" 385 << ", printer id: " << printer_info_cloud_.printer_id 386 << ", task in progress: " << task_in_progress_; 387 if (task_in_progress_) { 388 // Multiple Starts can get posted because of multiple notifications 389 // We want to ignore the other ones that happen when a task is in progress. 390 return; 391 } 392 Reset(); 393 if (!shutting_down_) { 394 // Check if we have work to do. 395 if (HavePendingTasks()) { 396 if (!task_in_progress_ && printer_update_pending_) { 397 printer_update_pending_ = false; 398 task_in_progress_ = UpdatePrinterInfo(); 399 VLOG(1) << "CP_CONNECTOR: Changed task in progress" 400 << ", printer id: " << printer_info_cloud_.printer_id 401 << ", task in progress: " << task_in_progress_; 402 } 403 if (!task_in_progress_ && job_check_pending_) { 404 task_in_progress_ = true; 405 VLOG(1) << "CP_CONNECTOR: Changed task in progress" 406 ", printer id: " << printer_info_cloud_.printer_id 407 << ", task in progress: " << task_in_progress_; 408 job_check_pending_ = false; 409 // We need to fetch any pending jobs for this printer 410 SetNextJSONHandler(&PrinterJobHandler::HandleJobMetadataResponse); 411 request_ = CloudPrintURLFetcher::Create(); 412 request_->StartGetRequest( 413 GetUrlForJobFetch( 414 cloud_print_server_url_, printer_info_cloud_.printer_id, 415 job_fetch_reason_), 416 this, 417 kCloudPrintAPIMaxRetryCount, 418 std::string()); 419 last_job_fetch_time_ = base::TimeTicks::Now(); 420 VLOG(1) << "CP_CONNECTOR: Last job fetch time" 421 << ", printer name: " << printer_info_.printer_name.c_str() 422 << ", timestamp: " << last_job_fetch_time_.ToInternalValue(); 423 job_fetch_reason_.clear(); 424 } 425 } 426 } 427} 428 429void PrinterJobHandler::Stop() { 430 VLOG(1) << "CP_CONNECTOR: Stopping printer job handler" 431 << ", printer id: " << printer_info_cloud_.printer_id; 432 task_in_progress_ = false; 433 VLOG(1) << "CP_CONNECTOR: Changed task in progress" 434 << ", printer id: " << printer_info_cloud_.printer_id 435 << ", task in progress: " << task_in_progress_; 436 Reset(); 437 if (HavePendingTasks()) { 438 MessageLoop::current()->PostTask( 439 FROM_HERE, base::Bind(&PrinterJobHandler::Start, this)); 440 } 441} 442 443void PrinterJobHandler::StartPrinting() { 444 VLOG(1) << "CP_CONNECTOR: Starting printing" 445 << ", printer id: " << printer_info_cloud_.printer_id; 446 // We are done with the request object for now. 447 request_ = NULL; 448 if (!shutting_down_) { 449 if (!print_thread_.Start()) { 450 VLOG(1) << "CP_CONNECTOR: Failed to start print thread" 451 << ", printer id: " << printer_info_cloud_.printer_id; 452 JobFailed(PRINT_FAILED); 453 } else { 454 print_thread_.message_loop()->PostTask( 455 FROM_HERE, base::Bind(&PrinterJobHandler::DoPrint, this, job_details_, 456 printer_info_.printer_name)); 457 } 458 } 459} 460 461void PrinterJobHandler::Reset() { 462 job_details_.Clear(); 463 request_ = NULL; 464 print_thread_.Stop(); 465} 466 467void PrinterJobHandler::UpdateJobStatus(PrintJobStatus status, 468 PrintJobError error) { 469 VLOG(1) << "CP_CONNECTOR: Updating job status" 470 << ", printer id: " << printer_info_cloud_.printer_id 471 << ", job id: " << job_details_.job_id_ 472 << ", job status: " << status; 473 if (shutting_down_) { 474 VLOG(1) << "CP_CONNECTOR: Job status update aborted (shutting down)" 475 << ", printer id: " << printer_info_cloud_.printer_id 476 << ", job id: " << job_details_.job_id_; 477 return; 478 } 479 if (job_details_.job_id_.empty()) { 480 VLOG(1) << "CP_CONNECTOR: Job status update aborted (empty job id)" 481 << ", printer id: " << printer_info_cloud_.printer_id; 482 return; 483 } 484 485 if (error == SUCCESS) { 486 SetNextJSONHandler( 487 &PrinterJobHandler::HandleSuccessStatusUpdateResponse); 488 } else { 489 SetNextJSONHandler( 490 &PrinterJobHandler::HandleFailureStatusUpdateResponse); 491 } 492 request_ = CloudPrintURLFetcher::Create(); 493 request_->StartGetRequest(GetUrlForJobStatusUpdate(cloud_print_server_url_, 494 job_details_.job_id_, 495 status), 496 this, 497 kCloudPrintAPIMaxRetryCount, 498 std::string()); 499} 500 501void PrinterJobHandler::RunScheduledJobCheck() { 502 CheckForJobs(kJobFetchReasonRetry); 503} 504 505void PrinterJobHandler::SetNextJSONHandler(JSONDataHandler handler) { 506 next_json_data_handler_ = handler; 507 next_data_handler_ = NULL; 508} 509 510void PrinterJobHandler::SetNextDataHandler(DataHandler handler) { 511 next_data_handler_ = handler; 512 next_json_data_handler_ = NULL; 513} 514 515void PrinterJobHandler::JobFailed(PrintJobError error) { 516 VLOG(1) << "CP_CONNECTOR: Job failed" 517 << ", printer id: " << printer_info_cloud_.printer_id 518 << ", job id: " << job_details_.job_id_ 519 << ", error: " << error; 520 if (!shutting_down_) { 521 UpdateJobStatus(PRINT_JOB_STATUS_ERROR, error); 522 // This job failed, but others may be pending. Schedule a check. 523 job_check_pending_ = true; 524 job_fetch_reason_ = kJobFetchReasonFailure; 525 } 526} 527 528void PrinterJobHandler::JobSpooled(PlatformJobId local_job_id) { 529 VLOG(1) << "CP_CONNECTOR: Job spooled" 530 << ", printer id: " << printer_info_cloud_.printer_id 531 << ", job id: " << local_job_id; 532 if (!shutting_down_) { 533 local_job_id_ = local_job_id; 534 UpdateJobStatus(PRINT_JOB_STATUS_IN_PROGRESS, SUCCESS); 535 print_thread_.Stop(); 536 } 537} 538 539bool PrinterJobHandler::UpdatePrinterInfo() { 540 if (!printer_watcher_) { 541 LOG(ERROR) << "CP_CONNECTOR: Printer watcher is missing." 542 << " Check printer server url for printer id: " 543 << printer_info_cloud_.printer_id; 544 return false; 545 } 546 547 VLOG(1) << "CP_CONNECTOR: Updating printer info" 548 << ", printer id: " << printer_info_cloud_.printer_id; 549 // We need to update the parts of the printer info that have changed 550 // (could be printer name, description, status or capabilities). 551 // First asynchronously fetch the capabilities. 552 printing::PrinterBasicInfo printer_info; 553 printer_watcher_->GetCurrentPrinterInfo(&printer_info); 554 555 // Asynchronously fetch the printer caps and defaults. The story will 556 // continue in OnReceivePrinterCaps. 557 print_system_->GetPrinterCapsAndDefaults( 558 printer_info.printer_name.c_str(), 559 base::Bind(&PrinterJobHandler::OnReceivePrinterCaps, 560 weak_ptr_factory_.GetWeakPtr())); 561 562 // While we are waiting for the data, pretend we have work to do and return 563 // true. 564 return true; 565} 566 567bool PrinterJobHandler::HavePendingTasks() { 568 return (job_check_pending_ || printer_update_pending_); 569} 570 571void PrinterJobHandler::FailedFetchingJobData() { 572 if (!shutting_down_) { 573 LOG(ERROR) << "CP_CONNECTOR: Failed fetching job data" 574 << ", printer name: " << printer_info_.printer_name 575 << ", job id: " << job_details_.job_id_; 576 JobFailed(INVALID_JOB_DATA); 577 } 578} 579 580void PrinterJobHandler::OnReceivePrinterCaps( 581 bool succeeded, 582 const std::string& printer_name, 583 const printing::PrinterCapsAndDefaults& caps_and_defaults) { 584 printing::PrinterBasicInfo printer_info; 585 if (printer_watcher_) 586 printer_watcher_->GetCurrentPrinterInfo(&printer_info); 587 588 std::string post_data; 589 std::string mime_boundary; 590 CreateMimeBoundaryForUpload(&mime_boundary); 591 592 if (succeeded) { 593 std::string caps_hash = 594 base::MD5String(caps_and_defaults.printer_capabilities); 595 if (caps_hash != printer_info_cloud_.caps_hash) { 596 // Hashes don't match, we need to upload new capabilities (the defaults 597 // go for free along with the capabilities) 598 printer_info_cloud_.caps_hash = caps_hash; 599 AddMultipartValueForUpload(kPrinterCapsValue, 600 caps_and_defaults.printer_capabilities, mime_boundary, 601 caps_and_defaults.caps_mime_type, &post_data); 602 AddMultipartValueForUpload(kPrinterDefaultsValue, 603 caps_and_defaults.printer_defaults, mime_boundary, 604 caps_and_defaults.defaults_mime_type, &post_data); 605 AddMultipartValueForUpload(kPrinterCapsHashValue, 606 caps_hash, mime_boundary, std::string(), &post_data); 607 } 608 } else { 609 LOG(ERROR) << "Failed to get printer caps and defaults" 610 << ", printer name: " << printer_name; 611 } 612 613 std::string tags_hash = GetHashOfPrinterInfo(printer_info); 614 if (tags_hash != printer_info_cloud_.tags_hash) { 615 printer_info_cloud_.tags_hash = tags_hash; 616 post_data += GetPostDataForPrinterInfo(printer_info, mime_boundary); 617 // Remove all the existing proxy tags. 618 std::string cp_tag_wildcard(kCloudPrintServiceProxyTagPrefix); 619 cp_tag_wildcard += ".*"; 620 AddMultipartValueForUpload(kPrinterRemoveTagValue, 621 cp_tag_wildcard, mime_boundary, std::string(), &post_data); 622 } 623 624 if (printer_info.printer_name != printer_info_.printer_name) { 625 AddMultipartValueForUpload(kPrinterNameValue, 626 printer_info.printer_name, mime_boundary, std::string(), &post_data); 627 } 628 if (printer_info.printer_description != printer_info_.printer_description) { 629 AddMultipartValueForUpload(kPrinterDescValue, 630 printer_info.printer_description, mime_boundary, 631 std::string(), &post_data); 632 } 633 if (printer_info.printer_status != printer_info_.printer_status) { 634 AddMultipartValueForUpload(kPrinterStatusValue, 635 base::StringPrintf("%d", printer_info.printer_status), mime_boundary, 636 std::string(), &post_data); 637 } 638 printer_info_ = printer_info; 639 if (!post_data.empty()) { 640 // Terminate the request body 641 post_data.append("--" + mime_boundary + "--\r\n"); 642 std::string mime_type("multipart/form-data; boundary="); 643 mime_type += mime_boundary; 644 SetNextJSONHandler(&PrinterJobHandler::HandlePrinterUpdateResponse); 645 request_ = CloudPrintURLFetcher::Create(); 646 request_->StartPostRequest( 647 GetUrlForPrinterUpdate( 648 cloud_print_server_url_, printer_info_cloud_.printer_id), 649 this, 650 kCloudPrintAPIMaxRetryCount, 651 mime_type, 652 post_data, 653 std::string()); 654 } else { 655 // We are done here. Go to the Stop state 656 VLOG(1) << "CP_CONNECTOR: Stopping printer job handler" 657 << ", printer name: " << printer_name; 658 MessageLoop::current()->PostTask( 659 FROM_HERE, base::Bind(&PrinterJobHandler::Stop, this)); 660 } 661} 662 663// The following methods are called on |print_thread_|. It is not safe to 664// access any members other than |job_handler_message_loop_proxy_|, 665// |job_spooler_| and |print_system_|. 666void PrinterJobHandler::DoPrint(const JobDetails& job_details, 667 const std::string& printer_name) { 668 job_spooler_ = print_system_->CreateJobSpooler(); 669 DCHECK(job_spooler_); 670 if (!job_spooler_) 671 return; 672 string16 document_name = 673 printing::PrintBackend::SimplifyDocumentTitle( 674 UTF8ToUTF16(job_details.job_title_)); 675 if (document_name.empty()) { 676 document_name = printing::PrintBackend::SimplifyDocumentTitle( 677 l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE)); 678 } 679 if (!job_spooler_->Spool(job_details.print_ticket_, 680 job_details.print_data_file_path_, 681 job_details.print_data_mime_type_, 682 printer_name, 683 UTF16ToUTF8(document_name), 684 job_details.tags_, 685 this)) { 686 OnJobSpoolFailed(); 687 } 688} 689 690} // namespace cloud_print 691