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. 4 5#include "printing/backend/print_backend.h" 6 7#include "build/build_config.h" 8 9#include <dlfcn.h> 10#include <errno.h> 11#include <pthread.h> 12 13#if defined(OS_MACOSX) 14#include <AvailabilityMacros.h> 15#else 16#include <gcrypt.h> 17#endif 18 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" 29 30#if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 4) 31const int CUPS_PRINTER_SCANNER = 0x2000000; // Scanner-only device 32#endif 33 34#if !defined(OS_MACOSX) 35GCRY_THREAD_OPTION_PTHREAD_IMPL; 36 37namespace { 38 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. 45 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). 49 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(). 55 56class GcryptInitializer { 57 public: 58 GcryptInitializer() { 59 Init(); 60 } 61 62 private: 63 void Init() { 64 const char* kGnuTlsFiles[] = { 65 "libgnutls.so.28", 66 "libgnutls.so.26", 67 "libgnutls.so", 68 }; 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; 75 } 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; 83 } 84 { 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"; 91 } 92 return; 93 } 94 LOG(ERROR) << "Cannot find libgnutls"; 95 } 96}; 97 98base::LazyInstance<GcryptInitializer> g_gcrypt_initializer = 99 LAZY_INSTANCE_INITIALIZER; 100 101} // namespace 102#endif // !defined(OS_MACOSX) 103 104namespace printing { 105 106static const char kCUPSPrinterInfoOpt[] = "printer-info"; 107static const char kCUPSPrinterStateOpt[] = "printer-state"; 108static const char kCUPSPrinterTypeOpt[] = "printer-type"; 109static const char kCUPSPrinterMakeModelOpt[] = "printer-make-and-model"; 110 111class PrintBackendCUPS : public PrintBackend { 112 public: 113 PrintBackendCUPS(const GURL& print_server_url, 114 http_encryption_t encryption, bool blocking); 115 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; 128 129 protected: 130 virtual ~PrintBackendCUPS() {} 131 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); 139 140 GURL print_server_url_; 141 http_encryption_t cups_encryption_; 142 bool blocking_; 143}; 144 145PrintBackendCUPS::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) { 151} 152 153bool PrintBackendCUPS::EnumeratePrinters(PrinterList* printer_list) { 154 DCHECK(printer_list); 155 printer_list->clear(); 156 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; 164 } 165 166 for (int printer_index = 0; printer_index < num_dests; printer_index++) { 167 const cups_dest_t& printer = destinations[printer_index]; 168 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; 177 } 178 179 PrinterBasicInfo printer_info; 180 printer_info.printer_name = printer.name; 181 printer_info.is_default = printer.is_default; 182 183 const char* info = cupsGetOption(kCUPSPrinterInfoOpt, 184 printer.num_options, printer.options); 185 if (info != NULL) 186 printer_info.printer_description = info; 187 188 const char* state = cupsGetOption(kCUPSPrinterStateOpt, 189 printer.num_options, printer.options); 190 if (state != NULL) 191 base::StringToInt(state, &printer_info.printer_status); 192 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; 198 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; 203 } 204 205 printer_list->push_back(printer_info); 206 } 207 208 cupsFreeDests(num_dests, destinations); 209 210 VLOG(1) << "CUPS: Enumerated printers" 211 << ", server: " << print_server_url_ 212 << ", # of printers: " << printer_list->size(); 213 return true; 214} 215 216std::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; 224} 225 226bool PrintBackendCUPS::GetPrinterSemanticCapsAndDefaults( 227 const std::string& printer_name, 228 PrinterSemanticCapsAndDefaults* printer_info) { 229 PrinterCapsAndDefaults info; 230 if (!GetPrinterCapsAndDefaults(printer_name, &info) ) 231 return false; 232 233 return parsePpdCapabilities( 234 printer_name, info.printer_capabilities, printer_info); 235} 236 237bool PrintBackendCUPS::GetPrinterCapsAndDefaults( 238 const std::string& printer_name, 239 PrinterCapsAndDefaults* printer_info) { 240 DCHECK(printer_info); 241 242 VLOG(1) << "CUPS: Getting caps and defaults" 243 << ", printer name: " << printer_name; 244 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; 251 } 252 253 std::string content; 254 bool res = file_util::ReadFileToString(ppd_path, &content); 255 256 base::DeleteFile(ppd_path, false); 257 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(); 264 } 265 266 return res; 267} 268 269std::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; 282 } 283 } 284 285 cupsFreeDests(num_dests, destinations); 286 return result; 287} 288 289bool 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); 295 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; 301} 302 303scoped_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 309 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); 315 316 print_backend_settings->GetString(kCUPSBlocking, 317 &cups_blocking); 318 319 print_backend_settings->GetInteger(kCUPSEncryption, &encryption); 320 } 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); 325} 326 327int 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); 334 } 335} 336 337base::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(); 380 } 381 } 382 } 383 return ppd_path; 384} 385 386} // namespace printing 387