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