Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / chrome / service / cloud_print / print_system_cups.cc
blob132a252b1fced997d70c4d3c48fdf7a751062b0b
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 "printing/backend/cups_helper.h"
32 #include "printing/backend/print_backend.h"
33 #include "printing/backend/print_backend_consts.h"
34 #include "url/gurl.h"
36 namespace {
38 // Print system config options.
39 const char kCUPSPrintServerURLs[] = "print_server_urls";
40 const char kCUPSUpdateTimeoutMs[] = "update_timeout_ms";
41 const char kCUPSNotifyDelete[] = "notify_delete";
42 const char kCUPSSupportedMimeTipes[] = "supported_mime_types";
44 // Default mime types supported by CUPS
45 // http://www.cups.org/articles.php?L205+TFAQ+Q
46 const char kCUPSDefaultSupportedTypes[] =
47 "application/pdf,application/postscript,image/jpeg,image/png,image/gif";
49 // Time interval to check for printer's updates.
50 const int kCheckForPrinterUpdatesMinutes = 5;
52 // Job update timeout
53 const int kJobUpdateTimeoutSeconds = 5;
55 // Job id for dry run (it should not affect CUPS job ids, since 0 job-id is
56 // invalid in CUPS.
57 const int kDryRunJobId = 0;
59 } // namespace
61 namespace cloud_print {
63 struct PrintServerInfoCUPS {
64 GURL url;
65 scoped_refptr<printing::PrintBackend> backend;
66 printing::PrinterList printers;
67 // CapsMap cache PPD until the next update and give a fast access to it by
68 // printer name. PPD request is relatively expensive and this should minimize
69 // the number of requests.
70 typedef std::map<std::string, printing::PrinterCapsAndDefaults> CapsMap;
71 CapsMap caps_cache;
74 class PrintSystemCUPS : public PrintSystem {
75 public:
76 explicit PrintSystemCUPS(const base::DictionaryValue* print_system_settings);
78 // PrintSystem implementation.
79 PrintSystemResult Init() override;
80 PrintSystem::PrintSystemResult EnumeratePrinters(
81 printing::PrinterList* printer_list) override;
82 void GetPrinterCapsAndDefaults(
83 const std::string& printer_name,
84 const PrinterCapsAndDefaultsCallback& callback) override;
85 bool IsValidPrinter(const std::string& printer_name) override;
86 bool ValidatePrintTicket(const std::string& printer_name,
87 const std::string& print_ticket_data,
88 const std::string& print_ticket_mime_type) override;
89 bool GetJobDetails(const std::string& printer_name,
90 PlatformJobId job_id,
91 PrintJobDetails* job_details) override;
92 PrintSystem::PrintServerWatcher* CreatePrintServerWatcher() override;
93 PrintSystem::PrinterWatcher* CreatePrinterWatcher(
94 const std::string& printer_name) override;
95 PrintSystem::JobSpooler* CreateJobSpooler() override;
96 bool UseCddAndCjt() override;
97 std::string GetSupportedMimeTypes() override;
99 // Helper functions.
100 PlatformJobId SpoolPrintJob(const std::string& print_ticket,
101 const base::FilePath& print_data_file_path,
102 const std::string& print_data_mime_type,
103 const std::string& printer_name,
104 const std::string& job_title,
105 const std::vector<std::string>& tags,
106 bool* dry_run);
107 bool GetPrinterInfo(const std::string& printer_name,
108 printing::PrinterBasicInfo* info);
109 bool ParsePrintTicket(const std::string& print_ticket,
110 std::map<std::string, std::string>* options);
112 // Synchronous version of GetPrinterCapsAndDefaults.
113 bool GetPrinterCapsAndDefaults(
114 const std::string& printer_name,
115 printing::PrinterCapsAndDefaults* printer_info);
117 base::TimeDelta GetUpdateTimeout() const {
118 return update_timeout_;
121 bool NotifyDelete() const {
122 // Notify about deleted printers only when we
123 // fetched printers list without errors.
124 return notify_delete_ && printer_enum_succeeded_;
127 private:
128 ~PrintSystemCUPS() override {}
130 // Following functions are wrappers around corresponding CUPS functions.
131 // <functions>2() are called when print server is specified, and plain
132 // version in another case. There is an issue specifing CUPS_HTTP_DEFAULT
133 // in the <functions>2(), it does not work in CUPS prior to 1.4.
134 int GetJobs(cups_job_t** jobs, const GURL& url,
135 http_encryption_t encryption, const char* name,
136 int myjobs, int whichjobs);
137 int PrintFile(const GURL& url, http_encryption_t encryption,
138 const char* name, const char* filename,
139 const char* title, int num_options, cups_option_t* options);
141 void InitPrintBackends(const base::DictionaryValue* print_system_settings);
142 void AddPrintServer(const std::string& url);
144 void UpdatePrinters();
146 // Full name contains print server url:port and printer name. Short name
147 // is the name of the printer in the CUPS server.
148 std::string MakeFullPrinterName(const GURL& url,
149 const std::string& short_printer_name);
150 PrintServerInfoCUPS* FindServerByFullName(
151 const std::string& full_printer_name, std::string* short_printer_name);
153 // Helper method to invoke a PrinterCapsAndDefaultsCallback.
154 static void RunCapsCallback(
155 const PrinterCapsAndDefaultsCallback& callback,
156 bool succeeded,
157 const std::string& printer_name,
158 const printing::PrinterCapsAndDefaults& printer_info);
160 // PrintServerList contains information about all print servers and backends
161 // this proxy is connected to.
162 typedef std::list<PrintServerInfoCUPS> PrintServerList;
163 PrintServerList print_servers_;
165 base::TimeDelta update_timeout_;
166 bool initialized_;
167 bool printer_enum_succeeded_;
168 bool notify_delete_;
169 http_encryption_t cups_encryption_;
170 std::string supported_mime_types_;
173 class PrintServerWatcherCUPS
174 : public PrintSystem::PrintServerWatcher {
175 public:
176 explicit PrintServerWatcherCUPS(PrintSystemCUPS* print_system)
177 : print_system_(print_system),
178 delegate_(NULL) {
181 // PrintSystem::PrintServerWatcher implementation.
182 bool StartWatching(
183 PrintSystem::PrintServerWatcher::Delegate* delegate) override {
184 delegate_ = delegate;
185 printers_hash_ = GetPrintersHash();
186 base::MessageLoop::current()->PostDelayedTask(
187 FROM_HERE,
188 base::Bind(&PrintServerWatcherCUPS::CheckForUpdates, this),
189 print_system_->GetUpdateTimeout());
190 return true;
193 bool StopWatching() override {
194 delegate_ = NULL;
195 return true;
198 void CheckForUpdates() {
199 if (delegate_ == NULL)
200 return; // Orphan call. We have been stopped already.
201 VLOG(1) << "CP_CUPS: Checking for new printers";
202 std::string new_hash = GetPrintersHash();
203 if (printers_hash_ != new_hash) {
204 printers_hash_ = new_hash;
205 delegate_->OnPrinterAdded();
207 base::MessageLoop::current()->PostDelayedTask(
208 FROM_HERE,
209 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::MessageLoop::current()->PostDelayedTask(
264 FROM_HERE,
265 base::Bind(&PrinterWatcherCUPS::JobStatusUpdate, this),
266 base::TimeDelta::FromSeconds(kJobUpdateTimeoutSeconds));
267 // Schedule next printer check.
268 // TODO(gene): Randomize time for the next printer update.
269 base::MessageLoop::current()->PostDelayedTask(
270 FROM_HERE,
271 base::Bind(&PrinterWatcherCUPS::PrinterUpdate, this),
272 print_system_->GetUpdateTimeout());
273 return true;
276 bool StopWatching() override {
277 delegate_ = NULL;
278 return true;
281 bool GetCurrentPrinterInfo(
282 printing::PrinterBasicInfo* printer_info) override {
283 DCHECK(printer_info);
284 return print_system_->GetPrinterInfo(printer_name_, printer_info);
287 void JobStatusUpdate() {
288 if (delegate_ == NULL)
289 return; // Orphan call. We have been stopped already.
290 // For CUPS proxy, we are going to fire OnJobChanged notification
291 // periodically. Higher level will check if there are any outstanding
292 // jobs for this printer and check their status. If printer has no
293 // outstanding jobs, OnJobChanged() will do nothing.
294 delegate_->OnJobChanged();
295 base::MessageLoop::current()->PostDelayedTask(
296 FROM_HERE,
297 base::Bind(&PrinterWatcherCUPS::JobStatusUpdate, this),
298 base::TimeDelta::FromSeconds(kJobUpdateTimeoutSeconds));
301 void PrinterUpdate() {
302 if (delegate_ == NULL)
303 return; // Orphan call. We have been stopped already.
304 VLOG(1) << "CP_CUPS: Checking for updates"
305 << ", printer name: " << printer_name_;
306 if (print_system_->NotifyDelete() &&
307 !print_system_->IsValidPrinter(printer_name_)) {
308 delegate_->OnPrinterDeleted();
309 VLOG(1) << "CP_CUPS: Printer deleted"
310 << ", printer name: " << printer_name_;
311 } else {
312 std::string new_hash = GetSettingsHash();
313 if (settings_hash_ != new_hash) {
314 settings_hash_ = new_hash;
315 delegate_->OnPrinterChanged();
316 VLOG(1) << "CP_CUPS: Printer configuration changed"
317 << ", printer name: " << printer_name_;
320 base::MessageLoop::current()->PostDelayedTask(
321 FROM_HERE,
322 base::Bind(&PrinterWatcherCUPS::PrinterUpdate, this),
323 print_system_->GetUpdateTimeout());
326 protected:
327 ~PrinterWatcherCUPS() override { StopWatching(); }
329 private:
330 std::string GetSettingsHash() {
331 printing::PrinterBasicInfo info;
332 if (!print_system_->GetPrinterInfo(printer_name_, &info))
333 return std::string();
335 printing::PrinterCapsAndDefaults caps;
336 if (!print_system_->GetPrinterCapsAndDefaults(printer_name_, &caps))
337 return std::string();
339 std::string to_hash(info.printer_name);
340 to_hash += info.printer_description;
341 std::map<std::string, std::string>::const_iterator it;
342 for (it = info.options.begin(); it != info.options.end(); ++it) {
343 to_hash += it->first;
344 to_hash += it->second;
347 to_hash += caps.printer_capabilities;
348 to_hash += caps.caps_mime_type;
349 to_hash += caps.printer_defaults;
350 to_hash += caps.defaults_mime_type;
352 return base::MD5String(to_hash);
354 std::string printer_name_;
355 PrintSystem::PrinterWatcher::Delegate* delegate_;
356 scoped_refptr<PrintSystemCUPS> print_system_;
357 std::string settings_hash_;
359 DISALLOW_COPY_AND_ASSIGN(PrinterWatcherCUPS);
362 class JobSpoolerCUPS : public PrintSystem::JobSpooler {
363 public:
364 explicit JobSpoolerCUPS(PrintSystemCUPS* print_system)
365 : print_system_(print_system) {
366 DCHECK(print_system_.get());
369 // PrintSystem::JobSpooler implementation.
370 bool Spool(const std::string& print_ticket,
371 const std::string& print_ticket_mime_type,
372 const base::FilePath& print_data_file_path,
373 const std::string& print_data_mime_type,
374 const std::string& printer_name,
375 const std::string& job_title,
376 const std::vector<std::string>& tags,
377 JobSpooler::Delegate* delegate) override {
378 DCHECK(delegate);
379 bool dry_run = false;
380 int job_id = print_system_->SpoolPrintJob(
381 print_ticket, print_data_file_path, print_data_mime_type,
382 printer_name, job_title, tags, &dry_run);
383 base::MessageLoop::current()->PostTask(
384 FROM_HERE,
385 base::Bind(&JobSpoolerCUPS::NotifyDelegate, delegate, job_id, dry_run));
386 return true;
389 static void NotifyDelegate(JobSpooler::Delegate* delegate,
390 int job_id, bool dry_run) {
391 if (dry_run || job_id)
392 delegate->OnJobSpoolSucceeded(job_id);
393 else
394 delegate->OnJobSpoolFailed();
397 protected:
398 ~JobSpoolerCUPS() override {}
400 private:
401 scoped_refptr<PrintSystemCUPS> print_system_;
403 DISALLOW_COPY_AND_ASSIGN(JobSpoolerCUPS);
406 PrintSystemCUPS::PrintSystemCUPS(
407 const base::DictionaryValue* print_system_settings)
408 : update_timeout_(base::TimeDelta::FromMinutes(
409 kCheckForPrinterUpdatesMinutes)),
410 initialized_(false),
411 printer_enum_succeeded_(false),
412 notify_delete_(true),
413 cups_encryption_(HTTP_ENCRYPT_NEVER),
414 supported_mime_types_(kCUPSDefaultSupportedTypes) {
415 if (print_system_settings) {
416 int timeout;
417 if (print_system_settings->GetInteger(kCUPSUpdateTimeoutMs, &timeout))
418 update_timeout_ = base::TimeDelta::FromMilliseconds(timeout);
420 int encryption;
421 if (print_system_settings->GetInteger(kCUPSEncryption, &encryption))
422 cups_encryption_ =
423 static_cast<http_encryption_t>(encryption);
425 bool notify_delete = true;
426 if (print_system_settings->GetBoolean(kCUPSNotifyDelete, &notify_delete))
427 notify_delete_ = notify_delete;
429 std::string types;
430 if (print_system_settings->GetString(kCUPSSupportedMimeTipes, &types))
431 supported_mime_types_ = types;
434 InitPrintBackends(print_system_settings);
437 void PrintSystemCUPS::InitPrintBackends(
438 const base::DictionaryValue* print_system_settings) {
439 const base::ListValue* url_list;
440 if (print_system_settings &&
441 print_system_settings->GetList(kCUPSPrintServerURLs, &url_list)) {
442 for (size_t i = 0; i < url_list->GetSize(); i++) {
443 std::string print_server_url;
444 if (url_list->GetString(i, &print_server_url))
445 AddPrintServer(print_server_url);
449 // If server list is empty, use default print server.
450 if (print_servers_.empty())
451 AddPrintServer(std::string());
454 void PrintSystemCUPS::AddPrintServer(const std::string& url) {
455 if (url.empty())
456 LOG(WARNING) << "No print server specified. Using default print server.";
458 // Get Print backend for the specific print server.
459 base::DictionaryValue backend_settings;
460 backend_settings.SetString(kCUPSPrintServerURL, url);
462 // Make CUPS requests non-blocking.
463 backend_settings.SetString(kCUPSBlocking, kValueFalse);
465 // Set encryption for backend.
466 backend_settings.SetInteger(kCUPSEncryption, cups_encryption_);
468 PrintServerInfoCUPS print_server;
469 print_server.backend =
470 printing::PrintBackend::CreateInstance(&backend_settings);
471 print_server.url = GURL(url.c_str());
473 print_servers_.push_back(print_server);
476 PrintSystem::PrintSystemResult PrintSystemCUPS::Init() {
477 UpdatePrinters();
478 initialized_ = true;
479 return PrintSystemResult(true, std::string());
482 void PrintSystemCUPS::UpdatePrinters() {
483 PrintServerList::iterator it;
484 printer_enum_succeeded_ = true;
485 for (it = print_servers_.begin(); it != print_servers_.end(); ++it) {
486 if (!it->backend->EnumeratePrinters(&it->printers))
487 printer_enum_succeeded_ = false;
488 it->caps_cache.clear();
489 printing::PrinterList::iterator printer_it;
490 for (printer_it = it->printers.begin();
491 printer_it != it->printers.end(); ++printer_it) {
492 printer_it->printer_name = MakeFullPrinterName(it->url,
493 printer_it->printer_name);
495 VLOG(1) << "CP_CUPS: Updated printers list"
496 << ", server: " << it->url
497 << ", # of printers: " << it->printers.size();
500 // Schedule next update.
501 base::MessageLoop::current()->PostDelayedTask(
502 FROM_HERE,
503 base::Bind(&PrintSystemCUPS::UpdatePrinters, this),
504 GetUpdateTimeout());
507 PrintSystem::PrintSystemResult PrintSystemCUPS::EnumeratePrinters(
508 printing::PrinterList* printer_list) {
509 DCHECK(initialized_);
510 printer_list->clear();
511 PrintServerList::iterator it;
512 for (it = print_servers_.begin(); it != print_servers_.end(); ++it) {
513 printer_list->insert(printer_list->end(),
514 it->printers.begin(), it->printers.end());
516 VLOG(1) << "CP_CUPS: Total printers enumerated: " << printer_list->size();
517 // TODO(sanjeevr): Maybe some day we want to report the actual server names
518 // for which the enumeration failed.
519 return PrintSystemResult(printer_enum_succeeded_, std::string());
522 void PrintSystemCUPS::GetPrinterCapsAndDefaults(
523 const std::string& printer_name,
524 const PrinterCapsAndDefaultsCallback& callback) {
525 printing::PrinterCapsAndDefaults printer_info;
526 bool succeeded = GetPrinterCapsAndDefaults(printer_name, &printer_info);
527 base::MessageLoop::current()->PostTask(
528 FROM_HERE,
529 base::Bind(&PrintSystemCUPS::RunCapsCallback,
530 callback,
531 succeeded,
532 printer_name,
533 printer_info));
536 bool PrintSystemCUPS::IsValidPrinter(const std::string& printer_name) {
537 return GetPrinterInfo(printer_name, NULL);
540 bool PrintSystemCUPS::ValidatePrintTicket(
541 const std::string& printer_name,
542 const std::string& print_ticket_data,
543 const std::string& print_ticket_mime_type) {
544 DCHECK(initialized_);
545 scoped_ptr<base::Value> ticket_value(
546 base::JSONReader::Read(print_ticket_data));
547 return ticket_value != NULL &&
548 ticket_value->IsType(base::Value::TYPE_DICTIONARY);
551 // Print ticket on linux is a JSON string containing only one dictionary.
552 bool PrintSystemCUPS::ParsePrintTicket(
553 const std::string& print_ticket,
554 std::map<std::string, std::string>* options) {
555 DCHECK(options);
556 scoped_ptr<base::Value> ticket_value(base::JSONReader::Read(print_ticket));
557 if (ticket_value == NULL ||
558 !ticket_value->IsType(base::Value::TYPE_DICTIONARY)) {
559 return false;
562 options->clear();
563 base::DictionaryValue* ticket_dict =
564 static_cast<base::DictionaryValue*>(ticket_value.get());
565 for (base::DictionaryValue::Iterator it(*ticket_dict); !it.IsAtEnd();
566 it.Advance()) {
567 std::string value;
568 if (it.value().GetAsString(&value))
569 (*options)[it.key()] = value;
572 return true;
575 bool PrintSystemCUPS::GetPrinterCapsAndDefaults(
576 const std::string& printer_name,
577 printing::PrinterCapsAndDefaults* printer_info) {
578 DCHECK(initialized_);
579 std::string short_printer_name;
580 PrintServerInfoCUPS* server_info =
581 FindServerByFullName(printer_name, &short_printer_name);
582 if (!server_info)
583 return false;
585 PrintServerInfoCUPS::CapsMap::iterator caps_it =
586 server_info->caps_cache.find(printer_name);
587 if (caps_it != server_info->caps_cache.end()) {
588 *printer_info = caps_it->second;
589 return true;
592 // TODO(gene): Retry multiple times in case of error.
593 crash_keys::ScopedPrinterInfo crash_key(
594 server_info->backend->GetPrinterDriverInfo(short_printer_name));
595 if (!server_info->backend->GetPrinterCapsAndDefaults(short_printer_name,
596 printer_info) ) {
597 return false;
600 server_info->caps_cache[printer_name] = *printer_info;
601 return true;
604 bool PrintSystemCUPS::GetJobDetails(const std::string& printer_name,
605 PlatformJobId job_id,
606 PrintJobDetails *job_details) {
607 DCHECK(initialized_);
608 DCHECK(job_details);
610 std::string short_printer_name;
611 PrintServerInfoCUPS* server_info =
612 FindServerByFullName(printer_name, &short_printer_name);
613 if (!server_info)
614 return false;
616 crash_keys::ScopedPrinterInfo crash_key(
617 server_info->backend->GetPrinterDriverInfo(short_printer_name));
618 cups_job_t* jobs = NULL;
619 int num_jobs = GetJobs(&jobs, server_info->url, cups_encryption_,
620 short_printer_name.c_str(), 1, -1);
621 bool error = (num_jobs == 0) && (cupsLastError() > IPP_OK_EVENTS_COMPLETE);
622 if (error) {
623 VLOG(1) << "CP_CUPS: Error getting jobs from CUPS server"
624 << ", printer name:" << printer_name
625 << ", error: " << static_cast<int>(cupsLastError());
626 return false;
629 // Check if the request is for dummy dry run job.
630 // We check this after calling GetJobs API to see if this printer is actually
631 // accessible through CUPS.
632 if (job_id == kDryRunJobId) {
633 job_details->status = PRINT_JOB_STATUS_COMPLETED;
634 VLOG(1) << "CP_CUPS: Dry run job succeeded"
635 << ", printer name: " << printer_name;
636 return true;
639 bool found = false;
640 for (int i = 0; i < num_jobs; i++) {
641 if (jobs[i].id == job_id) {
642 found = true;
643 switch (jobs[i].state) {
644 case IPP_JOB_PENDING :
645 case IPP_JOB_HELD :
646 case IPP_JOB_PROCESSING :
647 job_details->status = PRINT_JOB_STATUS_IN_PROGRESS;
648 break;
649 case IPP_JOB_STOPPED :
650 case IPP_JOB_CANCELLED :
651 case IPP_JOB_ABORTED :
652 job_details->status = PRINT_JOB_STATUS_ERROR;
653 break;
654 case IPP_JOB_COMPLETED :
655 job_details->status = PRINT_JOB_STATUS_COMPLETED;
656 break;
657 default:
658 job_details->status = PRINT_JOB_STATUS_INVALID;
660 job_details->platform_status_flags = jobs[i].state;
662 // We don't have any details on the number of processed pages here.
663 break;
667 if (found)
668 VLOG(1) << "CP_CUPS: Job found"
669 << ", printer name: " << printer_name
670 << ", cups job id: " << job_id
671 << ", cups job status: " << job_details->status;
672 else
673 LOG(WARNING) << "CP_CUPS: Job not found"
674 << ", printer name: " << printer_name
675 << ", cups job id: " << job_id;
677 cupsFreeJobs(num_jobs, jobs);
678 return found;
681 bool PrintSystemCUPS::GetPrinterInfo(const std::string& printer_name,
682 printing::PrinterBasicInfo* info) {
683 DCHECK(initialized_);
684 if (info)
685 VLOG(1) << "CP_CUPS: Getting printer info"
686 << ", printer name: " << printer_name;
688 std::string short_printer_name;
689 PrintServerInfoCUPS* server_info =
690 FindServerByFullName(printer_name, &short_printer_name);
691 if (!server_info)
692 return false;
694 printing::PrinterList::iterator it;
695 for (it = server_info->printers.begin();
696 it != server_info->printers.end(); ++it) {
697 if (it->printer_name == printer_name) {
698 if (info)
699 *info = *it;
700 return true;
703 return false;
706 PrintSystem::PrintServerWatcher*
707 PrintSystemCUPS::CreatePrintServerWatcher() {
708 DCHECK(initialized_);
709 return new PrintServerWatcherCUPS(this);
712 PrintSystem::PrinterWatcher* PrintSystemCUPS::CreatePrinterWatcher(
713 const std::string& printer_name) {
714 DCHECK(initialized_);
715 DCHECK(!printer_name.empty());
716 return new PrinterWatcherCUPS(this, printer_name);
719 PrintSystem::JobSpooler* PrintSystemCUPS::CreateJobSpooler() {
720 DCHECK(initialized_);
721 return new JobSpoolerCUPS(this);
724 bool PrintSystemCUPS::UseCddAndCjt() {
725 return false;
728 std::string PrintSystemCUPS::GetSupportedMimeTypes() {
729 return supported_mime_types_;
732 scoped_refptr<PrintSystem> PrintSystem::CreateInstance(
733 const base::DictionaryValue* print_system_settings) {
734 return new PrintSystemCUPS(print_system_settings);
737 int PrintSystemCUPS::PrintFile(const GURL& url, http_encryption_t encryption,
738 const char* name, const char* filename,
739 const char* title, int num_options,
740 cups_option_t* options) {
741 if (url.is_empty()) { // Use default (local) print server.
742 return cupsPrintFile(name, filename, title, num_options, options);
743 } else {
744 printing::HttpConnectionCUPS http(url, encryption);
745 http.SetBlocking(false);
746 return cupsPrintFile2(http.http(), name, filename,
747 title, num_options, options);
751 int PrintSystemCUPS::GetJobs(cups_job_t** jobs, const GURL& url,
752 http_encryption_t encryption,
753 const char* name, int myjobs, int whichjobs) {
754 if (url.is_empty()) { // Use default (local) print server.
755 return cupsGetJobs(jobs, name, myjobs, whichjobs);
756 } else {
757 printing::HttpConnectionCUPS http(url, encryption);
758 http.SetBlocking(false);
759 return cupsGetJobs2(http.http(), jobs, name, myjobs, whichjobs);
763 PlatformJobId PrintSystemCUPS::SpoolPrintJob(
764 const std::string& print_ticket,
765 const base::FilePath& print_data_file_path,
766 const std::string& print_data_mime_type,
767 const std::string& printer_name,
768 const std::string& job_title,
769 const std::vector<std::string>& tags,
770 bool* dry_run) {
771 DCHECK(initialized_);
772 VLOG(1) << "CP_CUPS: Spooling print job, printer name: " << printer_name;
774 std::string short_printer_name;
775 PrintServerInfoCUPS* server_info =
776 FindServerByFullName(printer_name, &short_printer_name);
777 if (!server_info)
778 return false;
780 crash_keys::ScopedPrinterInfo crash_key(
781 server_info->backend->GetPrinterDriverInfo(printer_name));
783 // We need to store options as char* string for the duration of the
784 // cupsPrintFile2 call. We'll use map here to store options, since
785 // Dictionary value from JSON parser returns wchat_t.
786 std::map<std::string, std::string> options;
787 bool res = ParsePrintTicket(print_ticket, &options);
788 DCHECK(res); // If print ticket is invalid we still print using defaults.
790 // Check if this is a dry run (test) job.
791 *dry_run = IsDryRunJob(tags);
792 if (*dry_run) {
793 VLOG(1) << "CP_CUPS: Dry run job spooled";
794 return kDryRunJobId;
797 std::vector<cups_option_t> cups_options;
798 std::map<std::string, std::string>::iterator it;
800 for (it = options.begin(); it != options.end(); ++it) {
801 cups_option_t opt;
802 opt.name = const_cast<char*>(it->first.c_str());
803 opt.value = const_cast<char*>(it->second.c_str());
804 cups_options.push_back(opt);
807 int job_id = PrintFile(server_info->url,
808 cups_encryption_,
809 short_printer_name.c_str(),
810 print_data_file_path.value().c_str(),
811 job_title.c_str(),
812 cups_options.size(),
813 &(cups_options[0]));
815 // TODO(alexyu): Output printer id.
816 VLOG(1) << "CP_CUPS: Job spooled"
817 << ", printer name: " << printer_name
818 << ", cups job id: " << job_id;
820 return job_id;
823 std::string PrintSystemCUPS::MakeFullPrinterName(
824 const GURL& url, const std::string& short_printer_name) {
825 std::string full_name;
826 full_name += "\\\\";
827 full_name += url.host();
828 if (!url.port().empty()) {
829 full_name += ":";
830 full_name += url.port();
832 full_name += "\\";
833 full_name += short_printer_name;
834 return full_name;
837 PrintServerInfoCUPS* PrintSystemCUPS::FindServerByFullName(
838 const std::string& full_printer_name, std::string* short_printer_name) {
839 size_t front = full_printer_name.find("\\\\");
840 size_t separator = full_printer_name.find("\\", 2);
841 if (front == std::string::npos || separator == std::string::npos) {
842 LOG(WARNING) << "CP_CUPS: Invalid UNC"
843 << ", printer name: " << full_printer_name;
844 return NULL;
846 std::string server = full_printer_name.substr(2, separator - 2);
848 PrintServerList::iterator it;
849 for (it = print_servers_.begin(); it != print_servers_.end(); ++it) {
850 std::string cur_server;
851 cur_server += it->url.host();
852 if (!it->url.port().empty()) {
853 cur_server += ":";
854 cur_server += it->url.port();
856 if (cur_server == server) {
857 *short_printer_name = full_printer_name.substr(separator + 1);
858 return &(*it);
862 LOG(WARNING) << "CP_CUPS: Server not found"
863 << ", printer name: " << full_printer_name;
864 return NULL;
867 void PrintSystemCUPS::RunCapsCallback(
868 const PrinterCapsAndDefaultsCallback& callback,
869 bool succeeded,
870 const std::string& printer_name,
871 const printing::PrinterCapsAndDefaults& printer_info) {
872 callback.Run(succeeded, printer_name, printer_info);
875 } // namespace cloud_print