blob: 22737dac7464ce878a7ad195a9b9b282ce3770a1 [file] [log] [blame]
[email protected]203fb822012-01-26 20:52:561// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]8e553f492010-10-25 20:05:442// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
skau8ffe7752016-06-29 02:10:285#include "printing/backend/print_backend_cups.h"
[email protected]8e553f492010-10-25 20:05:446
pwnall29c12622016-08-17 22:33:257#include <cups/ppd.h>
[email protected]8e553f492010-10-25 20:05:448#include <dlfcn.h>
9#include <errno.h>
[email protected]ce80f5f2011-08-09 05:41:2710#include <pthread.h>
11
skau8ffe7752016-06-29 02:10:2812#include <string>
13
thestig22dfc4012014-09-05 08:29:4414#include "base/files/file_util.h"
[email protected]625332e02010-12-14 07:48:4915#include "base/lazy_instance.h"
[email protected]8e553f492010-10-25 20:05:4416#include "base/logging.h"
[email protected]896d161f2013-06-11 22:52:2417#include "base/strings/string_number_conversions.h"
[email protected]20305ec2011-01-21 04:55:5218#include "base/synchronization/lock.h"
[email protected]8e553f492010-10-25 20:05:4419#include "base/values.h"
[email protected]8e553f492010-10-25 20:05:4420#include "printing/backend/cups_helper.h"
[email protected]9c6ca602010-12-16 00:55:3921#include "printing/backend/print_backend_consts.h"
[email protected]79fe2272013-07-13 20:01:4022#include "url/gurl.h"
[email protected]8e553f492010-10-25 20:05:4423
[email protected]8e553f492010-10-25 20:05:4424namespace printing {
25
thestig27bb2f802016-05-16 22:57:5626namespace {
27
28const char kCUPSPrinterInfoOpt[] = "printer-info";
29const char kCUPSPrinterStateOpt[] = "printer-state";
30const char kCUPSPrinterTypeOpt[] = "printer-type";
thestig27bb2f802016-05-16 22:57:5631
32bool PrinterBasicInfoFromCUPS(const cups_dest_t& printer,
33 PrinterBasicInfo* printer_info) {
34 // CUPS can have 'printers' that are actually scanners. (not MFC)
35 // At least on Mac. Check for scanners and skip them.
36 const char* type_str =
37 cupsGetOption(kCUPSPrinterTypeOpt, printer.num_options, printer.options);
38 if (type_str) {
39 int type;
40 if (base::StringToInt(type_str, &type) && (type & CUPS_PRINTER_SCANNER))
41 return false;
42 }
43
44 printer_info->printer_name = printer.name;
45 printer_info->is_default = printer.is_default;
46
47 const char* info =
48 cupsGetOption(kCUPSPrinterInfoOpt, printer.num_options, printer.options);
49 if (info)
50 printer_info->printer_description = info;
51
52 const char* state =
53 cupsGetOption(kCUPSPrinterStateOpt, printer.num_options, printer.options);
54 if (state)
55 base::StringToInt(state, &printer_info->printer_status);
56
thestigfac2cc42016-11-03 16:31:1157 const char* drv_info = cupsGetOption(kDriverNameTagName,
thestig27bb2f802016-05-16 22:57:5658 printer.num_options, printer.options);
59 if (drv_info)
60 printer_info->options[kDriverInfoTagName] = *drv_info;
61
62 // Store printer options.
63 for (int opt_index = 0; opt_index < printer.num_options; ++opt_index) {
64 printer_info->options[printer.options[opt_index].name] =
65 printer.options[opt_index].value;
66 }
67 return true;
68}
69
70} // namespace
[email protected]8e553f492010-10-25 20:05:4471
[email protected]ce6f5912012-05-16 23:07:3072PrintBackendCUPS::PrintBackendCUPS(const GURL& print_server_url,
73 http_encryption_t encryption,
74 bool blocking)
75 : print_server_url_(print_server_url),
76 cups_encryption_(encryption),
77 blocking_(blocking) {
[email protected]8e553f492010-10-25 20:05:4478}
79
[email protected]402197a2011-01-24 23:47:4180bool PrintBackendCUPS::EnumeratePrinters(PrinterList* printer_list) {
[email protected]8e553f492010-10-25 20:05:4481 DCHECK(printer_list);
82 printer_list->clear();
83
thestig3154ea22016-05-16 21:34:0384 cups_dest_t* destinations = nullptr;
[email protected]8e553f492010-10-25 20:05:4485 int num_dests = GetDests(&destinations);
thestig3154ea22016-05-16 21:34:0386 if (!num_dests && cupsLastError() > IPP_OK_EVENTS_COMPLETE) {
[email protected]6075da42012-07-23 23:22:4087 VLOG(1) << "CUPS: Error getting printers from CUPS server"
88 << ", server: " << print_server_url_
89 << ", error: " << static_cast<int>(cupsLastError());
[email protected]cfde8642011-02-03 23:53:2790 return false;
91 }
[email protected]8e553f492010-10-25 20:05:4492
[email protected]675a1ae2013-10-14 20:24:3793 for (int printer_index = 0; printer_index < num_dests; ++printer_index) {
[email protected]8e553f492010-10-25 20:05:4494 const cups_dest_t& printer = destinations[printer_index];
95
96 PrinterBasicInfo printer_info;
thestig27bb2f802016-05-16 22:57:5697 if (PrinterBasicInfoFromCUPS(printer, &printer_info))
98 printer_list->push_back(printer_info);
[email protected]8e553f492010-10-25 20:05:4499 }
100
101 cupsFreeDests(num_dests, destinations);
102
thestig3154ea22016-05-16 21:34:03103 VLOG(1) << "CUPS: Enumerated printers, server: " << print_server_url_
[email protected]6075da42012-07-23 23:22:40104 << ", # of printers: " << printer_list->size();
[email protected]402197a2011-01-24 23:47:41105 return true;
[email protected]8e553f492010-10-25 20:05:44106}
107
[email protected]ffcb1ca2011-05-28 21:50:29108std::string PrintBackendCUPS::GetDefaultPrinterName() {
[email protected]aa27b9532011-06-08 21:06:41109 // Not using cupsGetDefault() because it lies about the default printer.
110 cups_dest_t* dests;
111 int num_dests = GetDests(&dests);
thestig3154ea22016-05-16 21:34:03112 cups_dest_t* dest = cupsGetDest(nullptr, nullptr, num_dests, dests);
[email protected]976ac952012-09-07 21:21:28113 std::string name = dest ? std::string(dest->name) : std::string();
114 cupsFreeDests(num_dests, dests);
115 return name;
[email protected]ffcb1ca2011-05-28 21:50:29116}
117
thestig27bb2f802016-05-16 22:57:56118bool PrintBackendCUPS::GetPrinterBasicInfo(const std::string& printer_name,
119 PrinterBasicInfo* printer_info) {
120 cups_dest_t* dest = GetNamedDest(printer_name);
121 if (!dest)
122 return false;
123
124 DCHECK_EQ(printer_name, dest->name);
125 bool ret = PrinterBasicInfoFromCUPS(*dest, printer_info);
126 cupsFreeDests(1, dest);
127 return ret;
128}
129
[email protected]8b6098e2012-09-07 04:21:21130bool PrintBackendCUPS::GetPrinterSemanticCapsAndDefaults(
131 const std::string& printer_name,
132 PrinterSemanticCapsAndDefaults* printer_info) {
133 PrinterCapsAndDefaults info;
134 if (!GetPrinterCapsAndDefaults(printer_name, &info) )
135 return false;
136
[email protected]675a1ae2013-10-14 20:24:37137 return ParsePpdCapabilities(
[email protected]8b6098e2012-09-07 04:21:21138 printer_name, info.printer_capabilities, printer_info);
139}
140
[email protected]8e553f492010-10-25 20:05:44141bool PrintBackendCUPS::GetPrinterCapsAndDefaults(
142 const std::string& printer_name,
143 PrinterCapsAndDefaults* printer_info) {
144 DCHECK(printer_info);
145
thestig3154ea22016-05-16 21:34:03146 VLOG(1) << "CUPS: Getting caps and defaults, printer name: " << printer_name;
[email protected]8e553f492010-10-25 20:05:44147
[email protected]79f63882013-02-10 05:15:45148 base::FilePath ppd_path(GetPPD(printer_name.c_str()));
[email protected]8e553f492010-10-25 20:05:44149 // In some cases CUPS failed to get ppd file.
150 if (ppd_path.empty()) {
[email protected]8eb22312014-06-18 10:10:59151 LOG(ERROR) << "CUPS: Failed to get PPD, printer name: " << printer_name;
[email protected]8e553f492010-10-25 20:05:44152 return false;
153 }
154
155 std::string content;
[email protected]82f84b92013-08-30 18:23:50156 bool res = base::ReadFileToString(ppd_path, &content);
[email protected]8e553f492010-10-25 20:05:44157
[email protected]dd3aa792013-07-16 19:10:23158 base::DeleteFile(ppd_path, false);
[email protected]8e553f492010-10-25 20:05:44159
160 if (res) {
161 printer_info->printer_capabilities.swap(content);
162 printer_info->caps_mime_type = "application/pagemaker";
163 // In CUPS, printer defaults is a part of PPD file. Nothing to upload here.
164 printer_info->printer_defaults.clear();
165 printer_info->defaults_mime_type.clear();
166 }
167
168 return res;
169}
170
[email protected]4f9dd832012-03-16 08:54:57171std::string PrintBackendCUPS::GetPrinterDriverInfo(
172 const std::string& printer_name) {
[email protected]4f9dd832012-03-16 08:54:57173 std::string result;
[email protected]4f9dd832012-03-16 08:54:57174
thestig3154ea22016-05-16 21:34:03175 cups_dest_t* dest = GetNamedDest(printer_name);
176 if (!dest)
177 return result;
178
179 DCHECK_EQ(printer_name, dest->name);
180 const char* info =
thestigfac2cc42016-11-03 16:31:11181 cupsGetOption(kDriverNameTagName, dest->num_options, dest->options);
thestig3154ea22016-05-16 21:34:03182 if (info)
183 result = *info;
184 cupsFreeDests(1, dest);
[email protected]4f9dd832012-03-16 08:54:57185 return result;
[email protected]de6ac322012-03-05 23:17:25186}
187
[email protected]8e553f492010-10-25 20:05:44188bool PrintBackendCUPS::IsValidPrinter(const std::string& printer_name) {
thestig3154ea22016-05-16 21:34:03189 cups_dest_t* dest = GetNamedDest(printer_name);
190 if (!dest)
191 return false;
[email protected]8e553f492010-10-25 20:05:44192
thestig3154ea22016-05-16 21:34:03193 cupsFreeDests(1, dest);
194 return true;
[email protected]8e553f492010-10-25 20:05:44195}
196
skau8ffe7752016-06-29 02:10:28197#if !defined(OS_CHROMEOS)
skaua1e0b7a62016-12-22 23:23:14198scoped_refptr<PrintBackend> PrintBackend::CreateInstanceImpl(
[email protected]675a1ae2013-10-14 20:24:37199 const base::DictionaryValue* print_backend_settings) {
[email protected]9c6ca602010-12-16 00:55:39200 std::string print_server_url_str, cups_blocking;
[email protected]ce6f5912012-05-16 23:07:30201 int encryption = HTTP_ENCRYPT_NEVER;
[email protected]8e553f492010-10-25 20:05:44202 if (print_backend_settings) {
203 print_backend_settings->GetString(kCUPSPrintServerURL,
204 &print_server_url_str);
[email protected]9c6ca602010-12-16 00:55:39205
206 print_backend_settings->GetString(kCUPSBlocking,
207 &cups_blocking);
[email protected]ce6f5912012-05-16 23:07:30208
209 print_backend_settings->GetInteger(kCUPSEncryption, &encryption);
[email protected]8e553f492010-10-25 20:05:44210 }
iceman4179f17c2017-03-29 19:50:09211 GURL print_server_url(print_server_url_str);
[email protected]ce6f5912012-05-16 23:07:30212 return new PrintBackendCUPS(print_server_url,
213 static_cast<http_encryption_t>(encryption),
214 cups_blocking == kValueTrue);
[email protected]8e553f492010-10-25 20:05:44215}
skau8ffe7752016-06-29 02:10:28216#endif // !defined(OS_CHROMEOS)
[email protected]8e553f492010-10-25 20:05:44217
218int PrintBackendCUPS::GetDests(cups_dest_t** dests) {
Lei Zhangeee56ad62017-08-28 19:25:12219 if (print_server_url_.is_empty()) // Use default (local) print server.
[email protected]8e553f492010-10-25 20:05:44220 return cupsGetDests(dests);
thestigfac2cc42016-11-03 16:31:11221
222 HttpConnectionCUPS http(print_server_url_, cups_encryption_);
223 http.SetBlocking(blocking_);
224 return cupsGetDests2(http.http(), dests);
[email protected]8e553f492010-10-25 20:05:44225}
226
[email protected]023ad6ab2013-02-17 05:07:23227base::FilePath PrintBackendCUPS::GetPPD(const char* name) {
[email protected]8e553f492010-10-25 20:05:44228 // cupsGetPPD returns a filename stored in a static buffer in CUPS.
229 // Protect this code with lock.
[email protected]264300242011-11-07 06:03:30230 CR_DEFINE_STATIC_LOCAL(base::Lock, ppd_lock, ());
[email protected]20305ec2011-01-21 04:55:52231 base::AutoLock ppd_autolock(ppd_lock);
[email protected]79f63882013-02-10 05:15:45232 base::FilePath ppd_path;
thestig3154ea22016-05-16 21:34:03233 const char* ppd_file_path = nullptr;
[email protected]8e553f492010-10-25 20:05:44234 if (print_server_url_.is_empty()) { // Use default (local) print server.
235 ppd_file_path = cupsGetPPD(name);
[email protected]5a49cdca02010-12-06 18:17:39236 if (ppd_file_path)
[email protected]79f63882013-02-10 05:15:45237 ppd_path = base::FilePath(ppd_file_path);
[email protected]8e553f492010-10-25 20:05:44238 } else {
[email protected]5a49cdca02010-12-06 18:17:39239 // cupsGetPPD2 gets stuck sometimes in an infinite time due to network
240 // configuration/issues. To prevent that, use non-blocking http connection
241 // here.
242 // Note: After looking at CUPS sources, it looks like non-blocking
243 // connection will timeout after 10 seconds of no data period. And it will
thestig259626c2015-11-23 20:18:11244 // return the same way as if data was completely and successfully
245 // downloaded.
[email protected]ce6f5912012-05-16 23:07:30246 HttpConnectionCUPS http(print_server_url_, cups_encryption_);
[email protected]9c6ca602010-12-16 00:55:39247 http.SetBlocking(blocking_);
[email protected]8e553f492010-10-25 20:05:44248 ppd_file_path = cupsGetPPD2(http.http(), name);
[email protected]5a49cdca02010-12-06 18:17:39249 // Check if the get full PPD, since non-blocking call may simply return
250 // normally after timeout expired.
251 if (ppd_file_path) {
[email protected]cfde8642011-02-03 23:53:27252 // There is no reliable way right now to detect full and complete PPD
253 // get downloaded. If we reach http timeout, it may simply return
254 // downloaded part as a full response. It might be good enough to check
255 // http->data_remaining or http->_data_remaining, unfortunately http_t
256 // is an internal structure and fields are not exposed in CUPS headers.
257 // httpGetLength or httpGetLength2 returning the full content size.
258 // Comparing file size against that content length might be unreliable
259 // since some http reponses are encoded and content_length > file size.
260 // Let's just check for the obvious CUPS and http errors here.
[email protected]79f63882013-02-10 05:15:45261 ppd_path = base::FilePath(ppd_file_path);
[email protected]cfde8642011-02-03 23:53:27262 ipp_status_t error_code = cupsLastError();
263 int http_error = httpError(http.http());
264 if (error_code > IPP_OK_EVENTS_COMPLETE || http_error != 0) {
thestig3154ea22016-05-16 21:34:03265 LOG(ERROR) << "Error downloading PPD file, name: " << name
[email protected]cfde8642011-02-03 23:53:27266 << ", CUPS error: " << static_cast<int>(error_code)
267 << ", HTTP error: " << http_error;
[email protected]dd3aa792013-07-16 19:10:23268 base::DeleteFile(ppd_path, false);
[email protected]5a49cdca02010-12-06 18:17:39269 ppd_path.clear();
270 }
271 }
[email protected]8e553f492010-10-25 20:05:44272 }
[email protected]8e553f492010-10-25 20:05:44273 return ppd_path;
274}
275
thestig3154ea22016-05-16 21:34:03276cups_dest_t* PrintBackendCUPS::GetNamedDest(const std::string& printer_name) {
277 // Use default (local) print server.
278 if (print_server_url_.is_empty())
279 return cupsGetNamedDest(CUPS_HTTP_DEFAULT, printer_name.c_str(), nullptr);
280
281 HttpConnectionCUPS http(print_server_url_, cups_encryption_);
282 http.SetBlocking(blocking_);
283 return cupsGetNamedDest(http.http(), printer_name.c_str(), nullptr);
284}
285
[email protected]8e553f492010-10-25 20:05:44286} // namespace printing