Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "printing/backend/print_backend.h" | 5 #include "printing/backend/print_backend.h" |
| 6 | 6 |
| 7 #include <dlfcn.h> | 7 #include <dlfcn.h> |
| 8 #include <errno.h> | 8 #include <errno.h> |
| 9 #include <pthread.h> | 9 #include <pthread.h> |
| 10 | 10 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 static const char kCUPSPrinterInfoOpt[] = "printer-info"; | 24 static const char kCUPSPrinterInfoOpt[] = "printer-info"; |
| 25 static const char kCUPSPrinterStateOpt[] = "printer-state"; | 25 static const char kCUPSPrinterStateOpt[] = "printer-state"; |
| 26 static const char kCUPSPrinterTypeOpt[] = "printer-type"; | 26 static const char kCUPSPrinterTypeOpt[] = "printer-type"; |
| 27 static const char kCUPSPrinterMakeModelOpt[] = "printer-make-and-model"; | 27 static const char kCUPSPrinterMakeModelOpt[] = "printer-make-and-model"; |
| 28 | 28 |
| 29 class PrintBackendCUPS : public PrintBackend { | 29 class PrintBackendCUPS : public PrintBackend { |
| 30 public: | 30 public: |
| 31 PrintBackendCUPS(const GURL& print_server_url, | 31 PrintBackendCUPS(const GURL& print_server_url, |
| 32 http_encryption_t encryption, bool blocking); | 32 http_encryption_t encryption, bool blocking); |
| 33 | 33 |
| 34 private: | |
| 35 ~PrintBackendCUPS() override {} | |
| 36 | |
| 34 // PrintBackend implementation. | 37 // PrintBackend implementation. |
| 35 bool EnumeratePrinters(PrinterList* printer_list) override; | 38 bool EnumeratePrinters(PrinterList* printer_list) override; |
| 36 std::string GetDefaultPrinterName() override; | 39 std::string GetDefaultPrinterName() override; |
| 37 bool GetPrinterSemanticCapsAndDefaults( | 40 bool GetPrinterSemanticCapsAndDefaults( |
| 38 const std::string& printer_name, | 41 const std::string& printer_name, |
| 39 PrinterSemanticCapsAndDefaults* printer_info) override; | 42 PrinterSemanticCapsAndDefaults* printer_info) override; |
| 40 bool GetPrinterCapsAndDefaults(const std::string& printer_name, | 43 bool GetPrinterCapsAndDefaults(const std::string& printer_name, |
| 41 PrinterCapsAndDefaults* printer_info) override; | 44 PrinterCapsAndDefaults* printer_info) override; |
| 42 std::string GetPrinterDriverInfo(const std::string& printer_name) override; | 45 std::string GetPrinterDriverInfo(const std::string& printer_name) override; |
| 43 bool IsValidPrinter(const std::string& printer_name) override; | 46 bool IsValidPrinter(const std::string& printer_name) override; |
| 44 | 47 |
| 45 protected: | 48 // The following functions are wrappers around corresponding CUPS functions. |
|
Lei Zhang
2016/05/16 18:43:18
Despite being "wrappers", these two methods are no
| |
| 46 ~PrintBackendCUPS() override {} | 49 // <functions>2() are called when print server is specified, and plain version |
| 47 | 50 // in another case. There is an issue specifying CUPS_HTTP_DEFAULT in the |
| 48 private: | 51 // functions>2(), it does not work in CUPS prior to 1.4. |
| 49 // Following functions are wrappers around corresponding CUPS functions. | |
| 50 // <functions>2() are called when print server is specified, and plain | |
| 51 // version in another case. There is an issue specifing CUPS_HTTP_DEFAULT | |
| 52 // in the <functions>2(), it does not work in CUPS prior to 1.4. | |
| 53 int GetDests(cups_dest_t** dests); | 52 int GetDests(cups_dest_t** dests); |
| 54 base::FilePath GetPPD(const char* name); | 53 base::FilePath GetPPD(const char* name); |
| 55 | 54 |
| 55 // Wrapper around cupsGetNamedDest(). Returned result should be freed with | |
| 56 // cupsFreeDests(). | |
| 57 cups_dest_t* GetNamedDest(const std::string& printer_name); | |
| 58 | |
| 56 GURL print_server_url_; | 59 GURL print_server_url_; |
| 57 http_encryption_t cups_encryption_; | 60 http_encryption_t cups_encryption_; |
| 58 bool blocking_; | 61 bool blocking_; |
| 59 }; | 62 }; |
| 60 | 63 |
| 61 PrintBackendCUPS::PrintBackendCUPS(const GURL& print_server_url, | 64 PrintBackendCUPS::PrintBackendCUPS(const GURL& print_server_url, |
| 62 http_encryption_t encryption, | 65 http_encryption_t encryption, |
| 63 bool blocking) | 66 bool blocking) |
| 64 : print_server_url_(print_server_url), | 67 : print_server_url_(print_server_url), |
| 65 cups_encryption_(encryption), | 68 cups_encryption_(encryption), |
| 66 blocking_(blocking) { | 69 blocking_(blocking) { |
| 67 } | 70 } |
| 68 | 71 |
| 69 bool PrintBackendCUPS::EnumeratePrinters(PrinterList* printer_list) { | 72 bool PrintBackendCUPS::EnumeratePrinters(PrinterList* printer_list) { |
| 70 DCHECK(printer_list); | 73 DCHECK(printer_list); |
| 71 printer_list->clear(); | 74 printer_list->clear(); |
| 72 | 75 |
| 73 cups_dest_t* destinations = NULL; | 76 cups_dest_t* destinations = nullptr; |
| 74 int num_dests = GetDests(&destinations); | 77 int num_dests = GetDests(&destinations); |
| 75 if ((num_dests == 0) && (cupsLastError() > IPP_OK_EVENTS_COMPLETE)) { | 78 if (!num_dests && cupsLastError() > IPP_OK_EVENTS_COMPLETE) { |
| 76 VLOG(1) << "CUPS: Error getting printers from CUPS server" | 79 VLOG(1) << "CUPS: Error getting printers from CUPS server" |
| 77 << ", server: " << print_server_url_ | 80 << ", server: " << print_server_url_ |
| 78 << ", error: " << static_cast<int>(cupsLastError()); | 81 << ", error: " << static_cast<int>(cupsLastError()); |
| 79 return false; | 82 return false; |
| 80 } | 83 } |
| 81 | 84 |
| 82 for (int printer_index = 0; printer_index < num_dests; ++printer_index) { | 85 for (int printer_index = 0; printer_index < num_dests; ++printer_index) { |
| 83 const cups_dest_t& printer = destinations[printer_index]; | 86 const cups_dest_t& printer = destinations[printer_index]; |
| 84 | 87 |
| 85 // CUPS can have 'printers' that are actually scanners. (not MFC) | 88 // CUPS can have 'printers' that are actually scanners. (not MFC) |
| 86 // At least on Mac. Check for scanners and skip them. | 89 // At least on Mac. Check for scanners and skip them. |
| 87 const char* type_str = cupsGetOption(kCUPSPrinterTypeOpt, | 90 const char* type_str = cupsGetOption(kCUPSPrinterTypeOpt, |
| 88 printer.num_options, printer.options); | 91 printer.num_options, printer.options); |
| 89 if (type_str != NULL) { | 92 if (type_str) { |
| 90 int type; | 93 int type; |
| 91 if (base::StringToInt(type_str, &type) && (type & CUPS_PRINTER_SCANNER)) | 94 if (base::StringToInt(type_str, &type) && (type & CUPS_PRINTER_SCANNER)) |
| 92 continue; | 95 continue; |
| 93 } | 96 } |
| 94 | 97 |
| 95 PrinterBasicInfo printer_info; | 98 PrinterBasicInfo printer_info; |
| 96 printer_info.printer_name = printer.name; | 99 printer_info.printer_name = printer.name; |
| 97 printer_info.is_default = printer.is_default; | 100 printer_info.is_default = printer.is_default; |
| 98 | 101 |
| 99 const char* info = cupsGetOption(kCUPSPrinterInfoOpt, | 102 const char* info = cupsGetOption(kCUPSPrinterInfoOpt, |
| 100 printer.num_options, printer.options); | 103 printer.num_options, printer.options); |
| 101 if (info != NULL) | 104 if (info) |
| 102 printer_info.printer_description = info; | 105 printer_info.printer_description = info; |
| 103 | 106 |
| 104 const char* state = cupsGetOption(kCUPSPrinterStateOpt, | 107 const char* state = cupsGetOption(kCUPSPrinterStateOpt, |
| 105 printer.num_options, printer.options); | 108 printer.num_options, printer.options); |
| 106 if (state != NULL) | 109 if (state) |
| 107 base::StringToInt(state, &printer_info.printer_status); | 110 base::StringToInt(state, &printer_info.printer_status); |
| 108 | 111 |
| 109 const char* drv_info = cupsGetOption(kCUPSPrinterMakeModelOpt, | 112 const char* drv_info = cupsGetOption(kCUPSPrinterMakeModelOpt, |
| 110 printer.num_options, | 113 printer.num_options, |
| 111 printer.options); | 114 printer.options); |
| 112 if (drv_info) | 115 if (drv_info) |
| 113 printer_info.options[kDriverInfoTagName] = *drv_info; | 116 printer_info.options[kDriverInfoTagName] = *drv_info; |
| 114 | 117 |
| 115 // Store printer options. | 118 // Store printer options. |
| 116 for (int opt_index = 0; opt_index < printer.num_options; ++opt_index) { | 119 for (int opt_index = 0; opt_index < printer.num_options; ++opt_index) { |
| 117 printer_info.options[printer.options[opt_index].name] = | 120 printer_info.options[printer.options[opt_index].name] = |
| 118 printer.options[opt_index].value; | 121 printer.options[opt_index].value; |
| 119 } | 122 } |
| 120 | 123 |
| 121 printer_list->push_back(printer_info); | 124 printer_list->push_back(printer_info); |
| 122 } | 125 } |
| 123 | 126 |
| 124 cupsFreeDests(num_dests, destinations); | 127 cupsFreeDests(num_dests, destinations); |
| 125 | 128 |
| 126 VLOG(1) << "CUPS: Enumerated printers" | 129 VLOG(1) << "CUPS: Enumerated printers, server: " << print_server_url_ |
| 127 << ", server: " << print_server_url_ | |
| 128 << ", # of printers: " << printer_list->size(); | 130 << ", # of printers: " << printer_list->size(); |
| 129 return true; | 131 return true; |
| 130 } | 132 } |
| 131 | 133 |
| 132 std::string PrintBackendCUPS::GetDefaultPrinterName() { | 134 std::string PrintBackendCUPS::GetDefaultPrinterName() { |
| 133 // Not using cupsGetDefault() because it lies about the default printer. | 135 // Not using cupsGetDefault() because it lies about the default printer. |
| 134 cups_dest_t* dests; | 136 cups_dest_t* dests; |
| 135 int num_dests = GetDests(&dests); | 137 int num_dests = GetDests(&dests); |
| 136 cups_dest_t* dest = cupsGetDest(NULL, NULL, num_dests, dests); | 138 cups_dest_t* dest = cupsGetDest(nullptr, nullptr, num_dests, dests); |
| 137 std::string name = dest ? std::string(dest->name) : std::string(); | 139 std::string name = dest ? std::string(dest->name) : std::string(); |
| 138 cupsFreeDests(num_dests, dests); | 140 cupsFreeDests(num_dests, dests); |
| 139 return name; | 141 return name; |
| 140 } | 142 } |
| 141 | 143 |
| 142 bool PrintBackendCUPS::GetPrinterSemanticCapsAndDefaults( | 144 bool PrintBackendCUPS::GetPrinterSemanticCapsAndDefaults( |
| 143 const std::string& printer_name, | 145 const std::string& printer_name, |
| 144 PrinterSemanticCapsAndDefaults* printer_info) { | 146 PrinterSemanticCapsAndDefaults* printer_info) { |
| 145 PrinterCapsAndDefaults info; | 147 PrinterCapsAndDefaults info; |
| 146 if (!GetPrinterCapsAndDefaults(printer_name, &info) ) | 148 if (!GetPrinterCapsAndDefaults(printer_name, &info) ) |
| 147 return false; | 149 return false; |
| 148 | 150 |
| 149 return ParsePpdCapabilities( | 151 return ParsePpdCapabilities( |
| 150 printer_name, info.printer_capabilities, printer_info); | 152 printer_name, info.printer_capabilities, printer_info); |
| 151 } | 153 } |
| 152 | 154 |
| 153 bool PrintBackendCUPS::GetPrinterCapsAndDefaults( | 155 bool PrintBackendCUPS::GetPrinterCapsAndDefaults( |
| 154 const std::string& printer_name, | 156 const std::string& printer_name, |
| 155 PrinterCapsAndDefaults* printer_info) { | 157 PrinterCapsAndDefaults* printer_info) { |
| 156 DCHECK(printer_info); | 158 DCHECK(printer_info); |
| 157 | 159 |
| 158 VLOG(1) << "CUPS: Getting caps and defaults" | 160 VLOG(1) << "CUPS: Getting caps and defaults, printer name: " << printer_name; |
| 159 << ", printer name: " << printer_name; | |
| 160 | 161 |
| 161 base::FilePath ppd_path(GetPPD(printer_name.c_str())); | 162 base::FilePath ppd_path(GetPPD(printer_name.c_str())); |
| 162 // In some cases CUPS failed to get ppd file. | 163 // In some cases CUPS failed to get ppd file. |
| 163 if (ppd_path.empty()) { | 164 if (ppd_path.empty()) { |
| 164 LOG(ERROR) << "CUPS: Failed to get PPD, printer name: " << printer_name; | 165 LOG(ERROR) << "CUPS: Failed to get PPD, printer name: " << printer_name; |
| 165 return false; | 166 return false; |
| 166 } | 167 } |
| 167 | 168 |
| 168 std::string content; | 169 std::string content; |
| 169 bool res = base::ReadFileToString(ppd_path, &content); | 170 bool res = base::ReadFileToString(ppd_path, &content); |
| 170 | 171 |
| 171 base::DeleteFile(ppd_path, false); | 172 base::DeleteFile(ppd_path, false); |
| 172 | 173 |
| 173 if (res) { | 174 if (res) { |
| 174 printer_info->printer_capabilities.swap(content); | 175 printer_info->printer_capabilities.swap(content); |
| 175 printer_info->caps_mime_type = "application/pagemaker"; | 176 printer_info->caps_mime_type = "application/pagemaker"; |
| 176 // In CUPS, printer defaults is a part of PPD file. Nothing to upload here. | 177 // In CUPS, printer defaults is a part of PPD file. Nothing to upload here. |
| 177 printer_info->printer_defaults.clear(); | 178 printer_info->printer_defaults.clear(); |
| 178 printer_info->defaults_mime_type.clear(); | 179 printer_info->defaults_mime_type.clear(); |
| 179 } | 180 } |
| 180 | 181 |
| 181 return res; | 182 return res; |
| 182 } | 183 } |
| 183 | 184 |
| 184 std::string PrintBackendCUPS::GetPrinterDriverInfo( | 185 std::string PrintBackendCUPS::GetPrinterDriverInfo( |
| 185 const std::string& printer_name) { | 186 const std::string& printer_name) { |
| 186 cups_dest_t* destinations = NULL; | |
| 187 int num_dests = GetDests(&destinations); | |
| 188 std::string result; | 187 std::string result; |
| 189 for (int printer_index = 0; printer_index < num_dests; ++printer_index) { | |
| 190 const cups_dest_t& printer = destinations[printer_index]; | |
| 191 if (printer_name == printer.name) { | |
| 192 const char* info = cupsGetOption(kCUPSPrinterMakeModelOpt, | |
| 193 printer.num_options, | |
| 194 printer.options); | |
| 195 if (info) | |
| 196 result = *info; | |
| 197 } | |
| 198 } | |
| 199 | 188 |
| 200 cupsFreeDests(num_dests, destinations); | 189 cups_dest_t* dest = GetNamedDest(printer_name); |
| 190 if (!dest) | |
| 191 return result; | |
| 192 | |
| 193 DCHECK_EQ(printer_name, dest->name); | |
| 194 const char* info = | |
| 195 cupsGetOption(kCUPSPrinterMakeModelOpt, dest->num_options, dest->options); | |
| 196 if (info) | |
| 197 result = *info; | |
| 198 cupsFreeDests(1, dest); | |
| 201 return result; | 199 return result; |
| 202 } | 200 } |
| 203 | 201 |
| 204 bool PrintBackendCUPS::IsValidPrinter(const std::string& printer_name) { | 202 bool PrintBackendCUPS::IsValidPrinter(const std::string& printer_name) { |
| 205 // This is not very efficient way to get specific printer info. CUPS 1.4 | 203 cups_dest_t* dest = GetNamedDest(printer_name); |
| 206 // supports cupsGetNamedDest() function. However, CUPS 1.4 is not available | 204 if (!dest) |
| 207 // everywhere (for example, it supported from Mac OS 10.6 only). | 205 return false; |
| 208 PrinterList printer_list; | |
| 209 EnumeratePrinters(&printer_list); | |
| 210 | 206 |
| 211 PrinterList::iterator it; | 207 cupsFreeDests(1, dest); |
| 212 for (it = printer_list.begin(); it != printer_list.end(); ++it) | 208 return true; |
| 213 if (it->printer_name == printer_name) | |
| 214 return true; | |
| 215 return false; | |
| 216 } | 209 } |
| 217 | 210 |
| 218 scoped_refptr<PrintBackend> PrintBackend::CreateInstance( | 211 scoped_refptr<PrintBackend> PrintBackend::CreateInstance( |
| 219 const base::DictionaryValue* print_backend_settings) { | 212 const base::DictionaryValue* print_backend_settings) { |
| 220 std::string print_server_url_str, cups_blocking; | 213 std::string print_server_url_str, cups_blocking; |
| 221 int encryption = HTTP_ENCRYPT_NEVER; | 214 int encryption = HTTP_ENCRYPT_NEVER; |
| 222 if (print_backend_settings) { | 215 if (print_backend_settings) { |
| 223 print_backend_settings->GetString(kCUPSPrintServerURL, | 216 print_backend_settings->GetString(kCUPSPrintServerURL, |
| 224 &print_server_url_str); | 217 &print_server_url_str); |
| 225 | 218 |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 249 return cupsGetDests2(http.http(), dests); | 242 return cupsGetDests2(http.http(), dests); |
| 250 } | 243 } |
| 251 } | 244 } |
| 252 | 245 |
| 253 base::FilePath PrintBackendCUPS::GetPPD(const char* name) { | 246 base::FilePath PrintBackendCUPS::GetPPD(const char* name) { |
| 254 // cupsGetPPD returns a filename stored in a static buffer in CUPS. | 247 // cupsGetPPD returns a filename stored in a static buffer in CUPS. |
| 255 // Protect this code with lock. | 248 // Protect this code with lock. |
| 256 CR_DEFINE_STATIC_LOCAL(base::Lock, ppd_lock, ()); | 249 CR_DEFINE_STATIC_LOCAL(base::Lock, ppd_lock, ()); |
| 257 base::AutoLock ppd_autolock(ppd_lock); | 250 base::AutoLock ppd_autolock(ppd_lock); |
| 258 base::FilePath ppd_path; | 251 base::FilePath ppd_path; |
| 259 const char* ppd_file_path = NULL; | 252 const char* ppd_file_path = nullptr; |
| 260 if (print_server_url_.is_empty()) { // Use default (local) print server. | 253 if (print_server_url_.is_empty()) { // Use default (local) print server. |
| 261 ppd_file_path = cupsGetPPD(name); | 254 ppd_file_path = cupsGetPPD(name); |
| 262 if (ppd_file_path) | 255 if (ppd_file_path) |
| 263 ppd_path = base::FilePath(ppd_file_path); | 256 ppd_path = base::FilePath(ppd_file_path); |
| 264 } else { | 257 } else { |
| 265 // cupsGetPPD2 gets stuck sometimes in an infinite time due to network | 258 // cupsGetPPD2 gets stuck sometimes in an infinite time due to network |
| 266 // configuration/issues. To prevent that, use non-blocking http connection | 259 // configuration/issues. To prevent that, use non-blocking http connection |
| 267 // here. | 260 // here. |
| 268 // Note: After looking at CUPS sources, it looks like non-blocking | 261 // Note: After looking at CUPS sources, it looks like non-blocking |
| 269 // connection will timeout after 10 seconds of no data period. And it will | 262 // connection will timeout after 10 seconds of no data period. And it will |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 281 // http->data_remaining or http->_data_remaining, unfortunately http_t | 274 // http->data_remaining or http->_data_remaining, unfortunately http_t |
| 282 // is an internal structure and fields are not exposed in CUPS headers. | 275 // is an internal structure and fields are not exposed in CUPS headers. |
| 283 // httpGetLength or httpGetLength2 returning the full content size. | 276 // httpGetLength or httpGetLength2 returning the full content size. |
| 284 // Comparing file size against that content length might be unreliable | 277 // Comparing file size against that content length might be unreliable |
| 285 // since some http reponses are encoded and content_length > file size. | 278 // since some http reponses are encoded and content_length > file size. |
| 286 // Let's just check for the obvious CUPS and http errors here. | 279 // Let's just check for the obvious CUPS and http errors here. |
| 287 ppd_path = base::FilePath(ppd_file_path); | 280 ppd_path = base::FilePath(ppd_file_path); |
| 288 ipp_status_t error_code = cupsLastError(); | 281 ipp_status_t error_code = cupsLastError(); |
| 289 int http_error = httpError(http.http()); | 282 int http_error = httpError(http.http()); |
| 290 if (error_code > IPP_OK_EVENTS_COMPLETE || http_error != 0) { | 283 if (error_code > IPP_OK_EVENTS_COMPLETE || http_error != 0) { |
| 291 LOG(ERROR) << "Error downloading PPD file" | 284 LOG(ERROR) << "Error downloading PPD file, name: " << name |
| 292 << ", name: " << name | |
| 293 << ", CUPS error: " << static_cast<int>(error_code) | 285 << ", CUPS error: " << static_cast<int>(error_code) |
| 294 << ", HTTP error: " << http_error; | 286 << ", HTTP error: " << http_error; |
| 295 base::DeleteFile(ppd_path, false); | 287 base::DeleteFile(ppd_path, false); |
| 296 ppd_path.clear(); | 288 ppd_path.clear(); |
| 297 } | 289 } |
| 298 } | 290 } |
| 299 } | 291 } |
| 300 return ppd_path; | 292 return ppd_path; |
| 301 } | 293 } |
| 302 | 294 |
| 295 cups_dest_t* PrintBackendCUPS::GetNamedDest(const std::string& printer_name) { | |
| 296 // Use default (local) print server. | |
| 297 if (print_server_url_.is_empty()) | |
| 298 return cupsGetNamedDest(CUPS_HTTP_DEFAULT, printer_name.c_str(), nullptr); | |
| 299 | |
| 300 HttpConnectionCUPS http(print_server_url_, cups_encryption_); | |
| 301 http.SetBlocking(blocking_); | |
| 302 return cupsGetNamedDest(http.http(), printer_name.c_str(), nullptr); | |
| 303 } | |
| 304 | |
| 303 } // namespace printing | 305 } // namespace printing |
| OLD | NEW |