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/print_system.h" 6 7 #include <cups/cups.h> 8 #include <dlfcn.h> 9 #include <errno.h> 10 #include <pthread.h> 11 12 #include <algorithm> 13 #include <list> 14 #include <map> 15 16 #include "base/bind.h" 17 #include "base/files/file_path.h" 18 #include "base/json/json_reader.h" 19 #include "base/logging.h" 20 #include "base/md5.h" 21 #include "base/memory/scoped_ptr.h" 22 #include "base/message_loop/message_loop.h" 23 #include "base/rand_util.h" 24 #include "base/strings/string_number_conversions.h" 25 #include "base/strings/string_util.h" 26 #include "base/strings/utf_string_conversions.h" 27 #include "base/values.h" 28 #include "chrome/common/child_process_logging.h" 29 #include "chrome/common/cloud_print/cloud_print_constants.h" 30 #include "chrome/service/cloud_print/cloud_print_helpers.h" 31 #include "grit/generated_resources.h" 32 #include "printing/backend/cups_helper.h" 33 #include "printing/backend/print_backend.h" 34 #include "printing/backend/print_backend_consts.h" 35 #include "ui/base/l10n/l10n_util.h" 36 #include "url/gurl.h" 37 38 namespace { 39 40 // CUPS specific options. 41 const char kCUPSPrinterInfoOpt[] = "printer-info"; 42 const char kCUPSPrinterStateOpt[] = "printer-state"; 43 44 // Print system config options. 45 const char kCUPSPrintServerURLs[] = "print_server_urls"; 46 const char kCUPSUpdateTimeoutMs[] = "update_timeout_ms"; 47 const char kCUPSNotifyDelete[] = "notify_delete"; 48 const char kCUPSSupportedMimeTipes[] = "supported_mime_types"; 49 50 // Default mime types supported by CUPS 51 // http://www.cups.org/articles.php?L205+TFAQ+Q 52 const char kCUPSDefaultSupportedTypes[] = 53 "application/pdf,application/postscript,image/jpeg,image/png,image/gif"; 54 55 // Default port for IPP print servers. 56 const int kDefaultIPPServerPort = 631; 57 58 // Time interval to check for printer's updates. 59 const int kCheckForPrinterUpdatesMinutes = 5; 60 61 // Job update timeout 62 const int kJobUpdateTimeoutSeconds = 5; 63 64 // Job id for dry run (it should not affect CUPS job ids, since 0 job-id is 65 // invalid in CUPS. 66 const int kDryRunJobId = 0; 67 68 } // namespace 69 70 namespace cloud_print { 71 72 struct PrintServerInfoCUPS { 73 GURL url; 74 scoped_refptr<printing::PrintBackend> backend; 75 printing::PrinterList printers; 76 // CapsMap cache PPD until the next update and give a fast access to it by 77 // printer name. PPD request is relatively expensive and this should minimize 78 // the number of requests. 79 typedef std::map<std::string, printing::PrinterCapsAndDefaults> CapsMap; 80 CapsMap caps_cache; 81 }; 82 83 class PrintSystemCUPS : public PrintSystem { 84 public: 85 explicit PrintSystemCUPS(const DictionaryValue* print_system_settings); 86 87 // PrintSystem implementation. 88 virtual PrintSystemResult Init() OVERRIDE; 89 virtual PrintSystem::PrintSystemResult EnumeratePrinters( 90 printing::PrinterList* printer_list) OVERRIDE; 91 virtual void GetPrinterCapsAndDefaults( 92 const std::string& printer_name, 93 const PrinterCapsAndDefaultsCallback& callback) OVERRIDE; 94 virtual bool IsValidPrinter(const std::string& printer_name) OVERRIDE; 95 virtual bool ValidatePrintTicket( 96 const std::string& printer_name, 97 const std::string& print_ticket_data) OVERRIDE; 98 virtual bool GetJobDetails(const std::string& printer_name, 99 PlatformJobId job_id, 100 PrintJobDetails *job_details) OVERRIDE; 101 virtual PrintSystem::PrintServerWatcher* CreatePrintServerWatcher() OVERRIDE; 102 virtual PrintSystem::PrinterWatcher* CreatePrinterWatcher( 103 const std::string& printer_name) OVERRIDE; 104 virtual PrintSystem::JobSpooler* CreateJobSpooler() OVERRIDE; 105 virtual std::string GetSupportedMimeTypes() OVERRIDE; 106 107 // Helper functions. 108 PlatformJobId SpoolPrintJob(const std::string& print_ticket, 109 const base::FilePath& print_data_file_path, 110 const std::string& print_data_mime_type, 111 const std::string& printer_name, 112 const std::string& job_title, 113 const std::vector<std::string>& tags, 114 bool* dry_run); 115 bool GetPrinterInfo(const std::string& printer_name, 116 printing::PrinterBasicInfo* info); 117 bool ParsePrintTicket(const std::string& print_ticket, 118 std::map<std::string, std::string>* options); 119 120 // Synchronous version of GetPrinterCapsAndDefaults. 121 bool GetPrinterCapsAndDefaults( 122 const std::string& printer_name, 123 printing::PrinterCapsAndDefaults* printer_info); 124 125 base::TimeDelta GetUpdateTimeout() const { 126 return update_timeout_; 127 } 128 129 bool NotifyDelete() const { 130 // Notify about deleted printers only when we 131 // fetched printers list without errors. 132 return notify_delete_ && printer_enum_succeeded_; 133 } 134 135 private: 136 virtual ~PrintSystemCUPS() {} 137 138 // Following functions are wrappers around corresponding CUPS functions. 139 // <functions>2() are called when print server is specified, and plain 140 // version in another case. There is an issue specifing CUPS_HTTP_DEFAULT 141 // in the <functions>2(), it does not work in CUPS prior to 1.4. 142 int GetJobs(cups_job_t** jobs, const GURL& url, 143 http_encryption_t encryption, const char* name, 144 int myjobs, int whichjobs); 145 int PrintFile(const GURL& url, http_encryption_t encryption, 146 const char* name, const char* filename, 147 const char* title, int num_options, cups_option_t* options); 148 149 void InitPrintBackends(const DictionaryValue* print_system_settings); 150 void AddPrintServer(const std::string& url); 151 152 void UpdatePrinters(); 153 154 // Full name contains print server url:port and printer name. Short name 155 // is the name of the printer in the CUPS server. 156 std::string MakeFullPrinterName(const GURL& url, 157 const std::string& short_printer_name); 158 PrintServerInfoCUPS* FindServerByFullName( 159 const std::string& full_printer_name, std::string* short_printer_name); 160 161 // Helper method to invoke a PrinterCapsAndDefaultsCallback. 162 static void RunCapsCallback( 163 const PrinterCapsAndDefaultsCallback& callback, 164 bool succeeded, 165 const std::string& printer_name, 166 const printing::PrinterCapsAndDefaults& printer_info); 167 168 // PrintServerList contains information about all print servers and backends 169 // this proxy is connected to. 170 typedef std::list<PrintServerInfoCUPS> PrintServerList; 171 PrintServerList print_servers_; 172 173 base::TimeDelta update_timeout_; 174 bool initialized_; 175 bool printer_enum_succeeded_; 176 bool notify_delete_; 177 http_encryption_t cups_encryption_; 178 std::string supported_mime_types_; 179 }; 180 181 class PrintServerWatcherCUPS 182 : public PrintSystem::PrintServerWatcher { 183 public: 184 explicit PrintServerWatcherCUPS(PrintSystemCUPS* print_system) 185 : print_system_(print_system), 186 delegate_(NULL) { 187 } 188 189 // PrintSystem::PrintServerWatcher implementation. 190 virtual bool StartWatching( 191 PrintSystem::PrintServerWatcher::Delegate* delegate) OVERRIDE { 192 delegate_ = delegate; 193 printers_hash_ = GetPrintersHash(); 194 base::MessageLoop::current()->PostDelayedTask( 195 FROM_HERE, 196 base::Bind(&PrintServerWatcherCUPS::CheckForUpdates, this), 197 print_system_->GetUpdateTimeout()); 198 return true; 199 } 200 201 virtual bool StopWatching() OVERRIDE { 202 delegate_ = NULL; 203 return true; 204 } 205 206 void CheckForUpdates() { 207 if (delegate_ == NULL) 208 return; // Orphan call. We have been stopped already. 209 VLOG(1) << "CP_CUPS: Checking for new printers"; 210 std::string new_hash = GetPrintersHash(); 211 if (printers_hash_ != new_hash) { 212 printers_hash_ = new_hash; 213 delegate_->OnPrinterAdded(); 214 } 215 base::MessageLoop::current()->PostDelayedTask( 216 FROM_HERE, 217 base::Bind(&PrintServerWatcherCUPS::CheckForUpdates, this), 218 print_system_->GetUpdateTimeout()); 219 } 220 221 protected: 222 virtual ~PrintServerWatcherCUPS() { 223 StopWatching(); 224 } 225 226 private: 227 std::string GetPrintersHash() { 228 printing::PrinterList printer_list; 229 print_system_->EnumeratePrinters(&printer_list); 230 231 // Sort printer names. 232 std::vector<std::string> printers; 233 printing::PrinterList::iterator it; 234 for (it = printer_list.begin(); it != printer_list.end(); ++it) 235 printers.push_back(it->printer_name); 236 std::sort(printers.begin(), printers.end()); 237 238 std::string to_hash; 239 for (size_t i = 0; i < printers.size(); i++) 240 to_hash += printers[i]; 241 242 return base::MD5String(to_hash); 243 } 244 245 scoped_refptr<PrintSystemCUPS> print_system_; 246 PrintSystem::PrintServerWatcher::Delegate* delegate_; 247 std::string printers_hash_; 248 249 DISALLOW_COPY_AND_ASSIGN(PrintServerWatcherCUPS); 250 }; 251 252 class PrinterWatcherCUPS 253 : public PrintSystem::PrinterWatcher { 254 public: 255 PrinterWatcherCUPS(PrintSystemCUPS* print_system, 256 const std::string& printer_name) 257 : printer_name_(printer_name), 258 delegate_(NULL), 259 print_system_(print_system) { 260 } 261 262 // PrintSystem::PrinterWatcher implementation. 263 virtual bool StartWatching( 264 PrintSystem::PrinterWatcher::Delegate* delegate) OVERRIDE{ 265 scoped_refptr<printing::PrintBackend> print_backend( 266 printing::PrintBackend::CreateInstance(NULL)); 267 child_process_logging::ScopedPrinterInfoSetter prn_info( 268 print_backend->GetPrinterDriverInfo(printer_name_)); 269 if (delegate_ != NULL) 270 StopWatching(); 271 delegate_ = delegate; 272 settings_hash_ = GetSettingsHash(); 273 // Schedule next job status update. 274 base::MessageLoop::current()->PostDelayedTask( 275 FROM_HERE, 276 base::Bind(&PrinterWatcherCUPS::JobStatusUpdate, this), 277 base::TimeDelta::FromSeconds(kJobUpdateTimeoutSeconds)); 278 // Schedule next printer check. 279 // TODO(gene): Randomize time for the next printer update. 280 base::MessageLoop::current()->PostDelayedTask( 281 FROM_HERE, 282 base::Bind(&PrinterWatcherCUPS::PrinterUpdate, this), 283 print_system_->GetUpdateTimeout()); 284 return true; 285 } 286 287 virtual bool StopWatching() OVERRIDE{ 288 delegate_ = NULL; 289 return true; 290 } 291 292 virtual bool GetCurrentPrinterInfo( 293 printing::PrinterBasicInfo* printer_info) OVERRIDE { 294 DCHECK(printer_info); 295 return print_system_->GetPrinterInfo(printer_name_, printer_info); 296 } 297 298 void JobStatusUpdate() { 299 if (delegate_ == NULL) 300 return; // Orphan call. We have been stopped already. 301 // For CUPS proxy, we are going to fire OnJobChanged notification 302 // periodically. Higher level will check if there are any outstanding 303 // jobs for this printer and check their status. If printer has no 304 // outstanding jobs, OnJobChanged() will do nothing. 305 delegate_->OnJobChanged(); 306 base::MessageLoop::current()->PostDelayedTask( 307 FROM_HERE, 308 base::Bind(&PrinterWatcherCUPS::JobStatusUpdate, this), 309 base::TimeDelta::FromSeconds(kJobUpdateTimeoutSeconds)); 310 } 311 312 void PrinterUpdate() { 313 if (delegate_ == NULL) 314 return; // Orphan call. We have been stopped already. 315 VLOG(1) << "CP_CUPS: Checking for updates" 316 << ", printer name: " << printer_name_; 317 if (print_system_->NotifyDelete() && 318 !print_system_->IsValidPrinter(printer_name_)) { 319 delegate_->OnPrinterDeleted(); 320 VLOG(1) << "CP_CUPS: Printer deleted" 321 << ", printer name: " << printer_name_; 322 } else { 323 std::string new_hash = GetSettingsHash(); 324 if (settings_hash_ != new_hash) { 325 settings_hash_ = new_hash; 326 delegate_->OnPrinterChanged(); 327 VLOG(1) << "CP_CUPS: Printer configuration changed" 328 << ", printer name: " << printer_name_; 329 } 330 } 331 base::MessageLoop::current()->PostDelayedTask( 332 FROM_HERE, 333 base::Bind(&PrinterWatcherCUPS::PrinterUpdate, this), 334 print_system_->GetUpdateTimeout()); 335 } 336 337 protected: 338 virtual ~PrinterWatcherCUPS() { 339 StopWatching(); 340 } 341 342 private: 343 std::string GetSettingsHash() { 344 printing::PrinterBasicInfo info; 345 if (!print_system_->GetPrinterInfo(printer_name_, &info)) 346 return std::string(); 347 348 printing::PrinterCapsAndDefaults caps; 349 if (!print_system_->GetPrinterCapsAndDefaults(printer_name_, &caps)) 350 return std::string(); 351 352 std::string to_hash(info.printer_name); 353 to_hash += info.printer_description; 354 std::map<std::string, std::string>::const_iterator it; 355 for (it = info.options.begin(); it != info.options.end(); ++it) { 356 to_hash += it->first; 357 to_hash += it->second; 358 } 359 360 to_hash += caps.printer_capabilities; 361 to_hash += caps.caps_mime_type; 362 to_hash += caps.printer_defaults; 363 to_hash += caps.defaults_mime_type; 364 365 return base::MD5String(to_hash); 366 } 367 std::string printer_name_; 368 PrintSystem::PrinterWatcher::Delegate* delegate_; 369 scoped_refptr<PrintSystemCUPS> print_system_; 370 std::string settings_hash_; 371 372 DISALLOW_COPY_AND_ASSIGN(PrinterWatcherCUPS); 373 }; 374 375 class JobSpoolerCUPS : public PrintSystem::JobSpooler { 376 public: 377 explicit JobSpoolerCUPS(PrintSystemCUPS* print_system) 378 : print_system_(print_system) { 379 DCHECK(print_system_.get()); 380 } 381 382 // PrintSystem::JobSpooler implementation. 383 virtual bool Spool(const std::string& print_ticket, 384 const base::FilePath& print_data_file_path, 385 const std::string& print_data_mime_type, 386 const std::string& printer_name, 387 const std::string& job_title, 388 const std::vector<std::string>& tags, 389 JobSpooler::Delegate* delegate) OVERRIDE{ 390 DCHECK(delegate); 391 bool dry_run = false; 392 int job_id = print_system_->SpoolPrintJob( 393 print_ticket, print_data_file_path, print_data_mime_type, 394 printer_name, job_title, tags, &dry_run); 395 base::MessageLoop::current()->PostTask( 396 FROM_HERE, 397 base::Bind(&JobSpoolerCUPS::NotifyDelegate, delegate, job_id, dry_run)); 398 return true; 399 } 400 401 static void NotifyDelegate(JobSpooler::Delegate* delegate, 402 int job_id, bool dry_run) { 403 if (dry_run || job_id) 404 delegate->OnJobSpoolSucceeded(job_id); 405 else 406 delegate->OnJobSpoolFailed(); 407 } 408 409 protected: 410 virtual ~JobSpoolerCUPS() {} 411 412 private: 413 scoped_refptr<PrintSystemCUPS> print_system_; 414 415 DISALLOW_COPY_AND_ASSIGN(JobSpoolerCUPS); 416 }; 417 418 PrintSystemCUPS::PrintSystemCUPS(const DictionaryValue* print_system_settings) 419 : update_timeout_(base::TimeDelta::FromMinutes( 420 kCheckForPrinterUpdatesMinutes)), 421 initialized_(false), 422 printer_enum_succeeded_(false), 423 notify_delete_(true), 424 cups_encryption_(HTTP_ENCRYPT_NEVER), 425 supported_mime_types_(kCUPSDefaultSupportedTypes) { 426 if (print_system_settings) { 427 int timeout; 428 if (print_system_settings->GetInteger(kCUPSUpdateTimeoutMs, &timeout)) 429 update_timeout_ = base::TimeDelta::FromMilliseconds(timeout); 430 431 int encryption; 432 if (print_system_settings->GetInteger(kCUPSEncryption, &encryption)) 433 cups_encryption_ = 434 static_cast<http_encryption_t>(encryption); 435 436 bool notify_delete = true; 437 if (print_system_settings->GetBoolean(kCUPSNotifyDelete, ¬ify_delete)) 438 notify_delete_ = notify_delete; 439 440 std::string types; 441 if (print_system_settings->GetString(kCUPSSupportedMimeTipes, &types)) 442 supported_mime_types_ = types; 443 } 444 445 InitPrintBackends(print_system_settings); 446 } 447 448 void PrintSystemCUPS::InitPrintBackends( 449 const DictionaryValue* print_system_settings) { 450 const ListValue* url_list; 451 if (print_system_settings && 452 print_system_settings->GetList(kCUPSPrintServerURLs, &url_list)) { 453 for (size_t i = 0; i < url_list->GetSize(); i++) { 454 std::string print_server_url; 455 if (url_list->GetString(i, &print_server_url)) 456 AddPrintServer(print_server_url); 457 } 458 } 459 460 // If server list is empty, use default print server. 461 if (print_servers_.empty()) 462 AddPrintServer(std::string()); 463 } 464 465 void PrintSystemCUPS::AddPrintServer(const std::string& url) { 466 if (url.empty()) 467 LOG(WARNING) << "No print server specified. Using default print server."; 468 469 // Get Print backend for the specific print server. 470 DictionaryValue backend_settings; 471 backend_settings.SetString(kCUPSPrintServerURL, url); 472 473 // Make CUPS requests non-blocking. 474 backend_settings.SetString(kCUPSBlocking, kValueFalse); 475 476 // Set encryption for backend. 477 backend_settings.SetInteger(kCUPSEncryption, cups_encryption_); 478 479 PrintServerInfoCUPS print_server; 480 print_server.backend = 481 printing::PrintBackend::CreateInstance(&backend_settings); 482 print_server.url = GURL(url.c_str()); 483 484 print_servers_.push_back(print_server); 485 } 486 487 PrintSystem::PrintSystemResult PrintSystemCUPS::Init() { 488 UpdatePrinters(); 489 initialized_ = true; 490 return PrintSystemResult(true, std::string()); 491 } 492 493 void PrintSystemCUPS::UpdatePrinters() { 494 PrintServerList::iterator it; 495 printer_enum_succeeded_ = true; 496 for (it = print_servers_.begin(); it != print_servers_.end(); ++it) { 497 if (!it->backend->EnumeratePrinters(&it->printers)) 498 printer_enum_succeeded_ = false; 499 it->caps_cache.clear(); 500 printing::PrinterList::iterator printer_it; 501 for (printer_it = it->printers.begin(); 502 printer_it != it->printers.end(); ++printer_it) { 503 printer_it->printer_name = MakeFullPrinterName(it->url, 504 printer_it->printer_name); 505 } 506 VLOG(1) << "CP_CUPS: Updated printers list" 507 << ", server: " << it->url 508 << ", # of printers: " << it->printers.size(); 509 } 510 511 // Schedule next update. 512 base::MessageLoop::current()->PostDelayedTask( 513 FROM_HERE, 514 base::Bind(&PrintSystemCUPS::UpdatePrinters, this), 515 GetUpdateTimeout()); 516 } 517 518 PrintSystem::PrintSystemResult PrintSystemCUPS::EnumeratePrinters( 519 printing::PrinterList* printer_list) { 520 DCHECK(initialized_); 521 printer_list->clear(); 522 PrintServerList::iterator it; 523 for (it = print_servers_.begin(); it != print_servers_.end(); ++it) { 524 printer_list->insert(printer_list->end(), 525 it->printers.begin(), it->printers.end()); 526 } 527 VLOG(1) << "CP_CUPS: Total printers enumerated: " << printer_list->size(); 528 // TODO(sanjeevr): Maybe some day we want to report the actual server names 529 // for which the enumeration failed. 530 return PrintSystemResult(printer_enum_succeeded_, std::string()); 531 } 532 533 void PrintSystemCUPS::GetPrinterCapsAndDefaults( 534 const std::string& printer_name, 535 const PrinterCapsAndDefaultsCallback& callback) { 536 printing::PrinterCapsAndDefaults printer_info; 537 bool succeeded = GetPrinterCapsAndDefaults(printer_name, &printer_info); 538 base::MessageLoop::current()->PostTask( 539 FROM_HERE, 540 base::Bind(&PrintSystemCUPS::RunCapsCallback, 541 callback, 542 succeeded, 543 printer_name, 544 printer_info)); 545 } 546 547 bool PrintSystemCUPS::IsValidPrinter(const std::string& printer_name) { 548 return GetPrinterInfo(printer_name, NULL); 549 } 550 551 bool PrintSystemCUPS::ValidatePrintTicket(const std::string& printer_name, 552 const std::string& print_ticket_data) { 553 DCHECK(initialized_); 554 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket_data)); 555 return ticket_value != NULL && ticket_value->IsType(Value::TYPE_DICTIONARY); 556 } 557 558 // Print ticket on linux is a JSON string containing only one dictionary. 559 bool PrintSystemCUPS::ParsePrintTicket( 560 const std::string& print_ticket, 561 std::map<std::string, std::string>* options) { 562 DCHECK(options); 563 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket)); 564 if (ticket_value == NULL || !ticket_value->IsType(Value::TYPE_DICTIONARY)) 565 return false; 566 567 options->clear(); 568 DictionaryValue* ticket_dict = 569 static_cast<DictionaryValue*>(ticket_value.get()); 570 for (DictionaryValue::Iterator it(*ticket_dict); !it.IsAtEnd(); 571 it.Advance()) { 572 std::string value; 573 if (it.value().GetAsString(&value)) 574 (*options)[it.key()] = value; 575 } 576 577 return true; 578 } 579 580 bool PrintSystemCUPS::GetPrinterCapsAndDefaults( 581 const std::string& printer_name, 582 printing::PrinterCapsAndDefaults* printer_info) { 583 DCHECK(initialized_); 584 std::string short_printer_name; 585 PrintServerInfoCUPS* server_info = 586 FindServerByFullName(printer_name, &short_printer_name); 587 if (!server_info) 588 return false; 589 590 PrintServerInfoCUPS::CapsMap::iterator caps_it = 591 server_info->caps_cache.find(printer_name); 592 if (caps_it != server_info->caps_cache.end()) { 593 *printer_info = caps_it->second; 594 return true; 595 } 596 597 // TODO(gene): Retry multiple times in case of error. 598 child_process_logging::ScopedPrinterInfoSetter prn_info( 599 server_info->backend->GetPrinterDriverInfo(short_printer_name)); 600 if (!server_info->backend->GetPrinterCapsAndDefaults(short_printer_name, 601 printer_info) ) { 602 return false; 603 } 604 605 server_info->caps_cache[printer_name] = *printer_info; 606 return true; 607 } 608 609 bool PrintSystemCUPS::GetJobDetails(const std::string& printer_name, 610 PlatformJobId job_id, 611 PrintJobDetails *job_details) { 612 DCHECK(initialized_); 613 DCHECK(job_details); 614 615 std::string short_printer_name; 616 PrintServerInfoCUPS* server_info = 617 FindServerByFullName(printer_name, &short_printer_name); 618 if (!server_info) 619 return false; 620 621 child_process_logging::ScopedPrinterInfoSetter prn_info( 622 server_info->backend->GetPrinterDriverInfo(short_printer_name)); 623 cups_job_t* jobs = NULL; 624 int num_jobs = GetJobs(&jobs, server_info->url, cups_encryption_, 625 short_printer_name.c_str(), 1, -1); 626 bool error = (num_jobs == 0) && (cupsLastError() > IPP_OK_EVENTS_COMPLETE); 627 if (error) { 628 VLOG(1) << "CP_CUPS: Error getting jobs from CUPS server" 629 << ", printer name:" << printer_name 630 << ", error: " << static_cast<int>(cupsLastError()); 631 return false; 632 } 633 634 // Check if the request is for dummy dry run job. 635 // We check this after calling GetJobs API to see if this printer is actually 636 // accessible through CUPS. 637 if (job_id == kDryRunJobId) { 638 job_details->status = PRINT_JOB_STATUS_COMPLETED; 639 VLOG(1) << "CP_CUPS: Dry run job succeeded" 640 << ", printer name: " << printer_name; 641 return true; 642 } 643 644 bool found = false; 645 for (int i = 0; i < num_jobs; i++) { 646 if (jobs[i].id == job_id) { 647 found = true; 648 switch (jobs[i].state) { 649 case IPP_JOB_PENDING : 650 case IPP_JOB_HELD : 651 case IPP_JOB_PROCESSING : 652 job_details->status = PRINT_JOB_STATUS_IN_PROGRESS; 653 break; 654 case IPP_JOB_STOPPED : 655 case IPP_JOB_CANCELLED : 656 case IPP_JOB_ABORTED : 657 job_details->status = PRINT_JOB_STATUS_ERROR; 658 break; 659 case IPP_JOB_COMPLETED : 660 job_details->status = PRINT_JOB_STATUS_COMPLETED; 661 break; 662 default: 663 job_details->status = PRINT_JOB_STATUS_INVALID; 664 } 665 job_details->platform_status_flags = jobs[i].state; 666 667 // We don't have any details on the number of processed pages here. 668 break; 669 } 670 } 671 672 if (found) 673 VLOG(1) << "CP_CUPS: Job found" 674 << ", printer name: " << printer_name 675 << ", cups job id: " << job_id 676 << ", cups job status: " << job_details->status; 677 else 678 LOG(WARNING) << "CP_CUPS: Job not found" 679 << ", printer name: " << printer_name 680 << ", cups job id: " << job_id; 681 682 cupsFreeJobs(num_jobs, jobs); 683 return found; 684 } 685 686 bool PrintSystemCUPS::GetPrinterInfo(const std::string& printer_name, 687 printing::PrinterBasicInfo* info) { 688 DCHECK(initialized_); 689 if (info) 690 VLOG(1) << "CP_CUPS: Getting printer info" 691 << ", printer name: " << printer_name; 692 693 std::string short_printer_name; 694 PrintServerInfoCUPS* server_info = 695 FindServerByFullName(printer_name, &short_printer_name); 696 if (!server_info) 697 return false; 698 699 printing::PrinterList::iterator it; 700 for (it = server_info->printers.begin(); 701 it != server_info->printers.end(); ++it) { 702 if (it->printer_name == printer_name) { 703 if (info) 704 *info = *it; 705 return true; 706 } 707 } 708 return false; 709 } 710 711 PrintSystem::PrintServerWatcher* 712 PrintSystemCUPS::CreatePrintServerWatcher() { 713 DCHECK(initialized_); 714 return new PrintServerWatcherCUPS(this); 715 } 716 717 PrintSystem::PrinterWatcher* PrintSystemCUPS::CreatePrinterWatcher( 718 const std::string& printer_name) { 719 DCHECK(initialized_); 720 DCHECK(!printer_name.empty()); 721 return new PrinterWatcherCUPS(this, printer_name); 722 } 723 724 PrintSystem::JobSpooler* PrintSystemCUPS::CreateJobSpooler() { 725 DCHECK(initialized_); 726 return new JobSpoolerCUPS(this); 727 } 728 729 std::string PrintSystemCUPS::GetSupportedMimeTypes() { 730 return supported_mime_types_; 731 } 732 733 scoped_refptr<PrintSystem> PrintSystem::CreateInstance( 734 const DictionaryValue* print_system_settings) { 735 return new PrintSystemCUPS(print_system_settings); 736 } 737 738 int PrintSystemCUPS::PrintFile(const GURL& url, http_encryption_t encryption, 739 const char* name, const char* filename, 740 const char* title, int num_options, 741 cups_option_t* options) { 742 if (url.is_empty()) { // Use default (local) print server. 743 return cupsPrintFile(name, filename, title, num_options, options); 744 } else { 745 printing::HttpConnectionCUPS http(url, encryption); 746 http.SetBlocking(false); 747 return cupsPrintFile2(http.http(), name, filename, 748 title, num_options, options); 749 } 750 } 751 752 int PrintSystemCUPS::GetJobs(cups_job_t** jobs, const GURL& url, 753 http_encryption_t encryption, 754 const char* name, int myjobs, int whichjobs) { 755 if (url.is_empty()) { // Use default (local) print server. 756 return cupsGetJobs(jobs, name, myjobs, whichjobs); 757 } else { 758 printing::HttpConnectionCUPS http(url, encryption); 759 http.SetBlocking(false); 760 return cupsGetJobs2(http.http(), jobs, name, myjobs, whichjobs); 761 } 762 } 763 764 PlatformJobId PrintSystemCUPS::SpoolPrintJob( 765 const std::string& print_ticket, 766 const base::FilePath& print_data_file_path, 767 const std::string& print_data_mime_type, 768 const std::string& printer_name, 769 const std::string& job_title, 770 const std::vector<std::string>& tags, 771 bool* dry_run) { 772 DCHECK(initialized_); 773 VLOG(1) << "CP_CUPS: Spooling print job, printer name: " << printer_name; 774 775 std::string short_printer_name; 776 PrintServerInfoCUPS* server_info = 777 FindServerByFullName(printer_name, &short_printer_name); 778 if (!server_info) 779 return false; 780 781 child_process_logging::ScopedPrinterInfoSetter prn_info( 782 server_info->backend->GetPrinterDriverInfo(printer_name)); 783 784 // We need to store options as char* string for the duration of the 785 // cupsPrintFile2 call. We'll use map here to store options, since 786 // Dictionary value from JSON parser returns wchat_t. 787 std::map<std::string, std::string> options; 788 bool res = ParsePrintTicket(print_ticket, &options); 789 DCHECK(res); // If print ticket is invalid we still print using defaults. 790 791 // Check if this is a dry run (test) job. 792 *dry_run = IsDryRunJob(tags); 793 if (*dry_run) { 794 VLOG(1) << "CP_CUPS: Dry run job spooled"; 795 return kDryRunJobId; 796 } 797 798 std::vector<cups_option_t> cups_options; 799 std::map<std::string, std::string>::iterator it; 800 801 for (it = options.begin(); it != options.end(); ++it) { 802 cups_option_t opt; 803 opt.name = const_cast<char*>(it->first.c_str()); 804 opt.value = const_cast<char*>(it->second.c_str()); 805 cups_options.push_back(opt); 806 } 807 808 int job_id = PrintFile(server_info->url, 809 cups_encryption_, 810 short_printer_name.c_str(), 811 print_data_file_path.value().c_str(), 812 job_title.c_str(), 813 cups_options.size(), 814 &(cups_options[0])); 815 816 // TODO(alexyu): Output printer id. 817 VLOG(1) << "CP_CUPS: Job spooled" 818 << ", printer name: " << printer_name 819 << ", cups job id: " << job_id; 820 821 return job_id; 822 } 823 824 std::string PrintSystemCUPS::MakeFullPrinterName( 825 const GURL& url, const std::string& short_printer_name) { 826 std::string full_name; 827 full_name += "\\\\"; 828 full_name += url.host(); 829 if (!url.port().empty()) { 830 full_name += ":"; 831 full_name += url.port(); 832 } 833 full_name += "\\"; 834 full_name += short_printer_name; 835 return full_name; 836 } 837 838 PrintServerInfoCUPS* PrintSystemCUPS::FindServerByFullName( 839 const std::string& full_printer_name, std::string* short_printer_name) { 840 size_t front = full_printer_name.find("\\\\"); 841 size_t separator = full_printer_name.find("\\", 2); 842 if (front == std::string::npos || separator == std::string::npos) { 843 LOG(WARNING) << "CP_CUPS: Invalid UNC" 844 << ", printer name: " << full_printer_name; 845 return NULL; 846 } 847 std::string server = full_printer_name.substr(2, separator - 2); 848 849 PrintServerList::iterator it; 850 for (it = print_servers_.begin(); it != print_servers_.end(); ++it) { 851 std::string cur_server; 852 cur_server += it->url.host(); 853 if (!it->url.port().empty()) { 854 cur_server += ":"; 855 cur_server += it->url.port(); 856 } 857 if (cur_server == server) { 858 *short_printer_name = full_printer_name.substr(separator + 1); 859 return &(*it); 860 } 861 } 862 863 LOG(WARNING) << "CP_CUPS: Server not found" 864 << ", printer name: " << full_printer_name; 865 return NULL; 866 } 867 868 void PrintSystemCUPS::RunCapsCallback( 869 const PrinterCapsAndDefaultsCallback& callback, 870 bool succeeded, 871 const std::string& printer_name, 872 const printing::PrinterCapsAndDefaults& printer_info) { 873 callback.Run(succeeded, printer_name, printer_info); 874 } 875 876 } // namespace cloud_print 877