Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / service / cloud_print / print_system_cups.cc
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