Home | History | Annotate | Download | only in cloud_print
      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, &notify_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