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