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"
16 #include "base/bind.h"
17 #include "base/files/file_path.h"
18 #include "base/json/json_reader.h"
19 #include "base/logging.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"
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;
55 const int kJobUpdateTimeoutSeconds
= 5;
57 // Job id for dry run (it should not affect CUPS job ids, since 0 job-id is
59 const int kDryRunJobId
= 0;
63 namespace cloud_print
{
65 struct PrintServerInfoCUPS
{
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
;
76 class PrintSystemCUPS
: public PrintSystem
{
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
,
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
;
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
,
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_
;
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
,
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_
;
170 bool printer_enum_succeeded_
;
172 http_encryption_t cups_encryption_
;
173 std::string supported_mime_types_
;
176 class PrintServerWatcherCUPS
177 : public PrintSystem::PrintServerWatcher
{
179 explicit PrintServerWatcherCUPS(PrintSystemCUPS
* print_system
)
180 : print_system_(print_system
),
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(
191 base::Bind(&PrintServerWatcherCUPS::CheckForUpdates
, this),
192 print_system_
->GetUpdateTimeout());
196 virtual bool StopWatching() OVERRIDE
{
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(
212 base::Bind(&PrintServerWatcherCUPS::CheckForUpdates
, this),
213 print_system_
->GetUpdateTimeout());
217 virtual ~PrintServerWatcherCUPS() {
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());
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
{
250 PrinterWatcherCUPS(PrintSystemCUPS
* print_system
,
251 const std::string
& printer_name
)
252 : printer_name_(printer_name
),
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
)
266 delegate_
= delegate
;
267 settings_hash_
= GetSettingsHash();
268 // Schedule next job status update.
269 base::MessageLoop::current()->PostDelayedTask(
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(
277 base::Bind(&PrinterWatcherCUPS::PrinterUpdate
, this),
278 print_system_
->GetUpdateTimeout());
282 virtual bool StopWatching() OVERRIDE
{
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(
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_
;
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(
328 base::Bind(&PrinterWatcherCUPS::PrinterUpdate
, this),
329 print_system_
->GetUpdateTimeout());
333 virtual ~PrinterWatcherCUPS() {
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
{
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
{
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(
393 base::Bind(&JobSpoolerCUPS::NotifyDelegate
, delegate
, job_id
, dry_run
));
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
);
402 delegate
->OnJobSpoolFailed();
406 virtual ~JobSpoolerCUPS() {}
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
)),
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
) {
425 if (print_system_settings
->GetInteger(kCUPSUpdateTimeoutMs
, &timeout
))
426 update_timeout_
= base::TimeDelta::FromMilliseconds(timeout
);
429 if (print_system_settings
->GetInteger(kCUPSEncryption
, &encryption
))
431 static_cast<http_encryption_t
>(encryption
);
433 bool notify_delete
= true;
434 if (print_system_settings
->GetBoolean(kCUPSNotifyDelete
, ¬ify_delete
))
435 notify_delete_
= notify_delete
;
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
) {
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() {
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(
511 base::Bind(&PrintSystemCUPS::UpdatePrinters
, this),
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(
537 base::Bind(&PrintSystemCUPS::RunCapsCallback
,
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
) {
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
)) {
571 base::DictionaryValue
* ticket_dict
=
572 static_cast<base::DictionaryValue
*>(ticket_value
.get());
573 for (base::DictionaryValue::Iterator
it(*ticket_dict
); !it
.IsAtEnd();
576 if (it
.value().GetAsString(&value
))
577 (*options
)[it
.key()] = value
;
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
);
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
;
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
,
608 server_info
->caps_cache
[printer_name
] = *printer_info
;
612 bool PrintSystemCUPS::GetJobDetails(const std::string
& printer_name
,
613 PlatformJobId job_id
,
614 PrintJobDetails
*job_details
) {
615 DCHECK(initialized_
);
618 std::string short_printer_name
;
619 PrintServerInfoCUPS
* server_info
=
620 FindServerByFullName(printer_name
, &short_printer_name
);
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
);
631 VLOG(1) << "CP_CUPS: Error getting jobs from CUPS server"
632 << ", printer name:" << printer_name
633 << ", error: " << static_cast<int>(cupsLastError());
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
;
648 for (int i
= 0; i
< num_jobs
; i
++) {
649 if (jobs
[i
].id
== job_id
) {
651 switch (jobs
[i
].state
) {
652 case IPP_JOB_PENDING
:
654 case IPP_JOB_PROCESSING
:
655 job_details
->status
= PRINT_JOB_STATUS_IN_PROGRESS
;
657 case IPP_JOB_STOPPED
:
658 case IPP_JOB_CANCELLED
:
659 case IPP_JOB_ABORTED
:
660 job_details
->status
= PRINT_JOB_STATUS_ERROR
;
662 case IPP_JOB_COMPLETED
:
663 job_details
->status
= PRINT_JOB_STATUS_COMPLETED
;
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.
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
;
681 LOG(WARNING
) << "CP_CUPS: Job not found"
682 << ", printer name: " << printer_name
683 << ", cups job id: " << job_id
;
685 cupsFreeJobs(num_jobs
, jobs
);
689 bool PrintSystemCUPS::GetPrinterInfo(const std::string
& printer_name
,
690 printing::PrinterBasicInfo
* info
) {
691 DCHECK(initialized_
);
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
);
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
) {
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() {
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
);
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
);
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
,
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
);
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
);
801 VLOG(1) << "CP_CUPS: Dry run job spooled";
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
) {
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
,
817 short_printer_name
.c_str(),
818 print_data_file_path
.value().c_str(),
823 // TODO(alexyu): Output printer id.
824 VLOG(1) << "CP_CUPS: Job spooled"
825 << ", printer name: " << printer_name
826 << ", cups job id: " << job_id
;
831 std::string
PrintSystemCUPS::MakeFullPrinterName(
832 const GURL
& url
, const std::string
& short_printer_name
) {
833 std::string full_name
;
835 full_name
+= url
.host();
836 if (!url
.port().empty()) {
838 full_name
+= url
.port();
841 full_name
+= short_printer_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
;
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()) {
862 cur_server
+= it
->url
.port();
864 if (cur_server
== server
) {
865 *short_printer_name
= full_printer_name
.substr(separator
+ 1);
870 LOG(WARNING
) << "CP_CUPS: Server not found"
871 << ", printer name: " << full_printer_name
;
875 void PrintSystemCUPS::RunCapsCallback(
876 const PrinterCapsAndDefaultsCallback
& callback
,
878 const std::string
& printer_name
,
879 const printing::PrinterCapsAndDefaults
& printer_info
) {
880 callback
.Run(succeeded
, printer_name
, printer_info
);
883 } // namespace cloud_print