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