cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / printing / backend / print_backend_cups.cc
blob2eb21438078a43386d6a144fab973ddbfff58a1c
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 "printing/backend/print_backend.h"
7 #include "build/build_config.h"
9 #include <dlfcn.h>
10 #include <errno.h>
11 #include <pthread.h>
13 #if defined(OS_MACOSX)
14 #include <AvailabilityMacros.h>
15 #else
16 #include <gcrypt.h>
17 #endif
19 #include "base/debug/leak_annotations.h"
20 #include "base/file_util.h"
21 #include "base/lazy_instance.h"
22 #include "base/logging.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/synchronization/lock.h"
25 #include "base/values.h"
26 #include "printing/backend/cups_helper.h"
27 #include "printing/backend/print_backend_consts.h"
28 #include "url/gurl.h"
30 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 4)
31 const int CUPS_PRINTER_SCANNER = 0x2000000; // Scanner-only device
32 #endif
34 #if !defined(OS_MACOSX)
35 GCRY_THREAD_OPTION_PTHREAD_IMPL;
37 namespace {
39 // Init GCrypt library (needed for CUPS) using pthreads.
40 // There exists a bug in CUPS library, where it crashed with: "ath.c:184:
41 // _gcry_ath_mutex_lock: Assertion `*lock == ((ath_mutex_t) 0)' failed."
42 // It happened when multiple threads tried printing simultaneously.
43 // Google search for 'gnutls thread safety' provided a solution that
44 // initialized gcrypt and gnutls.
46 // TODO(phajdan.jr): Remove this after https://bugs.g10code.com/gnupg/issue1197
47 // gets fixed on all Linux distros we support (i.e. when they ship libgcrypt
48 // with the fix).
50 // Initially, we linked with -lgnutls and simply called gnutls_global_init(),
51 // but this did not work well since we build one binary on Ubuntu Hardy and
52 // expect it to run on many Linux distros. (See http://crbug.com/46954)
53 // So instead we use dlopen() and dlsym() to dynamically load and call
54 // gnutls_global_init().
56 class GcryptInitializer {
57 public:
58 GcryptInitializer() {
59 Init();
62 private:
63 void Init() {
64 const char* kGnuTlsFiles[] = {
65 "libgnutls.so.28",
66 "libgnutls.so.26",
67 "libgnutls.so",
69 gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
70 for (size_t i = 0; i < arraysize(kGnuTlsFiles); ++i) {
71 void* gnutls_lib = dlopen(kGnuTlsFiles[i], RTLD_NOW);
72 if (!gnutls_lib) {
73 VLOG(1) << "Cannot load " << kGnuTlsFiles[i];
74 continue;
76 const char* kGnuTlsInitFuncName = "gnutls_global_init";
77 int (*pgnutls_global_init)(void) = reinterpret_cast<int(*)()>(
78 dlsym(gnutls_lib, kGnuTlsInitFuncName));
79 if (!pgnutls_global_init) {
80 VLOG(1) << "Could not find " << kGnuTlsInitFuncName
81 << " in " << kGnuTlsFiles[i];
82 continue;
85 // GnuTLS has a genuine small memory leak that is easier to annotate
86 // than suppress. See http://crbug.com/176888#c7
87 // TODO(earthdok): remove this once the leak is fixed.
88 ANNOTATE_SCOPED_MEMORY_LEAK;
89 if ((*pgnutls_global_init)() != 0)
90 LOG(ERROR) << "gnutls_global_init() failed";
92 return;
94 LOG(ERROR) << "Cannot find libgnutls";
98 base::LazyInstance<GcryptInitializer> g_gcrypt_initializer =
99 LAZY_INSTANCE_INITIALIZER;
101 } // namespace
102 #endif // !defined(OS_MACOSX)
104 namespace printing {
106 static const char kCUPSPrinterInfoOpt[] = "printer-info";
107 static const char kCUPSPrinterStateOpt[] = "printer-state";
108 static const char kCUPSPrinterTypeOpt[] = "printer-type";
109 static const char kCUPSPrinterMakeModelOpt[] = "printer-make-and-model";
111 class PrintBackendCUPS : public PrintBackend {
112 public:
113 PrintBackendCUPS(const GURL& print_server_url,
114 http_encryption_t encryption, bool blocking);
116 // PrintBackend implementation.
117 virtual bool EnumeratePrinters(PrinterList* printer_list) OVERRIDE;
118 virtual std::string GetDefaultPrinterName() OVERRIDE;
119 virtual bool GetPrinterSemanticCapsAndDefaults(
120 const std::string& printer_name,
121 PrinterSemanticCapsAndDefaults* printer_info) OVERRIDE;
122 virtual bool GetPrinterCapsAndDefaults(
123 const std::string& printer_name,
124 PrinterCapsAndDefaults* printer_info) OVERRIDE;
125 virtual std::string GetPrinterDriverInfo(
126 const std::string& printer_name) OVERRIDE;
127 virtual bool IsValidPrinter(const std::string& printer_name) OVERRIDE;
129 protected:
130 virtual ~PrintBackendCUPS() {}
132 private:
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 GetDests(cups_dest_t** dests);
138 base::FilePath GetPPD(const char* name);
140 GURL print_server_url_;
141 http_encryption_t cups_encryption_;
142 bool blocking_;
145 PrintBackendCUPS::PrintBackendCUPS(const GURL& print_server_url,
146 http_encryption_t encryption,
147 bool blocking)
148 : print_server_url_(print_server_url),
149 cups_encryption_(encryption),
150 blocking_(blocking) {
153 bool PrintBackendCUPS::EnumeratePrinters(PrinterList* printer_list) {
154 DCHECK(printer_list);
155 printer_list->clear();
157 cups_dest_t* destinations = NULL;
158 int num_dests = GetDests(&destinations);
159 if ((num_dests == 0) && (cupsLastError() > IPP_OK_EVENTS_COMPLETE)) {
160 VLOG(1) << "CUPS: Error getting printers from CUPS server"
161 << ", server: " << print_server_url_
162 << ", error: " << static_cast<int>(cupsLastError());
163 return false;
166 for (int printer_index = 0; printer_index < num_dests; printer_index++) {
167 const cups_dest_t& printer = destinations[printer_index];
169 // CUPS can have 'printers' that are actually scanners. (not MFC)
170 // At least on Mac. Check for scanners and skip them.
171 const char* type_str = cupsGetOption(kCUPSPrinterTypeOpt,
172 printer.num_options, printer.options);
173 if (type_str != NULL) {
174 int type;
175 if (base::StringToInt(type_str, &type) && (type & CUPS_PRINTER_SCANNER))
176 continue;
179 PrinterBasicInfo printer_info;
180 printer_info.printer_name = printer.name;
181 printer_info.is_default = printer.is_default;
183 const char* info = cupsGetOption(kCUPSPrinterInfoOpt,
184 printer.num_options, printer.options);
185 if (info != NULL)
186 printer_info.printer_description = info;
188 const char* state = cupsGetOption(kCUPSPrinterStateOpt,
189 printer.num_options, printer.options);
190 if (state != NULL)
191 base::StringToInt(state, &printer_info.printer_status);
193 const char* drv_info = cupsGetOption(kCUPSPrinterMakeModelOpt,
194 printer.num_options,
195 printer.options);
196 if (drv_info)
197 printer_info.options[kDriverInfoTagName] = *drv_info;
199 // Store printer options.
200 for (int opt_index = 0; opt_index < printer.num_options; opt_index++) {
201 printer_info.options[printer.options[opt_index].name] =
202 printer.options[opt_index].value;
205 printer_list->push_back(printer_info);
208 cupsFreeDests(num_dests, destinations);
210 VLOG(1) << "CUPS: Enumerated printers"
211 << ", server: " << print_server_url_
212 << ", # of printers: " << printer_list->size();
213 return true;
216 std::string PrintBackendCUPS::GetDefaultPrinterName() {
217 // Not using cupsGetDefault() because it lies about the default printer.
218 cups_dest_t* dests;
219 int num_dests = GetDests(&dests);
220 cups_dest_t* dest = cupsGetDest(NULL, NULL, num_dests, dests);
221 std::string name = dest ? std::string(dest->name) : std::string();
222 cupsFreeDests(num_dests, dests);
223 return name;
226 bool PrintBackendCUPS::GetPrinterSemanticCapsAndDefaults(
227 const std::string& printer_name,
228 PrinterSemanticCapsAndDefaults* printer_info) {
229 PrinterCapsAndDefaults info;
230 if (!GetPrinterCapsAndDefaults(printer_name, &info) )
231 return false;
233 return parsePpdCapabilities(
234 printer_name, info.printer_capabilities, printer_info);
237 bool PrintBackendCUPS::GetPrinterCapsAndDefaults(
238 const std::string& printer_name,
239 PrinterCapsAndDefaults* printer_info) {
240 DCHECK(printer_info);
242 VLOG(1) << "CUPS: Getting caps and defaults"
243 << ", printer name: " << printer_name;
245 base::FilePath ppd_path(GetPPD(printer_name.c_str()));
246 // In some cases CUPS failed to get ppd file.
247 if (ppd_path.empty()) {
248 LOG(ERROR) << "CUPS: Failed to get PPD"
249 << ", printer name: " << printer_name;
250 return false;
253 std::string content;
254 bool res = base::ReadFileToString(ppd_path, &content);
256 base::DeleteFile(ppd_path, false);
258 if (res) {
259 printer_info->printer_capabilities.swap(content);
260 printer_info->caps_mime_type = "application/pagemaker";
261 // In CUPS, printer defaults is a part of PPD file. Nothing to upload here.
262 printer_info->printer_defaults.clear();
263 printer_info->defaults_mime_type.clear();
266 return res;
269 std::string PrintBackendCUPS::GetPrinterDriverInfo(
270 const std::string& printer_name) {
271 cups_dest_t* destinations = NULL;
272 int num_dests = GetDests(&destinations);
273 std::string result;
274 for (int printer_index = 0; printer_index < num_dests; printer_index++) {
275 const cups_dest_t& printer = destinations[printer_index];
276 if (printer_name == printer.name) {
277 const char* info = cupsGetOption(kCUPSPrinterMakeModelOpt,
278 printer.num_options,
279 printer.options);
280 if (info)
281 result = *info;
285 cupsFreeDests(num_dests, destinations);
286 return result;
289 bool PrintBackendCUPS::IsValidPrinter(const std::string& printer_name) {
290 // This is not very efficient way to get specific printer info. CUPS 1.4
291 // supports cupsGetNamedDest() function. However, CUPS 1.4 is not available
292 // everywhere (for example, it supported from Mac OS 10.6 only).
293 PrinterList printer_list;
294 EnumeratePrinters(&printer_list);
296 PrinterList::iterator it;
297 for (it = printer_list.begin(); it != printer_list.end(); ++it)
298 if (it->printer_name == printer_name)
299 return true;
300 return false;
303 scoped_refptr<PrintBackend> PrintBackend::CreateInstance(
304 const DictionaryValue* print_backend_settings) {
305 #if !defined(OS_MACOSX)
306 // Initialize gcrypt library.
307 g_gcrypt_initializer.Get();
308 #endif
310 std::string print_server_url_str, cups_blocking;
311 int encryption = HTTP_ENCRYPT_NEVER;
312 if (print_backend_settings) {
313 print_backend_settings->GetString(kCUPSPrintServerURL,
314 &print_server_url_str);
316 print_backend_settings->GetString(kCUPSBlocking,
317 &cups_blocking);
319 print_backend_settings->GetInteger(kCUPSEncryption, &encryption);
321 GURL print_server_url(print_server_url_str.c_str());
322 return new PrintBackendCUPS(print_server_url,
323 static_cast<http_encryption_t>(encryption),
324 cups_blocking == kValueTrue);
327 int PrintBackendCUPS::GetDests(cups_dest_t** dests) {
328 if (print_server_url_.is_empty()) { // Use default (local) print server.
329 return cupsGetDests(dests);
330 } else {
331 HttpConnectionCUPS http(print_server_url_, cups_encryption_);
332 http.SetBlocking(blocking_);
333 return cupsGetDests2(http.http(), dests);
337 base::FilePath PrintBackendCUPS::GetPPD(const char* name) {
338 // cupsGetPPD returns a filename stored in a static buffer in CUPS.
339 // Protect this code with lock.
340 CR_DEFINE_STATIC_LOCAL(base::Lock, ppd_lock, ());
341 base::AutoLock ppd_autolock(ppd_lock);
342 base::FilePath ppd_path;
343 const char* ppd_file_path = NULL;
344 if (print_server_url_.is_empty()) { // Use default (local) print server.
345 ppd_file_path = cupsGetPPD(name);
346 if (ppd_file_path)
347 ppd_path = base::FilePath(ppd_file_path);
348 } else {
349 // cupsGetPPD2 gets stuck sometimes in an infinite time due to network
350 // configuration/issues. To prevent that, use non-blocking http connection
351 // here.
352 // Note: After looking at CUPS sources, it looks like non-blocking
353 // connection will timeout after 10 seconds of no data period. And it will
354 // return the same way as if data was completely and sucessfully downloaded.
355 HttpConnectionCUPS http(print_server_url_, cups_encryption_);
356 http.SetBlocking(blocking_);
357 ppd_file_path = cupsGetPPD2(http.http(), name);
358 // Check if the get full PPD, since non-blocking call may simply return
359 // normally after timeout expired.
360 if (ppd_file_path) {
361 // There is no reliable way right now to detect full and complete PPD
362 // get downloaded. If we reach http timeout, it may simply return
363 // downloaded part as a full response. It might be good enough to check
364 // http->data_remaining or http->_data_remaining, unfortunately http_t
365 // is an internal structure and fields are not exposed in CUPS headers.
366 // httpGetLength or httpGetLength2 returning the full content size.
367 // Comparing file size against that content length might be unreliable
368 // since some http reponses are encoded and content_length > file size.
369 // Let's just check for the obvious CUPS and http errors here.
370 ppd_path = base::FilePath(ppd_file_path);
371 ipp_status_t error_code = cupsLastError();
372 int http_error = httpError(http.http());
373 if (error_code > IPP_OK_EVENTS_COMPLETE || http_error != 0) {
374 LOG(ERROR) << "Error downloading PPD file"
375 << ", name: " << name
376 << ", CUPS error: " << static_cast<int>(error_code)
377 << ", HTTP error: " << http_error;
378 base::DeleteFile(ppd_path, false);
379 ppd_path.clear();
383 return ppd_path;
386 } // namespace printing