IndexedDB: Protect against use-after-free in ChainedBlobWriter.
[chromium-blink-merge.git] / printing / backend / cups_helper.cc
blobc0271b2a2872f46b9eeb74b73f9b555a6a86aa89
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.
5 #include "printing/backend/cups_helper.h"
7 #include <cups/ppd.h>
9 #include "base/base_paths.h"
10 #include "base/files/file_util.h"
11 #include "base/logging.h"
12 #include "base/path_service.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/values.h"
17 #include "printing/backend/print_backend.h"
18 #include "printing/backend/print_backend_consts.h"
19 #include "printing/units.h"
20 #include "url/gurl.h"
22 namespace printing {
24 // This section contains helper code for PPD parsing for semantic capabilities.
25 namespace {
27 const char kColorDevice[] = "ColorDevice";
28 const char kColorModel[] = "ColorModel";
29 const char kColorMode[] = "ColorMode";
30 const char kProcessColorModel[] = "ProcessColorModel";
31 const char kPrintoutMode[] = "PrintoutMode";
32 const char kDraftGray[] = "Draft.Gray";
33 const char kHighGray[] = "High.Gray";
35 const char kDuplex[] = "Duplex";
36 const char kDuplexNone[] = "None";
37 const char kPageSize[] = "PageSize";
39 const double kMicronsPerPoint = 10.0f * kHundrethsMMPerInch / kPointsPerInch;
41 void ParseLpOptions(const base::FilePath& filepath,
42 const std::string& printer_name,
43 int* num_options, cups_option_t** options) {
44 std::string content;
45 if (!base::ReadFileToString(filepath, &content))
46 return;
48 const char kDest[] = "dest";
49 const char kDefault[] = "default";
50 const size_t kDestLen = sizeof(kDest) - 1;
51 const size_t kDefaultLen = sizeof(kDefault) - 1;
52 std::vector<std::string> lines;
53 base::SplitString(content, '\n', &lines);
55 for (size_t i = 0; i < lines.size(); ++i) {
56 std::string line = lines[i];
57 if (line.empty())
58 continue;
60 if (base::strncasecmp (line.c_str(), kDefault, kDefaultLen) == 0 &&
61 isspace(line[kDefaultLen])) {
62 line = line.substr(kDefaultLen);
63 } else if (base::strncasecmp (line.c_str(), kDest, kDestLen) == 0 &&
64 isspace(line[kDestLen])) {
65 line = line.substr(kDestLen);
66 } else {
67 continue;
70 base::TrimWhitespaceASCII(line, base::TRIM_ALL, &line);
71 if (line.empty())
72 continue;
74 size_t space_found = line.find(' ');
75 if (space_found == std::string::npos)
76 continue;
78 std::string name = line.substr(0, space_found);
79 if (name.empty())
80 continue;
82 if (base::strncasecmp(printer_name.c_str(), name.c_str(),
83 name.length()) != 0) {
84 continue; // This is not the required printer.
87 line = line.substr(space_found + 1);
88 // Remove extra spaces.
89 base::TrimWhitespaceASCII(line, base::TRIM_ALL, &line);
90 if (line.empty())
91 continue;
92 // Parse the selected printer custom options.
93 *num_options = cupsParseOptions(line.c_str(), 0, options);
97 void MarkLpOptions(const std::string& printer_name, ppd_file_t** ppd) {
98 cups_option_t* options = NULL;
99 int num_options = 0;
101 const char kSystemLpOptionPath[] = "/etc/cups/lpoptions";
102 const char kUserLpOptionPath[] = ".cups/lpoptions";
104 std::vector<base::FilePath> file_locations;
105 file_locations.push_back(base::FilePath(kSystemLpOptionPath));
106 base::FilePath homedir;
107 PathService::Get(base::DIR_HOME, &homedir);
108 file_locations.push_back(base::FilePath(homedir.Append(kUserLpOptionPath)));
110 for (std::vector<base::FilePath>::const_iterator it = file_locations.begin();
111 it != file_locations.end(); ++it) {
112 num_options = 0;
113 options = NULL;
114 ParseLpOptions(*it, printer_name, &num_options, &options);
115 if (num_options > 0 && options) {
116 cupsMarkOptions(*ppd, num_options, options);
117 cupsFreeOptions(num_options, options);
122 bool GetBasicColorModelSettings(ppd_file_t* ppd,
123 ColorModel* color_model_for_black,
124 ColorModel* color_model_for_color,
125 bool* color_is_default) {
126 ppd_option_t* color_model = ppdFindOption(ppd, kColorModel);
127 if (!color_model)
128 return false;
130 if (ppdFindChoice(color_model, printing::kBlack))
131 *color_model_for_black = printing::BLACK;
132 else if (ppdFindChoice(color_model, printing::kGray))
133 *color_model_for_black = printing::GRAY;
134 else if (ppdFindChoice(color_model, printing::kGrayscale))
135 *color_model_for_black = printing::GRAYSCALE;
137 if (ppdFindChoice(color_model, printing::kColor))
138 *color_model_for_color = printing::COLOR;
139 else if (ppdFindChoice(color_model, printing::kCMYK))
140 *color_model_for_color = printing::CMYK;
141 else if (ppdFindChoice(color_model, printing::kRGB))
142 *color_model_for_color = printing::RGB;
143 else if (ppdFindChoice(color_model, printing::kRGBA))
144 *color_model_for_color = printing::RGBA;
145 else if (ppdFindChoice(color_model, printing::kRGB16))
146 *color_model_for_color = printing::RGB16;
147 else if (ppdFindChoice(color_model, printing::kCMY))
148 *color_model_for_color = printing::CMY;
149 else if (ppdFindChoice(color_model, printing::kKCMY))
150 *color_model_for_color = printing::KCMY;
151 else if (ppdFindChoice(color_model, printing::kCMY_K))
152 *color_model_for_color = printing::CMY_K;
154 ppd_choice_t* marked_choice = ppdFindMarkedChoice(ppd, kColorModel);
155 if (!marked_choice)
156 marked_choice = ppdFindChoice(color_model, color_model->defchoice);
158 if (marked_choice) {
159 *color_is_default =
160 (base::strcasecmp(marked_choice->choice, printing::kBlack) != 0) &&
161 (base::strcasecmp(marked_choice->choice, printing::kGray) != 0) &&
162 (base::strcasecmp(marked_choice->choice, printing::kGrayscale) != 0);
164 return true;
167 bool GetPrintOutModeColorSettings(ppd_file_t* ppd,
168 ColorModel* color_model_for_black,
169 ColorModel* color_model_for_color,
170 bool* color_is_default) {
171 ppd_option_t* printout_mode = ppdFindOption(ppd, kPrintoutMode);
172 if (!printout_mode)
173 return false;
175 *color_model_for_color = printing::PRINTOUTMODE_NORMAL;
176 *color_model_for_black = printing::PRINTOUTMODE_NORMAL;
178 // Check to see if NORMAL_GRAY value is supported by PrintoutMode.
179 // If NORMAL_GRAY is not supported, NORMAL value is used to
180 // represent grayscale. If NORMAL_GRAY is supported, NORMAL is used to
181 // represent color.
182 if (ppdFindChoice(printout_mode, printing::kNormalGray))
183 *color_model_for_black = printing::PRINTOUTMODE_NORMAL_GRAY;
185 // Get the default marked choice to identify the default color setting
186 // value.
187 ppd_choice_t* printout_mode_choice = ppdFindMarkedChoice(ppd, kPrintoutMode);
188 if (!printout_mode_choice) {
189 printout_mode_choice = ppdFindChoice(printout_mode,
190 printout_mode->defchoice);
192 if (printout_mode_choice) {
193 if ((base::strcasecmp(printout_mode_choice->choice,
194 printing::kNormalGray) == 0) ||
195 (base::strcasecmp(printout_mode_choice->choice, kHighGray) == 0) ||
196 (base::strcasecmp(printout_mode_choice->choice, kDraftGray) == 0)) {
197 *color_model_for_black = printing::PRINTOUTMODE_NORMAL_GRAY;
198 *color_is_default = false;
201 return true;
204 bool GetColorModeSettings(ppd_file_t* ppd,
205 ColorModel* color_model_for_black,
206 ColorModel* color_model_for_color,
207 bool* color_is_default) {
208 // Samsung printers use "ColorMode" attribute in their ppds.
209 ppd_option_t* color_mode_option = ppdFindOption(ppd, kColorMode);
210 if (!color_mode_option)
211 return false;
213 if (ppdFindChoice(color_mode_option, printing::kColor))
214 *color_model_for_color = printing::COLORMODE_COLOR;
216 if (ppdFindChoice(color_mode_option, printing::kMonochrome))
217 *color_model_for_black = printing::COLORMODE_MONOCHROME;
219 ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode);
220 if (!mode_choice) {
221 mode_choice = ppdFindChoice(color_mode_option,
222 color_mode_option->defchoice);
225 if (mode_choice) {
226 *color_is_default =
227 (base::strcasecmp(mode_choice->choice, printing::kColor) == 0);
229 return true;
232 bool GetHPColorSettings(ppd_file_t* ppd,
233 ColorModel* color_model_for_black,
234 ColorModel* color_model_for_color,
235 bool* color_is_default) {
236 // HP printers use "Color/Color Model" attribute in their ppds.
237 ppd_option_t* color_mode_option = ppdFindOption(ppd, printing::kColor);
238 if (!color_mode_option)
239 return false;
241 if (ppdFindChoice(color_mode_option, printing::kColor))
242 *color_model_for_color = printing::HP_COLOR_COLOR;
243 if (ppdFindChoice(color_mode_option, printing::kBlack))
244 *color_model_for_black = printing::HP_COLOR_BLACK;
246 ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode);
247 if (!mode_choice) {
248 mode_choice = ppdFindChoice(color_mode_option,
249 color_mode_option->defchoice);
251 if (mode_choice) {
252 *color_is_default =
253 (base::strcasecmp(mode_choice->choice, printing::kColor) == 0);
255 return true;
258 bool GetProcessColorModelSettings(ppd_file_t* ppd,
259 ColorModel* color_model_for_black,
260 ColorModel* color_model_for_color,
261 bool* color_is_default) {
262 // Canon printers use "ProcessColorModel" attribute in their ppds.
263 ppd_option_t* color_mode_option = ppdFindOption(ppd, kProcessColorModel);
264 if (!color_mode_option)
265 return false;
267 if (ppdFindChoice(color_mode_option, printing::kRGB))
268 *color_model_for_color = printing::PROCESSCOLORMODEL_RGB;
269 else if (ppdFindChoice(color_mode_option, printing::kCMYK))
270 *color_model_for_color = printing::PROCESSCOLORMODEL_CMYK;
272 if (ppdFindChoice(color_mode_option, printing::kGreyscale))
273 *color_model_for_black = printing::PROCESSCOLORMODEL_GREYSCALE;
275 ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kProcessColorModel);
276 if (!mode_choice) {
277 mode_choice = ppdFindChoice(color_mode_option,
278 color_mode_option->defchoice);
281 if (mode_choice) {
282 *color_is_default =
283 (base::strcasecmp(mode_choice->choice, printing::kGreyscale) != 0);
285 return true;
288 bool GetColorModelSettings(ppd_file_t* ppd,
289 ColorModel* cm_black,
290 ColorModel* cm_color,
291 bool* is_color) {
292 bool is_color_device = false;
293 ppd_attr_t* attr = ppdFindAttr(ppd, kColorDevice, NULL);
294 if (attr && attr->value)
295 is_color_device = ppd->color_device;
297 *is_color = is_color_device;
298 return (is_color_device &&
299 GetBasicColorModelSettings(ppd, cm_black, cm_color, is_color)) ||
300 GetPrintOutModeColorSettings(ppd, cm_black, cm_color, is_color) ||
301 GetColorModeSettings(ppd, cm_black, cm_color, is_color) ||
302 GetHPColorSettings(ppd, cm_black, cm_color, is_color) ||
303 GetProcessColorModelSettings(ppd, cm_black, cm_color, is_color);
306 // Default port for IPP print servers.
307 const int kDefaultIPPServerPort = 631;
309 } // namespace
311 // Helper wrapper around http_t structure, with connection and cleanup
312 // functionality.
313 HttpConnectionCUPS::HttpConnectionCUPS(const GURL& print_server_url,
314 http_encryption_t encryption)
315 : http_(NULL) {
316 // If we have an empty url, use default print server.
317 if (print_server_url.is_empty())
318 return;
320 int port = print_server_url.IntPort();
321 if (port == url::PORT_UNSPECIFIED)
322 port = kDefaultIPPServerPort;
324 http_ = httpConnectEncrypt(print_server_url.host().c_str(), port, encryption);
325 if (http_ == NULL) {
326 LOG(ERROR) << "CP_CUPS: Failed connecting to print server: "
327 << print_server_url;
331 HttpConnectionCUPS::~HttpConnectionCUPS() {
332 if (http_ != NULL)
333 httpClose(http_);
336 void HttpConnectionCUPS::SetBlocking(bool blocking) {
337 httpBlocking(http_, blocking ? 1 : 0);
340 http_t* HttpConnectionCUPS::http() {
341 return http_;
344 bool ParsePpdCapabilities(
345 const std::string& printer_name,
346 const std::string& printer_capabilities,
347 PrinterSemanticCapsAndDefaults* printer_info) {
348 base::FilePath ppd_file_path;
349 if (!base::CreateTemporaryFile(&ppd_file_path))
350 return false;
352 int data_size = printer_capabilities.length();
353 if (data_size != base::WriteFile(
354 ppd_file_path,
355 printer_capabilities.data(),
356 data_size)) {
357 base::DeleteFile(ppd_file_path, false);
358 return false;
361 ppd_file_t* ppd = ppdOpenFile(ppd_file_path.value().c_str());
362 if (!ppd) {
363 int line = 0;
364 ppd_status_t ppd_status = ppdLastError(&line);
365 LOG(ERROR) << "Failed to open PDD file: error " << ppd_status << " at line "
366 << line << ", " << ppdErrorString(ppd_status);
367 return false;
369 ppdMarkDefaults(ppd);
370 MarkLpOptions(printer_name, &ppd);
372 printing::PrinterSemanticCapsAndDefaults caps;
373 caps.collate_capable = true;
374 caps.collate_default = true;
375 caps.copies_capable = true;
377 ppd_choice_t* duplex_choice = ppdFindMarkedChoice(ppd, kDuplex);
378 if (!duplex_choice) {
379 ppd_option_t* option = ppdFindOption(ppd, kDuplex);
380 if (option)
381 duplex_choice = ppdFindChoice(option, option->defchoice);
384 if (duplex_choice) {
385 caps.duplex_capable = true;
386 if (base::strcasecmp(duplex_choice->choice, kDuplexNone) != 0)
387 caps.duplex_default = printing::LONG_EDGE;
388 else
389 caps.duplex_default = printing::SIMPLEX;
392 bool is_color = false;
393 ColorModel cm_color = UNKNOWN_COLOR_MODEL, cm_black = UNKNOWN_COLOR_MODEL;
394 if (!GetColorModelSettings(ppd, &cm_black, &cm_color, &is_color)) {
395 VLOG(1) << "Unknown printer color model";
398 caps.color_changeable = ((cm_color != UNKNOWN_COLOR_MODEL) &&
399 (cm_black != UNKNOWN_COLOR_MODEL) &&
400 (cm_color != cm_black));
401 caps.color_default = is_color;
402 caps.color_model = cm_color;
403 caps.bw_model = cm_black;
405 if (ppd->num_sizes > 0 && ppd->sizes) {
406 VLOG(1) << "Paper list size - " << ppd->num_sizes;
407 ppd_option_t* paper_option = ppdFindOption(ppd, kPageSize);
408 for (int i = 0; i < ppd->num_sizes; ++i) {
409 gfx::Size paper_size_microns(
410 static_cast<int>(ppd->sizes[i].width * kMicronsPerPoint + 0.5),
411 static_cast<int>(ppd->sizes[i].length * kMicronsPerPoint + 0.5));
412 if (paper_size_microns.width() > 0 && paper_size_microns.height() > 0) {
413 PrinterSemanticCapsAndDefaults::Paper paper;
414 paper.size_um = paper_size_microns;
415 paper.vendor_id = ppd->sizes[i].name;
416 if (paper_option) {
417 ppd_choice_t* paper_choice =
418 ppdFindChoice(paper_option, ppd->sizes[i].name);
419 // Human readable paper name should be UTF-8 encoded, but some PPDs
420 // do not follow this standard.
421 if (paper_choice && base::IsStringUTF8(paper_choice->text)) {
422 paper.display_name = paper_choice->text;
425 caps.papers.push_back(paper);
426 if (i == 0 || ppd->sizes[i].marked) {
427 caps.default_paper = paper;
433 ppdClose(ppd);
434 base::DeleteFile(ppd_file_path, false);
436 *printer_info = caps;
437 return true;
440 } // namespace printing