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