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 19namespace printing { 20 21// This section contains helper code for PPD parsing for semantic capabilities. 22namespace { 23 24const char kColorDevice[] = "ColorDevice"; 25const char kColorModel[] = "ColorModel"; 26const char kColorMode[] = "ColorMode"; 27const char kProcessColorModel[] = "ProcessColorModel"; 28const char kPrintoutMode[] = "PrintoutMode"; 29const char kDraftGray[] = "Draft.Gray"; 30const char kHighGray[] = "High.Gray"; 31 32const char kDuplex[] = "Duplex"; 33const char kDuplexNone[] = "None"; 34 35#if !defined(OS_MACOSX) 36void 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 91void 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 117bool 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 162bool 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 199bool 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 227bool 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 253bool 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 283bool 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. 302const int kDefaultIPPServerPort = 631; 303 304} // namespace 305 306// Helper wrapper around http_t structure, with connection and cleanup 307// functionality. 308HttpConnectionCUPS::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 326HttpConnectionCUPS::~HttpConnectionCUPS() { 327 if (http_ != NULL) 328 httpClose(http_); 329} 330 331void HttpConnectionCUPS::SetBlocking(bool blocking) { 332 httpBlocking(http_, blocking ? 1 : 0); 333} 334 335http_t* HttpConnectionCUPS::http() { 336 return http_; 337} 338 339bool 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