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 "printing/backend/cups_helper.h"
32 #include "printing/backend/print_backend.h"
33 #include "printing/backend/print_backend_consts.h"
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;
53 const int kJobUpdateTimeoutSeconds
= 5;
55 // Job id for dry run (it should not affect CUPS job ids, since 0 job-id is
57 const int kDryRunJobId
= 0;
61 namespace cloud_print
{
63 struct PrintServerInfoCUPS
{
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
;
74 class PrintSystemCUPS
: public PrintSystem
{
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
,
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
;
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
,
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_
;
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
,
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_
;
167 bool printer_enum_succeeded_
;
169 http_encryption_t cups_encryption_
;
170 std::string supported_mime_types_
;
173 class PrintServerWatcherCUPS
174 : public PrintSystem::PrintServerWatcher
{
176 explicit PrintServerWatcherCUPS(PrintSystemCUPS
* print_system
)
177 : print_system_(print_system
),
181 // PrintSystem::PrintServerWatcher implementation.
183 PrintSystem::PrintServerWatcher::Delegate
* delegate
) override
{
184 delegate_
= delegate
;
185 printers_hash_
= GetPrintersHash();
186 base::MessageLoop::current()->PostDelayedTask(
188 base::Bind(&PrintServerWatcherCUPS::CheckForUpdates
, this),
189 print_system_
->GetUpdateTimeout());
193 bool StopWatching() override
{
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(
209 base::Bind(&PrintServerWatcherCUPS::CheckForUpdates
, this),
210 print_system_
->GetUpdateTimeout());
214 ~PrintServerWatcherCUPS() override
{ StopWatching(); }
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());
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
{
245 PrinterWatcherCUPS(PrintSystemCUPS
* print_system
,
246 const std::string
& printer_name
)
247 : printer_name_(printer_name
),
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
)
260 delegate_
= delegate
;
261 settings_hash_
= GetSettingsHash();
262 // Schedule next job status update.
263 base::MessageLoop::current()->PostDelayedTask(
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(
271 base::Bind(&PrinterWatcherCUPS::PrinterUpdate
, this),
272 print_system_
->GetUpdateTimeout());
276 bool StopWatching() override
{
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(
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_
;
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(
322 base::Bind(&PrinterWatcherCUPS::PrinterUpdate
, this),
323 print_system_
->GetUpdateTimeout());
327 ~PrinterWatcherCUPS() override
{ StopWatching(); }
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
{
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
{
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(
385 base::Bind(&JobSpoolerCUPS::NotifyDelegate
, delegate
, job_id
, dry_run
));
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
);
394 delegate
->OnJobSpoolFailed();
398 ~JobSpoolerCUPS() override
{}
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
)),
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
) {
417 if (print_system_settings
->GetInteger(kCUPSUpdateTimeoutMs
, &timeout
))
418 update_timeout_
= base::TimeDelta::FromMilliseconds(timeout
);
421 if (print_system_settings
->GetInteger(kCUPSEncryption
, &encryption
))
423 static_cast<http_encryption_t
>(encryption
);
425 bool notify_delete
= true;
426 if (print_system_settings
->GetBoolean(kCUPSNotifyDelete
, ¬ify_delete
))
427 notify_delete_
= notify_delete
;
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
) {
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() {
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(
503 base::Bind(&PrintSystemCUPS::UpdatePrinters
, this),
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(
529 base::Bind(&PrintSystemCUPS::RunCapsCallback
,
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
) {
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
)) {
563 base::DictionaryValue
* ticket_dict
=
564 static_cast<base::DictionaryValue
*>(ticket_value
.get());
565 for (base::DictionaryValue::Iterator
it(*ticket_dict
); !it
.IsAtEnd();
568 if (it
.value().GetAsString(&value
))
569 (*options
)[it
.key()] = value
;
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
);
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
;
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
,
600 server_info
->caps_cache
[printer_name
] = *printer_info
;
604 bool PrintSystemCUPS::GetJobDetails(const std::string
& printer_name
,
605 PlatformJobId job_id
,
606 PrintJobDetails
*job_details
) {
607 DCHECK(initialized_
);
610 std::string short_printer_name
;
611 PrintServerInfoCUPS
* server_info
=
612 FindServerByFullName(printer_name
, &short_printer_name
);
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
);
623 VLOG(1) << "CP_CUPS: Error getting jobs from CUPS server"
624 << ", printer name:" << printer_name
625 << ", error: " << static_cast<int>(cupsLastError());
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
;
640 for (int i
= 0; i
< num_jobs
; i
++) {
641 if (jobs
[i
].id
== job_id
) {
643 switch (jobs
[i
].state
) {
644 case IPP_JOB_PENDING
:
646 case IPP_JOB_PROCESSING
:
647 job_details
->status
= PRINT_JOB_STATUS_IN_PROGRESS
;
649 case IPP_JOB_STOPPED
:
650 case IPP_JOB_CANCELLED
:
651 case IPP_JOB_ABORTED
:
652 job_details
->status
= PRINT_JOB_STATUS_ERROR
;
654 case IPP_JOB_COMPLETED
:
655 job_details
->status
= PRINT_JOB_STATUS_COMPLETED
;
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.
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
;
673 LOG(WARNING
) << "CP_CUPS: Job not found"
674 << ", printer name: " << printer_name
675 << ", cups job id: " << job_id
;
677 cupsFreeJobs(num_jobs
, jobs
);
681 bool PrintSystemCUPS::GetPrinterInfo(const std::string
& printer_name
,
682 printing::PrinterBasicInfo
* info
) {
683 DCHECK(initialized_
);
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
);
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
) {
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() {
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
);
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
);
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
,
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
);
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
);
793 VLOG(1) << "CP_CUPS: Dry run job spooled";
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
) {
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
,
809 short_printer_name
.c_str(),
810 print_data_file_path
.value().c_str(),
815 // TODO(alexyu): Output printer id.
816 VLOG(1) << "CP_CUPS: Job spooled"
817 << ", printer name: " << printer_name
818 << ", cups job id: " << job_id
;
823 std::string
PrintSystemCUPS::MakeFullPrinterName(
824 const GURL
& url
, const std::string
& short_printer_name
) {
825 std::string full_name
;
827 full_name
+= url
.host();
828 if (!url
.port().empty()) {
830 full_name
+= url
.port();
833 full_name
+= short_printer_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
;
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()) {
854 cur_server
+= it
->url
.port();
856 if (cur_server
== server
) {
857 *short_printer_name
= full_printer_name
.substr(separator
+ 1);
862 LOG(WARNING
) << "CP_CUPS: Server not found"
863 << ", printer name: " << full_printer_name
;
867 void PrintSystemCUPS::RunCapsCallback(
868 const PrinterCapsAndDefaultsCallback
& callback
,
870 const std::string
& printer_name
,
871 const printing::PrinterCapsAndDefaults
& printer_info
) {
872 callback
.Run(succeeded
, printer_name
, printer_info
);
875 } // namespace cloud_print