Escape the ampersand in extension names before showing in menu.
[chromium-blink-merge.git] / chrome / service / cloud_print / print_system_cups.cc
blob9b3ec232d4ebb58c1f4cbc1e524b4357fe5e4344
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.
5 #include "chrome/service/cloud_print/print_system.h"
7 #include <cups/cups.h>
8 #include <dlfcn.h>
9 #include <errno.h>
10 #include <pthread.h>
12 #include <algorithm>
13 #include <list>
14 #include <map>
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"
38 namespace {
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";
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";
51 // Time interval to check for printer's updates.
52 const int kCheckForPrinterUpdatesMinutes = 5;
54 // Job update timeout
55 const int kJobUpdateTimeoutSeconds = 5;
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;
61 } // namespace
63 namespace cloud_print {
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;
76 class PrintSystemCUPS : public PrintSystem {
77 public:
78 explicit PrintSystemCUPS(const base::DictionaryValue* print_system_settings);
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;
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);
115 // Synchronous version of GetPrinterCapsAndDefaults.
116 bool GetPrinterCapsAndDefaults(
117 const std::string& printer_name,
118 printing::PrinterCapsAndDefaults* printer_info);
120 base::TimeDelta GetUpdateTimeout() const {
121 return update_timeout_;
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_;
130 private:
131 virtual ~PrintSystemCUPS() {}
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);
144 void InitPrintBackends(const base::DictionaryValue* print_system_settings);
145 void AddPrintServer(const std::string& url);
147 void UpdatePrinters();
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);
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);
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_;
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_;
176 class PrintServerWatcherCUPS
177 : public PrintSystem::PrintServerWatcher {
178 public:
179 explicit PrintServerWatcherCUPS(PrintSystemCUPS* print_system)
180 : print_system_(print_system),
181 delegate_(NULL) {
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;
196 virtual bool StopWatching() OVERRIDE {
197 delegate_ = NULL;
198 return true;
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();
210 base::MessageLoop::current()->PostDelayedTask(
211 FROM_HERE,
212 base::Bind(&PrintServerWatcherCUPS::CheckForUpdates, this),
213 print_system_->GetUpdateTimeout());
216 protected:
217 virtual ~PrintServerWatcherCUPS() {
218 StopWatching();
221 private:
222 std::string GetPrintersHash() {
223 printing::PrinterList printer_list;
224 print_system_->EnumeratePrinters(&printer_list);
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());
233 std::string to_hash;
234 for (size_t i = 0; i < printers.size(); i++)
235 to_hash += printers[i];
237 return base::MD5String(to_hash);
240 scoped_refptr<PrintSystemCUPS> print_system_;
241 PrintSystem::PrintServerWatcher::Delegate* delegate_;
242 std::string printers_hash_;
244 DISALLOW_COPY_AND_ASSIGN(PrintServerWatcherCUPS);
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) {
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;
282 virtual bool StopWatching() OVERRIDE{
283 delegate_ = NULL;
284 return true;
287 virtual bool GetCurrentPrinterInfo(
288 printing::PrinterBasicInfo* printer_info) OVERRIDE {
289 DCHECK(printer_info);
290 return print_system_->GetPrinterInfo(printer_name_, printer_info);
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));
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_;
326 base::MessageLoop::current()->PostDelayedTask(
327 FROM_HERE,
328 base::Bind(&PrinterWatcherCUPS::PrinterUpdate, this),
329 print_system_->GetUpdateTimeout());
332 protected:
333 virtual ~PrinterWatcherCUPS() {
334 StopWatching();
337 private:
338 std::string GetSettingsHash() {
339 printing::PrinterBasicInfo info;
340 if (!print_system_->GetPrinterInfo(printer_name_, &info))
341 return std::string();
343 printing::PrinterCapsAndDefaults caps;
344 if (!print_system_->GetPrinterCapsAndDefaults(printer_name_, &caps))
345 return std::string();
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;
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;
360 return base::MD5String(to_hash);
362 std::string printer_name_;
363 PrintSystem::PrinterWatcher::Delegate* delegate_;
364 scoped_refptr<PrintSystemCUPS> print_system_;
365 std::string settings_hash_;
367 DISALLOW_COPY_AND_ASSIGN(PrinterWatcherCUPS);
370 class JobSpoolerCUPS : public PrintSystem::JobSpooler {
371 public:
372 explicit JobSpoolerCUPS(PrintSystemCUPS* print_system)
373 : print_system_(print_system) {
374 DCHECK(print_system_.get());
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;
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();
405 protected:
406 virtual ~JobSpoolerCUPS() {}
408 private:
409 scoped_refptr<PrintSystemCUPS> print_system_;
411 DISALLOW_COPY_AND_ASSIGN(JobSpoolerCUPS);
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);
428 int encryption;
429 if (print_system_settings->GetInteger(kCUPSEncryption, &encryption))
430 cups_encryption_ =
431 static_cast<http_encryption_t>(encryption);
433 bool notify_delete = true;
434 if (print_system_settings->GetBoolean(kCUPSNotifyDelete, &notify_delete))
435 notify_delete_ = notify_delete;
437 std::string types;
438 if (print_system_settings->GetString(kCUPSSupportedMimeTipes, &types))
439 supported_mime_types_ = types;
442 InitPrintBackends(print_system_settings);
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);
457 // If server list is empty, use default print server.
458 if (print_servers_.empty())
459 AddPrintServer(std::string());
462 void PrintSystemCUPS::AddPrintServer(const std::string& url) {
463 if (url.empty())
464 LOG(WARNING) << "No print server specified. Using default print server.";
466 // Get Print backend for the specific print server.
467 base::DictionaryValue backend_settings;
468 backend_settings.SetString(kCUPSPrintServerURL, url);
470 // Make CUPS requests non-blocking.
471 backend_settings.SetString(kCUPSBlocking, kValueFalse);
473 // Set encryption for backend.
474 backend_settings.SetInteger(kCUPSEncryption, cups_encryption_);
476 PrintServerInfoCUPS print_server;
477 print_server.backend =
478 printing::PrintBackend::CreateInstance(&backend_settings);
479 print_server.url = GURL(url.c_str());
481 print_servers_.push_back(print_server);
484 PrintSystem::PrintSystemResult PrintSystemCUPS::Init() {
485 UpdatePrinters();
486 initialized_ = true;
487 return PrintSystemResult(true, std::string());
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);
503 VLOG(1) << "CP_CUPS: Updated printers list"
504 << ", server: " << it->url
505 << ", # of printers: " << it->printers.size();
508 // Schedule next update.
509 base::MessageLoop::current()->PostDelayedTask(
510 FROM_HERE,
511 base::Bind(&PrintSystemCUPS::UpdatePrinters, this),
512 GetUpdateTimeout());
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());
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());
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));
544 bool PrintSystemCUPS::IsValidPrinter(const std::string& printer_name) {
545 return GetPrinterInfo(printer_name, NULL);
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);
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;
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;
580 return true;
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;
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;
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;
608 server_info->caps_cache[printer_name] = *printer_info;
609 return true;
612 bool PrintSystemCUPS::GetJobDetails(const std::string& printer_name,
613 PlatformJobId job_id,
614 PrintJobDetails *job_details) {
615 DCHECK(initialized_);
616 DCHECK(job_details);
618 std::string short_printer_name;
619 PrintServerInfoCUPS* server_info =
620 FindServerByFullName(printer_name, &short_printer_name);
621 if (!server_info)
622 return false;
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;
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;
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;
668 job_details->platform_status_flags = jobs[i].state;
670 // We don't have any details on the number of processed pages here.
671 break;
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;
685 cupsFreeJobs(num_jobs, jobs);
686 return found;
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;
696 std::string short_printer_name;
697 PrintServerInfoCUPS* server_info =
698 FindServerByFullName(printer_name, &short_printer_name);
699 if (!server_info)
700 return false;
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;
711 return false;
714 PrintSystem::PrintServerWatcher*
715 PrintSystemCUPS::CreatePrintServerWatcher() {
716 DCHECK(initialized_);
717 return new PrintServerWatcherCUPS(this);
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);
727 PrintSystem::JobSpooler* PrintSystemCUPS::CreateJobSpooler() {
728 DCHECK(initialized_);
729 return new JobSpoolerCUPS(this);
732 bool PrintSystemCUPS::UseCddAndCjt() {
733 return false;
736 std::string PrintSystemCUPS::GetSupportedMimeTypes() {
737 return supported_mime_types_;
740 scoped_refptr<PrintSystem> PrintSystem::CreateInstance(
741 const base::DictionaryValue* print_system_settings) {
742 return new PrintSystemCUPS(print_system_settings);
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);
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);
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;
782 std::string short_printer_name;
783 PrintServerInfoCUPS* server_info =
784 FindServerByFullName(printer_name, &short_printer_name);
785 if (!server_info)
786 return false;
788 crash_keys::ScopedPrinterInfo crash_key(
789 server_info->backend->GetPrinterDriverInfo(printer_name));
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.
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;
805 std::vector<cups_option_t> cups_options;
806 std::map<std::string, std::string>::iterator it;
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);
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]));
823 // TODO(alexyu): Output printer id.
824 VLOG(1) << "CP_CUPS: Job spooled"
825 << ", printer name: " << printer_name
826 << ", cups job id: " << job_id;
828 return job_id;
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();
840 full_name += "\\";
841 full_name += short_printer_name;
842 return full_name;
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;
854 std::string server = full_printer_name.substr(2, separator - 2);
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();
864 if (cur_server == server) {
865 *short_printer_name = full_printer_name.substr(separator + 1);
866 return &(*it);
870 LOG(WARNING) << "CP_CUPS: Server not found"
871 << ", printer name: " << full_printer_name;
872 return NULL;
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);
883 } // namespace cloud_print