def get_attributes_from_ppd(self, ppd_file, attributes): res = {} temp_file = tempfile.NamedTemporaryFile(delete=False) try: if ppd_file[0:4] == "http": if get_local_ppd_path(ppd_file) is not None: # no need to make an HTTP request local_file = get_local_ppd_path(ppd_file) else: # fetch remote file and copy it to a temporary local one r = requests.get(requests.utils.quote(ppd_file, safe=":/")) with open(temp_file.name, "w") as tf: tf.write(r.content.decode("utf-8")) local_file = temp_file.name else: local_file = ppd_file ppd = cups.PPD(local_file) ppd.localize() for name in attributes: attr = ppd.findAttr(name) if attr is not None: res[name] = attr.value except Exception as e: self.log.error("Error reading PPD file %s: %s" % (ppd_file, str(e))) finally: os.unlink(temp_file.name) return res
def load_and_add_printers(self): """ 저장되어 있는 프린터정보를 이용해서 프린터 추가 """ backup_path = META_FILE_PRINTER_BACKUP_PATH if not os.path.exists(backup_path): return printer_list = backup_path + '/' + META_FILE_PRINTER_LIST if not os.path.exists(printer_list): return with open(printer_list) as f: try: for print_info in f.readlines(): print_name, uri = print_info.split(GRAC_PRINTER_DELIM) ppd = cups.PPD(backup_path + '/' + print_name) self._conn.addPrinter(print_name, device=uri, ppd=ppd) self._conn.enablePrinter(print_name) self._conn.acceptJobs(print_name) except: self.logger.error(grac_format_exc()) shutil.rmtree(backup_path)
def _prepare_update_from_cups(self, cups_connection, cups_printer): mapping = {3: "available", 4: "printing", 5: "error"} cups_vals = { "name": cups_printer["printer-info"], "model": cups_printer.get("printer-make-and-model", False), "location": cups_printer.get("printer-location", False), "uri": cups_printer.get("device-uri", False), "status": mapping.get(cups_printer.get("printer-state"), "unknown"), "status_message": cups_printer.get("printer-state-message", ""), } # prevent write if the field didn't change vals = { fieldname: value for fieldname, value in cups_vals.items() if not self or value != self[fieldname] } printer_uri = cups_printer["printer-uri-supported"] printer_system_name = printer_uri[printer_uri.rfind("/") + 1:] ppd_info = cups_connection.getPPD3(printer_system_name) ppd_path = ppd_info[2] if not ppd_path: return vals ppd = cups.PPD(ppd_path) option = ppd.findOption("InputSlot") try: os.unlink(ppd_path) except OSError as err: # ENOENT means No such file or directory # The file has already been deleted, we can continue the update if err.errno != errno.ENOENT: raise if not option: return vals tray_commands = [] cups_trays = { tray_option["choice"]: tray_option["text"] for tray_option in option.choices } # Add new trays tray_commands.extend([ (0, 0, { "name": text, "system_name": choice }) for choice, text in cups_trays.items() if choice not in self.tray_ids.mapped("system_name") ]) # Remove deleted trays tray_commands.extend([(2, tray.id) for tray in self.tray_ids.filtered( lambda record: record.system_name not in cups_trays.keys())]) if tray_commands: vals["tray_ids"] = tray_commands return vals
def _get_cups_ppd(self, cups_connection, cups_printer): """ Returns a PostScript Printer Description (PPD) file for the printer. """ printer_uri = cups_printer['printer-uri-supported'] printer_system_name = printer_uri[printer_uri.rfind('/') + 1:] ppd_info = cups_connection.getPPD3(printer_system_name) ppd_path = ppd_info[2] if not ppd_path: return False, False return ppd_path, cups.PPD(ppd_path)
def _prepare_update_from_cups(self, cups_connection, cups_printer): mapping = { 3: 'available', 4: 'printing', 5: 'error' } vals = { 'name': cups_printer['printer-info'], 'model': cups_printer.get('printer-make-and-model', False), 'location': cups_printer.get('printer-location', False), 'uri': cups_printer.get('device-uri', False), 'status': mapping.get(cups_printer.get( 'printer-state'), 'unknown'), 'status_message': cups_printer.get('printer-state-message', ''), } printer_uri = cups_printer['printer-uri-supported'] printer_system_name = printer_uri[printer_uri.rfind('/') + 1:] ppd_info = cups_connection.getPPD3(printer_system_name) ppd_path = ppd_info[2] if not ppd_path: return vals ppd = cups.PPD(ppd_path) option = ppd.findOption('InputSlot') try: os.unlink(ppd_path) except OSError as err: # ENOENT means No such file or directory # The file has already been deleted, we can continue the update if err.errno != errno.ENOENT: raise if not option: return vals vals['tray_ids'] = [] cups_trays = { tray_option['choice']: tray_option['text'] for tray_option in option.choices } # Add new trays vals['tray_ids'].extend([ (0, 0, {'name': text, 'system_name': choice}) for choice, text in cups_trays.items() if choice not in self.tray_ids.mapped('system_name') ]) # Remove deleted trays vals['tray_ids'].extend([ (2, tray.id) for tray in self.tray_ids.filtered( lambda record: record.system_name not in cups_trays.keys()) ]) return vals
def _prepare_update_from_cups(self, cups_connection, cups_printer): vals = super(PrintingPrinter, self)._prepare_update_from_cups( cups_connection, cups_printer) printer_uri = cups_printer['printer-uri-supported'] printer_system_name = printer_uri[printer_uri.rfind('/') + 1:] ppd_info = cups_connection.getPPD3(printer_system_name) ppd_path = ppd_info[2] if not ppd_path: return vals ppd = cups.PPD(ppd_path) input_options = ppd.findOption('InputSlot') output_options = ppd.findOption('OutputBin') try: os.unlink(ppd_path) except OSError as err: # ENOENT means No such file or directory # The file has already been deleted, we can continue the update if err.errno != errno.ENOENT: raise if input_options: vals['input_tray_ids'] = [ (0, 0, {'name': opt['text'], 'system_name': opt['choice']}) for opt in input_options.choices if opt['choice'] not in self.input_tray_ids.mapped('system_name') ] trays = [opt['choice'] for opt in input_options.choices] vals['input_tray_ids'].extend([ (2, tray.id) for tray in self.input_tray_ids if tray.system_name not in trays ]) if output_options: vals['output_tray_ids'] = [ (0, 0, {'name': opt['text'], 'system_name': opt['choice']}) for opt in output_options.choices if opt['choice'] not in self.output_tray_ids.mapped('system_name') ] trays = [opt['choice'] for opt in output_options.choices] vals['output_tray_ids'].extend([ (2, tray.id) for tray in self.output_tray_ids if tray.system_name not in trays ]) return vals
def getPPD(self): """ Obtain the printer's PPD. @returns: cups.PPD object, or False for raw queues @raise cups.IPPError: IPP error """ result = None if self._ppd is None: try: self._ppd = self.connection.getPPD(self.name) result = cups.PPD(self._ppd) except cups.IPPError as emargs: (e, m) = emargs.args if e == cups.IPP_NOT_FOUND: result = False else: raise if result is None and self._ppd is not None: result = cups.PPD(self._ppd) return result
def fetch_ppd(self, name, callback, check_uptodate=True): if check_uptodate and self._modtimes.has_key(name): # We have getPPD3 so we can check whether the PPD is up to # date. debugprint("%s: check if %s is up to date" % (self, name)) self._cups.getPPD3(name, modtime=self._modtimes[name], reply_handler=lambda c, r: self._got_ppd3( c, name, r, callback), error_handler=lambda c, r: self._got_ppd3( c, name, r, callback)) return try: f = self._cache[name] except RuntimeError as e: self._schedule_callback(callback, name, None, e) return except KeyError: if not self._cups: self._queued.append((name, callback)) if not self._connecting: self._connect() return debugprint("%s: fetch PPD for %s" % (self, name)) self._cups.getPPD3(name, reply_handler=lambda c, r: self._got_ppd3( c, name, r, callback), error_handler=lambda c, r: self._got_ppd3( c, name, r, callback)) return # Copy from our file object to a new temporary file, create a # PPD object from it, then remove the file. This way we don't # leave temporary files around even though we are caching... f.seek(0) (tmpfd, tmpfname) = tempfile.mkstemp() tmpf = file(tmpfname, "w") tmpf.writelines(f.readlines()) del tmpf os.close(tmpfd) try: ppd = cups.PPD(tmpfname) os.unlink(tmpfname) self._schedule_callback(callback, name, ppd, None) except Exception as e: os.unlink(tmpfname) self._schedule_callback(callback, name, None, e)
def fetch_ppd(self, name, callback, check_uptodate=True): if check_uptodate and name in self._modtimes: # We have getPPD3 so we can check whether the PPD is up to # date. debugprint("%s: check if %s is up to date" % (self, name)) self._cups.getPPD3(name, modtime=self._modtimes[name], reply_handler=lambda c, r: self._got_ppd3( c, name, r, callback), error_handler=lambda c, r: self._got_ppd3( c, name, r, callback)) return try: f = self._cache[name] except RuntimeError as e: self._schedule_callback(callback, name, None, e) return except KeyError: if not self._cups: self._queued.append((name, callback)) if not self._connecting: self._connect() return debugprint("%s: fetch PPD for %s" % (self, name)) self._cups.getPPD3(name, reply_handler=lambda c, r: self._got_ppd3( c, name, r, callback), error_handler=lambda c, r: self._got_ppd3( c, name, r, callback)) return # Copy from our file object to a new temporary file, create a # PPD object from it, then remove the file. This way we don't # leave temporary files around even though we are caching... f.seek(0) with NamedTemporaryFile() as tmpf: copyfileobj(f, tmpf) try: ppd = cups.PPD(tmpf.file) self._schedule_callback(callback, name, ppd, None) except Exception as e: self._schedule_callback(callback, name, None, e)
def _prepare_update_from_cups(self, cups_connection, cups_printer): vals = super(PrintingPrinter, self)._prepare_update_from_cups(cups_connection, cups_printer) printer_uri = cups_printer['printer-uri-supported'] printer_system_name = printer_uri[printer_uri.rfind('/') + 1:] ppd_info = cups_connection.getPPD3(printer_system_name) ppd_path = ppd_info[2] if not ppd_path: return vals ppd = cups.PPD(ppd_path) option = ppd.findOption('InputSlot') try: os.unlink(ppd_path) except OSError as err: # ENOENT means No such file or directory # The file has already been deleted, we can continue the update if err.errno != errno.ENOENT: raise if not option: return vals vals['tray_ids'] = [] cups_trays = { tray_option['choice']: tray_option['text'] for tray_option in option.choices } # Add new trays vals['tray_ids'].extend([ (0, 0, { 'name': text, 'system_name': choice }) for choice, text in cups_trays.items() if choice not in self.tray_ids.mapped('system_name') ]) # Remove deleted trays vals['tray_ids'].extend([ (2, tray.id) for tray in self.tray_ids.filtered( lambda record: record.system_name not in cups_trays.keys()) ]) return vals
def _get_values_for_option(self, cups_connection, cups_printer, option_key): printer_uri = cups_printer['printer-uri-supported'] printer_system_name = printer_uri[printer_uri.rfind('/') + 1:] ppd_info = cups_connection.getPPD3(printer_system_name) ppd_path = ppd_info[2] if not ppd_path: return False ppd = cups.PPD(ppd_path) option = ppd.findOption(option_key) try: os.unlink(ppd_path) except OSError as err: # ENOENT means No such file or directory # The file has already been deleted, we can continue the update if err.errno != errno.ENOENT: raise return option
def _prepare_update_from_cups(self, cups_connection, cups_printer): vals = super(PrintingPrinter, self)._prepare_update_from_cups(cups_connection, cups_printer) printer_uri = cups_printer['printer-uri-supported'] printer_system_name = printer_uri[printer_uri.rfind('/') + 1:] ppd_info = cups_connection.getPPD3(printer_system_name) ppd_path = ppd_info[2] if not ppd_path: return vals ppd = cups.PPD(ppd_path) option = ppd.findOption('InputSlot') try: os.unlink(ppd_path) except OSError as err: if err.errno == errno.ENOENT: pass raise if not option: return vals vals_trays = [] tray_names = set(tray.system_name for tray in self.tray_ids) for tray_option in option.choices: if tray_option['choice'] not in tray_names: tray_vals = { 'name': tray_option['text'], 'system_name': tray_option['choice'], } vals_trays.append((0, 0, tray_vals)) cups_trays = set(tray_option['choice'] for tray_option in option.choices) for tray in self.tray_ids: if tray.system_name not in cups_trays: vals_trays.append((2, tray.id)) vals['tray_ids'] = vals_trays return vals
def test_cups_module(): cups.setUser("root") cups.setPasswordCB(callback) conn = cups.Connection() printers = list(conn.getPrinters().keys()) if 0: print("Getting cupsd.conf") file("cupsd.conf", "w") conn.getFile("/admin/conf/cupsd.conf", "cupsd.conf") print("Putting cupsd.conf") conn.putFile("/admin/conf/cupsd.conf", "cupsd.conf") print("Getting PPD for %s" % printers[len(printers) - 1]) f = conn.getPPD(printers[len(printers) - 1]) ppd = cups.PPD(f) ppd.markDefaults() print(ppd.conflicts()) groups = ppd.optionGroups for group in groups: for opt in group.options: print(list(map(lambda x: x["text"], opt.choices)))
def test_writePPD(self): # copy test file because it gets deleted after usage ppd = os.path.join(self.base_dir, "test.ppd") copy_ppd = os.path.join("/tmp", "test.ppd") if not os.path.exists(copy_ppd): copyfile(ppd, copy_ppd) self.cups.client.getServerPPD.return_value = copy_ppd self.cups.client.getServerPPD.side_effect = PPDException("fake exception") with pytest.raises(PPDException): self.cups.writePPD(None, None, None, None) self.cups.client.getServerPPD.side_effect = None res = self.cups.writePPD(None, "test.ppd", None, {"PageSize": "A4"}) assert "gotoPrinterPPD" in res # open the newly written PPD and check if the value has been set path = get_local_ppd_path(res["gotoPrinterPPD"][0]) ppd = cups.PPD(path) option = ppd.findOption("PageSize") assert option.defchoice == "A4" os.unlink(path)
def MissingExecutables(self, ppd_filename): ppd = cups.PPD(ppd_filename) return cupshelpers.missingExecutables(ppd)
def NewPrinter (self, status, name, mfg, mdl, des, cmd): if name.find("/") >= 0: # name is a URI, no queue was generated, because no suitable # driver was found title = _("Missing printer driver") devid = "MFG:%s;MDL:%s;DES:%s;CMD:%s;" % (mfg, mdl, des, cmd) if (mfg and mdl) or des: if (mfg and mdl): device = "%s %s" % (mfg, mdl) else: device = des text = _("No printer driver for %s.") % device else: text = _("No driver for this printer.") n = Notify.Notification.new (title, text, 'printer') if "actions" in Notify.get_server_caps(): n.set_urgency (Notify.Urgency.CRITICAL) n.set_timeout (Notify.EXPIRES_NEVER) n.add_action ("setup-printer", _("Search"), lambda x, y: self.setup_printer (x, y, name, devid)) else: self.setup_printer (None, None, name, devid) else: # name is the name of the queue which hal_lpadmin has set up # automatically. c = cups.Connection () try: printer = c.getPrinters ()[name] except KeyError: return try: filename = c.getPPD (name) except cups.IPPError: return del c # Check for missing packages cups.ppdSetConformance (cups.PPD_CONFORM_RELAXED) ppd = cups.PPD (filename) import os os.unlink (filename) import sys sys.path.append (APPDIR) import cupshelpers (missing_pkgs, missing_exes) = cupshelpers.missingPackagesAndExecutables (ppd) from cupshelpers.ppds import ppdMakeModelSplit (make, model) = ppdMakeModelSplit (printer['printer-make-and-model']) driver = make + " " + model if status < self.STATUS_GENERIC_DRIVER: title = _("Printer added") else: title = _("Missing printer driver") if len (missing_pkgs) > 0: pkgs = reduce (lambda x,y: x + ", " + y, missing_pkgs) title = _("Install printer driver") text = (_("`%s' requires driver installation: %s.") % (name, pkgs)) n = Notify.Notification.new (title, text, 'printer') import installpackage if "actions" in Notify.get_server_caps(): try: self.packagekit = installpackage.PackageKit () n.set_timeout (Notify.EXPIRES_NEVER) n.add_action ("install-driver", _("Install"), lambda x, y: self.install_driver (x, y, missing_pkgs)) except: pass else: try: self.packagekit = installpackage.PackageKit () self.packagekit.InstallPackageName (0, 0, missing_pkgs[0]) except: pass elif status == self.STATUS_SUCCESS: devid = "MFG:%s;MDL:%s;DES:%s;CMD:%s;" % (mfg, mdl, des, cmd) text = _("`%s' is ready for printing.") % name n = Notify.Notification.new (title, text, 'printer') if "actions" in Notify.get_server_caps(): n.set_urgency (Notify.Urgency.NORMAL) n.add_action ("test-page", _("Print test page"), lambda x, y: self.print_test_page (x, y, name)) n.add_action ("configure", _("Configure"), lambda x, y: self.configure (x, y, name)) else: # Model mismatch devid = "MFG:%s;MDL:%s;DES:%s;CMD:%s;" % (mfg, mdl, des, cmd) text = (_("`%s' has been added, using the `%s' driver.") % (name, driver)) n = Notify.Notification.new (title, text, 'printer') if "actions" in Notify.get_server_caps(): n.set_urgency (Notify.Urgency.CRITICAL) n.add_action ("test-page", _("Print test page"), lambda x, y: self.print_test_page (x, y, name, devid)) n.add_action ("find-driver", _("Find driver"), lambda x, y: self.find_driver (x, y, name, devid)) n.set_timeout (Notify.EXPIRES_NEVER) else: self.configure (None, None, name) self.timeout_ready () n.show () self.notification = n
def getCapabilities(self, gcpid, cupsprintername, overrideoptionsstring): """Gets capabilities of printer and maps them against list Args: gcpid: printer id from google cupsprintername: name of the printer in cups overrideoptionsstring: override for print job Returns: List of capabilities """ overrideoptions = overrideoptionsstring.split(' ') import cups connection = cups.Connection() cupsprinters = connection.getPrinters() capabilities = {"capabilities": []} overridecapabilities = {} ignorecapabilities = ['Orientation'] for optiontext in overrideoptions: if '=' in optiontext: optionparts = optiontext.split('=') option = optionparts[0] if option in ignorecapabilities: continue value = optionparts[1] overridecapabilities[option] = value # landscape if optiontext == 'landscape' or optiontext == 'nolandscape': overridecapabilities['Orientation'] = 'Landscape' overrideDefaultDefaults = {'Duplex': 'None'} for capability in overrideDefaultDefaults: if capability not in overridecapabilities: overridecapabilities[capability] = overrideDefaultDefaults[ capability] attrs = cups.PPD(connection.getPPD(cupsprintername)).attributes for attr in attrs: if attr.name.startswith('Default'): # gcp setting, reverse back to GCP capability gcpname = None hashname = attr.name.replace('Default', '') # find item name from hashes details = self.getPrinterDetails(gcpid) fulldetails = details['printers'][0] gcpoption = None addedCapabilities = [] for capability in fulldetails['capabilities']: if hashname == self.getInternalName( capability, 'capability'): gcpname = capability['name'] for option in capability['options']: internalCapability = self.getInternalName( option, 'option', gcpname, addedCapabilities) addedCapabilities.append(internalCapability) if attr.value == internalCapability: gcpoption = option['name'] break addedOptions = [] for overridecapability in overridecapabilities: if 'Default' + overridecapability == attr.name: selectedoption = overridecapabilities[ overridecapability] for option in capability['options']: internalOption = self.getInternalName( option, 'option', gcpname, addedOptions) addedOptions.append(internalOption) if selectedoption == internalOption: gcpoption = option['name'] break break break # hardcoded to feature type temporarily if gcpname != None and gcpoption != None: capabilities['capabilities'].append({ 'type': 'Feature', 'name': gcpname, 'options': [{ 'name': gcpoption }] }) return capabilities
def main(): """Application entry point. """ configure_logging() plugin_manager = create_plugin_manager() config = PiConfigParser("~/.config/pibooth/pibooth.cfg", plugin_manager) conn = cups.Connection() name = config.get('PRINTER', 'printer_name') if not name or name.lower() == 'default': name = conn.getDefault() if not name and conn.getPrinters(): name = list(conn.getPrinters().keys())[0] # Take first one elif name not in conn.getPrinters(): name = None if not name: if name.lower() == 'default': LOGGER.warning( "No printer configured in CUPS (see http://localhost:631)") return else: LOGGER.warning( "No printer named '%s' in CUPS (see http://localhost:631)", name) return else: LOGGER.info("Connected to printer '%s'", name) f = conn.getPPD(name) ppd = cups.PPD(f) groups = ppd.optionGroups options = [] for group in groups: group_name = "{} - {}".format(group.name, group.text) for opt in group.options: option = {'group': group_name} values = list(map(lambda x: x["choice"], opt.choices)) texts = list(map(lambda x: x["text"], opt.choices)) option['keyword'] = opt.keyword option['value'] = opt.defchoice option['description'] = opt.text if values != texts: option['choices'] = dict([(v, texts[values.index(v)]) for v in values]) else: option['choices'] = values options.append(option) if '--json' in sys.argv: print( json.dumps( dict([(option['keyword'], option['value']) for option in options]))) else: for option in options: print("{} = {}".format(option['keyword'], option['value'])) print(" Description: {}".format(option['description'])) if isinstance(option['choices'], dict): choices = [ "{} = {}".format(value, descr) for value, descr in option['choices'].items() ] print(" Choices: {}".format(choices[0])) for choice in choices[1:]: print(" {}".format(choice)) else: print(" Choices: {}".format(", ".join( option['choices']))) print()
if match: exp = re.compile(match) list = filter(lambda x: exp.match(x), list) n = len(list) i = 0 for name in list: i += 1 try: ppd = d.cat(name) (fd, fname) = tempfile.mkstemp() f = os.fdopen(fd, "w") f.write(ppd) del f try: PPD = cups.PPD(fname) except: os.unlink(fname) raise os.unlink(fname) (pkgs, exes) = missingPackagesAndExecutables(PPD) if pkgs or exes: raise MissingExecutables attr = PPD.findAttr('1284DeviceID') if attr: pieces = attr.value.split(';') mfg = mdl = None for piece in pieces: s = piece.split(':', 1)
def getConfigurePrinterTemplate(self, data): """ Generates a GUI template from a PPD file. """ if self.client is None: return ppd_file = None name = None delete = True # extract name from data if isinstance(data, str): name = data elif isinstance(data, dict): if "gotoPrinterPPD" in data and data["gotoPrinterPPD"] is not None: ppd_file = data["gotoPrinterPPD"] if get_local_ppd_path(ppd_file) is not None: ppd_file = get_local_ppd_path(ppd_file) delete = False else: r = requests.get(requests.utils.quote(ppd_file, safe=":/")) temp_file = tempfile.NamedTemporaryFile(delete=False) with open(temp_file.name, "w") as tf: tf.write(r.content.decode("utf-8")) ppd_file = temp_file.name else: name = data["serverPPD"] else: name = str(data) template = { "type": "widget", "class": "gosa.ui.tabview.TabView", "addOptions": { "flex": 1 }, "properties": { "width": 800, "height": 600, "windowTitle": "tr('Configure printer')", "dialogName": "configurePrinter", "cancelable": True }, "extensions": { "validator": { "target": "form", "name": "Constraints", "properties": {} } }, "children": [] } try: if ppd_file is None: ppd_file = self.client.getServerPPD(name) if not os.path.exists(ppd_file): raise CupsException(C.make_error('PPD_NOT_FOUND', ppd=ppd_file)) ppd = cups.PPD(ppd_file) ppd.localize() model_attr = ppd.findAttr("ModelName") if model_attr: template["properties"]["windowTitle"] = N_( "Configure printer: %s" % model_attr.value) constraints = {} for constraint in ppd.constraints: if constraint.option1 not in constraints: constraints[constraint.option1] = {} choice1 = constraint.choice1 if constraint.choice1 is not None else "__choice__" if choice1 not in constraints[constraint.option1]: constraints[constraint.option1][choice1] = [] option2 = ppd.findOption(constraint.option2) # find choice choice_title = constraint.choice2 for choice in option2.choices: if choice["choice"] == constraint.choice2: choice_title = choice["text"] constraints[constraint.option1][choice1].append({ "option": constraint.option2, "optionTitle": option2.text, "choice": constraint.choice2, "choiceTitle": choice_title }) template["extensions"]["validator"]["properties"][ "constraints"] = constraints for group in sorted(ppd.optionGroups, key=functools.cmp_to_key( self.__compare_group)): template["children"].append(self.__read_group(group)) return template except cups.IPPError as e: raise CupsException( C.make_error('ERROR_GETTING_SERVER_PPD', str(e))) finally: if delete is True and ppd_file and os.access(ppd_file, os.F_OK): os.unlink(ppd_file)
def writePPD(self, printer_cn, server_ppd_file, custom_ppd_file, data): if self.client is None: return server_ppd = None dir = self.env.config.get("cups.spool", default="/tmp/spool") if not os.path.exists(dir): os.makedirs(dir) try: server_ppd = self.client.getServerPPD(server_ppd_file) is_server_ppd = True ppd = cups.PPD(server_ppd) except Exception as e: self.log.error(str(e)) is_server_ppd = False if custom_ppd_file is not None: ppd = cups.PPD(os.path.join(dir, custom_ppd_file)) else: raise PPDException(C.make_error('COULD_NOT_READ_SOURCE_PPD')) if isinstance(data, str): data = loads(data) # apply options for option_name, value in data.items(): option = ppd.findOption(option_name) if option is not None: conflicts = ppd.markOption(option_name, value) if conflicts > 0: raise PPDException( C.make_error('OPTION_CONFLICT', option=option_name, value=value, conflicts=conflicts)) else: raise PPDException( C.make_error('OPTION_NOT_FOUND', option=option_name)) # calculate hash value for new PPD temp_file = tempfile.NamedTemporaryFile(delete=False) try: with open(temp_file.name, "w") as tf: ppd.writeFd(tf.fileno()) with open(temp_file.name, "r") as tf: result = tf.read() hash = hashlib.md5(repr(result).encode('utf-8')).hexdigest() index = PluginRegistry.getInstance("ObjectIndex") new_file = os.path.join(dir, "%s.ppd" % hash) if new_file == custom_ppd_file: # nothing to to return {} if not is_server_ppd: # check if anyone else is using a file with this hash value and delete the old file if not query = { "_type": "GotoPrinter", "gotoPrinterPPD": "%s.ppd" % hash } if printer_cn is not None: query["not_"] = {"cn": printer_cn} res = index.search(query, {"dn": 1}) if len(res) == 0: # delete file os.unlink(custom_ppd_file) with open(new_file, "w") as f: f.write(result) return { "gotoPrinterPPD": ["%s/ppd/modified/%s.ppd" % (get_server_url(), hash)], "configured": [True] } except Exception as e: self.log.error(str(e)) return {} finally: os.unlink(temp_file.name) if server_ppd is not None: os.unlink(server_ppd)
def display(self): self.answers = {} answers = self.troubleshooter.answers if not answers['cups_queue_listed']: return False parent = self.troubleshooter.get_window() name = answers['cups_queue'] tmpf = None try: cups.setServer('') self.op = TimedOperation(cups.Connection, parent=parent) c = self.op.run() self.op = TimedOperation(c.getPPD, args=(name, ), parent=parent) tmpf = self.op.run() except RuntimeError: return False except cups.IPPError: return False self.install_button.hide() title = None text = None try: ppd = cups.PPD(tmpf) self.answers['cups_printer_ppd_valid'] = True def options(options_list): o = {} for option in options_list: o[option.keyword] = option.defchoice return o defaults = {} for group in ppd.optionGroups: g = options(group.options) for subgroup in group.subgroups: g[subgroup.name] = options(subgroup.options) defaults[group.name] = g self.answers['cups_printer_ppd_defaults'] = defaults except RuntimeError: title = _("Invalid PPD File") self.answers['cups_printer_ppd_valid'] = False try: self.op = TimedSubprocess(parent=parent, args=['cupstestppd', '-rvv', tmpf], close_fds=True, stdin=file("/dev/null"), stdout=subprocess.PIPE, stderr=subprocess.PIPE) result = self.op.run() self.answers['cupstestppd_output'] = result text = _("The PPD file for printer '%s' does not conform " "to the specification. " "Possible reason follows:") % name text += '\n' + reduce(lambda x, y: x + '\n' + y, result[0]) except OSError: # Perhaps cupstestppd is not in the path. text = _("There is a problem with the PPD file for " "printer '%s'.") % name if tmpf: os.unlink(tmpf) if title == None and not answers['cups_printer_remote']: (pkgs, exes) = cupshelpers.missingPackagesAndExecutables(ppd) self.answers['missing_pkgs_and_exes'] = (pkgs, exes) if len(pkgs) > 0 or len(exes) > 0: title = _("Missing Printer Driver") if len(pkgs) > 0: try: self.packagekit = installpackage.PackageKit() except: pkgs = [] if len(pkgs) > 0: self.package = pkgs[0] text = _( "Printer '%s' requires the %s package but it " "is not currently installed.") % (name, self.package) self.install_button.show() else: text = _( "Printer '%s' requires the '%s' program but it " "is not currently installed.") % (name, (exes + pkgs)[0]) if title != None: self.label.set_markup('<span weight="bold" size="larger">' + title + '</span>\n\n' + text) return title != None
def getCapabilities(self, gcpid, cupsprintername, overrideoptionsstring): """Gets capabilities of printer and maps them against list Args: gcpid: printer id from google cupsprintername: name of the printer in cups overrideoptionsstring: override for print job Returns: List of capabilities """ overrideoptions = overrideoptionsstring.split(' ') import cups connection = cups.Connection() cupsprinters = connection.getPrinters() capabilities = {"capabilities": []} overridecapabilities = {} for optiontext in overrideoptions: if '=' in optiontext: optionparts = optiontext.split('=') option = optionparts[0] value = optionparts[1] overridecapabilities[option] = value attrs = cups.PPD(connection.getPPD(cupsprintername)).attributes for attr in attrs: if attr.name.startswith('DefaultGCP_'): # gcp setting, reverse back to GCP capability gcpname = None hashname = attr.name.replace('DefaultGCP_', '') # find item name from hashes details = self.getPrinterDetails(gcpid) fulldetails = details['printers'][0] gcpoption = None for capability in fulldetails['capabilities']: if hashname == hashlib.sha256( self.sanitizeText( capability['name'])).hexdigest()[:7]: gcpname = capability['name'] for option in capability['options']: if attr.value == hashlib.sha256( self.sanitizeText( option['name'])).hexdigest()[:7]: gcpoption = option['name'] break for overridecapability in overridecapabilities: if 'Default' + overridecapability == attr.name: selectedoption = overridecapabilities[ overridecapability] for option in capability['options']: if selectedoption == hashlib.sha256( self.sanitizeText(option['name']) ).hexdigest()[:7]: gcpoption = option['name'] break break break # hardcoded to feature type temporarily if gcpname != None and gcpoption != None: capabilities['capabilities'].append({ 'type': 'Feature', 'name': gcpname, 'options': [{ 'name': gcpoption }] }) return capabilities
class Printer: _flags_blacklist = ["options", "local"] def __init__(self, name, connection, **kw): """ @param name: printer name @type name: string @param connection: CUPS connection @type connection: CUPS.Connection object @param kw: printer attributes @type kw: dict indexed by string """ self.name = name self.connection = connection self.class_members = [] have_kw = len(kw) > 0 fetch_attrs = True if have_kw: self.update(**kw) if self.is_class: fetch_attrs = True else: fetch_attrs = False if fetch_attrs: self.getAttributes() self._ppd = None # load on demand def __del__(self): if self._ppd != None: os.unlink(self._ppd) def __repr__(self): return "<cupshelpers.Printer \"%s\">" % self.name def _expand_flags(self): def _ascii_lower(str): return str.translate( string.maketrans(string.ascii_uppercase, string.ascii_lowercase)) prefix = "CUPS_PRINTER_" prefix_length = len(prefix) # loop over cups constants for name in cups.__dict__: if name.startswith(prefix): attr_name = \ _ascii_lower(name[prefix_length:]) if attr_name in self._flags_blacklist: continue if attr_name == "class": attr_name = "is_class" # set as attribute setattr(self, attr_name, bool(self.type & getattr(cups, name))) def update(self, **kw): """ Update object from printer attributes. @param kw: printer attributes @type kw: dict indexed by string """ self.state = kw.get('printer-state', 0) self.enabled = self.state != cups.IPP_PRINTER_STOPPED self.device_uri = kw.get('device-uri', "") self.info = kw.get('printer-info', "") self.is_shared = kw.get('printer-is-shared', None) self.location = kw.get('printer-location', "") self.make_and_model = kw.get('printer-make-and-model', "") self.type = kw.get('printer-type', 0) self.uri_supported = kw.get('printer-uri-supported', "") if type(self.uri_supported) != list: self.uri_supported = [self.uri_supported] self._expand_flags() if self.is_shared is None: self.is_shared = not self.not_shared del self.not_shared self.class_members = kw.get('member-names', []) if type(self.class_members) != list: self.class_members = [self.class_members] self.class_members.sort() self.other_attributes = kw def getAttributes(self): """ Fetch further attributes for the printer. Normally only a small set of attributes is fetched. This method is for fetching more. """ attrs = self.connection.getPrinterAttributes(self.name) self.attributes = {} self.other_attributes = {} self.possible_attributes = { 'landscape': ('False', ['True', 'False']), 'page-border': ('none', ['none', 'single', 'single-thick', 'double', 'double-thick']), } for key, value in attrs.iteritems(): if key.endswith("-default"): name = key[:-len("-default")] if name in [ "job-sheets", "printer-error-policy", "printer-op-policy", # handled below "notify-events", # cannot be set "document-format", # cannot be set "notify-lease-duration" ]: # cannot be set continue supported = attrs.get(name + "-supported", None) or \ self.possible_attributes.get(name, None) or \ "" # Convert a list into a comma-separated string, since # it can only really have been misinterpreted as a list # by CUPS. if isinstance(value, list): value = reduce(lambda x, y: x + ',' + y, value) self.attributes[name] = value if attrs.has_key(name + "-supported"): supported = attrs[name + "-supported"] self.possible_attributes[name] = (value, supported) elif (not key.endswith("-supported") and key != 'job-sheets-default' and key != 'printer-error-policy' and key != 'printer-op-policy' and not key.startswith('requesting-user-name-')): self.other_attributes[key] = value self.job_sheet_start, self.job_sheet_end = attrs.get( 'job-sheets-default', ('none', 'none')) self.job_sheets_supported = attrs.get('job-sheets-supported', ['none']) self.error_policy = attrs.get('printer-error-policy', 'none') self.error_policy_supported = attrs.get( 'printer-error-policy-supported', ['none']) self.op_policy = attrs.get('printer-op-policy', "") or "default" self.op_policy_supported = attrs.get('printer-op-policy-supported', ["default"]) self.default_allow = True self.except_users = [] if attrs.has_key('requesting-user-name-allowed'): self.except_users = attrs['requesting-user-name-allowed'] self.default_allow = False elif attrs.has_key('requesting-user-name-denied'): self.except_users = attrs['requesting-user-name-denied'] self.except_users_string = ', '.join(self.except_users) self.update(**attrs) def getServer(self): """ Find out which server defines this printer. @returns: server URI or None """ if not self.uri_supported[0].startswith('ipp://'): return None uri = self.uri_supported[0][6:] uri = uri.split('/')[0] uri = uri.split(':')[0] if uri == "localhost.localdomain": uri = "localhost" return uri def getPPD(self): """ Obtain the printer's PPD. @returns: cups.PPD object, or False for raw queues @raise cups.IPPError: IPP error """ result = None if self._ppd is None: try: self._ppd = self.connection.getPPD(self.name) result = cups.PPD(self._ppd) except cups.IPPError, (e, m): if e == cups.IPP_NOT_FOUND: result = False else: raise if result == None and self._ppd != None: result = cups.PPD(self._ppd) return result
def getPPDAttributes(self, name): return cups.PPD(self._connection.getPPD(name)).attributes