Avoid crashing when going back/forward to debug URLs on a sad WebUI tab.
[chromium-blink-merge.git] / chrome / service / cloud_print / print_system_cups.cc
blob86b453a1a04a73bc242af38206a85cdee6c7167b
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/location.h"
20 #include "base/logging.h"
21 #include "base/md5.h"
22 #include "base/memory/scoped_ptr.h"
23 #include "base/rand_util.h"
24 #include "base/single_thread_task_runner.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "base/strings/string_util.h"
27 #include "base/strings/utf_string_conversions.h"
28 #include "base/thread_task_runner_handle.h"
29 #include "base/values.h"
30 #include "chrome/common/cloud_print/cloud_print_constants.h"
31 #include "chrome/common/crash_keys.h"
32 #include "chrome/service/cloud_print/cloud_print_service_helpers.h"
33 #include "printing/backend/cups_helper.h"
34 #include "printing/backend/print_backend.h"
35 #include "printing/backend/print_backend_consts.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 PrintSystemResult Init() override;
82 PrintSystem::PrintSystemResult EnumeratePrinters(
83 printing::PrinterList* printer_list) override;
84 void GetPrinterCapsAndDefaults(
85 const std::string& printer_name,
86 const PrinterCapsAndDefaultsCallback& callback) override;
87 bool IsValidPrinter(const std::string& printer_name) override;
88 bool ValidatePrintTicket(const std::string& printer_name,
89 const std::string& print_ticket_data,
90 const std::string& print_ticket_mime_type) override;
91 bool GetJobDetails(const std::string& printer_name,
92 PlatformJobId job_id,
93 PrintJobDetails* job_details) override;
94 PrintSystem::PrintServerWatcher* CreatePrintServerWatcher() override;
95 PrintSystem::PrinterWatcher* CreatePrinterWatcher(
96 const std::string& printer_name) override;
97 PrintSystem::JobSpooler* CreateJobSpooler() override;
98 bool UseCddAndCjt() override;
99 std::string GetSupportedMimeTypes() override;
101 // Helper functions.
102 PlatformJobId SpoolPrintJob(const std::string& print_ticket,
103 const base::FilePath& print_data_file_path,
104 const std::string& print_data_mime_type,
105 const std::string& printer_name,
106 const std::string& job_title,
107 const std::vector<std::string>& tags,
108 bool* dry_run);
109 bool GetPrinterInfo(const std::string& printer_name,
110 printing::PrinterBasicInfo* info);
111 bool ParsePrintTicket(const std::string& print_ticket,
112 std::map<std::string, std::string>* options);
114 // Synchronous version of GetPrinterCapsAndDefaults.
115 bool GetPrinterCapsAndDefaults(
116 const std::string& printer_name,
117 printing::PrinterCapsAndDefaults* printer_info);
119 base::TimeDelta GetUpdateTimeout() const {
120 return update_timeout_;
123 bool NotifyDelete() const {
124 // Notify about deleted printers only when we
125 // fetched printers list without errors.
126 return notify_delete_ && printer_enum_succeeded_;
129 private:
130 ~PrintSystemCUPS() override {}
132 // Following functions are wrappers around corresponding CUPS functions.
133 // <functions>2() are called when print server is specified, and plain
134 // version in another case. There is an issue specifing CUPS_HTTP_DEFAULT
135 // in the <functions>2(), it does not work in CUPS prior to 1.4.
136 int GetJobs(cups_job_t** jobs, const GURL& url,
137 http_encryption_t encryption, const char* name,
138 int myjobs, int whichjobs);
139 int PrintFile(const GURL& url, http_encryption_t encryption,
140 const char* name, const char* filename,
141 const char* title, int num_options, cups_option_t* options);
143 void InitPrintBackends(const base::DictionaryValue* print_system_settings);
144 void AddPrintServer(const std::string& url);
146 void UpdatePrinters();
148 // Full name contains print server url:port and printer name. Short name
149 // is the name of the printer in the CUPS server.
150 std::string MakeFullPrinterName(const GURL& url,
151 const std::string& short_printer_name);
152 PrintServerInfoCUPS* FindServerByFullName(
153 const std::string& full_printer_name, std::string* short_printer_name);
155 // Helper method to invoke a PrinterCapsAndDefaultsCallback.
156 static void RunCapsCallback(
157 const PrinterCapsAndDefaultsCallback& callback,
158 bool succeeded,
159 const std::string& printer_name,
160 const printing::PrinterCapsAndDefaults& printer_info);
162 // PrintServerList contains information about all print servers and backends
163 // this proxy is connected to.
164 typedef std::list<PrintServerInfoCUPS> PrintServerList;
165 PrintServerList print_servers_;
167 base::TimeDelta update_timeout_;
168 bool initialized_;
169 bool printer_enum_succeeded_;
170 bool notify_delete_;
171 http_encryption_t cups_encryption_;
172 std::string supported_mime_types_;
175 class PrintServerWatcherCUPS
176 : public PrintSystem::PrintServerWatcher {
177 public:
178 explicit PrintServerWatcherCUPS(PrintSystemCUPS* print_system)
179 : print_system_(print_system),
180 delegate_(NULL) {
183 // PrintSystem::PrintServerWatcher implementation.
184 bool StartWatching(
185 PrintSystem::PrintServerWatcher::Delegate* delegate) override {
186 delegate_ = delegate;
187 printers_hash_ = GetPrintersHash();
188 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
189 FROM_HERE, base::Bind(&PrintServerWatcherCUPS::CheckForUpdates, this),
190 print_system_->GetUpdateTimeout());
191 return true;
194 bool StopWatching() override {
195 delegate_ = NULL;
196 return true;
199 void CheckForUpdates() {
200 if (delegate_ == NULL)
201 return; // Orphan call. We have been stopped already.
202 VLOG(1) << "CP_CUPS: Checking for new printers";
203 std::string new_hash = GetPrintersHash();
204 if (printers_hash_ != new_hash) {
205 printers_hash_ = new_hash;
206 delegate_->OnPrinterAdded();
208 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
209 FROM_HERE, base::Bind(&PrintServerWatcherCUPS::CheckForUpdates, this),
210 print_system_->GetUpdateTimeout());
213 protected:
214 ~PrintServerWatcherCUPS() override { StopWatching(); }
216 private:
217 std::string GetPrintersHash() {
218 printing::PrinterList printer_list;
219 print_system_->EnumeratePrinters(&printer_list);
221 // Sort printer names.
222 std::vector<std::string> printers;
223 printing::PrinterList::iterator it;
224 for (it = printer_list.begin(); it != printer_list.end(); ++it)
225 printers.push_back(it->printer_name);
226 std::sort(printers.begin(), printers.end());
228 std::string to_hash;
229 for (size_t i = 0; i < printers.size(); i++)
230 to_hash += printers[i];
232 return base::MD5String(to_hash);
235 scoped_refptr<PrintSystemCUPS> print_system_;
236 PrintSystem::PrintServerWatcher::Delegate* delegate_;
237 std::string printers_hash_;
239 DISALLOW_COPY_AND_ASSIGN(PrintServerWatcherCUPS);
242 class PrinterWatcherCUPS
243 : public PrintSystem::PrinterWatcher {
244 public:
245 PrinterWatcherCUPS(PrintSystemCUPS* print_system,
246 const std::string& printer_name)
247 : printer_name_(printer_name),
248 delegate_(NULL),
249 print_system_(print_system) {
252 // PrintSystem::PrinterWatcher implementation.
253 bool StartWatching(PrintSystem::PrinterWatcher::Delegate* delegate) override {
254 scoped_refptr<printing::PrintBackend> print_backend(
255 printing::PrintBackend::CreateInstance(NULL));
256 crash_keys::ScopedPrinterInfo crash_key(
257 print_backend->GetPrinterDriverInfo(printer_name_));
258 if (delegate_ != NULL)
259 StopWatching();
260 delegate_ = delegate;
261 settings_hash_ = GetSettingsHash();
262 // Schedule next job status update.
263 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
264 FROM_HERE, base::Bind(&PrinterWatcherCUPS::JobStatusUpdate, this),
265 base::TimeDelta::FromSeconds(kJobUpdateTimeoutSeconds));
266 // Schedule next printer check.
267 // TODO(gene): Randomize time for the next printer update.
268 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
269 FROM_HERE, base::Bind(&PrinterWatcherCUPS::PrinterUpdate, this),
270 print_system_->GetUpdateTimeout());
271 return true;
274 bool StopWatching() override {
275 delegate_ = NULL;
276 return true;
279 bool GetCurrentPrinterInfo(
280 printing::PrinterBasicInfo* printer_info) override {
281 DCHECK(printer_info);
282 return print_system_->GetPrinterInfo(printer_name_, printer_info);
285 void JobStatusUpdate() {
286 if (delegate_ == NULL)
287 return; // Orphan call. We have been stopped already.
288 // For CUPS proxy, we are going to fire OnJobChanged notification
289 // periodically. Higher level will check if there are any outstanding
290 // jobs for this printer and check their status. If printer has no
291 // outstanding jobs, OnJobChanged() will do nothing.
292 delegate_->OnJobChanged();
293 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
294 FROM_HERE, base::Bind(&PrinterWatcherCUPS::JobStatusUpdate, this),
295 base::TimeDelta::FromSeconds(kJobUpdateTimeoutSeconds));
298 void PrinterUpdate() {
299 if (delegate_ == NULL)
300 return; // Orphan call. We have been stopped already.
301 VLOG(1) << "CP_CUPS: Checking for updates"
302 << ", printer name: " << printer_name_;
303 if (print_system_->NotifyDelete() &&
304 !print_system_->IsValidPrinter(printer_name_)) {
305 delegate_->OnPrinterDeleted();
306 VLOG(1) << "CP_CUPS: Printer deleted"
307 << ", printer name: " << printer_name_;
308 } else {
309 std::string new_hash = GetSettingsHash();
310 if (settings_hash_ != new_hash) {
311 settings_hash_ = new_hash;
312 delegate_->OnPrinterChanged();
313 VLOG(1) << "CP_CUPS: Printer configuration changed"
314 << ", printer name: " << printer_name_;
317 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
318 FROM_HERE, base::Bind(&PrinterWatcherCUPS::PrinterUpdate, this),
319 print_system_->GetUpdateTimeout());
322 protected:
323 ~PrinterWatcherCUPS() override { StopWatching(); }
325 private:
326 std::string GetSettingsHash() {
327 printing::PrinterBasicInfo info;
328 if (!print_system_->GetPrinterInfo(printer_name_, &info))
329 return std::string();
331 printing::PrinterCapsAndDefaults caps;
332 if (!print_system_->GetPrinterCapsAndDefaults(printer_name_, &caps))
333 return std::string();
335 std::string to_hash(info.printer_name);
336 to_hash += info.printer_description;
337 std::map<std::string, std::string>::const_iterator it;
338 for (it = info.options.begin(); it != info.options.end(); ++it) {
339 to_hash += it->first;
340 to_hash += it->second;
343 to_hash += caps.printer_capabilities;
344 to_hash += caps.caps_mime_type;
345 to_hash += caps.printer_defaults;
346 to_hash += caps.defaults_mime_type;
348 return base::MD5String(to_hash);
350 std::string printer_name_;
351 PrintSystem::PrinterWatcher::Delegate* delegate_;
352 scoped_refptr<PrintSystemCUPS> print_system_;
353 std::string settings_hash_;
355 DISALLOW_COPY_AND_ASSIGN(PrinterWatcherCUPS);
358 class JobSpoolerCUPS : public PrintSystem::JobSpooler {
359 public:
360 explicit JobSpoolerCUPS(PrintSystemCUPS* print_system)
361 : print_system_(print_system) {
362 DCHECK(print_system_.get());
365 // PrintSystem::JobSpooler implementation.
366 bool Spool(const std::string& print_ticket,
367 const std::string& print_ticket_mime_type,
368 const base::FilePath& print_data_file_path,
369 const std::string& print_data_mime_type,
370 const std::string& printer_name,
371 const std::string& job_title,
372 const std::vector<std::string>& tags,
373 JobSpooler::Delegate* delegate) override {
374 DCHECK(delegate);
375 bool dry_run = false;
376 int job_id = print_system_->SpoolPrintJob(
377 print_ticket, print_data_file_path, print_data_mime_type,
378 printer_name, job_title, tags, &dry_run);
379 base::ThreadTaskRunnerHandle::Get()->PostTask(
380 FROM_HERE,
381 base::Bind(&JobSpoolerCUPS::NotifyDelegate, delegate, job_id, dry_run));
382 return true;
385 static void NotifyDelegate(JobSpooler::Delegate* delegate,
386 int job_id, bool dry_run) {
387 if (dry_run || job_id)
388 delegate->OnJobSpoolSucceeded(job_id);
389 else
390 delegate->OnJobSpoolFailed();
393 protected:
394 ~JobSpoolerCUPS() override {}
396 private:
397 scoped_refptr<PrintSystemCUPS> print_system_;
399 DISALLOW_COPY_AND_ASSIGN(JobSpoolerCUPS);
402 PrintSystemCUPS::PrintSystemCUPS(
403 const base::DictionaryValue* print_system_settings)
404 : update_timeout_(base::TimeDelta::FromMinutes(
405 kCheckForPrinterUpdatesMinutes)),
406 initialized_(false),
407 printer_enum_succeeded_(false),
408 notify_delete_(true),
409 cups_encryption_(HTTP_ENCRYPT_NEVER),
410 supported_mime_types_(kCUPSDefaultSupportedTypes) {
411 if (print_system_settings) {
412 int timeout;
413 if (print_system_settings->GetInteger(kCUPSUpdateTimeoutMs, &timeout))
414 update_timeout_ = base::TimeDelta::FromMilliseconds(timeout);
416 int encryption;
417 if (print_system_settings->GetInteger(kCUPSEncryption, &encryption))
418 cups_encryption_ =
419 static_cast<http_encryption_t>(encryption);
421 bool notify_delete = true;
422 if (print_system_settings->GetBoolean(kCUPSNotifyDelete, &notify_delete))
423 notify_delete_ = notify_delete;
425 std::string types;
426 if (print_system_settings->GetString(kCUPSSupportedMimeTipes, &types))
427 supported_mime_types_ = types;
430 InitPrintBackends(print_system_settings);
433 void PrintSystemCUPS::InitPrintBackends(
434 const base::DictionaryValue* print_system_settings) {
435 const base::ListValue* url_list;
436 if (print_system_settings &&
437 print_system_settings->GetList(kCUPSPrintServerURLs, &url_list)) {
438 for (size_t i = 0; i < url_list->GetSize(); i++) {
439 std::string print_server_url;
440 if (url_list->GetString(i, &print_server_url))
441 AddPrintServer(print_server_url);
445 // If server list is empty, use default print server.
446 if (print_servers_.empty())
447 AddPrintServer(std::string());
450 void PrintSystemCUPS::AddPrintServer(const std::string& url) {
451 if (url.empty())
452 LOG(WARNING) << "No print server specified. Using default print server.";
454 // Get Print backend for the specific print server.
455 base::DictionaryValue backend_settings;
456 backend_settings.SetString(kCUPSPrintServerURL, url);
458 // Make CUPS requests non-blocking.
459 backend_settings.SetString(kCUPSBlocking, kValueFalse);
461 // Set encryption for backend.
462 backend_settings.SetInteger(kCUPSEncryption, cups_encryption_);
464 PrintServerInfoCUPS print_server;
465 print_server.backend =
466 printing::PrintBackend::CreateInstance(&backend_settings);
467 print_server.url = GURL(url.c_str());
469 print_servers_.push_back(print_server);
472 PrintSystem::PrintSystemResult PrintSystemCUPS::Init() {
473 UpdatePrinters();
474 initialized_ = true;
475 return PrintSystemResult(true, std::string());
478 void PrintSystemCUPS::UpdatePrinters() {
479 PrintServerList::iterator it;
480 printer_enum_succeeded_ = true;
481 for (it = print_servers_.begin(); it != print_servers_.end(); ++it) {
482 if (!it->backend->EnumeratePrinters(&it->printers))
483 printer_enum_succeeded_ = false;
484 it->caps_cache.clear();
485 printing::PrinterList::iterator printer_it;
486 for (printer_it = it->printers.begin();
487 printer_it != it->printers.end(); ++printer_it) {
488 printer_it->printer_name = MakeFullPrinterName(it->url,
489 printer_it->printer_name);
491 VLOG(1) << "CP_CUPS: Updated printers list"
492 << ", server: " << it->url
493 << ", # of printers: " << it->printers.size();
496 // Schedule next update.
497 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
498 FROM_HERE, base::Bind(&PrintSystemCUPS::UpdatePrinters, this),
499 GetUpdateTimeout());
502 PrintSystem::PrintSystemResult PrintSystemCUPS::EnumeratePrinters(
503 printing::PrinterList* printer_list) {
504 DCHECK(initialized_);
505 printer_list->clear();
506 PrintServerList::iterator it;
507 for (it = print_servers_.begin(); it != print_servers_.end(); ++it) {
508 printer_list->insert(printer_list->end(),
509 it->printers.begin(), it->printers.end());
511 VLOG(1) << "CP_CUPS: Total printers enumerated: " << printer_list->size();
512 // TODO(sanjeevr): Maybe some day we want to report the actual server names
513 // for which the enumeration failed.
514 return PrintSystemResult(printer_enum_succeeded_, std::string());
517 void PrintSystemCUPS::GetPrinterCapsAndDefaults(
518 const std::string& printer_name,
519 const PrinterCapsAndDefaultsCallback& callback) {
520 printing::PrinterCapsAndDefaults printer_info;
521 bool succeeded = GetPrinterCapsAndDefaults(printer_name, &printer_info);
522 base::ThreadTaskRunnerHandle::Get()->PostTask(
523 FROM_HERE, base::Bind(&PrintSystemCUPS::RunCapsCallback, callback,
524 succeeded, printer_name, printer_info));
527 bool PrintSystemCUPS::IsValidPrinter(const std::string& printer_name) {
528 return GetPrinterInfo(printer_name, NULL);
531 bool PrintSystemCUPS::ValidatePrintTicket(
532 const std::string& printer_name,
533 const std::string& print_ticket_data,
534 const std::string& print_ticket_mime_type) {
535 DCHECK(initialized_);
536 scoped_ptr<base::Value> ticket_value(
537 base::JSONReader::Read(print_ticket_data));
538 return ticket_value != NULL &&
539 ticket_value->IsType(base::Value::TYPE_DICTIONARY);
542 // Print ticket on linux is a JSON string containing only one dictionary.
543 bool PrintSystemCUPS::ParsePrintTicket(
544 const std::string& print_ticket,
545 std::map<std::string, std::string>* options) {
546 DCHECK(options);
547 scoped_ptr<base::Value> ticket_value(base::JSONReader::Read(print_ticket));
548 if (ticket_value == NULL ||
549 !ticket_value->IsType(base::Value::TYPE_DICTIONARY)) {
550 return false;
553 options->clear();
554 base::DictionaryValue* ticket_dict =
555 static_cast<base::DictionaryValue*>(ticket_value.get());
556 for (base::DictionaryValue::Iterator it(*ticket_dict); !it.IsAtEnd();
557 it.Advance()) {
558 std::string value;
559 if (it.value().GetAsString(&value))
560 (*options)[it.key()] = value;
563 return true;
566 bool PrintSystemCUPS::GetPrinterCapsAndDefaults(
567 const std::string& printer_name,
568 printing::PrinterCapsAndDefaults* printer_info) {
569 DCHECK(initialized_);
570 std::string short_printer_name;
571 PrintServerInfoCUPS* server_info =
572 FindServerByFullName(printer_name, &short_printer_name);
573 if (!server_info)
574 return false;
576 PrintServerInfoCUPS::CapsMap::iterator caps_it =
577 server_info->caps_cache.find(printer_name);
578 if (caps_it != server_info->caps_cache.end()) {
579 *printer_info = caps_it->second;
580 return true;
583 // TODO(gene): Retry multiple times in case of error.
584 crash_keys::ScopedPrinterInfo crash_key(
585 server_info->backend->GetPrinterDriverInfo(short_printer_name));
586 if (!server_info->backend->GetPrinterCapsAndDefaults(short_printer_name,
587 printer_info) ) {
588 return false;
591 server_info->caps_cache[printer_name] = *printer_info;
592 return true;
595 bool PrintSystemCUPS::GetJobDetails(const std::string& printer_name,
596 PlatformJobId job_id,
597 PrintJobDetails *job_details) {
598 DCHECK(initialized_);
599 DCHECK(job_details);
601 std::string short_printer_name;
602 PrintServerInfoCUPS* server_info =
603 FindServerByFullName(printer_name, &short_printer_name);
604 if (!server_info)
605 return false;
607 crash_keys::ScopedPrinterInfo crash_key(
608 server_info->backend->GetPrinterDriverInfo(short_printer_name));
609 cups_job_t* jobs = NULL;
610 int num_jobs = GetJobs(&jobs, server_info->url, cups_encryption_,
611 short_printer_name.c_str(), 1, -1);
612 bool error = (num_jobs == 0) && (cupsLastError() > IPP_OK_EVENTS_COMPLETE);
613 if (error) {
614 VLOG(1) << "CP_CUPS: Error getting jobs from CUPS server"
615 << ", printer name:" << printer_name
616 << ", error: " << static_cast<int>(cupsLastError());
617 return false;
620 // Check if the request is for dummy dry run job.
621 // We check this after calling GetJobs API to see if this printer is actually
622 // accessible through CUPS.
623 if (job_id == kDryRunJobId) {
624 job_details->status = PRINT_JOB_STATUS_COMPLETED;
625 VLOG(1) << "CP_CUPS: Dry run job succeeded"
626 << ", printer name: " << printer_name;
627 return true;
630 bool found = false;
631 for (int i = 0; i < num_jobs; i++) {
632 if (jobs[i].id == job_id) {
633 found = true;
634 switch (jobs[i].state) {
635 case IPP_JOB_PENDING :
636 case IPP_JOB_HELD :
637 case IPP_JOB_PROCESSING :
638 job_details->status = PRINT_JOB_STATUS_IN_PROGRESS;
639 break;
640 case IPP_JOB_STOPPED :
641 case IPP_JOB_CANCELLED :
642 case IPP_JOB_ABORTED :
643 job_details->status = PRINT_JOB_STATUS_ERROR;
644 break;
645 case IPP_JOB_COMPLETED :
646 job_details->status = PRINT_JOB_STATUS_COMPLETED;
647 break;
648 default:
649 job_details->status = PRINT_JOB_STATUS_INVALID;
651 job_details->platform_status_flags = jobs[i].state;
653 // We don't have any details on the number of processed pages here.
654 break;
658 if (found)
659 VLOG(1) << "CP_CUPS: Job found"
660 << ", printer name: " << printer_name
661 << ", cups job id: " << job_id
662 << ", cups job status: " << job_details->status;
663 else
664 LOG(WARNING) << "CP_CUPS: Job not found"
665 << ", printer name: " << printer_name
666 << ", cups job id: " << job_id;
668 cupsFreeJobs(num_jobs, jobs);
669 return found;
672 bool PrintSystemCUPS::GetPrinterInfo(const std::string& printer_name,
673 printing::PrinterBasicInfo* info) {
674 DCHECK(initialized_);
675 if (info)
676 VLOG(1) << "CP_CUPS: Getting printer info"
677 << ", printer name: " << printer_name;
679 std::string short_printer_name;
680 PrintServerInfoCUPS* server_info =
681 FindServerByFullName(printer_name, &short_printer_name);
682 if (!server_info)
683 return false;
685 printing::PrinterList::iterator it;
686 for (it = server_info->printers.begin();
687 it != server_info->printers.end(); ++it) {
688 if (it->printer_name == printer_name) {
689 if (info)
690 *info = *it;
691 return true;
694 return false;
697 PrintSystem::PrintServerWatcher*
698 PrintSystemCUPS::CreatePrintServerWatcher() {
699 DCHECK(initialized_);
700 return new PrintServerWatcherCUPS(this);
703 PrintSystem::PrinterWatcher* PrintSystemCUPS::CreatePrinterWatcher(
704 const std::string& printer_name) {
705 DCHECK(initialized_);
706 DCHECK(!printer_name.empty());
707 return new PrinterWatcherCUPS(this, printer_name);
710 PrintSystem::JobSpooler* PrintSystemCUPS::CreateJobSpooler() {
711 DCHECK(initialized_);
712 return new JobSpoolerCUPS(this);
715 bool PrintSystemCUPS::UseCddAndCjt() {
716 return false;
719 std::string PrintSystemCUPS::GetSupportedMimeTypes() {
720 return supported_mime_types_;
723 scoped_refptr<PrintSystem> PrintSystem::CreateInstance(
724 const base::DictionaryValue* print_system_settings) {
725 return new PrintSystemCUPS(print_system_settings);
728 int PrintSystemCUPS::PrintFile(const GURL& url, http_encryption_t encryption,
729 const char* name, const char* filename,
730 const char* title, int num_options,
731 cups_option_t* options) {
732 if (url.is_empty()) { // Use default (local) print server.
733 return cupsPrintFile(name, filename, title, num_options, options);
734 } else {
735 printing::HttpConnectionCUPS http(url, encryption);
736 http.SetBlocking(false);
737 return cupsPrintFile2(http.http(), name, filename,
738 title, num_options, options);
742 int PrintSystemCUPS::GetJobs(cups_job_t** jobs, const GURL& url,
743 http_encryption_t encryption,
744 const char* name, int myjobs, int whichjobs) {
745 if (url.is_empty()) { // Use default (local) print server.
746 return cupsGetJobs(jobs, name, myjobs, whichjobs);
747 } else {
748 printing::HttpConnectionCUPS http(url, encryption);
749 http.SetBlocking(false);
750 return cupsGetJobs2(http.http(), jobs, name, myjobs, whichjobs);
754 PlatformJobId PrintSystemCUPS::SpoolPrintJob(
755 const std::string& print_ticket,
756 const base::FilePath& print_data_file_path,
757 const std::string& print_data_mime_type,
758 const std::string& printer_name,
759 const std::string& job_title,
760 const std::vector<std::string>& tags,
761 bool* dry_run) {
762 DCHECK(initialized_);
763 VLOG(1) << "CP_CUPS: Spooling print job, printer name: " << printer_name;
765 std::string short_printer_name;
766 PrintServerInfoCUPS* server_info =
767 FindServerByFullName(printer_name, &short_printer_name);
768 if (!server_info)
769 return false;
771 crash_keys::ScopedPrinterInfo crash_key(
772 server_info->backend->GetPrinterDriverInfo(printer_name));
774 // We need to store options as char* string for the duration of the
775 // cupsPrintFile2 call. We'll use map here to store options, since
776 // Dictionary value from JSON parser returns wchat_t.
777 std::map<std::string, std::string> options;
778 bool res = ParsePrintTicket(print_ticket, &options);
779 DCHECK(res); // If print ticket is invalid we still print using defaults.
781 // Check if this is a dry run (test) job.
782 *dry_run = IsDryRunJob(tags);
783 if (*dry_run) {
784 VLOG(1) << "CP_CUPS: Dry run job spooled";
785 return kDryRunJobId;
788 std::vector<cups_option_t> cups_options;
789 std::map<std::string, std::string>::iterator it;
791 for (it = options.begin(); it != options.end(); ++it) {
792 cups_option_t opt;
793 opt.name = const_cast<char*>(it->first.c_str());
794 opt.value = const_cast<char*>(it->second.c_str());
795 cups_options.push_back(opt);
798 int job_id = PrintFile(server_info->url,
799 cups_encryption_,
800 short_printer_name.c_str(),
801 print_data_file_path.value().c_str(),
802 job_title.c_str(),
803 cups_options.size(),
804 &(cups_options[0]));
806 // TODO(alexyu): Output printer id.
807 VLOG(1) << "CP_CUPS: Job spooled"
808 << ", printer name: " << printer_name
809 << ", cups job id: " << job_id;
811 return job_id;
814 std::string PrintSystemCUPS::MakeFullPrinterName(
815 const GURL& url, const std::string& short_printer_name) {
816 std::string full_name;
817 full_name += "\\\\";
818 full_name += url.host();
819 if (!url.port().empty()) {
820 full_name += ":";
821 full_name += url.port();
823 full_name += "\\";
824 full_name += short_printer_name;
825 return full_name;
828 PrintServerInfoCUPS* PrintSystemCUPS::FindServerByFullName(
829 const std::string& full_printer_name, std::string* short_printer_name) {
830 size_t front = full_printer_name.find("\\\\");
831 size_t separator = full_printer_name.find("\\", 2);
832 if (front == std::string::npos || separator == std::string::npos) {
833 LOG(WARNING) << "CP_CUPS: Invalid UNC"
834 << ", printer name: " << full_printer_name;
835 return NULL;
837 std::string server = full_printer_name.substr(2, separator - 2);
839 PrintServerList::iterator it;
840 for (it = print_servers_.begin(); it != print_servers_.end(); ++it) {
841 std::string cur_server;
842 cur_server += it->url.host();
843 if (!it->url.port().empty()) {
844 cur_server += ":";
845 cur_server += it->url.port();
847 if (cur_server == server) {
848 *short_printer_name = full_printer_name.substr(separator + 1);
849 return &(*it);
853 LOG(WARNING) << "CP_CUPS: Server not found"
854 << ", printer name: " << full_printer_name;
855 return NULL;
858 void PrintSystemCUPS::RunCapsCallback(
859 const PrinterCapsAndDefaultsCallback& callback,
860 bool succeeded,
861 const std::string& printer_name,
862 const printing::PrinterCapsAndDefaults& printer_info) {
863 callback.Run(succeeded, printer_name, printer_info);
866 } // namespace cloud_print