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/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, &notify_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