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