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