Home | History | Annotate | Download | only in backend
      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     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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.
    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 
    326 HttpConnectionCUPS::~HttpConnectionCUPS() {
    327   if (http_ != NULL)
    328     httpClose(http_);
    329 }
    330 
    331 void HttpConnectionCUPS::SetBlocking(bool blocking) {
    332   httpBlocking(http_, blocking ?  1 : 0);
    333 }
    334 
    335 http_t* HttpConnectionCUPS::http() {
    336   return http_;
    337 }
    338 
    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