Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / printing / backend / print_backend_cups.cc
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 #include "base/debug/leak_annotations.h"
14 #include "base/files/file_util.h"
15 #include "base/lazy_instance.h"
16 #include "base/logging.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/synchronization/lock.h"
19 #include "base/values.h"
20 #include "printing/backend/cups_helper.h"
21 #include "printing/backend/print_backend_consts.h"
22 #include "url/gurl.h"
23
24 namespace printing {
25
26 static const char kCUPSPrinterInfoOpt[] = "printer-info";
27 static const char kCUPSPrinterStateOpt[] = "printer-state";
28 static const char kCUPSPrinterTypeOpt[] = "printer-type";
29 static const char kCUPSPrinterMakeModelOpt[] = "printer-make-and-model";
30
31 class PrintBackendCUPS : public PrintBackend {
32  public:
33   PrintBackendCUPS(const GURL& print_server_url,
34                    http_encryption_t encryption, bool blocking);
35
36   // PrintBackend implementation.
37   bool EnumeratePrinters(PrinterList* printer_list) override;
38   std::string GetDefaultPrinterName() override;
39   bool GetPrinterSemanticCapsAndDefaults(
40       const std::string& printer_name,
41       PrinterSemanticCapsAndDefaults* printer_info) override;
42   bool GetPrinterCapsAndDefaults(const std::string& printer_name,
43                                  PrinterCapsAndDefaults* printer_info) override;
44   std::string GetPrinterDriverInfo(const std::string& printer_name) override;
45   bool IsValidPrinter(const std::string& printer_name) override;
46
47  protected:
48   ~PrintBackendCUPS() override {}
49
50  private:
51   // Following functions are wrappers around corresponding CUPS functions.
52   // <functions>2()  are called when print server is specified, and plain
53   // version in another case. There is an issue specifing CUPS_HTTP_DEFAULT
54   // in the <functions>2(), it does not work in CUPS prior to 1.4.
55   int GetDests(cups_dest_t** dests);
56   base::FilePath GetPPD(const char* name);
57
58   GURL print_server_url_;
59   http_encryption_t cups_encryption_;
60   bool blocking_;
61 };
62
63 PrintBackendCUPS::PrintBackendCUPS(const GURL& print_server_url,
64                                    http_encryption_t encryption,
65                                    bool blocking)
66     : print_server_url_(print_server_url),
67       cups_encryption_(encryption),
68       blocking_(blocking) {
69 }
70
71 bool PrintBackendCUPS::EnumeratePrinters(PrinterList* printer_list) {
72   DCHECK(printer_list);
73   printer_list->clear();
74
75   cups_dest_t* destinations = NULL;
76   int num_dests = GetDests(&destinations);
77   if ((num_dests == 0) && (cupsLastError() > IPP_OK_EVENTS_COMPLETE)) {
78     VLOG(1) << "CUPS: Error getting printers from CUPS server"
79             << ", server: " << print_server_url_
80             << ", error: " << static_cast<int>(cupsLastError());
81     return false;
82   }
83
84   for (int printer_index = 0; printer_index < num_dests; ++printer_index) {
85     const cups_dest_t& printer = destinations[printer_index];
86
87     // CUPS can have 'printers' that are actually scanners. (not MFC)
88     // At least on Mac. Check for scanners and skip them.
89     const char* type_str = cupsGetOption(kCUPSPrinterTypeOpt,
90         printer.num_options, printer.options);
91     if (type_str != NULL) {
92       int type;
93       if (base::StringToInt(type_str, &type) && (type & CUPS_PRINTER_SCANNER))
94         continue;
95     }
96
97     PrinterBasicInfo printer_info;
98     printer_info.printer_name = printer.name;
99     printer_info.is_default = printer.is_default;
100
101     const char* info = cupsGetOption(kCUPSPrinterInfoOpt,
102         printer.num_options, printer.options);
103     if (info != NULL)
104       printer_info.printer_description = info;
105
106     const char* state = cupsGetOption(kCUPSPrinterStateOpt,
107         printer.num_options, printer.options);
108     if (state != NULL)
109       base::StringToInt(state, &printer_info.printer_status);
110
111     const char* drv_info = cupsGetOption(kCUPSPrinterMakeModelOpt,
112                                          printer.num_options,
113                                          printer.options);
114     if (drv_info)
115       printer_info.options[kDriverInfoTagName] = *drv_info;
116
117     // Store printer options.
118     for (int opt_index = 0; opt_index < printer.num_options; ++opt_index) {
119       printer_info.options[printer.options[opt_index].name] =
120           printer.options[opt_index].value;
121     }
122
123     printer_list->push_back(printer_info);
124   }
125
126   cupsFreeDests(num_dests, destinations);
127
128   VLOG(1) << "CUPS: Enumerated printers"
129           << ", server: " << print_server_url_
130           << ", # of printers: " << printer_list->size();
131   return true;
132 }
133
134 std::string PrintBackendCUPS::GetDefaultPrinterName() {
135   // Not using cupsGetDefault() because it lies about the default printer.
136   cups_dest_t* dests;
137   int num_dests = GetDests(&dests);
138   cups_dest_t* dest = cupsGetDest(NULL, NULL, num_dests, dests);
139   std::string name = dest ? std::string(dest->name) : std::string();
140   cupsFreeDests(num_dests, dests);
141   return name;
142 }
143
144 bool PrintBackendCUPS::GetPrinterSemanticCapsAndDefaults(
145     const std::string& printer_name,
146     PrinterSemanticCapsAndDefaults* printer_info) {
147   PrinterCapsAndDefaults info;
148   if (!GetPrinterCapsAndDefaults(printer_name, &info) )
149     return false;
150
151   return ParsePpdCapabilities(
152       printer_name, info.printer_capabilities, printer_info);
153 }
154
155 bool PrintBackendCUPS::GetPrinterCapsAndDefaults(
156     const std::string& printer_name,
157     PrinterCapsAndDefaults* printer_info) {
158   DCHECK(printer_info);
159
160   VLOG(1) << "CUPS: Getting caps and defaults"
161           << ", printer name: " << printer_name;
162
163   base::FilePath ppd_path(GetPPD(printer_name.c_str()));
164   // In some cases CUPS failed to get ppd file.
165   if (ppd_path.empty()) {
166     LOG(ERROR) << "CUPS: Failed to get PPD, printer name: " << printer_name;
167     return false;
168   }
169
170   std::string content;
171   bool res = base::ReadFileToString(ppd_path, &content);
172
173   base::DeleteFile(ppd_path, false);
174
175   if (res) {
176     printer_info->printer_capabilities.swap(content);
177     printer_info->caps_mime_type = "application/pagemaker";
178     // In CUPS, printer defaults is a part of PPD file. Nothing to upload here.
179     printer_info->printer_defaults.clear();
180     printer_info->defaults_mime_type.clear();
181   }
182
183   return res;
184 }
185
186 std::string PrintBackendCUPS::GetPrinterDriverInfo(
187     const std::string& printer_name) {
188   cups_dest_t* destinations = NULL;
189   int num_dests = GetDests(&destinations);
190   std::string result;
191   for (int printer_index = 0; printer_index < num_dests; ++printer_index) {
192     const cups_dest_t& printer = destinations[printer_index];
193     if (printer_name == printer.name) {
194       const char* info = cupsGetOption(kCUPSPrinterMakeModelOpt,
195                                        printer.num_options,
196                                        printer.options);
197       if (info)
198         result = *info;
199     }
200   }
201
202   cupsFreeDests(num_dests, destinations);
203   return result;
204 }
205
206 bool PrintBackendCUPS::IsValidPrinter(const std::string& printer_name) {
207   // This is not very efficient way to get specific printer info. CUPS 1.4
208   // supports cupsGetNamedDest() function. However, CUPS 1.4 is not available
209   // everywhere (for example, it supported from Mac OS 10.6 only).
210   PrinterList printer_list;
211   EnumeratePrinters(&printer_list);
212
213   PrinterList::iterator it;
214   for (it = printer_list.begin(); it != printer_list.end(); ++it)
215     if (it->printer_name == printer_name)
216       return true;
217   return false;
218 }
219
220 scoped_refptr<PrintBackend> PrintBackend::CreateInstance(
221     const base::DictionaryValue* print_backend_settings) {
222   std::string print_server_url_str, cups_blocking;
223   int encryption = HTTP_ENCRYPT_NEVER;
224   if (print_backend_settings) {
225     print_backend_settings->GetString(kCUPSPrintServerURL,
226                                       &print_server_url_str);
227
228     print_backend_settings->GetString(kCUPSBlocking,
229                                       &cups_blocking);
230
231     print_backend_settings->GetInteger(kCUPSEncryption, &encryption);
232   }
233   GURL print_server_url(print_server_url_str.c_str());
234   return new PrintBackendCUPS(print_server_url,
235                               static_cast<http_encryption_t>(encryption),
236                               cups_blocking == kValueTrue);
237 }
238
239 int PrintBackendCUPS::GetDests(cups_dest_t** dests) {
240   if (print_server_url_.is_empty()) {  // Use default (local) print server.
241     // GnuTLS has a genuine small memory leak that is easier to annotate
242     // than suppress. See http://crbug.com/176888#c7
243     // In theory any CUPS function can trigger this leak, but in
244     // PrintBackendCUPS, this is the most likely spot.
245     // TODO(earthdok): remove this once the leak is fixed.
246     ANNOTATE_SCOPED_MEMORY_LEAK;
247     return cupsGetDests(dests);
248   } else {
249     HttpConnectionCUPS http(print_server_url_, cups_encryption_);
250     http.SetBlocking(blocking_);
251     return cupsGetDests2(http.http(), dests);
252   }
253 }
254
255 base::FilePath PrintBackendCUPS::GetPPD(const char* name) {
256   // cupsGetPPD returns a filename stored in a static buffer in CUPS.
257   // Protect this code with lock.
258   CR_DEFINE_STATIC_LOCAL(base::Lock, ppd_lock, ());
259   base::AutoLock ppd_autolock(ppd_lock);
260   base::FilePath ppd_path;
261   const char* ppd_file_path = NULL;
262   if (print_server_url_.is_empty()) {  // Use default (local) print server.
263     ppd_file_path = cupsGetPPD(name);
264     if (ppd_file_path)
265       ppd_path = base::FilePath(ppd_file_path);
266   } else {
267     // cupsGetPPD2 gets stuck sometimes in an infinite time due to network
268     // configuration/issues. To prevent that, use non-blocking http connection
269     // here.
270     // Note: After looking at CUPS sources, it looks like non-blocking
271     // connection will timeout after 10 seconds of no data period. And it will
272     // return the same way as if data was completely and sucessfully downloaded.
273     HttpConnectionCUPS http(print_server_url_, cups_encryption_);
274     http.SetBlocking(blocking_);
275     ppd_file_path = cupsGetPPD2(http.http(), name);
276     // Check if the get full PPD, since non-blocking call may simply return
277     // normally after timeout expired.
278     if (ppd_file_path) {
279       // There is no reliable way right now to detect full and complete PPD
280       // get downloaded. If we reach http timeout, it may simply return
281       // downloaded part as a full response. It might be good enough to check
282       // http->data_remaining or http->_data_remaining, unfortunately http_t
283       // is an internal structure and fields are not exposed in CUPS headers.
284       // httpGetLength or httpGetLength2 returning the full content size.
285       // Comparing file size against that content length might be unreliable
286       // since some http reponses are encoded and content_length > file size.
287       // Let's just check for the obvious CUPS and http errors here.
288       ppd_path = base::FilePath(ppd_file_path);
289       ipp_status_t error_code = cupsLastError();
290       int http_error = httpError(http.http());
291       if (error_code > IPP_OK_EVENTS_COMPLETE || http_error != 0) {
292         LOG(ERROR) << "Error downloading PPD file"
293                    << ", name: " << name
294                    << ", CUPS error: " << static_cast<int>(error_code)
295                    << ", HTTP error: " << http_error;
296         base::DeleteFile(ppd_path, false);
297         ppd_path.clear();
298       }
299     }
300   }
301   return ppd_path;
302 }
303
304 }  // namespace printing