Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / printing / backend / cups_helper.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/cups_helper.h"
6
7 #include <cups/ppd.h>
8
9 #include "base/file_util.h"
10 #include "base/logging.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "base/values.h"
15 #include "printing/backend/print_backend.h"
16 #include "printing/backend/print_backend_consts.h"
17 #include "url/gurl.h"
18
19 namespace printing {
20
21 // This section contains helper code for PPD parsing for semantic capabilities.
22 namespace {
23
24 const char kColorDevice[] = "ColorDevice";
25 const char kColorModel[] = "ColorModel";
26 const char kColorMode[] = "ColorMode";
27 const char kProcessColorModel[] = "ProcessColorModel";
28 const char kPrintoutMode[] = "PrintoutMode";
29 const char kDraftGray[] = "Draft.Gray";
30 const char kHighGray[] = "High.Gray";
31
32 const char kDuplex[] = "Duplex";
33 const char kDuplexNone[] = "None";
34
35 #if !defined(OS_MACOSX)
36 void ParseLpOptions(const base::FilePath& filepath,
37                     const std::string& printer_name,
38                     int* num_options, cups_option_t** options) {
39   std::string content;
40   if (!base::ReadFileToString(filepath, &content))
41     return;
42
43   const char kDest[] = "dest";
44   const char kDefault[] = "default";
45   const size_t kDestLen = sizeof(kDest) - 1;
46   const size_t kDefaultLen = sizeof(kDefault) - 1;
47   std::vector<std::string> lines;
48   base::SplitString(content, '\n', &lines);
49
50   for (size_t i = 0; i < lines.size(); ++i) {
51     std::string line = lines[i];
52     if (line.empty())
53       continue;
54
55     if (base::strncasecmp (line.c_str(), kDefault, kDefaultLen) == 0 &&
56         isspace(line[kDefaultLen])) {
57       line = line.substr(kDefaultLen);
58     } else if (base::strncasecmp (line.c_str(), kDest, kDestLen) == 0 &&
59                isspace(line[kDestLen])) {
60       line = line.substr(kDestLen);
61     } else {
62       continue;
63     }
64
65     base::TrimWhitespaceASCII(line, base::TRIM_ALL, &line);
66     if (line.empty())
67       continue;
68
69     size_t space_found = line.find(' ');
70     if (space_found == std::string::npos)
71       continue;
72
73     std::string name = line.substr(0, space_found);
74     if (name.empty())
75       continue;
76
77     if (base::strncasecmp(printer_name.c_str(), name.c_str(),
78                           name.length()) != 0) {
79       continue;  // This is not the required printer.
80     }
81
82     line = line.substr(space_found + 1);
83     // Remove extra spaces.
84     base::TrimWhitespaceASCII(line, base::TRIM_ALL, &line);
85     if (line.empty())
86       continue;
87     // Parse the selected printer custom options.
88     *num_options = cupsParseOptions(line.c_str(), 0, options);
89   }
90 }
91
92 void MarkLpOptions(const std::string& printer_name, ppd_file_t** ppd) {
93   cups_option_t* options = NULL;
94   int num_options = 0;
95   ppdMarkDefaults(*ppd);
96
97   const char kSystemLpOptionPath[] = "/etc/cups/lpoptions";
98   const char kUserLpOptionPath[] = ".cups/lpoptions";
99
100   std::vector<base::FilePath> file_locations;
101   file_locations.push_back(base::FilePath(kSystemLpOptionPath));
102   file_locations.push_back(base::FilePath(
103       base::GetHomeDir().Append(kUserLpOptionPath)));
104
105   for (std::vector<base::FilePath>::const_iterator it = file_locations.begin();
106        it != file_locations.end(); ++it) {
107     num_options = 0;
108     options = NULL;
109     ParseLpOptions(*it, printer_name, &num_options, &options);
110     if (num_options > 0 && options) {
111       cupsMarkOptions(*ppd, num_options, options);
112       cupsFreeOptions(num_options, options);
113     }
114   }
115 }
116 #endif  // !defined(OS_MACOSX)
117
118 bool GetBasicColorModelSettings(ppd_file_t* ppd,
119                                 ColorModel* color_model_for_black,
120                                 ColorModel* color_model_for_color,
121                                 bool* color_is_default) {
122   ppd_option_t* color_model = ppdFindOption(ppd, kColorModel);
123   if (!color_model)
124     return false;
125
126   if (ppdFindChoice(color_model, printing::kBlack))
127     *color_model_for_black = printing::BLACK;
128   else if (ppdFindChoice(color_model, printing::kGray))
129     *color_model_for_black = printing::GRAY;
130   else if (ppdFindChoice(color_model, printing::kGrayscale))
131     *color_model_for_black = printing::GRAYSCALE;
132
133   if (ppdFindChoice(color_model, printing::kColor))
134     *color_model_for_color = printing::COLOR;
135   else if (ppdFindChoice(color_model, printing::kCMYK))
136     *color_model_for_color = printing::CMYK;
137   else if (ppdFindChoice(color_model, printing::kRGB))
138     *color_model_for_color = printing::RGB;
139   else if (ppdFindChoice(color_model, printing::kRGBA))
140     *color_model_for_color = printing::RGBA;
141   else if (ppdFindChoice(color_model, printing::kRGB16))
142     *color_model_for_color = printing::RGB16;
143   else if (ppdFindChoice(color_model, printing::kCMY))
144     *color_model_for_color = printing::CMY;
145   else if (ppdFindChoice(color_model, printing::kKCMY))
146     *color_model_for_color = printing::KCMY;
147   else if (ppdFindChoice(color_model, printing::kCMY_K))
148     *color_model_for_color = printing::CMY_K;
149
150   ppd_choice_t* marked_choice = ppdFindMarkedChoice(ppd, kColorModel);
151   if (!marked_choice)
152     marked_choice = ppdFindChoice(color_model, color_model->defchoice);
153
154   if (marked_choice) {
155     *color_is_default =
156         (base::strcasecmp(marked_choice->choice, printing::kBlack) != 0) &&
157         (base::strcasecmp(marked_choice->choice, printing::kGray) != 0) &&
158         (base::strcasecmp(marked_choice->choice, printing::kGrayscale) != 0);
159   }
160   return true;
161 }
162
163 bool GetPrintOutModeColorSettings(ppd_file_t* ppd,
164                                   ColorModel* color_model_for_black,
165                                   ColorModel* color_model_for_color,
166                                   bool* color_is_default) {
167   ppd_option_t* printout_mode = ppdFindOption(ppd, kPrintoutMode);
168   if (!printout_mode)
169     return false;
170
171   *color_model_for_color = printing::PRINTOUTMODE_NORMAL;
172   *color_model_for_black = printing::PRINTOUTMODE_NORMAL;
173
174   // Check to see if NORMAL_GRAY value is supported by PrintoutMode.
175   // If NORMAL_GRAY is not supported, NORMAL value is used to
176   // represent grayscale. If NORMAL_GRAY is supported, NORMAL is used to
177   // represent color.
178   if (ppdFindChoice(printout_mode, printing::kNormalGray))
179     *color_model_for_black = printing::PRINTOUTMODE_NORMAL_GRAY;
180
181   // Get the default marked choice to identify the default color setting
182   // value.
183   ppd_choice_t* printout_mode_choice = ppdFindMarkedChoice(ppd, kPrintoutMode);
184   if (!printout_mode_choice) {
185       printout_mode_choice = ppdFindChoice(printout_mode,
186                                            printout_mode->defchoice);
187   }
188   if (printout_mode_choice) {
189     if ((base::strcasecmp(printout_mode_choice->choice,
190                           printing::kNormalGray) == 0) ||
191         (base::strcasecmp(printout_mode_choice->choice, kHighGray) == 0) ||
192         (base::strcasecmp(printout_mode_choice->choice, kDraftGray) == 0)) {
193       *color_model_for_black = printing::PRINTOUTMODE_NORMAL_GRAY;
194       *color_is_default = false;
195     }
196   }
197   return true;
198 }
199
200 bool GetColorModeSettings(ppd_file_t* ppd,
201                           ColorModel* color_model_for_black,
202                           ColorModel* color_model_for_color,
203                           bool* color_is_default) {
204   // Samsung printers use "ColorMode" attribute in their ppds.
205   ppd_option_t* color_mode_option = ppdFindOption(ppd, kColorMode);
206   if (!color_mode_option)
207     return false;
208
209   if (ppdFindChoice(color_mode_option, printing::kColor))
210     *color_model_for_color = printing::COLORMODE_COLOR;
211
212   if (ppdFindChoice(color_mode_option, printing::kMonochrome))
213     *color_model_for_black = printing::COLORMODE_MONOCHROME;
214
215   ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode);
216   if (!mode_choice) {
217     mode_choice = ppdFindChoice(color_mode_option,
218                                 color_mode_option->defchoice);
219   }
220
221   if (mode_choice) {
222     *color_is_default =
223         (base::strcasecmp(mode_choice->choice, printing::kColor) == 0);
224   }
225   return true;
226 }
227
228 bool GetHPColorSettings(ppd_file_t* ppd,
229                         ColorModel* color_model_for_black,
230                         ColorModel* color_model_for_color,
231                         bool* color_is_default) {
232   // HP printers use "Color/Color Model" attribute in their ppds.
233   ppd_option_t* color_mode_option = ppdFindOption(ppd, printing::kColor);
234   if (!color_mode_option)
235     return false;
236
237   if (ppdFindChoice(color_mode_option, printing::kColor))
238     *color_model_for_color = printing::HP_COLOR_COLOR;
239   if (ppdFindChoice(color_mode_option, printing::kBlack))
240     *color_model_for_black = printing::HP_COLOR_BLACK;
241
242   ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode);
243   if (!mode_choice) {
244     mode_choice = ppdFindChoice(color_mode_option,
245                                 color_mode_option->defchoice);
246   }
247   if (mode_choice) {
248     *color_is_default =
249         (base::strcasecmp(mode_choice->choice, printing::kColor) == 0);
250   }
251   return true;
252 }
253
254 bool GetProcessColorModelSettings(ppd_file_t* ppd,
255                                   ColorModel* color_model_for_black,
256                                   ColorModel* color_model_for_color,
257                                   bool* color_is_default) {
258   // Canon printers use "ProcessColorModel" attribute in their ppds.
259   ppd_option_t* color_mode_option =  ppdFindOption(ppd, kProcessColorModel);
260   if (!color_mode_option)
261     return false;
262
263   if (ppdFindChoice(color_mode_option, printing::kRGB))
264     *color_model_for_color = printing::PROCESSCOLORMODEL_RGB;
265   else if (ppdFindChoice(color_mode_option, printing::kCMYK))
266     *color_model_for_color = printing::PROCESSCOLORMODEL_CMYK;
267
268   if (ppdFindChoice(color_mode_option, printing::kGreyscale))
269     *color_model_for_black = printing::PROCESSCOLORMODEL_GREYSCALE;
270
271   ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kProcessColorModel);
272   if (!mode_choice) {
273     mode_choice = ppdFindChoice(color_mode_option,
274                                 color_mode_option->defchoice);
275   }
276
277   if (mode_choice) {
278     *color_is_default =
279         (base::strcasecmp(mode_choice->choice, printing::kGreyscale) != 0);
280   }
281   return true;
282 }
283
284 bool GetColorModelSettings(ppd_file_t* ppd,
285                            ColorModel* cm_black,
286                            ColorModel* cm_color,
287                            bool* is_color) {
288   bool is_color_device = false;
289   ppd_attr_t* attr = ppdFindAttr(ppd, kColorDevice, NULL);
290   if (attr && attr->value)
291     is_color_device = ppd->color_device;
292
293   *is_color = is_color_device;
294   return (is_color_device &&
295           GetBasicColorModelSettings(ppd, cm_black, cm_color, is_color)) ||
296       GetPrintOutModeColorSettings(ppd, cm_black, cm_color, is_color) ||
297       GetColorModeSettings(ppd, cm_black, cm_color, is_color) ||
298       GetHPColorSettings(ppd, cm_black, cm_color, is_color) ||
299       GetProcessColorModelSettings(ppd, cm_black, cm_color, is_color);
300 }
301
302 // Default port for IPP print servers.
303 const int kDefaultIPPServerPort = 631;
304
305 }  // namespace
306
307 // Helper wrapper around http_t structure, with connection and cleanup
308 // functionality.
309 HttpConnectionCUPS::HttpConnectionCUPS(const GURL& print_server_url,
310                                        http_encryption_t encryption)
311     : http_(NULL) {
312   // If we have an empty url, use default print server.
313   if (print_server_url.is_empty())
314     return;
315
316   int port = print_server_url.IntPort();
317   if (port == url::PORT_UNSPECIFIED)
318     port = kDefaultIPPServerPort;
319
320   http_ = httpConnectEncrypt(print_server_url.host().c_str(), port, encryption);
321   if (http_ == NULL) {
322     LOG(ERROR) << "CP_CUPS: Failed connecting to print server: "
323                << print_server_url;
324   }
325 }
326
327 HttpConnectionCUPS::~HttpConnectionCUPS() {
328   if (http_ != NULL)
329     httpClose(http_);
330 }
331
332 void HttpConnectionCUPS::SetBlocking(bool blocking) {
333   httpBlocking(http_, blocking ?  1 : 0);
334 }
335
336 http_t* HttpConnectionCUPS::http() {
337   return http_;
338 }
339
340 bool ParsePpdCapabilities(
341     const std::string& printer_name,
342     const std::string& printer_capabilities,
343     PrinterSemanticCapsAndDefaults* printer_info) {
344   base::FilePath ppd_file_path;
345   if (!base::CreateTemporaryFile(&ppd_file_path))
346     return false;
347
348   int data_size = printer_capabilities.length();
349   if (data_size != base::WriteFile(
350                        ppd_file_path,
351                        printer_capabilities.data(),
352                        data_size)) {
353     base::DeleteFile(ppd_file_path, false);
354     return false;
355   }
356
357   ppd_file_t* ppd = ppdOpenFile(ppd_file_path.value().c_str());
358   if (!ppd)
359     return false;
360
361   printing::PrinterSemanticCapsAndDefaults caps;
362 #if !defined(OS_MACOSX)
363   MarkLpOptions(printer_name, &ppd);
364 #endif
365   ppd_choice_t* duplex_choice = ppdFindMarkedChoice(ppd, kDuplex);
366   if (!duplex_choice) {
367     ppd_option_t* option = ppdFindOption(ppd, kDuplex);
368     if (option)
369       duplex_choice = ppdFindChoice(option, option->defchoice);
370   }
371
372   if (duplex_choice) {
373     caps.duplex_capable = true;
374     if (base::strcasecmp(duplex_choice->choice, kDuplexNone) != 0)
375       caps.duplex_default = printing::LONG_EDGE;
376     else
377       caps.duplex_default = printing::SIMPLEX;
378   }
379
380   bool is_color = false;
381   ColorModel cm_color = UNKNOWN_COLOR_MODEL, cm_black = UNKNOWN_COLOR_MODEL;
382   if (!GetColorModelSettings(ppd, &cm_black, &cm_color, &is_color)) {
383     VLOG(1) << "Unknown printer color model";
384   }
385
386   caps.color_changeable = ((cm_color != UNKNOWN_COLOR_MODEL) &&
387                            (cm_black != UNKNOWN_COLOR_MODEL) &&
388                            (cm_color != cm_black));
389   caps.color_default = is_color;
390   caps.color_model = cm_color;
391   caps.bw_model = cm_black;
392
393   ppdClose(ppd);
394   base::DeleteFile(ppd_file_path, false);
395
396   *printer_info = caps;
397   return true;
398 }
399
400 }  // namespace printing