%PDF- %PDF-
Direktori : /usr/share/system-config-printer/ |
Current File : //usr/share/system-config-printer/newprinter.py |
#!/usr/bin/python3 ## system-config-printer ## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Red Hat, Inc. ## Authors: ## Tim Waugh <twaugh@redhat.com> ## Florian Festi <ffesti@redhat.com> ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # config is generated from config.py.in by configure import config import authconn import cupshelpers from OpenPrintingRequest import OpenPrintingRequest import errno import sys, os, tempfile, time, re, http.client import locale import string import subprocess from timedops import * import dbus from gi.repository import Gdk from gi.repository import Gtk import functools import cups try: import pysmb PYSMB_AVAILABLE=True except: PYSMB_AVAILABLE=False import options from gi.repository import GObject from gi.repository import GLib from gui import GtkGUI from optionwidgets import OptionWidget from debug import * import probe_printer import urllib.request, urllib.parse from smburi import SMBURI from errordialogs import * from PhysicalDevice import PhysicalDevice import firewallsettings import asyncconn import ppdsloader import dnssdresolve import installpackage import gettext gettext.install(domain=config.PACKAGE, localedir=config.localedir) HTTPS_TIMEOUT = 15.0 TEXT_adjust_firewall = _("The firewall may need adjusting in order to " "detect network printers. Adjust the " "firewall now?") def validDeviceURI (uri): """Returns True is the provided URI is valid.""" (scheme, rest) = urllib.parse.splittype (uri) if scheme is None or scheme == '': return False return True # Both the printer properties window and the new printer window # need to be able to drive 'class members' selections. def moveClassMembers(treeview_from, treeview_to): selection = treeview_from.get_selection() model_from, rows = selection.get_selected_rows() rows = [Gtk.TreeRowReference.new(model_from, row) for row in rows] model_to = treeview_to.get_model() for row in rows: path = row.get_path() iter = model_from.get_iter(path) row_data = model_from.get(iter, 0) model_to.append(row_data) model_from.remove(iter) def getCurrentClassMembers(treeview): model = treeview.get_model() iter = model.get_iter_first() result = [] while iter: result.append(model.get(iter, 0)[0]) iter = model.iter_next(iter) result.sort() return result def checkNPName(printers, name): if not name: return False name = name.lower() for printer in printers.values(): if not printer.discovered and printer.name.lower()==name: return False return True def ready (win, cursor=None): try: gdkwin = win.get_window() if gdkwin: gdkwin.set_cursor (cursor) while Gtk.events_pending (): Gtk.main_iteration () except: nonfatalException () def busy (win): ready (win, Gdk.Cursor.new(Gdk.CursorType.WATCH)) def on_delete_just_hide (widget, event): widget.hide () return True # stop other handlers def _singleton (x): """If we don't know whether getPPDs() or getPPDs2() was used, this function can unwrap an item from a list in either case.""" if isinstance (x, list): return x[0] return x def download_gpg_fingerprint(url): """Get GPG fingerprint from URL. Check that the URL is HTTPS with a valid and trusted server certificate, read it, extract the GPG fingerprint from it, and return it. Return None if the URL is invalid, not trusted, or the fingerprint can't be found. """ if not url.startswith('https://'): debugprint('Not a https fingerprint URL: %s, ignoring driver' % url) return None content = None try: with urllib.request.urlopen(url, timeout=HTTPS_TIMEOUT) as resp: if resp.status == 200: content = resp.read().decode('utf-8') except Exception as e: debugprint('Cannot retrieve %s: %s' % (url, e)) if content is None: debugprint('Cannot retrieve %s' % url) return None keyid_re = re.compile(r' ((?:(?:[0-9A-F]{4})(?:\s+|$)){10})$', re.M) m = keyid_re.search(content) if m: return m.group(1).strip().replace(' ','') return None class NewPrinterGUI(GtkGUI): __gsignals__ = { 'destroy': (GObject.SignalFlags.RUN_LAST, None, ()), 'printer-added' : (GObject.SignalFlags.RUN_LAST, None, (str,)), 'printer-modified': (GObject.SignalFlags.RUN_LAST, None, (str, # printer name bool,)), # PPD modified? 'driver-download-checked': (GObject.SignalFlags.RUN_LAST, None, (str,)), 'dialog-canceled': (GObject.SignalFlags.RUN_LAST, None, ()), } # Page numbers used to name the different stages PAGE_DESCRIBE_PRINTER = 0 PAGE_SELECT_DEVICE = 1 PAGE_SELECT_INSTALL_METHOD = 2 PAGE_CHOOSE_DRIVER_FROM_DB = 3 PAGE_CHOOSE_CLASS_MEMBERS = 4 PAGE_APPLY = 5 PAGE_INSTALLABLE_OPTIONS = 6 PAGE_DOWNLOAD_DRIVER = 7 # Values returned by _handlePrinterInstallationMode() and # related sub-functions, to know whether to continue the # execution of the remaining logic from calling functions INSTALL_RESULT_DONE = True INSTALL_RESULT_OPS_PENDING = False new_printer_device_tabs = { "parallel" : 0, # empty tab "usb" : 0, "bluetooth" : 0, "hal" : 0, "beh" : 0, "hp" : 0, "hpfax" : 0, "dnssd" : 0, "socket": 2, "lpd" : 3, "scsi" : 4, "serial" : 5, "smb" : 6, "network": 7, } def __init__(self): GObject.GObject.__init__ (self) self.language = locale.getlocale (locale.LC_MESSAGES) self.options = {} # keyword -> Option object self.changed = set() self.conflicts = set() self.device = None self.ppd = None self.remotecupsqueue = False self.exactdrivermatch = False self.installable_options = False self.ppdsloader = None self.installed_driver_files = [] self.searchedfordriverpackages = False self.founddownloadabledrivers = False self.founddownloadableppd = False self.downloadable_driver_for_printer = None self.downloadable_printers = [] self.nextnptab_rerun = False self.printers = {} # set in init() self.recommended_model_selected = False self._searchdialog = None self._installdialog = None self.getWidgets({"NewPrinterWindow": ["NewPrinterWindow", "ntbkNewPrinter", "btnNPBack", "btnNPForward", "btnNPApply", "spinner", "entNPName", "entNPDescription", "entNPLocation", "tvNPDevices", "ntbkNPType", "lblNPDeviceDescription", "expNPDeviceURIs", "tvNPDeviceURIs", "cmbNPTSerialBaud", "cmbNPTSerialParity", "cmbNPTSerialBits", "cmbNPTSerialFlow", "btnNPTLpdProbe", "entNPTLpdHost", "entNPTLpdQueue", "entNPTJetDirectHostname", "entNPTJetDirectPort", "entSMBURI", "btnSMBBrowse", "tblSMBAuth", "rbtnSMBAuthPrompt", "rbtnSMBAuthSet", "entSMBUsername", "entSMBPassword", "btnSMBVerify", "entNPTNetworkHostname", "btnNetworkFind", "lblNetworkFindSearching", "lblNetworkFindNotFound", "entNPTDevice", "tvNCMembers", "tvNCNotMembers", "btnNCAddMember", "btnNCDelMember", "ntbkPPDSource", "rbtnNPPPD", "tvNPMakes", "rbtnNPFoomatic", "filechooserPPD", "rbtnNPDownloadableDriverSearch", "entNPDownloadableDriverSearch", "btnNPDownloadableDriverSearch", "btnNPDownloadableDriverSearch_label", "cmbNPDownloadableDriverFoundPrinters", "tvNPModels", "tvNPDrivers", "rbtnChangePPDasIs", "rbtnChangePPDKeepSettings", "scrNPInstallableOptions", "vbNPInstallOptions", "tvNPDownloadableDrivers", "ntbkNPDownloadableDriverProperties", "lblNPDownloadableDriverSupplier", "cbNPDownloadableDriverSupplierVendor", "lblNPDownloadableDriverLicense", "cbNPDownloadableDriverLicensePatents", "cbNPDownloadableDriverLicenseFree", "lblNPDownloadableDriverDescription", "lblNPDownloadableDriverSupportContacts", "hsDownloadableDriverPerfText", "hsDownloadableDriverPerfLineArt", "hsDownloadableDriverPerfGraphics", "hsDownloadableDriverPerfPhoto", "lblDownloadableDriverPerfTextUnknown", "lblDownloadableDriverPerfLineArtUnknown", "lblDownloadableDriverPerfGraphicsUnknown", "lblDownloadableDriverPerfPhotoUnknown", "frmNPDownloadableDriverLicenseTerms", "tvNPDownloadableDriverLicense", "rbtnNPDownloadLicenseYes", "rbtnNPDownloadLicenseNo"], "WaitWindow": ["WaitWindow", "lblWait"], "SMBBrowseDialog": ["SMBBrowseDialog", "tvSMBBrowser", "btnSMBBrowseOk"]}, domain=config.PACKAGE) # Fill in liststores for combo-box widgets for (widget, opts) in [(self.cmbNPTSerialBaud, [[_("Default"), ""], ["1200", "1200"], ["2400", "2400"], ["4800", "4800"], ["9600", "9600"], ["19200", "19200"], ["38400", "38400"], ["57600", "57600"], ["115200", "115200"]]), (self.cmbNPTSerialParity, [[_("Default"), ""], [_("None"), "none"], [_("Odd"), "odd"], [_("Even"), "even"]]), (self.cmbNPTSerialBits, [[_("Default"), ""], ["8", "8"], ["7", "7"]]), (self.cmbNPTSerialFlow, [[_("Default"), ""], [_("None"), "none"], [_("XON/XOFF (Software)"), "soft"], [_("RTS/CTS (Hardware)"), "hard"], [_("DTR/DSR (Hardware)"), "hard"]]), ]: store = Gtk.ListStore (str, str) for row in opts: store.append (row) widget.set_model (store) cell = Gtk.CellRendererText () widget.clear () widget.pack_start (cell, True) widget.add_attribute (cell, 'text', 0) # Set up some lists m = Gtk.SelectionMode.MULTIPLE s = Gtk.SelectionMode.SINGLE b = Gtk.SelectionMode.BROWSE for name, model, treeview, selection_mode in ( (_("Members of this class"), Gtk.ListStore(str), self.tvNCMembers, m), (_("Others"), Gtk.ListStore(str), self.tvNCNotMembers, m), (_("Devices"), Gtk.ListStore(str), self.tvNPDevices, s), (_("Connections"), Gtk.ListStore(str), self.tvNPDeviceURIs, s), (_("Makes"), Gtk.ListStore(str, str), self.tvNPMakes,s), (_("Models"), Gtk.ListStore(str, str), self.tvNPModels,s), (_("Drivers"), Gtk.ListStore(str), self.tvNPDrivers,s), (_("Downloadable Drivers"), Gtk.ListStore(str), self.tvNPDownloadableDrivers, b), ): cell = Gtk.CellRendererText() column = Gtk.TreeViewColumn(name, cell, text=0) treeview.set_model(model) treeview.append_column(column) treeview.get_selection().set_mode(selection_mode) # Since some dialogs are reused we can't let the delete-event's # default handler destroy them self.SMBBrowseDialog.connect ("delete-event", on_delete_just_hide) self.WaitWindow_handler = self.WaitWindow.connect ("delete-event", on_delete_just_hide) self.ntbkNewPrinter.set_show_tabs(False) self.ntbkPPDSource.set_show_tabs(False) self.ntbkNPType.set_show_tabs(False) self.ntbkNPDownloadableDriverProperties.set_show_tabs(False) self.spinner_count = 0 # Set up OpenPrinting widgets. self.opreq = None self.opreq_handlers = None combobox = self.cmbNPDownloadableDriverFoundPrinters cell = Gtk.CellRendererText() combobox.pack_start (cell, True) combobox.add_attribute(cell, 'text', 0) if config.DOWNLOADABLE_ONLYFREE: for widget in [self.cbNPDownloadableDriverLicenseFree, self.cbNPDownloadableDriverLicensePatents]: widget.hide () if os.path.exists('/etc/apt/sources.list') or os.path.exists( '/etc/apt/sources.list.d'): config.packagesystem = 'deb' self.packageinstaller = 'apt' elif os.path.exists('/etc/yum.conf'): config.packagesystem = 'rpm' self.packageinstaller = 'yum' else: # No known package system, so we only load single PPDs via # OpenPrinting config.DOWNLOADABLE_ONLYPPD = True def protect_toggle (toggle_widget): active = getattr (toggle_widget, 'protect_active', None) if active is not None: toggle_widget.set_active (active) for widget in [self.cbNPDownloadableDriverSupplierVendor, self.cbNPDownloadableDriverLicenseFree, self.cbNPDownloadableDriverLicensePatents]: widget.connect ('clicked', protect_toggle) for widget in [self.hsDownloadableDriverPerfText, self.hsDownloadableDriverPerfLineArt, self.hsDownloadableDriverPerfGraphics, self.hsDownloadableDriverPerfPhoto]: widget.connect ('change-value', lambda x, y, z: True) # Device list slct = self.tvNPDevices.get_selection () slct.set_select_function (self.device_select_function, None) self.tvNPDevices.set_row_separator_func (self.device_row_separator_fn, None) self.tvNPDevices.connect ("row-activated", self.device_row_activated) self.tvNPDevices.connect ("row-expanded", self.device_row_expanded) # Devices expander self.expNPDeviceURIs.connect ("notify::expanded", self.on_expNPDeviceURIs_expanded) self.expNPDeviceURIs.set_expanded(1) # SMB browser self.smb_store = Gtk.TreeStore (GObject.TYPE_PYOBJECT) self.btnSMBBrowse.set_sensitive (True) if not PYSMB_AVAILABLE: self.btnSMBBrowse.set_tooltip_text (_("Browsing requires pysmbc " "module")) self.tvSMBBrowser.set_model (self.smb_store) # SMB list columns col = Gtk.TreeViewColumn (_("Share")) cell = Gtk.CellRendererText () col.pack_start (cell, False) col.set_cell_data_func (cell, self.smbbrowser_cell_share, None) self.tvSMBBrowser.append_column (col) col = Gtk.TreeViewColumn (_("Comment")) cell = Gtk.CellRendererText () col.pack_start (cell, False) col.set_cell_data_func (cell, self.smbbrowser_cell_comment, None) self.tvSMBBrowser.append_column (col) slct = self.tvSMBBrowser.get_selection () slct.set_select_function (self.smb_select_function, None) self.SMBBrowseDialog.set_transient_for(self.NewPrinterWindow) self.tvNPDrivers.set_has_tooltip(True) self.tvNPDrivers.connect("query-tooltip", self.on_NPDrivers_query_tooltip) ppd_filter = Gtk.FileFilter() ppd_filter.set_name(_("PostScript Printer Description files (*.ppd, *.PPD, *.ppd.gz, *.PPD.gz, *.PPD.GZ)")) ppd_filter.add_pattern("*.ppd") ppd_filter.add_pattern("*.PPD") ppd_filter.add_pattern("*.ppd.gz") ppd_filter.add_pattern("*.PPD.gz") ppd_filter.add_pattern("*.PPD.GZ") self.filechooserPPD.add_filter(ppd_filter) ppd_filter = Gtk.FileFilter() ppd_filter.set_name(_("All files (*)")) ppd_filter.add_pattern("*") self.filechooserPPD.add_filter(ppd_filter) self.device_selected = -1 self.dialog_mode = "printer" self.connect_signals () debugprint ("+%s" % self) def __del__ (self): debugprint ("-%s" % self) def do_destroy (self): debugprint ("DESTROY: %s" % self) if self.SMBBrowseDialog: self.SMBBrowseDialog.destroy () self.SMBBrowseDialog = None if self.NewPrinterWindow: self.NewPrinterWindow.destroy () self.NewPrinterWindow = None if self.WaitWindow: self.WaitWindow.destroy () self.WaitWindow = None def inc_spinner_task (self): if self.spinner_count == 0: self.spinner.show () self.spinner.start () self.spinner_count += 1 def dec_spinner_task (self): self.spinner_count -= 1 if self.spinner_count == 0: self.spinner.hide () self.spinner.stop () def show_IPP_Error (self, exception, message): debugprint ("%s: IPP error dialog (%s, %s)" % (self, repr (exception), message)) return show_IPP_Error (exception, message, parent=self.NewPrinterWindow) def option_changed(self, option): if option.is_changed(): self.changed.add(option) else: self.changed.discard(option) if option.conflicts: self.conflicts.add(option) else: self.conflicts.discard(option) self.setDataButtonState() return def setDataButtonState(self): self.btnNPForward.set_sensitive(not bool(self.conflicts)) def makeNameUnique(self, name): """Make a suggested queue name valid and unique.""" name = name.replace (" ", "-") name = name.replace ("/", "-") name = name.replace ("#", "-") if not checkNPName (self.printers, name): suffix=2 while not checkNPName (self.printers, name + "-" + str (suffix)): suffix += 1 if suffix == 100: break name += "-" + str (suffix) return name def destroy (self): self.emit ('destroy') def init(self, dialog_mode, device_uri=None, name=None, ppd=None, devid="", host=None, encryption=None, parent=None, xid=0): self.parent = parent if not self.parent: self.NewPrinterWindow.set_focus_on_map (False) self.dialog_mode = dialog_mode self._device_uri = device_uri self.orig_ppd = ppd self.devid = devid self._host = host self._encryption = encryption self._name = name if not host: self._host = cups.getServer () if not encryption: self._encryption = cups.getEncryption () self.options = {} # keyword -> Option object self.changed = set() self.conflicts = set() self.fetchDevices_conn = None self.ppds = None self.ppdsmatch_result = None self.printer_finder = None if not self._validInitParameters (): raise RuntimeError # Get a current list of printers so that we can know whether # the chosen name is unique. try: self.cups = authconn.Connection (parent=self.NewPrinterWindow, host=self._host, encryption=self._encryption) except cups.HTTPError as e: (s,) = e.args show_HTTP_Error (s, self.parent) return False except RuntimeError: show_HTTP_Error (-1, self.parent) return False except Exception as e: nonfatalException (e) return False try: self.printers = cupshelpers.getPrinters (self.cups) except cups.IPPError as e: (e, m) = e.args show_IPP_Error (e, m, parent=self.parent) return False # Initialise widgets. self.lblNetworkFindSearching.hide () self.entNPTNetworkHostname.set_sensitive (True) self.entNPTNetworkHostname.set_text ('') self.btnNetworkFind.set_sensitive (True) self.lblNetworkFindNotFound.hide () # Clear out any previous list of makes. model = self.tvNPMakes.get_model() model.clear() combobox = self.cmbNPDownloadableDriverFoundPrinters combobox.set_model (Gtk.ListStore (str, str)) self.entNPDownloadableDriverSearch.set_text ('') label = self.btnNPDownloadableDriverSearch_label label.set_text (_("Search")) self.entNPTJetDirectPort.set_text('9100') self.rbtnSMBAuthPrompt.set_active(True) if xid != 0 and self.parent: self.NewPrinterWindow.show_now() self.NewPrinterWindow.set_transient_for (self.parent) if self.dialog_mode == "printer": self._initialisePrinterMode () elif self.dialog_mode == "class": self._initialiseClassMode () elif self.dialog_mode == "device": self._initialiseDeviceMode () elif self.dialog_mode == "printer_with_uri": self._initialisePrinterWithURIMode () elif self.dialog_mode == "ppd": self._initialisePPDMode () elif self.dialog_mode == "download_driver": self._initialiseDownloadDriverMode () return True if xid == 0 and self.parent: self.NewPrinterWindow.set_transient_for (parent) self.NewPrinterWindow.show() self.setNPButtons() return True def _validInitParameters (self): if self.dialog_mode in ['printer_with_uri', 'device', 'ppd']: return self._device_uri is not None elif self.dialog_mode == 'download_driver': return self.devid != "" return True def _initialiseClassMode (self): self._initialiseWidgetsForMode ("class") self.NewPrinterWindow.set_title (_("New Class")) self.fillNewClassMembers () # Start on name page self.ntbkNewPrinter.set_current_page (self.PAGE_DESCRIBE_PRINTER) def _initialisePrinterMode (self): self._initialiseWidgetsForMode ("printer") self.NewPrinterWindow.set_title (_("New Printer")) # Start on devices page (SELECT_DEVICE, not DESCRIBE_PRINTER) self.ntbkNewPrinter.set_current_page (self.PAGE_SELECT_DEVICE) self.fillDeviceTab () self.rbtnNPFoomatic.set_active (True) self.on_rbtnNPFoomatic_toggled (self.rbtnNPFoomatic) def _initialiseDeviceMode (self): self.NewPrinterWindow.set_title (_("Change Device URI")) self.ntbkNewPrinter.set_current_page (self.PAGE_SELECT_DEVICE) self.fillDeviceTab (self._device_uri) def _initialisePrinterWithURIMode (self): self._initialiseWidgetsForMode ("printer") self._initialiseDeviceFromURI () self.NewPrinterWindow.set_title (_("New Printer")) self.ntbkNewPrinter.set_current_page (self.PAGE_SELECT_INSTALL_METHOD) self.rbtnNPFoomatic.set_active (True) self.on_rbtnNPFoomatic_toggled (self.rbtnNPFoomatic) self.rbtnChangePPDKeepSettings.set_active (True) self._initialiseAutoVariables () self.nextNPTab (step = 0) def _initialiseDownloadDriverMode (self): self.NewPrinterWindow.set_title (_("Download Printer Driver")) self.ntbkNewPrinter.set_current_page (self.PAGE_DOWNLOAD_DRIVER) self.nextnptab_rerun = True self.nextNPTab (step = 0) def _initialisePPDMode (self): self._initialiseDeviceFromURI () self.NewPrinterWindow.set_title (_("Change Driver")) # We'll need to know the Device ID for this device. if not self.devid: scheme = str (self._device_uri.split (":", 1)[0]) schemes = [scheme] if scheme in ["socket", "lpd", "ipp"]: schemes.extend (["snmp", "dnssd"]) self.fetchDevices_conn = asyncconn.Connection () self.fetchDevices_conn._begin_operation (_("fetching device list")) self.inc_spinner_task () cupshelpers.getDevices (self.fetchDevices_conn, include_schemes=schemes, reply_handler=self.change_ppd_got_devs, error_handler=self.change_ppd_got_devs) self.ntbkNewPrinter.set_current_page (self.PAGE_SELECT_INSTALL_METHOD) self.rbtnNPFoomatic.set_active (True) self.on_rbtnNPFoomatic_toggled (self.rbtnNPFoomatic) self.rbtnChangePPDKeepSettings.set_active (True) self._initialiseAutoVariables () def _initialiseWidgetsForMode (self, mode_name): self.entNPName.set_text (self.makeNameUnique (mode_name)) self.entNPName.grab_focus () for widget in [self.entNPLocation, self.entNPDescription, self.entSMBURI, self.entSMBUsername, self.entSMBPassword]: widget.set_text ('') def _initialiseDeviceFromURI (self): device_dict = { } self.device = cupshelpers.Device (self._device_uri, **device_dict) def _initialiseAutoVariables (self): self.auto_make = "" self.auto_model = "" self.auto_driver = None def change_ppd_got_devs (self, conn, result): self.fetchDevices_conn._end_operation () self.fetchDevices_conn.destroy () self.fetchDevices_conn = None self.dec_spinner_task () if isinstance (result, Exception): current_devices = {} else: current_devices = result devid = None mm = None if self.devid != "": devid = self.devid else: device = current_devices.get (self.device.uri) if device: devid = device.id mm = device.make_and_model self.device = device # We'll also need the list of PPDs self.ntbkNewPrinter.set_current_page (self.PAGE_SELECT_INSTALL_METHOD) self.nextNPTab(step = 0) def on_ppdsloader_finished_next (self, ppdsloader): """ This method is called when the PPDs loader has finished loading PPDs in preparation for the next screen the user will see, having clicked 'Forward'. We are creating a new queue, and dialog_mode is either "printer" or "printer_with_uri". """ self._getPPDs_reply (ppdsloader) if not self.ppds: return if ppdsloader._jockey_has_answered: self.searchedfordriverpackages = True debugprint ("Loaded PPDs this time; try nextNPTab again...") self.nextnptab_rerun = True if self.ntbkNewPrinter.get_current_page () == self.PAGE_SELECT_INSTALL_METHOD: self.nextNPTab (step = 0) else: self.nextNPTab () # get PPDs def _getPPDs_reply (self, ppdsloader): exc = ppdsloader.get_error () if exc: ppdsloader.destroy () try: raise exc except cups.IPPError as e: (e, m) = e.args self.show_IPP_Error (e, m) return ppds = ppdsloader.get_ppds () if ppds: self.ppds = ppds self.ppdsmatch_result = ppdsloader.get_ppdsmatch_result () if ppdsloader._jockey_has_answered: self.installed_driver_files = ppdsloader.get_installed_files () else: self.ppds = None self.ppdsmatch_result = None ppdsloader.destroy () self.ppdsloader = None # Class members def fillNewClassMembers(self): model = self.tvNCMembers.get_model() model.clear() model = self.tvNCNotMembers.get_model() model.clear() try: self.printers = cupshelpers.getPrinters (self.cups) except cups.IPPError: self.printers = {} for printer in self.printers.keys(): if not self.printers[printer].type & cups.CUPS_PRINTER_CLASS: model.append((printer,)) def on_btnNCAddMember_clicked(self, button): moveClassMembers(self.tvNCNotMembers, self.tvNCMembers) self.btnNPApply.set_sensitive( bool(getCurrentClassMembers(self.tvNCMembers))) button.set_sensitive(False) def on_btnNCDelMember_clicked(self, button): moveClassMembers(self.tvNCMembers, self.tvNCNotMembers) self.btnNPApply.set_sensitive( bool(getCurrentClassMembers(self.tvNCMembers))) button.set_sensitive(False) def on_tvNCMembers_cursor_changed(self, widget): selection = widget.get_selection() if selection is None: return model_from, rows = selection.get_selected_rows() self.btnNCDelMember.set_sensitive(rows != []) def on_tvNCNotMembers_cursor_changed(self, widget): selection = widget.get_selection() if selection is None: return model_from, rows = selection.get_selected_rows() self.btnNCAddMember.set_sensitive(rows != []) # Navigation buttons def on_NPCancel(self, widget, event=None): if self.fetchDevices_conn: self.fetchDevices_conn.destroy () self.fetchDevices_conn = None self.dec_spinner_task () if self.ppdsloader: self.ppdsloader.destroy () self.ppdsloader = None if self.printer_finder: self.printer_finder.cancel () self.printer_finder = None self.dec_spinner_task () self.NewPrinterWindow.hide() if self.opreq is not None: for handler in self.opreq_handlers: self.opreq.disconnect (handler) self.opreq_handlers = None self.opreq.cancel () self.opreq = None self.device = None self.printers = {} self.emit ('dialog-canceled') return True def on_btnNPBack_clicked(self, widget): self.nextNPTab(-1) def on_btnNPForward_clicked(self, widget): self.nextNPTab() def installdriverpackage (self, driver): install_info = self._getDriverInstallationInfo (driver) if not install_info: return False name = install_info['name'] repo = install_info['repo'] keyid = install_info['keyid'] fmt = _("Installing driver %s") % name self._installdialog = Gtk.MessageDialog (parent=self.NewPrinterWindow, modal=True, destroy_with_parent=True, type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.CANCEL, text=fmt) self._installdialog.format_secondary_text (_("Installing ...")) # Add a progress bar to the message box dialogarea = self._installdialog.get_message_area() pbar = Gtk.ProgressBar() dialogarea.add(pbar) pbar.show() # Save a reference to the progress bar in the dialog, so that # we can easily reference it from do_installdriverpackage() self._installdialog._progress_bar = pbar self._installdialog.connect ("response", self._installdialog_response) self._installdialog.show_all () # Perform the actual installation of the printer driver ret = self.do_installdriverpackage (name, repo, keyid) if self._installdialog: self._installdialog.hide () self._installdialog.destroy () self._installdialog = None return ret def do_installdriverpackage(self, name, repo, keyid): debugprint('Installing driver: %s; Repo: %s; Key ID: %s' % (repr (name), repr (repo), repr (keyid))) # Do the installation with a command line helper script new_environ = os.environ.copy() new_environ['LC_ALL'] = "C" if keyid: args = ["install-printerdriver", name, repo, keyid] else: args = ["install-printerdriver", name, repo] debugprint ("Running command: " + repr(args)) ret = True try: self.p = subprocess.Popen (args, env=new_environ, close_fds=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE) # Keep the UI refreshed while we wait for # the drivers query to complete. (stdout, stderr) = (self.p.stdout, self.p.stderr) done = False pbar = self._installdialog._progress_bar while self.p.poll() is None: line = stdout.readline ().strip() if (len(line) > 0): if line == "done": done = True break elif line.startswith(b"P"): try: percentage = float(line[1:]) if percentage >= 0: pbar.set_fraction(percentage/100) else: pbar.set_pulse_step(-percentage/100) pbar.pulse() except: pass else: self.installed_driver_files.append(line.decode("utf-8")); while Gtk.events_pending (): Gtk.main_iteration () if not line: time.sleep (0.1) if self.p.returncode != 0 and not done: ret = False except: # Problem executing command. ret = False if not ret: self.installed_driver_files = []; return ret def _getDriverInstallationInfo (self, driver): pkgs = driver.get('packages', {}) arches = list(pkgs.keys()) if len(arches) == 0: debugprint('No packages for driver') return None if len(arches) > 1: debugprint('Returned more than one matching architecture, please report this as a bug: %s' % repr (arches)) return None pkgs = pkgs[arches[0]] if len(pkgs) != 1: debugprint('Returned more than one package, this is currently not handled') return None pkg = list(pkgs.keys())[0] name = '' if pkg.endswith('.deb'): name = pkg.split('_')[0] elif pkg.endswith('.rpm'): name = '-'.join(pkg.split('-')[0:-2]) else: raise ValueError('Unknown package type: ' + pkg) # require signature for binary packages; architecture # independent packages are usually PPDs, which we trust enough keyid = None if 'fingerprint' not in pkgs[pkg]: if config.DOWNLOADABLE_PKG_ONLYSIGNED and arches[0] not in ['all', 'noarch']: debugprint('Not installing driver as it does not have a GPG fingerprint URL') return None else: keyid = download_gpg_fingerprint(pkgs[pkg]['fingerprint']) if config.DOWNLOADABLE_PKG_ONLYSIGNED and arches[0] not in ['all', 'noarch'] and not keyid: debugprint('Not installing driver as it does not have a valid GPG fingerprint') return None repo = pkgs[pkg].get('repositories', {}).get(self.packageinstaller) if not repo: debugprint('Local package system %s not found in %s' % (self.packageinstaller, repr (pkgs[pkg].get('repositories', {})))) return None # All good: return necessary information to install the driver return { 'name': name, 'repo': repo, 'keyid': keyid } def _installdialog_response (self, dialog, response): self.p.terminate () def nextNPTab(self, step=1): page_nr = self.ntbkNewPrinter.get_current_page() debugprint ("Next clicked on page %d" % page_nr) keep_going = True if self.dialog_mode == "printer" or self.dialog_mode == "printer_with_uri" or \ self.dialog_mode == "ppd" or self.dialog_mode == "download_driver": install_result = self._handlePrinterInstallationMode (step) if install_result == self.INSTALL_RESULT_OPS_PENDING: # Do not continue if the installation process says so # (e.g. waiting for a CUPS to return a list of PPDs) keep_going = False if not keep_going: debugprint ('Interrupting execution of nextNPTab(): Operations pending') return order = self._getPagesOrderForDialogMode () next_page_nr = order[order.index(page_nr)+step] # fill Installable Options tab fetch_ppd = False try: if order.index (self.PAGE_APPLY) > -1: # There is a copy settings page in this set fetch_ppd = next_page_nr == self.PAGE_APPLY and step >= 0 except ValueError: fetch_ppd = next_page_nr == self.PAGE_INSTALLABLE_OPTIONS and step >= 0 debugprint ("Will fetch ppd? %d" % fetch_ppd) if fetch_ppd: self.ppd = self.getNPPPD() self.installable_options = False if self.ppd is None: return # Prepare Installable Options screen. if isinstance(self.ppd, cups.PPD): self.fillNPInstallableOptions() else: # Put a label there explaining why the page is empty. ppd = self.ppd self.ppd = None self.fillNPInstallableOptions() self.ppd = ppd if not self.installable_options: if next_page_nr == self.PAGE_INSTALLABLE_OPTIONS: # step over if empty next_page_nr = order[order.index(next_page_nr)+1] # Step over empty Installable Options tab when moving backwards. if next_page_nr == self.PAGE_INSTALLABLE_OPTIONS and \ not self.installable_options and step < 0: next_page_nr = order[order.index(next_page_nr)-1] debugprint ("Will advance to page %d" % next_page_nr) if step >= 0 and next_page_nr == self.PAGE_DOWNLOAD_DRIVER: # About to show downloadable drivers self.fillDownloadableDrivers () if step >= 0 and next_page_nr == self.PAGE_DESCRIBE_PRINTER: # About to choose a name. # Suggest an appropriate name. name = None descr = None try: if (self.device.id and not self.device.type in ("socket", "lpd", "ipp", "http", "https", "bluetooth")): name = "%s %s" % (self.device.id_dict["MFG"], self.device.id_dict["MDL"]) except: nonfatalException () try: if name is None and isinstance (self.ppd, cups.PPD): mname = self.ppd.findAttr ("modelName").value make, model = cupshelpers.ppds.ppdMakeModelSplit (mname) if make and model: name = "%s %s" % (make, model) elif make or model: name = "%s%s" % (make, model) except: nonfatalException () if name: descr = name else: name = 'printer' name = self.makeNameUnique (name) self.entNPName.set_text (name) if descr: self.entNPDescription.set_text (descr) self.ntbkNewPrinter.set_current_page(next_page_nr) self.setNPButtons() def _getPagesOrderForDialogMode (self): order = [] if self.dialog_mode == "class": order = [ self.PAGE_DESCRIBE_PRINTER, self.PAGE_CHOOSE_CLASS_MEMBERS, self.PAGE_APPLY, ] elif self.dialog_mode == "download_driver": order = [ self.PAGE_DOWNLOAD_DRIVER ] elif self.dialog_mode == "printer": if self.remotecupsqueue: order = [ self.PAGE_SELECT_DEVICE, self.PAGE_DESCRIBE_PRINTER ] elif (self.founddownloadabledrivers and not self.rbtnNPDownloadableDriverSearch.get_active()): if self.exactdrivermatch: order = [ self.PAGE_SELECT_DEVICE, self.PAGE_DOWNLOAD_DRIVER, self.PAGE_INSTALLABLE_OPTIONS, self.PAGE_DESCRIBE_PRINTER ] else: order = [ self.PAGE_SELECT_DEVICE, self.PAGE_DOWNLOAD_DRIVER, self.PAGE_SELECT_INSTALL_METHOD, self.PAGE_CHOOSE_DRIVER_FROM_DB, self.PAGE_INSTALLABLE_OPTIONS, self.PAGE_DESCRIBE_PRINTER ] elif (self.exactdrivermatch and not self.rbtnNPDownloadableDriverSearch.get_active()): order = [ self.PAGE_SELECT_DEVICE, self.PAGE_INSTALLABLE_OPTIONS, self.PAGE_DESCRIBE_PRINTER ] elif self.rbtnNPFoomatic.get_active(): order = [ self.PAGE_SELECT_DEVICE, self.PAGE_SELECT_INSTALL_METHOD, self.PAGE_CHOOSE_DRIVER_FROM_DB, self.PAGE_INSTALLABLE_OPTIONS, self.PAGE_DESCRIBE_PRINTER ] elif self.rbtnNPPPD.get_active(): order = [ self.PAGE_SELECT_DEVICE, self.PAGE_SELECT_INSTALL_METHOD, self.PAGE_INSTALLABLE_OPTIONS, self.PAGE_DESCRIBE_PRINTER ] else: # Downloadable driver order = [ self.PAGE_SELECT_DEVICE, self.PAGE_SELECT_INSTALL_METHOD, self.PAGE_DOWNLOAD_DRIVER, self.PAGE_INSTALLABLE_OPTIONS, self.PAGE_DESCRIBE_PRINTER ] elif self.dialog_mode == "ppd": if self.rbtnNPFoomatic.get_active(): order = [ self.PAGE_SELECT_INSTALL_METHOD, self.PAGE_CHOOSE_DRIVER_FROM_DB, self.PAGE_APPLY, self.PAGE_INSTALLABLE_OPTIONS ] elif self.rbtnNPPPD.get_active(): order = [ self.PAGE_SELECT_INSTALL_METHOD, self.PAGE_APPLY, self.PAGE_INSTALLABLE_OPTIONS ] else: # Downloadable driver order = [ self.PAGE_SELECT_INSTALL_METHOD, self.PAGE_DOWNLOAD_DRIVER, self.PAGE_APPLY, self.PAGE_INSTALLABLE_OPTIONS ] elif self.dialog_mode == "device": order = [ self.PAGE_SELECT_DEVICE, ] else: # dialog_mode == "printer-from-uri" if self.remotecupsqueue: order = [ self.PAGE_DESCRIBE_PRINTER, ] elif (self.founddownloadabledrivers and not self.rbtnNPDownloadableDriverSearch.get_active()): if self.exactdrivermatch: order = [ self.PAGE_DOWNLOAD_DRIVER, self.PAGE_INSTALLABLE_OPTIONS, self.PAGE_DESCRIBE_PRINTER ] else: order = [ self.PAGE_DOWNLOAD_DRIVER, self.PAGE_SELECT_INSTALL_METHOD, self.PAGE_CHOOSE_DRIVER_FROM_DB, self.PAGE_INSTALLABLE_OPTIONS, self.PAGE_DESCRIBE_PRINTER ] elif (self.exactdrivermatch and not self.rbtnNPDownloadableDriverSearch.get_active()): order = [ self.PAGE_INSTALLABLE_OPTIONS, self.PAGE_DESCRIBE_PRINTER ] elif self.rbtnNPFoomatic.get_active(): order = [ self.PAGE_SELECT_INSTALL_METHOD, self.PAGE_CHOOSE_DRIVER_FROM_DB, self.PAGE_INSTALLABLE_OPTIONS, self.PAGE_DESCRIBE_PRINTER ] elif self.rbtnNPPPD.get_active(): order = [ self.PAGE_SELECT_INSTALL_METHOD, self.PAGE_INSTALLABLE_OPTIONS, self.PAGE_DESCRIBE_PRINTER ] else: # Downloadable driver order = [ self.PAGE_SELECT_INSTALL_METHOD, self.PAGE_DOWNLOAD_DRIVER, self.PAGE_INSTALLABLE_OPTIONS, self.PAGE_DESCRIBE_PRINTER ] return order def _handlePrinterInstallationMode (self, step): busy (self.NewPrinterWindow) page_nr = self.ntbkNewPrinter.get_current_page () page_nr = self.ntbkNewPrinter.get_current_page () # Let's assume everything will be completed by default result = self.INSTALL_RESULT_DONE if (((page_nr == self.PAGE_SELECT_DEVICE or page_nr == self.PAGE_DOWNLOAD_DRIVER) and step > 0) or ((page_nr == self.PAGE_SELECT_INSTALL_METHOD or page_nr == self.PAGE_DOWNLOAD_DRIVER) and step == 0)): # The function below will return INSTALL_RESULT_OPS_PENDING # if, for some reason, it spawns an asynchronous operation # and needs to wait for its results before continuing. result = self._handlePrinterInstallationStage (page_nr, step) if page_nr == self.PAGE_CHOOSE_DRIVER_FROM_DB: if not self.device.id: # Choose an appropriate name when no Device ID # is available, based on the model the user has # selected. try: model, iter = self.tvNPModels.get_selection ().\ get_selected () name = model.get(iter, 0)[0] name = self.makeNameUnique (name) self.entNPName.set_text (name) except: nonfatalException () ready (self.NewPrinterWindow) return result def _handlePrinterInstallationStage (self, page_nr, step): if self.dialog_mode != "download_driver": uri = self.device.uri if uri and uri.startswith ("smb"): # User has selected an smb device uri = SMBURI (uri=uri[6:]).sanitize_uri () self._installSMBBackendIfNeeded () if page_nr == self.PAGE_SELECT_DEVICE or page_nr == self.PAGE_SELECT_INSTALL_METHOD: self._selectDeviceForInstallation (uri) elif page_nr == self.PAGE_DOWNLOAD_DRIVER and self.nextnptab_rerun == False: self._handleDriverInstallation () devid = None if not self.remotecupsqueue or self.dialog_mode == "ppd": if self.dialog_mode != "download_driver": devid = self.device.id # ID of selected device if not devid: devid = self.devid # ID supplied at init() if not devid: devid = None if self.ppds is None and self.dialog_mode != "download_driver": self._loadPPDsForDevice (devid, uri) # _loadPPDsForDevice () is an asynchronous operation, so # let the caller know that it can't continue for now. return self.INSTALL_RESULT_OPS_PENDING self.nextnptab_rerun = False if page_nr == self.PAGE_SELECT_DEVICE or page_nr == self.PAGE_SELECT_INSTALL_METHOD: self._installHPScannerFilesIfNeeded () return self._installPrinterFromDeviceID (devid, page_nr, step) def _installSMBBackendIfNeeded (self): # Does the backend need to be installed? if (self.nextnptab_rerun == False and not self.searchedfordriverpackages and (self._host == 'localhost' or self._host[0] == '/') and not os.access ("/usr/lib/cups/backend/smb", os.F_OK)): debugprint ("No smb backend so attempting install") try: pk = installpackage.PackageKit () # The following call means a blocking, synchronous, D-Bus call pk.InstallPackageName (0, 0, "smbclient") except: nonfatalException () def _selectDeviceForInstallation (self, uri): self._initialiseAutoVariables () self.device.uri = self.getDeviceURI () # Cancel the printer finder now as the user has # already selected their device. if self.fetchDevices_conn: self.fetchDevices_conn.destroy () self.fetchDevices_conn = None self.dec_spinner_task () if self.printer_finder: self.printer_finder.cancel () self.printer_finder = None self.dec_spinner_task () if (not self.device.id and self.device.type in ["socket", "lpd", "ipp"]): # This is a network printer whose model we don't yet know. # Try to discover it. self.getNetworkPrinterMakeModel () # Try to access the PPD, in this case our detected IPP # printer is a queue on a remote CUPS server which is # not automatically set up on our local CUPS server # (for example DNS-SD broadcasted queue from Mac OS X) self.remotecupsqueue = None res = re.search (r"ipp://(\S+?)(:\d+|)/printers/(\S+)", uri) if res: resg = res.groups() if len (resg[1]) > 0: port = int (resg[1][1:]) else: port = 631 try: debugprint('Download ppd file from remote server') conn = http.client.HTTPConnection(resg[0], port) conn.request("GET", "/printers/%s.ppd" % resg[2]) resp = conn.getresponse() if resp.status == 200: self.remotecupsqueue = resg[2] ppdcontent = resp.read() with tempfile.NamedTemporaryFile () as tmpf: tmpf.write(ppdcontent) tmpf.flush() try: ppd = cups.PPD(tmpf.name) except (cups.IPPError, RuntimeError): raise IOError("Server's ppd file is corrupted.") except: pass # We also want to fetch the printer-info and # printer-location attributes, to pre-fill those # fields for this new queue. try: encryption = cups.HTTP_ENCRYPT_IF_REQUESTED c = cups.Connection (host=resg[0], port=port, encryption=encryption) r = ['printer-info', 'printer-location'] attrs = c.getPrinterAttributes (uri=uri, requested_attributes=r) info = attrs.get ('printer-info', '') location = attrs.get ('printer-location', '') if len (info) > 0: self.entNPDescription.set_text (info) if len (location) > 0: self.device.location = location except RuntimeError: pass except: nonfatalException () elif ((uri.startswith ("dnssd:") or uri.startswith("mdns:")) and uri.find ("/cups") != -1 and self.device.info): # Remote CUPS queue discovered by "dnssd" CUPS backend self.remotecupsqueue = self.device.info def _handleDriverInstallation (self): # Install package of the driver found on OpenPrinting treeview = self.tvNPDownloadableDrivers model, iter = treeview.get_selection ().get_selected () driver = None if iter is not None: driver = model.get_value (iter, 1) if driver is None or driver == 0 or 'packages' not in driver: return # Find the package name, repository, and fingerprint # and install the package result = self.installdriverpackage (driver) if not result or len(self.installed_driver_files) == 0: return # We actually installed a package, delete the # PPD list to get it regenerated self.ppds = None if (self.dialog_mode != "download_driver" and (not self.device.id and (not self.device.make_and_model or self.device.make_and_model == "Unknown") and self.downloadable_driver_for_printer)): self.device.make_and_model = self.downloadable_driver_for_printer def _installHPScannerFilesIfNeeded (self): if (hasattr (self.device, 'hp_scannable') and self.device.hp_scannable and not os.access ("/etc/sane.d/dll.d/hpaio", os.R_OK) and not os.access ("/etc/sane.d/dll.d/hplip", os.R_OK)): debugprint ("No HPLIP sane backend so " "attempting install") try: pk = installpackage.PackageKit () # The following call means a blocking, synchronous, D-Bus call pk.InstallPackageName (0, 0, "libsane-hpaio") except: pass def _loadPPDsForDevice (self, devid, uri): debugprint ("nextNPTab: need PPDs loaded") p = ppdsloader.PPDsLoader (device_id=devid, device_uri=uri, parent=self.NewPrinterWindow, host=self._host, encryption=self._encryption) self.ppdsloader = p p.connect ('finished',self.on_ppdsloader_finished_next) p.run () def _installPrinterFromDeviceID (self, devid, page_nr, step): ppdname = None self.id_matched_ppdnames = [] try: if self.dialog_mode == "download_driver": ppdname = "download" status = "generic" elif self.remotecupsqueue: # We have a remote CUPS queue, let the client queue # stay raw so that the driver on the server gets used if self.ppd is None: ppdname = 'raw' self.ppd = ppdname name = self.remotecupsqueue name = self.makeNameUnique (name) self.entNPName.set_text (name) status = "exact" elif (self.device.id or (self.device.make_and_model and self.device.make_and_model != "Unknown") or devid): if self.device.id: id_dict = self.device.id_dict elif devid: id_dict = cupshelpers.parseDeviceID (devid) else: id_dict = {} (id_dict["MFG"], id_dict["MDL"]) = cupshelpers.ppds.\ ppdMakeModelSplit (self.device.make_and_model) id_dict["DES"] = "" id_dict["CMD"] = [] devid = "MFG:%s;MDL:%s;" % (id_dict["MFG"], id_dict["MDL"]) fit = self.ppds.\ getPPDNamesFromDeviceID (id_dict["MFG"], id_dict["MDL"], id_dict["DES"], id_dict["CMD"], self.device.uri, self.device.make_and_model) debugprint ("Suitable PPDs found: %s" % repr(fit)) ppdnamelist = self.ppds.\ orderPPDNamesByPreference (list(fit.keys ()), self.installed_driver_files, devid=id_dict, fit=fit) debugprint ("PPDs in priority order: %s" % repr(ppdnamelist)) self.id_matched_ppdnames = ppdnamelist ppdname = ppdnamelist[0] status = fit[ppdname] elif (self.dialog_mode == "ppd" and self.orig_ppd): attr = self.orig_ppd.findAttr("NickName") if not attr: attr = self.orig_ppd.findAttr("ModelName") if attr and attr.value: value = attr.value if value.endswith (" (recommended)"): value = value[:-14] mfgmdl = cupshelpers.ppds.ppdMakeModelSplit (value) (make, model) = mfgmdl # Search for ppdname with that make-and-model ppds = self.ppds.getInfoFromModel (make, model) for ppd, info in ppds.items (): if (_singleton (info. get ("ppd-make-and-model")) == value): ppdname = ppd break if ppdname: status = "exact" else: ppdname = 'raw' self.ppd = ppdname status = "generic" elif self.dialog_mode == "ppd": # Special CUPS names for a raw queue. ppdname = 'raw' self.ppd = ppdname status = "exact" else: (status, ppdname) = self.ppds.\ getPPDNameFromDeviceID ("Generic", "Printer", "Generic Printer", [], self.device.uri) status = "generic" except: nonfatalException () if (ppdname and (not self.remotecupsqueue or self.dialog_mode == "ppd")): return self._installPrinterOrSearchForDriver (devid, ppdname, status, page_nr, step) # No operations are pending if reached. return self.INSTALL_RESULT_DONE def _installPrinterOrSearchForDriver (self, devid, ppdname, status, page_nr, step): try: if ppdname != "download": ppddict = self.ppds.getInfoFromPPDName (ppdname) make_model = _singleton (ppddict['ppd-make-and-model']) (make, model) = \ cupshelpers.ppds.ppdMakeModelSplit (make_model) self.auto_make = make self.auto_model = model self.auto_driver = ppdname self.fillDriverList(make, model) if ((status == "exact" or status == "exact-cmd") and \ self.dialog_mode != "ppd"): self.exactdrivermatch = True if step == 0: page_nr = self.PAGE_INSTALLABLE_OPTIONS; else: self.exactdrivermatch = False if (self.dialog_mode != "ppd" and self.searchedfordriverpackages == False and devid and len(devid) > 0 and not (devid.find("MFG:generic;") >= 0 or devid.find("MFG:Generic;") >= 0 or devid.find("MFG:unknown") >= 0 or devid.find("MFG:Unknown") >= 0 or devid.find("MDL:unknown") >= 0 or devid.find("MDL:Unknown") >= 0 or devid.find("MFG:;") >= 0 or devid.find("MDL:;") >= 0)): # Query driver packages and PPD files on # OpenPrinting debugprint ("nextNPTab: No exact driver match, querying OpenPrinting") debugprint ('nextNPTab: Searching for "%s"' % devid) self.searchedfordriverpackages = True self._searchdialog_canceled = False fmt = _("Searching") self._searchdialog = Gtk.MessageDialog ( parent=self.NewPrinterWindow, modal=True, destroy_with_parent=True, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.CANCEL, text=fmt) self._searchdialog.format_secondary_text ( _("Searching for drivers")) self.opreq = OpenPrintingRequest () self._searchdialog.connect ( "response", self._searchdialog_response) self._searchdialog.show_all () self.opreq_handlers = [] self.opreq_handlers.append ( self.opreq.connect ( 'finished', self.opreq_id_search_done)) self.opreq_handlers.append ( self.opreq.connect ( 'error', self.opreq_id_search_error)) self.opreq_user_search = False self.opreq.searchPrinters (devid) # Searching for drivers in OpenPrinting takes times, so # let the caller know that it can't continue for now. return self.INSTALL_RESULT_OPS_PENDING except: nonfatalException () if (self.dialog_mode == "ppd" or (self.dialog_mode != "download_driver" and not self.remotecupsqueue and page_nr != self.PAGE_DOWNLOAD_DRIVER)): self.fillMakeList() # No operations are pending if reached. return self.INSTALL_RESULT_DONE def _searchdialog_response (self, dialog, response): # Cancel clicked while performing openprinting search self.btnNPDownloadableDriverSearch.set_sensitive (True) self.btnNPDownloadableDriverSearch_label.set_text (_("Search")) self.installed_driver_files = [] self.searchedfordriverpackages = True self.founddownloadabledrivers = False self.founddownloadableppd = False ready (self.NewPrinterWindow) # Cancel the openprinting request. GLib.idle_add (self.opreq.cancel) def opreq_id_search_done (self, opreq, printers, drivers): for handler in self.opreq_handlers: opreq.disconnect (handler) Gdk.threads_enter () try: self.opreq_user_search = False self.opreq_handlers = None self.opreq = None self._searchdialog.hide () self._searchdialog.destroy () self._searchdialog = None # Check whether we have found something if len (printers) < 1: # No. ready (self.NewPrinterWindow) self.founddownloadabledrivers = False if self.dialog_mode == "download_driver": self.on_NPCancel(None) else: self.nextNPTab () else: self.downloadable_printers = printers self.downloadable_drivers = drivers self.founddownloadabledrivers = True try: self.NewPrinterWindow.show() self.setNPButtons() if not self.fillDownloadableDrivers(): ready (self.NewPrinterWindow) self.founddownloadabledrivers = False if self.dialog_mode == "download_driver": self.on_NPCancel(None) else: self.nextNPTab () else: if self.dialog_mode == "download_driver": self.nextNPTab (step = 0) else: self.nextNPTab () except: nonfatalException () self.nextNPTab () finally: Gdk.threads_leave () def opreq_id_search_error (self, opreq, status, err): debugprint ("OpenPrinting request failed (%d): %s" % (status, repr (err))) self.opreq_id_search_done (opreq, list(), dict()) def _installSelectedDriverFromOpenPrinting(self): # Install package of the driver found on OpenPrinting treeview = self.tvNPDownloadableDrivers model, iter = treeview.get_selection ().get_selected () driver = None if iter is not None: driver = model.get_value (iter, 1) if (driver is None or driver == 0 or 'packages' not in driver): return # Find the package name, repository, and fingerprint # and install the package if not self.installdriverpackage (driver) or \ len(self.installed_driver_files) <= 0: return # We actually installed a package, delete the # PPD list to get it regenerated self.ppds = None if self.dialog_mode != "download_driver": if (not self.device.id and (not self.device.make_and_model or self.device.make_and_model == "Unknown") and self.downloadable_driver_for_printer): self.device.make_and_model = \ self.downloadable_driver_for_printer def setNPButtons(self): nr = self.ntbkNewPrinter.get_current_page() if self.dialog_mode == "device": self.btnNPBack.hide() self.btnNPForward.hide() self.btnNPApply.show() try: uri = self.getDeviceURI () valid = validDeviceURI (uri) except AttributeError: # No device selected yet. valid = False self.btnNPApply.set_sensitive (valid) return if self.dialog_mode == "ppd": if nr == self.PAGE_APPLY: if not self.installable_options: # There are no installable options, so this is the # last page. debugprint ("No installable options") self.btnNPForward.hide () self.btnNPApply.show () else: self.btnNPForward.show () self.btnNPApply.hide () return elif nr == self.PAGE_INSTALLABLE_OPTIONS: self.btnNPForward.hide() self.btnNPApply.show() return else: self.btnNPForward.show() self.btnNPApply.hide() if nr == self.PAGE_SELECT_INSTALL_METHOD: self.btnNPBack.hide() self.btnNPForward.show() downloadable_selected = False if self.rbtnNPDownloadableDriverSearch.get_active (): combobox = self.cmbNPDownloadableDriverFoundPrinters iter = combobox.get_active_iter () if iter and combobox.get_model ().get_value (iter, 1): downloadable_selected = True self.btnNPForward.set_sensitive(bool( (self.rbtnNPFoomatic.get_active() and self.tvNPMakes.get_cursor()[0] is not None) or self.filechooserPPD.get_filename() or downloadable_selected)) return else: self.btnNPBack.show() if self.dialog_mode == "download_driver": self.btnNPBack.hide() self.btnNPForward.hide() self.btnNPApply.show() accepted = self._is_driver_license_accepted() self.btnNPApply.set_sensitive(accepted) return # class/printer if nr == self.PAGE_SELECT_DEVICE: valid = False try: uri = self.getDeviceURI () valid = validDeviceURI (uri) except: nonfatalException () self.btnNPForward.set_sensitive(valid) self.btnNPBack.hide () else: self.btnNPBack.show() self.btnNPForward.show() self.btnNPApply.hide() if nr == self.PAGE_DESCRIBE_PRINTER: self.btnNPBack.show() if self.dialog_mode == "printer" or \ self.dialog_mode == "printer_with_uri": self.btnNPForward.hide() self.btnNPApply.show() self.btnNPApply.set_sensitive( checkNPName(self.printers, self.entNPName.get_text())) if self.dialog_mode == "class": # This is the first page for the New Class dialog, so # hide the Back button. self.btnNPBack.hide () if self.dialog_mode == "printer_with_uri" and \ (self.remotecupsqueue or \ (self.exactdrivermatch and \ not self.installable_options)): self.btnNPBack.hide () if nr == self.PAGE_SELECT_INSTALL_METHOD: downloadable_selected = False if self.rbtnNPDownloadableDriverSearch.get_active (): combobox = self.cmbNPDownloadableDriverFoundPrinters iter = combobox.get_active_iter () if iter and combobox.get_model ().get_value (iter, 1): downloadable_selected = True self.btnNPForward.set_sensitive(bool( self.rbtnNPFoomatic.get_active() or self.filechooserPPD.get_filename() or downloadable_selected)) # If we have an auto-detected printer for which there was no # driver found, we have already the URI and so this step is # not needed in the wizard. This makes manufacturer?PPD selection # the first step if self.dialog_mode == "printer_with_uri": self.btnNPBack.hide() if nr == self.PAGE_CHOOSE_DRIVER_FROM_DB: model, iter = self.tvNPDrivers.get_selection().get_selected() self.btnNPForward.set_sensitive(bool(iter)) if nr == self.PAGE_CHOOSE_CLASS_MEMBERS: self.btnNPForward.hide() self.btnNPApply.show() self.btnNPApply.set_sensitive( bool(getCurrentClassMembers(self.tvNCMembers))) if nr == self.PAGE_INSTALLABLE_OPTIONS: if self.dialog_mode == "printer_with_uri" and \ self.exactdrivermatch: self.btnNPBack.hide () if nr == self.PAGE_DOWNLOAD_DRIVER: accepted = self._is_driver_license_accepted() self.btnNPForward.set_sensitive(accepted) def _is_driver_license_accepted(self): current_page = self.ntbkNPDownloadableDriverProperties.get_current_page() if current_page == self.PAGE_SELECT_DEVICE: return self.rbtnNPDownloadLicenseYes.get_active () treeview = self.tvNPDownloadableDrivers model, iter = treeview.get_selection ().get_selected () if not iter: path, column = treeview.get_cursor() if path: iter = model.get_iter (path) return iter is not None def on_entNPName_changed(self, widget): # restrict text = widget.get_text() new_text = text new_text = new_text.replace("/", "") new_text = new_text.replace("#", "") new_text = new_text.replace(" ", "") if text!=new_text: widget.set_text(new_text) if self.dialog_mode == "printer": self.btnNPApply.set_sensitive( checkNPName(self.printers, new_text)) else: self.btnNPForward.set_sensitive( checkNPName(self.printers, new_text)) def fetchDevices(self, network=False, current_uri=None): debugprint ("fetchDevices") self.inc_spinner_task () # Search for Bluetooth printers together with the network printers # as the Bluetooth search takes rather long time network_schemes = ["dnssd", "snmp", "driverless", "bjnp", "bluetooth"] error_handler = self.error_getting_devices if network == False: reply_handler = (lambda x, y: self.local_devices_reply (x, y, current_uri)) cupshelpers.getDevices (self.fetchDevices_conn, exclude_schemes=network_schemes, reply_handler=reply_handler, error_handler=error_handler) else: reply_handler = (lambda x, y: self.network_devices_reply (x, y, current_uri)) cupshelpers.getDevices (self.fetchDevices_conn, include_schemes=network_schemes, reply_handler=reply_handler, error_handler=error_handler) def error_getting_devices (self, conn, exc): # Just ignore the error. debugprint ("Error fetching devices: %s" % repr (exc)) self.dec_spinner_task () self.fetchDevices_conn._end_operation () self.fetchDevices_conn.destroy () self.fetchDevices_conn = None def local_devices_reply (self, conn, result, current_uri): self.dec_spinner_task () # Now we've got the local devices, start a request for the # network devices. self.fetchDevices (network=True, current_uri=current_uri) # Add the local devices to the list. self.add_devices (result, current_uri) def network_devices_reply (self, conn, result, current_uri): self.fetchDevices_conn._end_operation () self.fetchDevices_conn.destroy () self.fetchDevices_conn = None # Add the network devices to the list. no_more = True need_resolving = {} for uri, device in result.items (): if uri.startswith ("dnssd://"): need_resolving[uri] = device no_more = False for uri in need_resolving.keys (): del result[uri] self.add_devices (result, current_uri, no_more=no_more) if len (need_resolving) > 0: resolver = dnssdresolve.DNSSDHostNamesResolver (need_resolving) self.inc_spinner_task () resolver.resolve (reply_handler=lambda devices: self.dnssd_resolve_reply (current_uri, devices)) self.dec_spinner_task () self.check_firewall () def dnssd_resolve_reply (self, current_uri, devices): self.add_devices (devices, current_uri, no_more=True) self.dec_spinner_task () self.check_firewall () def get_hpfax_device_id(self, faxuri): new_environ = os.environ.copy() new_environ['LC_ALL'] = "C" new_environ['DISPLAY'] = "" args = ["hp-info", "-x", "-i", "-d" + faxuri] debugprint (faxuri + ": " + repr(args)) try: p = subprocess.Popen (args, env=new_environ, close_fds=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = p.communicate () except: # Problem executing command. return None faxtype = -1 for line in stdout.decode ().split ("\n"): if line.find ("fax-type") == -1: continue res = re.search (r"(\d+)", line) if res: resg = res.groups() try: faxtype = int(resg[0]) except: faxtype = -1 if faxtype >= 0: break if faxtype <= 0: return None elif faxtype == 4: return 'MFG:HP;MDL:Fax 2;DES:HP Fax 2;' else: return 'MFG:HP;MDL:Fax;DES:HP Fax;' def get_hplip_scan_type_for_uri(self, uri): args = ["hp-query", "-k", "scan-type", "-d", uri] debugprint (uri + ": " + repr(args)) try: p = subprocess.Popen (args, close_fds=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = p.communicate () if p.returncode != 0: return None except: # Problem executing command. return None scan_type = stdout.decode ().strip () fields = scan_type.split ("=", 1) if len (fields) < 2: return None value = fields[1] if value == '0': return None return value def get_hplip_uri_for_network_printer(self, host, mode): if mode == "print": mod = "-c" elif mode == "fax": mod = "-f" else: mod = "-c" args = ["hp-makeuri", mod, host] debugprint (host + ": " + repr(args)) uri = None try: p = subprocess.Popen (args, close_fds=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = p.communicate () if p.returncode != 0: return None except: # Problem executing command. return None uri = stdout.decode ().strip () return uri def getNetworkPrinterMakeModel(self, host=None, device=None): """ Try to determine the make and model for the currently selected network printer, and store this in the data structure for the printer. Returns (hostname or None, uri or None). """ uri = None if device is None: device = self.device # Determine host name/IP if host is None: s = device.uri.find ("://") if s != -1: s += 3 e = device.uri[s:].find (":") if e == -1: e = device.uri[s:].find ("/") if e == -1: e = device.uri[s:].find ("?") if e == -1: e = len (device.uri) host = device.uri[s:s+e] # Try to get make and model via SNMP if host: args = ["/usr/lib/cups/backend/snmp", host] debugprint (host + ": " + repr(args)) stdout = None try: p = subprocess.Popen (args, close_fds=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = p.communicate () if p.returncode != 0: stdout = None except: # Problem executing command. pass if stdout is not None: try: line = stdout.decode ('utf-8').strip () except UnicodeDecodeError: # Work-around snmp backend output encoded as iso-8859-1 (despire RFC 2571). # If it's neither iso-8859-1, make a best guess by ignoring problematic bytes. line = stdout.decode (encoding='iso-8859-1', errors='ignore').strip () words = probe_printer.wordsep (line) n = len (words) if n < 4: words.extend (['','','','']) words = words[:4] n = 4 elif n > 6: words = words[:6] n = 6 if n == 6: (device_class, uri, make_and_model, info, device_id, device_location) = words elif n == 5: (device_class, uri, make_and_model, info, device_id) = words elif n == 4: (device_class, uri, make_and_model, info) = words if n == 4: # No Device ID given so we'll have to make one # up. debugprint ("No Device ID from snmp backend") (mk, md) = cupshelpers.ppds.\ ppdMakeModelSplit (make_and_model) device.id = "MFG:%s;MDL:%s;DES:%s %s;" % (mk, md, mk, md) else: debugprint ("Got Device ID: %s" % device_id) device.id = device_id device.id_dict = cupshelpers.parseDeviceID (device.id) device.make_and_model = make_and_model device.info = info if n == 6: device.location = device_location return (host, uri) def fillDeviceTab(self, current_uri=None): self.device_selected = -1 model = Gtk.TreeStore (str, # device-info GObject.TYPE_PYOBJECT, # PhysicalDevice obj bool) # Separator? other = cupshelpers.Device('', **{'device-info' :_("Enter URI")}) physother = PhysicalDevice (other) self.devices = [physother] uri_iter = model.append (None, row=[physother.get_info (), physother, False]) network_iter = model.append (None, row=[_("Network Printer"), None, False]) network_dict = { 'device-class': 'network', 'device-info': _("Find Network Printer") } network = cupshelpers.Device ('network', **network_dict) find_nw_iter = model.append (network_iter, row=[network_dict['device-info'], PhysicalDevice (network), False]) model.insert_after (network_iter, find_nw_iter, row=['', None, True]) smbdev_dict = { 'device-class': 'network', 'device-info': _("Windows Printer via SAMBA") } smbdev = cupshelpers.Device ('smb', **smbdev_dict) find_smb_iter = model.append (network_iter, row=[smbdev_dict['device-info'], PhysicalDevice (smbdev), False]) model.insert_after (find_nw_iter, find_smb_iter, row=['', None, True]) self.devices_uri_iter = uri_iter self.devices_find_nw_iter = find_nw_iter self.devices_network_iter = network_iter self.devices_network_fetched = False self.tvNPDevices.set_model (model) self.entNPTDevice.set_text ('') self.expNPDeviceURIs.hide () column = self.tvNPDevices.get_column (0) self.tvNPDevices.set_cursor (Gtk.TreePath(), column, False) self.current_uri = current_uri self.firewall = None debugprint ("Fetching devices") self.start_fetching_devices () def on_firewall_read (self, data): f = self.firewall allowed = True try: ipp_allowed = f.check_ipp_client_allowed () mdns_allowed = f.check_mdns_allowed () allowed = (ipp_allowed and mdns_allowed) secondary_text = TEXT_adjust_firewall + "\n\n" if not ipp_allowed: secondary_text += ("- " + _("Allow all incoming IPP Browse packets") + "\n") f.add_service (firewallsettings.IPP_CLIENT_SERVICE) if not mdns_allowed: secondary_text += ("- " + _("Allow all incoming mDNS traffic") + "\n") f.add_service (firewallsettings.MDNS_SERVICE) if not allowed: debugprint ("Asking for permission to adjust firewall:\n%s" % secondary_text) dialog = Gtk.MessageDialog (parent=self.NewPrinterWindow, modal=True, destroy_with_parent=True, message_type=Gtk.MessageType.QUESTION, buttons=Gtk.ButtonsType.NONE, text= _("Adjust Firewall")) dialog.format_secondary_markup (secondary_text) dialog.add_buttons (_("Do It Later"), Gtk.ResponseType.NO, _("Adjust Firewall"), Gtk.ResponseType.YES) dialog.connect ('response', self.adjust_firewall_response) dialog.show () except (dbus.DBusException, Exception): nonfatalException () if allowed: debugprint ("Firewall all OK, no changes needed") def adjust_firewall_response (self, dialog, response): dialog.destroy () if response == Gtk.ResponseType.YES: ipp_server_allowed = self.firewall.check_ipp_server_allowed () if not ipp_server_allowed: self.firewall.add_service (firewallsettings.IPP_SERVER_SERVICE) self.firewall.write () debugprint ("Fetching network devices after firewall dialog response") self.fetchDevices_conn = asyncconn.Connection () self.fetchDevices_conn._begin_operation (_("fetching device list")) self.fetchDevices (network=True) def start_fetching_devices (self): self.fetchDevices_conn = asyncconn.Connection () self.fetchDevices_conn._begin_operation (_("fetching device list")) self.fetchDevices (network=False, current_uri=self.current_uri) del self.current_uri def add_devices (self, devices, current_uri, no_more=False): if current_uri: if current_uri in devices: current = devices.pop(current_uri) elif current_uri.replace (":9100", "") in devices: current_uri = current_uri.replace (":9100", "") current = devices.pop(current_uri) elif no_more: current = cupshelpers.Device (current_uri) current.info = "Current device" else: current_uri = None devices = list(devices.values()) for device in devices: if device.type == "socket": # Remove default port to more easily find duplicate URIs device.uri = device.uri.replace (":9100", "") # Map generic URIs to something canonical def replace_generic (device): if device.uri == "hp:/no_device_found": device.uri = "hp" elif device.uri == "hpfax:/no_device_found": device.uri = "hpfax" return device devices = list(map (replace_generic, devices)) # Mark duplicate URIs for deletion for i in range (len (devices) - 1): for j in range (i + 1, len (devices)): device1 = devices[i] device2 = devices[j] if device1.uri == "delete" or device2.uri == "delete": continue if device1.uri == device2.uri: # Keep the one with the longer (better) device ID if (not device1.id): device1.uri = "delete" elif (not device2.id): device2.uri = "delete" elif (len (device1.id) < len (device2.id)): device1.uri = "delete" else: device2.uri = "delete" devices = [x for x in devices if x.uri not in ("hp", "hpfax", "hal", "beh", "smb", "scsi", "http", "bjnp", "delete")] newdevices = [] for device in devices: debugprint("Adding device with URI %s" % device.uri) if (hasattr (device, 'address')): debugprint(" Device address %s" % device.address) if (hasattr (device, 'hostname')): debugprint(" Device host name %s" % device.hostname) physicaldevice = PhysicalDevice (device) debugprint (" Created physical device %s" % repr(physicaldevice)) try: i = self.devices.index (physicaldevice) debugprint (" Physical device %d is the same printer" % i) self.devices[i].add_device (device) debugprint (" New physical device %s is same as physical device %d: %s" % (repr(physicaldevice), i, repr(self.devices[i]))) debugprint (" Joining devices") except ValueError: self.devices.append (physicaldevice) newdevices.append (physicaldevice) debugprint (" Physical device %s is a completely new device" % repr(physicaldevice)) self.devices.sort() if current_uri: current_device = PhysicalDevice (current) try: i = self.devices.index (current_device) self.devices[i].add_device (current) current_device = self.devices[i] except ValueError: self.devices.append (current_device) newdevices.append (current_device) else: current_device = None model = self.tvNPDevices.get_model () network_iter = self.devices_network_iter find_nw_iter = self.devices_find_nw_iter for newdevice in newdevices: device = None try: i = self.devices.index (newdevice) device = self.devices[i] except ValueError: debugprint("ERROR: Cannot identify new physical device with its entry in the device list (%s)" % repr(newdevice)) continue devs = device.get_devices () network = devs[0].device_class == 'network' info = device.get_info () if device == current_device: info += _(" (Current)") row=[info, device, False] if network: if devs[0].uri != devs[0].type: # An actual network printer device. Put this at the top. iter = model.insert_before (network_iter, find_nw_iter, row=row) # If this is the currently selected device we need # to expand the "Network Printer" row so that it # is visible. if device == current_device: network_path = model.get_path (network_iter) self.tvNPDevices.expand_row (network_path, False) else: # Just a method of finding one. iter = model.append (network_iter, row=row) else: # Insert this local device in order. network_path = model.get_path (network_iter) iter = model.get_iter_first () while model.get_path (iter) != network_path: physdev = model.get_value (iter, 1) if physdev > device: break iter = model.iter_next (iter) iter = model.insert_before (None, iter, row=row) if device == current_device: device_select_path = model.get_path (iter) self.tvNPDevices.scroll_to_cell (device_select_path, row_align=0.5) column = self.tvNPDevices.get_column (0) self.tvNPDevices.set_cursor (device_select_path, column, False) connection_select_path = 0 if current_uri: model = self.tvNPDeviceURIs.get_model () iter = model.get_iter_first () i = 0 while iter: dev = model.get_value (iter, 1) if current_uri == dev.uri: connection_select_path = i break iter = model.iter_next (iter) i += 1 elif not self.device_selected: # Select the device. column = self.tvNPDevices.get_column (0) self.tvNPDevices.set_cursor (Gtk.TreePath(), column, False) # Select the connection. column = self.tvNPDeviceURIs.get_column (0) self.tvNPDeviceURIs.set_cursor (connection_select_path, column, False) ## SMB browsing def install_python3_smbc_if_needed (self): global PYSMB_AVAILABLE global pysmb # Make the import of pysmb globally available # Does the SMB client library need to be installed? if PYSMB_AVAILABLE: return True debugprint ("No SMB client library present so attempting install") try: pk = installpackage.PackageKit () # The following call means a blocking, synchronous, D-Bus call pk.InstallPackageName (0, 0, "python3-smbc") except DBusException as e: debugprint ("Error during installation/setup of SMB client.") debugprint("{}".format(e)) return False try: import pysmb PYSMB_AVAILABLE=True except ModuleNotFoundError as e: debugprint ("SMB client setup failed.") debugprint("{}".format(e)) return False debugprint ("SMB client successfully installed and set up.") return True def browse_smb_hosts(self): """Initialise the SMB tree store.""" store = self.smb_store store.clear () busy(self.SMBBrowseDialog) class X: pass dummy = X() dummy.smbc_type = pysmb.smbc.PRINTER_SHARE dummy.name = _('Scanning...') dummy.comment = '' store.append(None, [dummy]) while Gtk.events_pending (): Gtk.main_iteration () debug = 0 if get_debugging (): debug = 10 smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog) ctx = pysmb.smbc.Context (debug=debug, auth_fn=smbc_auth.callback) entries = None try: while smbc_auth.perform_authentication () > 0: try: entries = ctx.opendir ("smb://").getdents () except Exception as e: smbc_auth.failed (e) except RuntimeError as e: (e, s) = e.args if e != errno.ENOENT: debugprint ("Runtime error: %s" % repr ((e, s))) except: nonfatalException () store.clear () if entries: for entry in entries: if entry.smbc_type in [pysmb.smbc.WORKGROUP, pysmb.smbc.SERVER]: iter = store.append (None, [entry]) i = store.append (iter) specified_uri = SMBURI (uri=self.entSMBURI.get_text ()) (group, host, share, user, password) = specified_uri.separate () if len (host) > 0: # The user has specified a server before clicking Browse. # Append the server as a top-level entry. class FakeEntry: pass toplevel = FakeEntry () toplevel.smbc_type = pysmb.smbc.SERVER toplevel.name = host toplevel.comment = '' iter = store.append (None, [toplevel]) i = store.append (iter) # Now expand it. path = store.get_path (iter) self.tvSMBBrowser.expand_row (path, 0) ready(self.SMBBrowseDialog) if store.get_iter_first () is None: self.SMBBrowseDialog.hide () show_info_dialog (_("No Print Shares"), _("There were no print shares found. " "Please check that the Samba service is " "marked as trusted in your firewall " "configuration."), parent=self.NewPrinterWindow) def smb_select_function (self, selection, model, path, path_selected, data): """Don't allow this path to be selected unless it is a leaf.""" iter = self.smb_store.get_iter (path) return not self.smb_store.iter_has_child (iter) def smbbrowser_cell_share (self, column, cell, model, iter, data): entry = model.get_value (iter, 0) share = '' if entry is not None: share = entry.name cell.set_property ('text', share) def smbbrowser_cell_comment (self, column, cell, model, iter, data): entry = model.get_value (iter, 0) comment = '' if entry is not None: comment = entry.comment cell.set_property ('text', comment) def on_tvSMBBrowser_row_activated (self, view, path, column): """Handle double-clicks in the SMB tree view.""" store = self.smb_store iter = store.get_iter (path) entry = store.get_value (iter, 0) if entry and entry.smbc_type == pysmb.smbc.PRINTER_SHARE: # This is a share, not a host. self.btnSMBBrowseOk.clicked () return if view.row_expanded (path): view.collapse_row (path) else: self.on_tvSMBBrowser_row_expanded (view, iter, path) def on_tvSMBBrowser_row_expanded (self, view, iter, path): """Handler for expanding a row in the SMB tree view.""" model = view.get_model () entry = model.get_value (iter, 0) if entry is None: return if entry.smbc_type == pysmb.smbc.WORKGROUP: # Workgroup # Be careful though: if there is a server with the # same name as the workgroup we will get a list of its # shares, not the workgroup's servers. try: if self.expanding_row: return except: self.expanding_row = 1 busy (self.SMBBrowseDialog) uri = "smb://%s/" % entry.name debug = 0 if get_debugging (): debug = 10 smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog) ctx = pysmb.smbc.Context (debug=debug, auth_fn=smbc_auth.callback) entries = [] try: while smbc_auth.perform_authentication () > 0: try: entries = ctx.opendir (uri).getdents () except Exception as e: smbc_auth.failed (e) except RuntimeError as e: (e, s) = e.args if e != errno.ENOENT: debugprint ("Runtime error: %s" % repr ((e, s))) except: nonfatalException() while model.iter_has_child (iter): i = model.iter_nth_child (iter, 0) model.remove (i) for entry in entries: if entry.smbc_type in [pysmb.smbc.SERVER, pysmb.smbc.PRINTER_SHARE]: i = model.append (iter, [entry]) if entry.smbc_type == pysmb.smbc.SERVER: n = model.append (i) view.expand_row (path, 0) del self.expanding_row ready (self.SMBBrowseDialog) elif entry.smbc_type == pysmb.smbc.SERVER: # Server try: if self.expanding_row: return except: self.expanding_row = 1 busy (self.SMBBrowseDialog) uri = "smb://%s/" % entry.name debug = 0 if get_debugging (): debug = 10 smbc_auth = pysmb.AuthContext (self.SMBBrowseDialog) ctx = pysmb.smbc.Context (debug=debug, auth_fn=smbc_auth.callback) shares = [] try: while smbc_auth.perform_authentication () > 0: try: shares = ctx.opendir (uri).getdents () except Exception as e: smbc_auth.failed (e) except RuntimeError as e: (e, s) = e.args if e != errno.EACCES and e != errno.EPERM: debugprint ("Runtime error: %s" % repr ((e, s))) except: nonfatalException() while model.iter_has_child (iter): i = model.iter_nth_child (iter, 0) model.remove (i) for share in shares: if share.smbc_type == pysmb.smbc.PRINTER_SHARE: i = model.append (iter, [share]) debugprint (repr (share)) view.expand_row (path, 0) del self.expanding_row ready (self.SMBBrowseDialog) def set_btnSMBVerify_sensitivity (self, on): self.btnSMBVerify.set_sensitive (on) if not PYSMB_AVAILABLE or not on: self.btnSMBVerify.set_tooltip_text (_("Verification requires the " "%s module") % "pysmbc") def on_entSMBURI_changed (self, ent): allowed_chars = string.ascii_letters+string.digits+'_-./:%[]@' self.entry_changed(ent, allowed_chars) uri = ent.get_text () (group, host, share, user, password) = SMBURI (uri=uri).separate () if user: self.entSMBUsername.set_text (user) if password: self.entSMBPassword.set_text (password) if user or password: uri = SMBURI (group=group, host=host, share=share).get_uri () ent.set_text(uri) self.rbtnSMBAuthSet.set_active(True) elif self.entSMBUsername.get_text () == '': self.rbtnSMBAuthPrompt.set_active(True) self.set_btnSMBVerify_sensitivity (bool(uri)) self.setNPButtons () def on_tvSMBBrowser_cursor_changed(self, widget): selection = self.tvSMBBrowser.get_selection() if selection is None: return store, iter = selection.get_selected() is_share = False if iter: entry = store.get_value (iter, 0) if entry: is_share = entry.smbc_type == pysmb.smbc.PRINTER_SHARE self.btnSMBBrowseOk.set_sensitive(iter is not None and is_share) def on_btnSMBBrowse_clicked(self, button): """Check whether the needed SMB client library is available and""" """install it if needed""" if not self.install_python3_smbc_if_needed(): return self.btnSMBBrowseOk.set_sensitive(False) try: # Note: we do the browsing from *this* machine, regardless # of which CUPS server we are connected to. f = firewallsettings.FirewallD () if not f.running: f = firewallsettings.SystemConfigFirewall () allowed = f.check_samba_client_allowed () secondary_text = TEXT_adjust_firewall + "\n\n" if not allowed: dialog = Gtk.MessageDialog (parent=self.NewPrinterWindow, modal=True, destroy_with_parent=True, message_type=Gtk.MessageType.QUESTION, buttons=Gtk.ButtonsType.NONE, text=_("Adjust Firewall")) secondary_text += ("- " + _("Allow all incoming SMB/CIFS " "browse packets")) dialog.format_secondary_markup (secondary_text) dialog.add_buttons (_("Do It Later"), Gtk.ResponseType.NO, _("Adjust Firewall"), Gtk.ResponseType.YES) response = dialog.run () dialog.destroy () if response == Gtk.ResponseType.YES: f.add_service (firewallsettings.SAMBA_CLIENT_SERVICE) f.write () except (dbus.DBusException, Exception): nonfatalException () self.SMBBrowseDialog.show() self.browse_smb_hosts() def on_btnSMBBrowseOk_clicked(self, button): store, iter = self.tvSMBBrowser.get_selection().get_selected() is_share = False if iter: entry = store.get_value (iter, 0) if entry: is_share = entry.smbc_type == pysmb.smbc.PRINTER_SHARE if not iter or not is_share: self.SMBBrowseDialog.hide() return parent_iter = store.iter_parent (iter) domain_iter = store.iter_parent (parent_iter) share = store.get_value (iter, 0) host = store.get_value (parent_iter, 0) if domain_iter: group = store.get_value (domain_iter, 0).name else: group = '' uri = SMBURI (group=group, host=host.name, share=share.name).get_uri () self.entSMBUsername.set_text ('') self.entSMBPassword.set_text ('') self.entSMBURI.set_text (uri) self.SMBBrowseDialog.hide() def on_btnSMBBrowseCancel_clicked(self, widget, *args): self.SMBBrowseDialog.hide() def on_btnSMBBrowseRefresh_clicked(self, button): self.browse_smb_hosts() def on_rbtnSMBAuthSet_toggled(self, widget): self.tblSMBAuth.set_sensitive(widget.get_active()) def on_btnSMBVerify_clicked(self, button): """Check whether the needed SMB client library is available and""" """install it if needed""" if not self.install_python3_smbc_if_needed(): return uri = self.entSMBURI.get_text () (group, host, share, u, p) = SMBURI (uri=uri).separate () user = '' passwd = '' reason = None auth_set = self.rbtnSMBAuthSet.get_active() if auth_set: user = self.entSMBUsername.get_text () passwd = self.entSMBPassword.get_text () accessible = False canceled = False busy (self.NewPrinterWindow) try: debug = 0 if get_debugging (): debug = 10 if auth_set: # No prompting. def do_auth (svr, shr, wg, un, pw): return (group, user, passwd) ctx = pysmb.smbc.Context (debug=debug, auth_fn=do_auth) try: ctx.optionUseKerberos = True except AttributeError: # requires pysmbc >= 1.0.12 pass f = ctx.open ("smb://%s/%s" % (host, share), os.O_RDWR, 0o777) accessible = True else: # May need to prompt. smbc_auth = pysmb.AuthContext (self.NewPrinterWindow, workgroup=group, user=user, passwd=passwd) ctx = pysmb.smbc.Context (debug=debug, auth_fn=smbc_auth.callback) while smbc_auth.perform_authentication () > 0: try: f = ctx.open ("smb://%s/%s" % (host, share), os.O_RDWR, 0o777) accessible = True except Exception as e: smbc_auth.failed (e) if not accessible: canceled = True except RuntimeError as e: (e, s) = e.args debugprint ("Error accessing share: %s" % repr ((e, s))) reason = s except: nonfatalException() ready (self.NewPrinterWindow) if accessible: show_info_dialog (_("Print Share Verified"), _("This print share is accessible."), parent=self.NewPrinterWindow) return if not canceled: text = _("This print share is not accessible.") if reason: text = reason show_error_dialog (_("Print Share Inaccessible"), text, parent=self.NewPrinterWindow) def entry_changed(self, entry, allowed_chars): "Remove all chars from entry's text that are not in allowed_chars." origtext = entry.get_text() new_text = origtext for char in origtext: if char not in allowed_chars: new_text = new_text.replace(char, "") debugprint ("removed disallowed character %s" % repr (char)) if origtext!=new_text: entry.set_text(new_text) def on_entNPTDevice_changed(self, ent): allowed_chars = string.ascii_letters+string.digits+'_-./:%[]()@?=&+' self.entry_changed(ent, allowed_chars) self.setNPButtons() def on_entNPTJetDirectHostname_changed(self, ent): allowed_chars = string.ascii_letters+string.digits+'_-.:%[]' self.entry_changed(ent, allowed_chars) self.setNPButtons() def on_entNPTJetDirectPort_changed(self, ent): self.entry_changed(ent, string.digits) self.setNPButtons() def on_expNPDeviceURIs_expanded (self, widget, UNUSED): # When the expanded is not expanded we want its packing to be # 'expand = false' so that it aligns at the bottom (it packs # to the end of its vbox). But when it is expanded we'd like # it to expand with the window. # # Adjust its 'expand' packing state depending on whether the # widget is expanded. parent = widget.get_parent () (expand, fill, padding, pack_type) = parent.query_child_packing (widget) expand = widget.get_expanded () parent.set_child_packing (widget, expand, fill, padding, pack_type) def device_row_separator_fn (self, model, iter, data): return model.get_value (iter, 2) def device_row_activated (self, view, path, column): if view.row_expanded (path): view.collapse_row (path) else: view.expand_row (path, False) def check_firewall (self): view = self.tvNPDevices model = view.get_model () if not model: return network_path = model.get_path (self.devices_network_iter) if not view.row_expanded (network_path): # 'Network' not expanded return if self.firewall is not None: # Already checked return if self.spinner_count > 0: # Still discovering devices? debugprint ("Skipping firewall adjustment: " "discovery in progress") return # Any network printers found? for physdev in self.devices: for device in physdev.get_devices (): if (device.device_class == 'network' and device.uri != device.type): debugprint ("Skipping firewall adjustment: " "network printers found") return # If not, ask about the firewall try: if (self._host == 'localhost' or self._host[0] == '/'): self.firewall = firewallsettings.FirewallD () if not self.firewall.running: self.firewall = firewallsettings.SystemConfigFirewall () debugprint ("Examining firewall") self.firewall.read (reply_handler=self.on_firewall_read) except (dbus.DBusException, Exception): nonfatalException () def device_row_expanded (self, view, iter, path): model = view.get_model () if not model or not iter: return network_path = model.get_path (self.devices_network_iter) if path == network_path: self.check_firewall () def device_select_function (self, selection, model, path, *UNUSED): """ Allow this path to be selected as long as there is a device associated with it. Otherwise, expand or collapse it. """ model = self.tvNPDevices.get_model () iter = model.get_iter (path) if model.get_value (iter, 1) is not None: return True self.device_row_activated (self.tvNPDevices, path, None) return False def on_tvNPDevices_cursor_changed(self, widget): # Reset previous driver search result self.installed_driver_files = [] self.searchedfordriverpackages = False self.founddownloadabledrivers = False self.founddownloadableppd = False self.downloadable_printers = [] self.device_selected += 1 path, column = widget.get_cursor () if path is None: return model = widget.get_model () iter = model.get_iter (path) physicaldevice = model.get_value (iter, 1) if physicaldevice is None: return show_uris = True for device in physicaldevice.get_devices (): if device.type == "parallel": device.menuentry = _("Parallel Port") elif device.type == "serial": device.menuentry = _("Serial Port") elif device.type == "usb": if (hasattr(device, "uri") and device.uri.lower().find("fax") > -1): device.menuentry = _("Fax") + " - " + _("USB") else: device.menuentry = _("USB") elif device.type == "bluetooth": device.menuentry = _("Bluetooth") elif device.type == "hp": device.menuentry = _("HP Linux Imaging and Printing (HPLIP)") elif device.type == "hpfax": device.menuentry = _("Fax") + " - " + \ _("HP Linux Imaging and Printing (HPLIP)") elif device.type == "hal": device.menuentry = _("Hardware Abstraction Layer (HAL)") elif device.type == "socket": device.menuentry = _("AppSocket/HP JetDirect") elif device.type == "lpd": (scheme, rest) = urllib.parse.splittype (device.uri) (hostport, rest) = urllib.parse.splithost (rest) (queue, rest) = urllib.parse.splitquery (rest) if queue != '': if queue[0] == '/': queue = queue[1:] device.menuentry = (_("LPD/LPR queue '%s'") % queue) else: device.menuentry = _("LPD/LPR queue") elif device.type == "smb": device.menuentry = _("Windows Printer via SAMBA") elif device.type == "ipp": (scheme, rest) = urllib.parse.splittype (device.uri) (hostport, rest) = urllib.parse.splithost (rest) (queue, rest) = urllib.parse.splitquery (rest) if queue != '': if queue[0] == '/': queue = queue[1:] if queue.startswith("printers/"): queue = queue[9:] if 'driverless' in device.info: drvless = "Driverless " device.driverless = True else: drvless = "" if queue != '': device.menuentry = (("%s" + _("IPP") + " (%s)") % (drvless, queue)) else: device.menuentry = (("%s" + _("IPP")) % drvless) elif device.type == "http" or device.type == "https": device.menuentry = _("HTTP") elif device.type == "dnssd" or device.type == "mdns": (scheme, rest) = urllib.parse.splittype (device.uri) (name, rest) = urllib.parse.splithost (rest) (cupsqueue, rest) = urllib.parse.splitquery (rest) if cupsqueue != '' and cupsqueue[0] == '/': cupsqueue = cupsqueue[1:] if cupsqueue == 'cups': device.menuentry = _("Remote CUPS printer via DNS-SD") if device.info != '': device.menuentry += " (%s)" % device.info else: protocol = None if name.find("._ipp") != -1: protocol = "IPP" elif name.find("._printer") != -1: protocol = "LPD" elif name.find("._pdl-datastream") != -1: protocol = "AppSocket/JetDirect" if protocol is not None: device.menuentry = (_("%s network printer via DNS-SD") % protocol) else: device.menuentry = \ _("Network printer via DNS-SD") else: show_uris = False device.menuentry = device.uri model = Gtk.ListStore (str, # URI description GObject.TYPE_PYOBJECT) # cupshelpers.Device self.tvNPDeviceURIs.set_model (model) # If this is a network device, check whether HPLIP can drive it. if getattr (physicaldevice, 'checked_hplip', None) != True: hp_drivable = False hp_scannable = False is_network = False remotecups = False host = None device_dict = { 'device-class': 'network' } if physicaldevice._network_host: host = physicaldevice._network_host for device in physicaldevice.get_devices (): if device.type == "hp": # We already know that HPLIP can drive this device. hp_drivable = True # But can we scan using it? if self.get_hplip_scan_type_for_uri (device.uri): hp_scannable = True break elif device.type in ["socket", "lpd", "ipp", "dnssd", "mdns"]: # This is a network printer. if host is None and device.type in ["socket", "lpd", "ipp"]: (scheme, rest) = urllib.parse.splittype (device.uri) (hostport, rest) = urllib.parse.splithost (rest) if hostport is not None: (host, port) = urllib.parse.splitport (hostport) if host: is_network = True remotecups = ((device.uri.startswith('dnssd:') or \ device.uri.startswith('mdns:')) and \ device.uri.endswith('/cups')) if (not device.make_and_model or \ device.make_and_model == "Unknown") and not \ remotecups: self.getNetworkPrinterMakeModel(host=host, device=device) device_dict['device-info'] = device.info device_dict['device-make-and-model'] = (device. make_and_model) device_dict['device-id'] = device.id device_dict['device-location'] = device.location if not hp_drivable and is_network and not remotecups and \ (not device.make_and_model or \ device.make_and_model == "Unknown" or \ device.make_and_model.lower ().startswith ("hp") or \ device.make_and_model.lower ().startswith ("hewlett")): if (hasattr (physicaldevice, "dnssd_hostname") and \ physicaldevice.dnssd_hostname): hpliphost = physicaldevice.dnssd_hostname else: hpliphost = host hplipuri = self.get_hplip_uri_for_network_printer (hpliphost, "print") if hplipuri: dev = cupshelpers.Device (hplipuri, **device_dict) dev.menuentry = "HP Linux Imaging and Printing (HPLIP)" physicaldevice.add_device (dev) # Can we scan using this device? if self.get_hplip_scan_type_for_uri (device.uri): hp_scannable = True # Now check to see if we can also send faxes using # this device. faxuri = self.get_hplip_uri_for_network_printer (hpliphost, "fax") if faxuri: faxdevid = self.get_hpfax_device_id (faxuri) device_dict['device-id'] = faxdevid device_dict['device-info'] = _("Fax") faxdev = cupshelpers.Device (faxuri, **device_dict) faxdev.menuentry = _("Fax") + " - " + \ "HP Linux Imaging and Printing (HPLIP)" physicaldevice.add_device (faxdev) if hp_scannable: physicaldevice.hp_scannable = True physicaldevice.checked_hplip = True device.hp_scannable = getattr (physicaldevice, 'hp_scannable', None) # Fill the list of connections for this device. n = 0 for device in physicaldevice.get_devices (): model.append ((device.menuentry, device)) n += 1 column = self.tvNPDeviceURIs.get_column (0) self.tvNPDeviceURIs.set_cursor (Gtk.TreePath(), column, False) if show_uris: self.expNPDeviceURIs.show_all () else: self.expNPDeviceURIs.hide () def on_tvNPDeviceURIs_cursor_changed(self, widget): path, column = widget.get_cursor () if path is None: return model = widget.get_model () iter = model.get_iter (path) device = model.get_value(iter, 1) self.device = device self.lblNPDeviceDescription.set_text ('') page = self.new_printer_device_tabs.get (device.type, self.PAGE_SELECT_DEVICE) self.ntbkNPType.set_current_page(page) debugprint("Selected connection type. URI: %s" % device.uri) location = '' type = device.type url = device.uri.split(":", 1)[-1] if page == self.PAGE_DESCRIBE_PRINTER: # This is the "no options" page, with just a label to describe # the selected device. if device.type == "parallel": text = _("A printer connected to the parallel port.") elif device.type == "usb": if (hasattr(device, "uri") and device.uri.lower().find("fax") > -1): device.menuentry = _("Fax") + " - " + _("USB") text = _("A fax machine or the fax function " "of a multi-function device connected " "to a USB port.") else: text = _("A printer connected to a USB port.") elif device.type == "bluetooth": text = _("A printer connected via Bluetooth.") elif device.type == "hp": text = _("HPLIP software driving a printer, " "or the printer function of a multi-function device.") elif device.type == "hpfax": text = _("HPLIP software driving a fax machine, " "or the fax function of a multi-function device.") elif device.type == "hal": text = _("Local printer detected by the " "Hardware Abstraction Layer (HAL).") elif device.type == "dnssd" or device.type == "mdns": (scheme, rest) = urllib.parse.splittype (device.uri) (name, rest) = urllib.parse.splithost (rest) (cupsqueue, rest) = urllib.parse.splitquery (rest) if cupsqueue != '' and cupsqueue[0] == '/': cupsqueue = cupsqueue[1:] if cupsqueue == 'cups': text = _("Remote CUPS printer via DNS-SD") else: protocol = None if name.find("._ipp") != -1: protocol = "IPP" elif name.find("._printer") != -1: protocol = "LPD" elif name.find("._pdl-datastream") != -1: protocol = "AppSocket/JetDirect" if protocol is not None: text = _("%s network printer via DNS-SD") % protocol else: text = _("Network printer via DNS-SD") else: text = device.uri self.lblNPDeviceDescription.set_text (text) elif device.type=="socket": (scheme, rest) = urllib.parse.splittype (device.uri) host = '' port = 9100 if scheme == "socket": (hostport, rest) = urllib.parse.splithost (rest) (host, port) = urllib.parse.splitnport (hostport, defport=port) debugprint ("socket: host is %s, port is %s" % (host, repr (port))) if device.location != '': location = device.location else: location = host self.entNPTJetDirectHostname.set_text (host) self.entNPTJetDirectPort.set_text (str (port)) elif device.type=="serial": if not device.is_class: parts = device.uri.split("?", 1) if len (parts) > 1: options = parts[1] else: options = "" options = options.split("+") option_dict = {} for option in options: name, value = option.split("=") option_dict[name] = value for widget, name in ( (self.cmbNPTSerialBaud, "baud"), (self.cmbNPTSerialBits, "bits"), (self.cmbNPTSerialParity, "parity"), (self.cmbNPTSerialFlow, "flow")): if name in option_dict: # option given in URI? model = widget.get_model() iter = model.get_iter_first() nr = 0 while iter: value = model.get(iter,1)[0] if str (value) == str (option_dict[name]): break iter = model.iter_next(iter) nr += 1 if iter: widget.set_active(nr) else: widget.set_active (0) else: widget.set_active(0) # XXX FILL TABS FOR VALID DEVICE URIs elif device.type=="lpd": self.entNPTLpdHost.set_text ('') self.entNPTLpdQueue.set_text ('') self.entNPTLpdQueue.set_completion (None) self.btnNPTLpdProbe.set_sensitive (False) if len (device.uri) > 6: host = device.uri[6:] i = host.find ("/") if i != -1: printer = host[i + 1:] host = host[:i] else: printer = "" self.entNPTLpdHost.set_text (host) self.entNPTLpdQueue.set_text (printer) location = host self.btnNPTLpdProbe.set_sensitive (True) elif device.uri == "smb": self.entSMBURI.set_text('') self.btnSMBVerify.set_sensitive(False) elif device.type == "smb": self.entSMBUsername.set_text ('') self.entSMBPassword.set_text ('') self.entSMBURI.set_text(device.uri[6:]) self.set_btnSMBVerify_sensitivity (True) else: if device.uri: self.entNPTDevice.set_text(device.uri) try: if len (location) == 0 and self.device.device_class == "direct": # Set location to the name of this host. if (self._host == 'localhost' or self._host[0] == '/'): u = os.uname () location = u[1] else: location = self._host # Pre-fill location field. self.entNPLocation.set_text (location) except: nonfatalException () self.setNPButtons() def on_entNPTLpdHost_changed(self, ent): hostname = ent.get_text() self.btnNPTLpdProbe.set_sensitive (len (hostname) > 0) self.setNPButtons() def on_entNPTLpdQueue_changed(self, ent): self.setNPButtons() def on_btnNPTLpdProbe_clicked(self, button): # read hostname, probe, fill printer names hostname = self.entNPTLpdHost.get_text() server = probe_printer.LpdServer(hostname) self.lblWait.set_markup ('<span weight="bold" size="larger">' + _('Searching') + '</span>\n\n' + _('Searching for printers')) self.WaitWindow.set_transient_for (self.NewPrinterWindow) self.WaitWindow.show_now () busy (self.WaitWindow) def stop (widget, event): server.destroy () return True self.WaitWindow.disconnect (self.WaitWindow_handler) signal = self.WaitWindow.connect ("delete-event", stop) printers = server.probe() self.WaitWindow.disconnect (signal) self.WaitWindow_handler = self.WaitWindow.connect ("delete-event", on_delete_just_hide) self.WaitWindow.hide () model = Gtk.ListStore (str) for printer in printers: model.append ([printer]) completion = Gtk.EntryCompletion () completion.set_model (model) completion.set_text_column (0) completion.set_minimum_key_length (0) self.entNPTLpdQueue.set_completion (completion) ### Find Network Printer def on_entNPTNetworkHostname_changed(self, ent): text = ent.get_text () if text.find (":") != -1: # The user is typing in a URI. In that case, switch to URI entry. ent.set_text ('') debugprint ("URI detected (%s) -> Enter URI" % text) self.entNPTDevice.set_text (text) model = self.tvNPDevices.get_model () path = model.get_path (self.devices_uri_iter) self.tvNPDevices.set_cursor (path=path, start_editing=False) self.entNPTDevice.select_region (0, 0) self.entNPTDevice.set_position (-1) return allowed_chars = string.ascii_letters+string.digits+'_-.:%[]' self.entry_changed(ent, allowed_chars) s = ent.get_text () self.btnNetworkFind.set_sensitive (len (s) > 0) self.lblNetworkFindNotFound.hide () self.setNPButtons () def on_btnNetworkFind_clicked(self, button): host = self.entNPTNetworkHostname.get_text () def found_callback (new_device): if self.printer_finder is None: return GLib.idle_add (self.found_network_printer_callback, new_device) self.btnNetworkFind.set_sensitive (False) self.entNPTNetworkHostname.set_sensitive (False) self.network_found = 0 self.lblNetworkFindNotFound.hide () self.lblNetworkFindSearching.show_all () finder = probe_printer.PrinterFinder () self.inc_spinner_task () finder.find (host, found_callback) self.printer_finder = finder def found_network_printer_callback (self, new_device): Gdk.threads_enter () if new_device: self.network_found += 1 dev = PhysicalDevice (new_device) try: i = self.devices.index (dev) # Adding a new URI to an existing physical device. self.devices[i].add_device (new_device) (path, column) = self.tvNPDevices.get_cursor () if path: model = self.tvNPDevices.get_model () iter = model.get_iter (path) if model.get_value (iter, 1) == self.devices[i]: self.on_tvNPDevices_cursor_changed (self.tvNPDevices) except ValueError: # New physical device. dev.checked_hplip = True self.devices.append (dev) self.devices.sort () model = self.tvNPDevices.get_model () iter = model.insert_before (None, self.devices_find_nw_iter, row=[dev.get_info (), dev, False]) # If this is the first one we've found, select it. if self.network_found == 1: path = model.get_path (iter) self.tvNPDevices.set_cursor (path, None, False) else: self.printer_finder = None self.dec_spinner_task () self.lblNetworkFindSearching.hide () self.entNPTNetworkHostname.set_sensitive (True) self.btnNetworkFind.set_sensitive (True) if self.network_found == 0: self.lblNetworkFindNotFound.set_markup ('<i>' + _("No printer was " "found at that " "address.") + '</i>') self.lblNetworkFindNotFound.show () Gdk.threads_leave () ### def getDeviceURI(self): if self.dialog_mode in ['printer_with_uri', 'ppd']: return self.device.uri type = self.device.type page = self.new_printer_device_tabs.get (type, self.PAGE_SELECT_DEVICE) device = type if page == self.PAGE_DESCRIBE_PRINTER: # The "no options page". We already have the URI. device = self.device.uri elif type == "socket": # JetDirect host = self.entNPTJetDirectHostname.get_text() port = self.entNPTJetDirectPort.get_text() if host: device += "://" + host if port: device += ":" + port elif type == "lpd": # LPD host = self.entNPTLpdHost.get_text() printer = self.entNPTLpdQueue.get_text() if host: device += "://" + host if printer: device += "/" + printer elif type == "serial": # Serial options = [] for widget, name in ( (self.cmbNPTSerialBaud, "baud"), (self.cmbNPTSerialBits, "bits"), (self.cmbNPTSerialParity, "parity"), (self.cmbNPTSerialFlow, "flow")): model = widget.get_model () iter = widget.get_active_iter() option = model.get_value (iter, 1) if option != "": options.append(name + "=" + option) options = "+".join(options) device = self.device.uri.split("?")[0] #"serial:/dev/ttyS%s" if options: device = device + "?" + options elif type == "smb": uri = self.entSMBURI.get_text () (group, host, share, u, p) = SMBURI (uri=uri).separate () user = '' password = '' if self.rbtnSMBAuthSet.get_active (): user = self.entSMBUsername.get_text () password = self.entSMBPassword.get_text () uri = SMBURI (group=group, host=host, share=share, user=user, password=password).get_uri () if uri: device += "://" + uri else: device = self.entNPTDevice.get_text() return device # PPD def on_rbtnNPFoomatic_toggled(self, widget): rbtn1 = self.rbtnNPFoomatic.get_active() rbtn2 = self.rbtnNPPPD.get_active() rbtn3 = self.rbtnNPDownloadableDriverSearch.get_active() self.tvNPMakes.set_sensitive(rbtn1) self.filechooserPPD.set_sensitive(rbtn2) if rbtn1: page = self.PAGE_DESCRIBE_PRINTER if rbtn2: page = self.PAGE_SELECT_DEVICE if rbtn3: page = self.PAGE_SELECT_INSTALL_METHOD self.ntbkPPDSource.set_current_page (page) if not rbtn3 and self.opreq: # Need to cancel a search in progress. for handler in self.opreq_handlers: self.opreq.disconnect (handler) self.opreq_handlers = None self.opreq.cancel () self.opreq = None self.btnNPDownloadableDriverSearch.set_sensitive (True) self.btnNPDownloadableDriverSearch_label.set_text (_("Search")) # Clear printer list. model = Gtk.ListStore (str, str) combobox = self.cmbNPDownloadableDriverFoundPrinters combobox.set_model (model) combobox.set_sensitive (False) for widget in [self.entNPDownloadableDriverSearch, self.cmbNPDownloadableDriverFoundPrinters]: widget.set_sensitive(rbtn3) self.btnNPDownloadableDriverSearch.\ set_sensitive (rbtn3 and (self.opreq is None)) self.setNPButtons() def on_filechooserPPD_selection_changed(self, widget): self.setNPButtons() def on_btnNPDownloadableDriverSearch_clicked(self, widget): self.searchedfordriverpackages = True if self.opreq is not None: for handler in self.opreq_handlers: self.opreq.disconnect (handler) self.opreq_handlers = None self.opreq.cancel () self.opreq = None widget.set_sensitive (False) label = self.btnNPDownloadableDriverSearch_label label.set_text (_("Searching")) searchterm = self.entNPDownloadableDriverSearch.get_text () debugprint ('Searching for "%s"' % repr (searchterm)) self.opreq = OpenPrintingRequest () self.opreq_handlers = [] self.opreq_handlers.append ( self.opreq.connect ('finished', self.opreq_user_search_done)) self.opreq_handlers.append ( self.opreq.connect ('error', self.opreq_user_search_error)) self.opreq.searchPrinters (searchterm) self.cmbNPDownloadableDriverFoundPrinters.set_sensitive (False) def opreq_user_search_done (self, opreq, printers, drivers): for handler in self.opreq_handlers: opreq.disconnect (handler) self.opreq_user_search = True self.opreq_handlers = None self.opreq = None self.founddownloadabledrivers = True self.downloadable_printers = printers self.downloadable_drivers = drivers button = self.btnNPDownloadableDriverSearch label = self.btnNPDownloadableDriverSearch_label Gdk.threads_enter () try: label.set_text (_("Search")) button.set_sensitive (True) model = Gtk.ListStore (str, str) if len (self.downloadable_printers) != 1: if len (self.downloadable_printers) > 1: first = _("-- Select from search results --") else: first = _("-- No matches found --") iter = model.append (None) model.set_value (iter, 0, first) model.set_value (iter, 1, '') sorted_list = [] for printer_id, printer_name in self.downloadable_printers: sorted_list.append ((printer_id, printer_name)) sorted_list.sort (key=functools.cmp_to_key(lambda x, y: cups.modelSort (x[1], y[1]))) sought = self.entNPDownloadableDriverSearch.get_text ().lower () select_index = 0 for id, name in sorted_list: iter = model.append (None) model.set_value (iter, 0, name) model.set_value (iter, 1, id) if name.lower () == sought: select_index = model.get_path (iter)[0] combobox = self.cmbNPDownloadableDriverFoundPrinters combobox.set_model (model) combobox.set_active (select_index) combobox.set_sensitive (True) self.setNPButtons () except: nonfatalException() Gdk.threads_leave () def opreq_user_search_error (self, opreq, status, err): debugprint ("OpenPrinting request failed (%d): %s" % (status, repr (err))) self.opreq_user_search_done (opreq, list(), dict()) def on_cmbNPDownloadableDriverFoundPrinters_changed(self, widget): self.setNPButtons () if self.opreq is not None: for handler in self.opreq_handlers: self.opreq.disconnect (handler) self.opreq_handlers = None self.opreq.cancel () self.opreq = None self.btnNPDownloadableDriverSearch.set_sensitive (True) self.btnNPDownloadableDriverSearch_label.set_text (_("Search")) def fillDownloadableDrivers(self): print ("filldownloadableDrivers") self.downloadable_driver_for_printer = None if self.opreq_user_search == True: widget = self.cmbNPDownloadableDriverFoundPrinters model = widget.get_model () iter = widget.get_active_iter () if iter: printer_id = model.get_value (iter, 1) printer_str = model.get_value (iter, 0) if printer_id == '': widget.set_active (1) iter = widget.get_active_iter () if iter: printer_id = model.get_value (iter, 1) printer_str = model.get_value (iter, 0) else: printer_id = None printer_str = None else: printer_id = None printer_str = None else: printer_id, printer_str = self.downloadable_printers[0] if printer_id is None: # If none selected, show all. # This also happens for ID-matching. printer_ids = [x[0] for x in self.downloadable_printers] else: printer_ids = [printer_id] if printer_str: self.downloadable_driver_for_printer = printer_str model = Gtk.ListStore (str, # driver name GObject.TYPE_PYOBJECT) # driver data recommended_iter = None first_iter = None for printer_id in printer_ids: drivers = self.downloadable_drivers[printer_id] for driver in drivers.values (): if ((not 'ppds' in driver or len(driver['ppds']) <= 0) and (config.DOWNLOADABLE_ONLYPPD or (not self._getDriverInstallationInfo (driver)))): debugprint ("Removed invalid driver entry %s" % \ driver['name']) continue iter = model.append (None) if first_iter is None: first_iter = iter model.set_value (iter, 0, driver['name']) model.set_value (iter, 1, driver) if driver['recommended']: recommended_iter = iter if first_iter is None: return False if not self.rbtnNPDownloadableDriverSearch.get_active() and \ self.dialog_mode != "download_driver": iter = model.append (None) model.set_value (iter, 0, _("Local Driver")) model.set_value (iter, 1, 0) if recommended_iter is None: recommended_iter = first_iter treeview = self.tvNPDownloadableDrivers treeview.set_model (model) if recommended_iter is not None: treeview.get_selection ().select_iter (recommended_iter) self.on_tvNPDownloadableDrivers_cursor_changed(treeview) return True def on_rbtnNPDownloadLicense_toggled(self, widget): self.setNPButtons () # PPD from foomatic def fillMakeList(self): self.recommended_make_selected = False makes = self.ppds.getMakes() model = self.tvNPMakes.get_model() model.clear() found = False if self.auto_make: auto_make_norm = cupshelpers.ppds.normalize (self.auto_make) else: auto_make_norm = None for make in makes: recommended = (auto_make_norm and cupshelpers.ppds.normalize (make) == auto_make_norm) if self.device and self.device.make_and_model and recommended: text = make + _(" (recommended)") else: text = make iter = model.append((text, make,)) if recommended: path = model.get_path(iter) self.tvNPMakes.set_cursor (path, None, False) self.tvNPMakes.scroll_to_cell(path, None, True, 0.5, 0.5) found = True if not found: self.tvNPMakes.set_cursor (Gtk.TreePath(), None, False) self.tvNPMakes.scroll_to_cell(0, None, True, 0.0, 0.0) # Also pre-fill the OpenPrinting.org search box. search = '' if self.device and self.device.id_dict: devid_dict = self.device.id_dict if devid_dict["MFG"] and devid_dict["MDL"]: search = devid_dict["MFG"] + " " + devid_dict["MDL"] elif devid_dict["DES"]: search = devid_dict["DES"] elif devid_dict["MFG"]: search = devid_dict["MFG"] if search == '' and self.auto_make is not None: search += self.auto_make if self.auto_model is not None: search += " " + self.auto_model if (search.startswith("Generic") or search.startswith("Unknown")): search = '' self.entNPDownloadableDriverSearch.set_text (search) def on_tvNPMakes_cursor_changed(self, tvNPMakes): path, column = tvNPMakes.get_cursor() if path is not None and self.ppds is not None: model = tvNPMakes.get_model () iter = model.get_iter (path) self.NPMake = model.get(iter, 1)[0] recommended_make = (self.auto_make and cupshelpers.ppds.normalize (self.auto_make) == cupshelpers.ppds.normalize (self.NPMake)) self.recommended_make_selected = recommended_make self.fillModelList() def fillModelList(self): self.recommended_model_selected = False models = self.ppds.getModels(self.NPMake) model = self.tvNPModels.get_model() model.clear() selected = False is_auto_make = (cupshelpers.ppds.normalize (self.NPMake) == cupshelpers.ppds.normalize (self.auto_make)) if is_auto_make: auto_model_norm = cupshelpers.ppds.normalize (self.auto_model) for pmodel in models: recommended = (is_auto_make and cupshelpers.ppds.normalize (pmodel) == auto_model_norm) if self.device and self.device.make_and_model and recommended: text = pmodel + _(" (recommended)") else: text = pmodel iter = model.append((text, pmodel,)) if recommended: path = model.get_path(iter) self.tvNPModels.set_cursor (path, None, False) self.tvNPModels.scroll_to_cell(path, None, True, 0.5, 0.5) selected = True if not selected: self.tvNPModels.set_cursor (Gtk.TreePath(), None, False) self.tvNPModels.scroll_to_cell(0, None, True, 0.0, 0.0) self.tvNPModels.columns_autosize() def fillDriverList(self, pmake, pmodel): self.NPModel = pmodel model = self.tvNPDrivers.get_model() model.clear() if self.device: devid = self.device.id_dict else: devid = None if (self.device and self.device.make_and_model and self.recommended_model_selected and self.id_matched_ppdnames): # Use the actual device-make-and-model string. make_and_model = self.device.make_and_model # and the ID-matched list of PPDs. self.NPDrivers = self.id_matched_ppdnames debugprint ("ID matched PPDs: %s" % repr (self.NPDrivers)) elif self.ppds: # Use a generic make and model string for generating the # driver preference list. make_and_model = pmake + " " + pmodel ppds = self.ppds.getInfoFromModel(pmake, pmodel) ppdnames = list(ppds.keys ()) files = self.installed_driver_files try: self.NPDrivers = self.ppds.orderPPDNamesByPreference(ppdnames, files, make_and_model, devid) except: nonfatalException () self.NPDrivers = ppdnames # Put the current driver first. if self.auto_driver and self.device: drivers = [] for driver in self.NPDrivers: if driver == self.auto_driver: drivers.insert (0, driver) else: drivers.append (driver) self.NPDrivers = drivers else: # No available PPDs for some reason(!) debugprint ("No PPDs available?") self.NPDrivers = [] driverlist = [] NPDrivers = [] i = 0 for ppdname in self.NPDrivers: ppd = self.ppds.getInfoFromPPDName (ppdname) driver = _singleton (ppd["ppd-make-and-model"]) driver = driver.replace(" (recommended)", "") try: lpostfix = " [%s]" % _singleton (ppd["ppd-natural-language"]) driver += lpostfix except KeyError: pass duplicate = driver in driverlist if (not (self.device and self.device.make_and_model) and self.auto_driver == ppdname): driverlist.append (driver) NPDrivers.append (ppdname) i += 1 iter = model.append ((driver + _(" (Current)"),)) path = model.get_path (iter) self.tvNPDrivers.get_selection().select_path(path) self.tvNPDrivers.scroll_to_cell(path, None, True, 0.5, 0.0) elif self.device and i == 0: driverlist.append (driver) NPDrivers.append (ppdname) i += 1 iter = model.append ((driver + _(" (recommended)"),)) path = model.get_path (iter) self.tvNPDrivers.get_selection().select_path(path) self.tvNPDrivers.scroll_to_cell(path, None, True, 0.5, 0.0) else: if duplicate: continue driverlist.append (driver) NPDrivers.append (ppdname) i += 1 model.append((driver, )) self.NPDrivers = NPDrivers self.tvNPDrivers.columns_autosize() def on_NPDrivers_query_tooltip(self, tv, x, y, keyboard_mode, tooltip): if keyboard_mode: path = tv.get_cursor()[0] if path is None: return False else: bin_x, bin_y = tv.convert_widget_to_bin_window_coords(x, y) ret = tv.get_path_at_pos (bin_x, bin_y) if ret is None: return False path = ret[0] drivername = self.NPDrivers[path[0]] ppddict = self.ppds.getInfoFromPPDName(drivername) markup = _singleton (ppddict['ppd-make-and-model']) if (drivername.startswith ("foomatic:")): markup += " " markup += _("This PPD is generated by foomatic.") tooltip.set_markup(markup) return True def on_tvNPModels_cursor_changed(self, widget): path, column = widget.get_cursor() if path is not None: model = widget.get_model () iter = model.get_iter (path) pmodel = model.get(iter, 1)[0] # Find out if this is the auto-detected make and model recommended_model = (self.recommended_make_selected and self.auto_model and self.auto_model.lower () == pmodel.lower ()) self.recommended_model_selected = recommended_model self.fillDriverList(self.NPMake, pmodel) self.on_tvNPDrivers_cursor_changed(self.tvNPDrivers) def on_tvNPDrivers_cursor_changed(self, widget): self.setNPButtons() def on_tvNPDownloadableDrivers_cursor_changed(self, widget): # Clear out the properties. self.lblNPDownloadableDriverSupplier.set_text ('') self.lblNPDownloadableDriverLicense.set_text ('') self.lblNPDownloadableDriverDescription.set_text ('') self.lblNPDownloadableDriverSupportContacts.set_text ('') self.rbtnNPDownloadLicenseNo.set_active (True) self.frmNPDownloadableDriverLicenseTerms.hide () selection = widget.get_selection () if selection is None: return model, iter = selection.get_selected () if not iter: path, column = widget.get_cursor() if iter: iter = model.get_iter (path) else: return driver = model.get_value (iter, 1) if driver == 0: self.ntbkNPDownloadableDriverProperties.set_current_page (self.PAGE_DESCRIBE_PRINTER) self.setNPButtons() return self.ntbkNPDownloadableDriverProperties.set_current_page (self.PAGE_SELECT_DEVICE) supplier = driver.get('supplier', _("OpenPrinting")) vendor = self.cbNPDownloadableDriverSupplierVendor active = driver['manufacturersupplied'] def set_protect_active (widget, active): widget.protect_active = active widget.set_active (active) set_protect_active (vendor, active) self.lblNPDownloadableDriverSupplier.set_text (supplier) license = driver.get('license', _("Distributable")) patents = self.cbNPDownloadableDriverLicensePatents free = self.cbNPDownloadableDriverLicenseFree set_protect_active (patents, driver['patents']) set_protect_active (free, driver['freesoftware']) self.lblNPDownloadableDriverLicense.set_text (license) description = driver.get('shortdescription', _("None")) self.lblNPDownloadableDriverDescription.set_markup (description) if 'functionality' in driver: functionality = driver['functionality'] for field in ["Graphics", "LineArt", "Photo", "Text"]: key = field.lower () value = None hs = self.__dict__.get ("hsDownloadableDriverPerf%s" % field) unknown = self.__dict__.get ("lblDownloadableDriverPerf%sUnknown" % field) if key in functionality: if hs: try: value = int (functionality[key]) hs.set_range (0, 100) hs.set_value (value) hs.show_all () unknown.hide () except: pass if value is None: hs.hide () unknown.show_all () supportcontacts = "" if 'supportcontacts' in driver: for supportentry in driver['supportcontacts']: if supportentry['name']: supportcontact = " - " + supportentry['name'] supportcontact_extra = "" if supportentry['url']: supportcontact_extra = supportcontact_extra + \ supportentry['url'] if supportentry['level']: if supportcontact_extra: supportcontact_extra = supportcontact_extra + _(", ") supportcontact_extra = supportcontact_extra + \ supportentry['level'] if supportcontact_extra: supportcontact = supportcontact + \ _("\n(%s)") % supportcontact_extra if supportcontacts: supportcontacts = supportcontacts + "\n" supportcontacts = supportcontacts + supportcontact if not supportcontacts: supportcontacts = _("No support contacts known") self.lblNPDownloadableDriverSupportContacts.set_text (supportcontacts) if 'licensetext' in driver: self.frmNPDownloadableDriverLicenseTerms.show () terms = driver.get('licensetext', _("Not specified.")) self.tvNPDownloadableDriverLicense.get_buffer ().set_text (terms) else: self.frmNPDownloadableDriverLicenseTerms.hide () if not driver['nonfreesoftware'] and not driver['patents']: self.rbtnNPDownloadLicenseYes.set_active (True) self.rbtnNPDownloadLicenseYes.hide () self.rbtnNPDownloadLicenseNo.hide () else: self.rbtnNPDownloadLicenseNo.set_active (True) self.rbtnNPDownloadLicenseYes.show () self.rbtnNPDownloadLicenseNo.show () self.frmNPDownloadableDriverLicenseTerms.show () terms = driver.get('licensetext', _("Not specified.")) self.tvNPDownloadableDriverLicense.get_buffer ().set_text (terms) if 'ppds' in driver and len(driver["ppds"]) > 0: self.founddownloadableppd = True else: self.founddownloadableppd = False self.setNPButtons() def getNPPPD(self): ppd = None try: if ((self.rbtnNPFoomatic.get_active() or len(self.installed_driver_files) > 0) and self.founddownloadableppd == False): model, iter = self.tvNPDrivers.get_selection().get_selected() nr = model.get_path(iter)[0] ppd = self.NPDrivers[nr] elif self.rbtnNPPPD.get_active(): ppd = cups.PPD(self.filechooserPPD.get_filename()) else: # PPD of the driver downloaded from OpenPrinting XXX treeview = self.tvNPDownloadableDrivers model, iter = treeview.get_selection ().get_selected () driver = model.get_value (iter, 1) if driver != 0 and 'ppds' in driver: # Only need to download a PPD. if (len(driver['ppds']) > 0): file_to_download = driver['ppds'][0] debugprint ("ppd file to download [" + file_to_download+ "]") file_to_download = file_to_download.strip() if (len(file_to_download) > 0): ppdurlobj = urllib.request.urlopen(file_to_download) ppdcontent = ppdurlobj.read() ppdurlobj.close() with tempfile.NamedTemporaryFile () as tmpf: tmpf.write(ppdcontent) tmpf.flush () ppd = cups.PPD(tmpf.name) except (RuntimeError, urllib.error.HTTPError) as e: debugprint ("RuntimeError: " + repr (e)) if self.rbtnNPFoomatic.get_active(): # Foomatic database problem of some sort. err_title = _('Database error') err_text = _("The '%s' driver cannot be " "used with printer '%s %s'.") model, iter = (self.tvNPDrivers.get_selection(). get_selected()) nr = model.get_path(iter)[0] driver = self.NPDrivers[nr] if driver.startswith ("gutenprint"): # This printer references some XML that is not # installed by default. Point the user at the # package they need to install. err = _("You will need to install the '%s' package " "in order to use this driver.") % \ "gutenprint-foomatic" else: err = err_text % (driver, self.NPMake, self.NPModel) elif self.rbtnNPPPD.get_active(): # This error came from trying to open the PPD file. err_title = _('PPD error') filename = self.filechooserPPD.get_filename() err = _('Failed to read PPD file. Possible reason ' 'follows:') + '\n' try: # We want this to be in the current natural language, # so we intentionally don't set LC_ALL=C here. p = subprocess.Popen (['/usr/bin/cupstestppd', '-rvv', filename], close_fds=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = p.communicate () err += stdout.decode () except: # Problem executing command. raise else: # Failed to get PPD downloaded from OpenPrinting XXX err_title = _('Downloadable drivers') err = _("Failed to download PPD.") show_error_dialog (err_title, err, self.NewPrinterWindow) return None debugprint("ppd: " + repr(ppd)) if isinstance(ppd, str): self.cups._begin_operation (_("fetching PPD")) try: if ppd != "raw": f = self.cups.getServerPPD(ppd) ppd = cups.PPD(f) os.unlink(f) except RuntimeError: nonfatalException() debugprint ("libcups from CUPS 1.3 not available: never mind") except cups.IPPError: nonfatalException() debugprint ("CUPS 1.3 server not available: never mind") self.cups._end_operation () return ppd # Installable Options def fillNPInstallableOptions(self): debugprint ("Examining installable options") self.installable_options = False self.options = { } container = self.vbNPInstallOptions for child in container.get_children(): container.remove(child) if not self.ppd: l = Gtk.Label(label=_("No Installable Options")) container.add(l) l.show() debugprint ("No PPD so no installable options") return # build option tabs for group in self.ppd.optionGroups: if group.name != "InstallableOptions": continue self.installable_options = True grid = Gtk.Grid() grid.set_column_spacing(6) grid.set_row_spacing(6) container.add(grid) rows = 0 for nr, option in enumerate(group.options): if option.keyword == "PageRegion": continue rows += 1 o = OptionWidget(option, self.ppd, self) grid.attach(o.conflictIcon, 0, nr, 1, 1) hbox = Gtk.Box() if o.label: a = Gtk.Alignment.new (0.5, 0.5, 1.0, 1.0) a.set_padding (0, 0, 0, 6) a.add (o.label) grid.attach(a, 1, nr, 1, 1) grid.attach(hbox, 2, nr, 1, 1) else: grid.attach(hbox, 1, nr, 2, 1) hbox.pack_start(o.selector, False, False, 0) self.options[option.keyword] = o if not self.installable_options: l = Gtk.Label(label=_("No Installable Options")) container.add(l) l.show() self.scrNPInstallableOptions.hide() self.scrNPInstallableOptions.show_all() # Create new Printer def on_btnNPApply_clicked(self, widget): if self.fetchDevices_conn: self.fetchDevices_conn.destroy () self.fetchDevices_conn = None self.dec_spinner_task () if self.ppdsloader: self.ppdsloader.destroy () self.ppdsloader = None if self.printer_finder: self.printer_finder.cancel () self.printer_finder = None self.dec_spinner_task () if self.dialog_mode in ("class", "printer", "printer_with_uri"): name = self.entNPName.get_text() location = self.entNPLocation.get_text() info = self.entNPDescription.get_text() else: name = self._name ppd = self.ppd if self.dialog_mode == "class": members = getCurrentClassMembers(self.tvNCMembers) try: for member in members: try: self.cups.addPrinterToClass(member, name) except RuntimeError: # Printer already in class? continue except cups.IPPError as e: (e, msg) = e.args self.show_IPP_Error(e, msg) return elif self.dialog_mode == "printer" or \ self.dialog_mode == "printer_with_uri": uri = None if self.device.uri: uri = self.device.uri else: uri = self.getDeviceURI() if not self.ppd: # XXX needed? # Go back to previous page to re-select driver. self.nextNPTab(-1) return # write Installable Options to ppd for option in self.options.values(): option.writeback() busy (self.NewPrinterWindow) while Gtk.events_pending (): Gtk.main_iteration () self.cups._begin_operation (_("adding printer %s") % name) try: if isinstance(ppd, str): self.cups.addPrinter(name, ppdname=ppd, device=uri, info=info, location=location) elif ppd is None: # raw queue self.cups.addPrinter(name, device=uri, info=info, location=location) else: # Note: LC_MESSAGES is used here to determine the # page size instead of LC_PAPER. This is not # really correct, but it's what CUPS does so to # avoid confusion we'll follow that method. cupshelpers.setPPDPageSize(ppd, self.language[0]) self.cups.addPrinter(name, ppd=ppd, device=uri, info=info, location=location) except cups.IPPError as e: (e, msg) = e.args ready (self.NewPrinterWindow) self.show_IPP_Error(e, msg) self.cups._end_operation() return except: ready (self.NewPrinterWindow) self.cups._end_operation() fatalException (1) self.cups._end_operation() ready (self.NewPrinterWindow) if self.dialog_mode in ("class", "printer", "printer_with_uri"): self.cups._begin_operation (_("modifying printer %s") % name) try: cupshelpers.activateNewPrinter (self.cups, name) self.cups.setPrinterLocation(name, location) self.cups.setPrinterInfo(name, info) except cups.IPPError as e: (e, msg) = e.args self.show_IPP_Error(e, msg) self.cups._end_operation () return self.cups._end_operation () elif self.dialog_mode == "device": self.cups._begin_operation (_("modifying printer %s") % name) try: uri = self.getDeviceURI() self.cups.addPrinter(name, device=uri) except cups.IPPError as e: (e, msg) = e.args self.show_IPP_Error(e, msg) self.cups._end_operation () return self.cups._end_operation () elif self.dialog_mode == "ppd": if not ppd: ppd = self.ppd = self.getNPPPD() if not ppd: # Go back to previous page to re-select driver. self.nextNPTab(-1) return self.cups._begin_operation (_("modifying printer %s") % name) # set ppd on server and retrieve it # cups doesn't offer a way to just download a ppd ;(= raw = False if isinstance(ppd, str): if self.rbtnChangePPDasIs.get_active(): # To use the PPD as-is we need to prevent CUPS copying # the old options over. Do this by setting it to a # raw queue (no PPD) first. try: self.cups.addPrinter(name, ppdname='raw') except cups.IPPError as e: (e, msg) = e.args self.show_IPP_Error(e, msg) try: self.cups.addPrinter(name, ppdname=ppd) except cups.IPPError as e: (e, msg) = e.args self.show_IPP_Error(e, msg) self.cups._end_operation () return try: filename = self.cups.getPPD(name) ppd = cups.PPD(filename) os.unlink(filename) except cups.IPPError as e: (e, msg) = e.args if e == cups.IPP_NOT_FOUND: raw = True else: self.show_IPP_Error(e, msg) self.cups._end_operation () return else: # We have an actual PPD to upload, not just a name. if ((not self.rbtnChangePPDasIs.get_active()) and isinstance (self.orig_ppd, cups.PPD)): cupshelpers.copyPPDOptions(self.orig_ppd, ppd) else: cupshelpers.setPPDPageSize(ppd, self.language[0]) # write Installable Options to ppd for option in self.options.values(): option.writeback() try: self.cups.addPrinter(name, ppd=ppd) except cups.IPPError as e: (e, msg) = e.args self.show_IPP_Error(e, msg) self.cups._end_operation () return self.cups._end_operation () elif self.dialog_mode == "download_driver": self.nextNPTab(0); self.NewPrinterWindow.hide() if self.dialog_mode in ["printer", "printer_with_uri", "class"]: self.emit ('printer-added', name) elif self.dialog_mode == "download_driver": self.emit ('driver-download-checked', self.installed_driver_files) else: self.emit ('printer-modified', name, self.orig_ppd != self.ppd) self.device = None self.printers = {} def show_help(): print ("\nThis is the test/debug mode of the new-printer dialog of " \ "system-config-printer.\n\n" "Options:\n\n" " --setup-printer URI\n" " Select the (detected) CUPS device URI on start up\n" " and run the new-printer wizard for it.\n\n" " --devid Supply a device ID which should be used for the\n" " setup of the new printer with \"--setup-printer\".\n" " This can be any printer's ID, so that driver \n" " selection can be tested for printers which are not\n" " physically available.\n") if __name__ == '__main__': import getopt try: opts, args = getopt.gnu_getopt (sys.argv[1:], '', ['setup-printer=', 'devid=']) except getopt.GetoptError: show_help () sys.exit (1) setup_printer = None devid = "" for opt, optarg in opts: if opt == '--setup-printer': setup_printer = optarg elif opt == '--devid': devid = optarg os.environ["SYSTEM_CONFIG_PRINTER_UI"] = "ui" import ppdippstr locale.setlocale (locale.LC_ALL, "") ppdippstr.init () set_debugging (True) cupshelpers.set_debugprint_fn (debugprint) n = NewPrinterGUI () def on_signal (*args): Gtk.main_quit () n.connect ("printer-added", on_signal) n.connect ("printer-modified", on_signal) n.connect ("dialog-canceled", on_signal) if setup_printer is not None: n.init ("printer_with_uri", device_uri=setup_printer, devid=devid) else: n.init ("printer") Gtk.main ()