def __init__ (self, parent=None, try_as_root=True, lock=False, host=None, port=None, encryption=None): if host != None: cups.setServer (host) if port != None: cups.setPort (port) if encryption != None: cups.setEncryption (encryption) self._use_password = '' self._parent = parent self._try_as_root = try_as_root self._use_user = cups.getUser () self._server = cups.getServer () self._port = cups.getPort() self._encryption = cups.getEncryption () self._prompt_allowed = True self._operation_stack = [] self._lock = lock self._gui_event = threading.Event () creds = global_authinfocache.lookup_auth_info (host=host, port=port) if creds != None: if (creds[0] != 'root' or try_as_root): (self._use_user, self._use_password) = creds del creds self._connect ()
def start(self): """Start the notification server. """ if self._thread: raise EnvironmentError("Server is already running") LOGGER.info("Starting RSS feed listener at %s", self._rss_uri) self._thread = threading.Thread(target=self.serve_forever) self._thread.daemon = True self._thread.start() self.cancel_subscriptions() # Renew notifications subscription if osp.exists(cups.getServer()): hostname = 'localhost' else: hostname = cups.getServer() cups_uri = "ipp://{}:{}".format(hostname, cups.getPort()) try: self._conn.createSubscription(cups_uri, recipient_uri=self._rss_uri, events=self._filters, job_id=self._job_id) except cups.IPPError as e: status = e.args[0] if status == 1030: LOGGER.warn("The job {} not exists".format(self._job_id)) raise JobNotExists() raise e
def __init__(self, parent=None, try_as_root=True, lock=False, host=None, port=None, encryption=None): if host != None: cups.setServer(host) if port != None: cups.setPort(port) if encryption != None: cups.setEncryption(encryption) self._use_password = '' self._parent = parent self._try_as_root = try_as_root self._use_user = cups.getUser() self._server = cups.getServer() self._port = cups.getPort() self._encryption = cups.getEncryption() self._prompt_allowed = True self._operation_stack = [] self._lock = lock self._gui_event = threading.Event() self._connect()
def action_ok(self, cr, uid, ids, context=None): # Update CUPS URL url = 'https://%s:%s' % ( cups.getServer(), cups.getPort() ) id = self.pool.get('ir.model.data')._get_id(cr, uid, 'printjob', 'action_open_cups') id = self.pool.get('ir.model.data').browse(cr, uid, id, context).res_id self.pool.get('ir.actions.url').write(cr, uid, [id], { 'url': url, }, context) # Update Printers try: connection = cups.Connection() printers = connection.getPrinters() except: return {} ids = self.pool.get('printjob.printer').search(cr, uid, [('system_name','in',printers.keys())], context=context) for printer in self.pool.get('printjob.printer').browse(cr, uid, ids, context): del printers[printer.system_name] for name in printers: printer = printers[name] self.pool.get('printjob.printer').create(cr, uid, { 'name': printer['printer-info'], 'system_name': name, 'model': printer.get('printer-make-and-model', False), 'location': printer.get('printer-location', False), 'uri': printer.get('device-uri', False), }, context) return {}
def run (self): if self.host == None: self.host = cups.getServer () if self.port == None: self.port = cups.getPort () if self._encryption == None: self._encryption = cups.getEncryption () if self.user: cups.setUser (self.user) else: self.user = cups.getUser () try: cups.setPasswordCB2 (self._auth) except AttributeError: # Requires pycups >= 1.9.47. Fall back to rubbish API. cups.setPasswordCB (self._auth) try: conn = cups.Connection (host=self.host, port=self.port, encryption=self._encryption) self._reply (None) except RuntimeError, e: conn = None self._error (e)
def __init__ (self, parent=None, try_as_root=True): self._use_password = '' self._parent = parent self._try_as_root = try_as_root self._use_user = cups.getUser () self._server = cups.getServer () self._port = cups.getPort() self._connect () self._prompt_allowed = True
def __init__(self, bus=None, my_jobs=True, specific_dests=None, monitor_jobs=True, host=None, port=None, encryption=None): GObject.GObject.__init__(self) self.my_jobs = my_jobs self.specific_dests = specific_dests self.monitor_jobs = monitor_jobs self.jobs = {} self.printer_state_reasons = {} self.printers = set() self.process_pending_events = True self.fetch_jobs_timer = None self.cups_connection_in_error = False if host: cups.setServer(host) if port: cups.setPort(port) if encryption: cups.setEncryption(encryption) self.user = cups.getUser() self.host = cups.getServer() self.port = cups.getPort() self.encryption = cups.getEncryption() self.ppdcache = ppdcache.PPDCache(host=self.host, port=self.port, encryption=self.encryption) self.which_jobs = "not-completed" self.reasons_seen = {} self.connecting_timers = {} self.still_connecting = set() self.connecting_to_device = {} self.received_any_dbus_signals = False self.update_timer = None if bus is None: try: bus = dbus.SystemBus() except dbus.exceptions.DBusException: # System bus not running. pass self.bus = bus if bus is not None: bus.add_signal_receiver(self.handle_dbus_signal, path=self.DBUS_PATH, dbus_interface=self.DBUS_IFACE) self.sub_id = -1
def __init__(self, bus=None, my_jobs=True, specific_dests=None, monitor_jobs=True, host=None, port=None, encryption=None): GObject.GObject.__init__ (self) self.my_jobs = my_jobs self.specific_dests = specific_dests self.monitor_jobs = monitor_jobs self.jobs = {} self.printer_state_reasons = {} self.printers = set() self.process_pending_events = True self.fetch_jobs_timer = None self.cups_connection_in_error = False if host: cups.setServer (host) if port: cups.setPort (port) if encryption: cups.setEncryption (encryption) self.user = cups.getUser () self.host = cups.getServer () self.port = cups.getPort () self.encryption = cups.getEncryption () self.ppdcache = ppdcache.PPDCache (host=self.host, port=self.port, encryption=self.encryption) self.which_jobs = "not-completed" self.reasons_seen = {} self.connecting_timers = {} self.still_connecting = set() self.connecting_to_device = {} self.received_any_dbus_signals = False self.update_timer = None if bus is None: try: bus = dbus.SystemBus () except dbus.exceptions.DBusException: # System bus not running. pass self.bus = bus if bus is not None: bus.add_signal_receiver (self.handle_dbus_signal, path=self.DBUS_PATH, dbus_interface=self.DBUS_IFACE) self.sub_id = -1
def start(self): """Start the notification server. """ if self._thread: raise EnvironmentError("Server is already running") self._thread = threading.Thread(target=self.serve_forever) self._thread.daemon = True self._thread.start() self.cancel_subscriptions() # Renew notifications subscription cups_uri = "ipp://localhost:{}".format(cups.getPort()) self._conn.createSubscription(cups_uri, recipient_uri=self.notif_uri, events=[ 'job-completed', 'job-created', 'job-stopped', 'printer-restarted', 'printer-shutdown', 'printer-stopped' ])
def start(self): """Start the notification server. """ if self._thread: raise EnvironmentError("Server is already running") LOGGER.info("Starting RSS feed listener at %s", self._rss_uri) self._thread = threading.Thread(target=self.serve_forever) self._thread.daemon = True self._thread.start() self.cancel_subscriptions() # Renew notifications subscription if osp.exists(cups.getServer()): hostname = 'localhost' else: hostname = cups.getServer() cups_uri = "ipp://{}:{}".format(hostname, cups.getPort()) self._conn.createSubscription(cups_uri, recipient_uri=self._rss_uri, events=self._filters)
def __init__(self, parent=None, try_as_root=True, lock=False, host=None, port=None, encryption=None): if host != None: cups.setServer(host) if port != None: cups.setPort(port) if encryption != None: cups.setEncryption(encryption) self._use_password = "" self._parent = parent self._try_as_root = try_as_root self._use_user = cups.getUser() self._server = cups.getServer() self._port = cups.getPort() self._encryption = cups.getEncryption() self._prompt_allowed = True self._operation_stack = [] self._lock = lock self._gui_event = threading.Event() self._connect()
def run (self): if self.host == None: self.host = cups.getServer () if self.port == None: self.port = cups.getPort () if self._encryption == None: self._encryption = cups.getEncryption () if self.user: cups.setUser (self.user) else: self.user = cups.getUser () cups.setPasswordCB2 (self._auth) try: conn = cups.Connection (host=self.host, port=self.port, encryption=self._encryption) self._reply (None) except RuntimeError, e: conn = None self._error (e)
def generate(self): if not self.host: conn = cups.Connection() else: if not self.port: self.port = 631 conn = cups.Connection(self.host, self.port) printers = conn.getPrinters() for p in sorted(printers.keys()): v = printers[p] if v['printer-is-shared']: attrs = conn.getPrinterAttributes(p) uri = urllib.parse.urlparse(v['printer-uri-supported']) f = conn.getPPD(p) ppd = cups.PPD(f) os.unlink(f) port_no = None if hasattr(uri, 'port'): port_no = uri.port if not port_no: port_no = self.port if not port_no: port_no = cups.getPort() if not self.host: self.host = uri.hostname if hasattr(uri, 'path'): rp = uri.path else: rp = uri[2] rp = rp[1:] txtRec = [] txtRec.append('"rp={0}"'.format(rp)); txtRec.append('"ty={0}"'.format(v['printer-make-and-model'])) txtRec.append('"adminurl=https://{0}:{1}/{2}"'.format(self.host, port_no, rp)) txtRec.append('"priority=0"') product = ppd.findAttr('Product').value txtRec.append('"product={0}"'.format(product)) txtRec.append('"note={0}"'.format(v['printer-location'])) fmts = [] for a in attrs['document-format-supported']: if a in DOCUMENT_TYPES: fmts.append(a) fmts = ','.join(fmts) txtRec.append('"pdl=application/pdf,{0}"'.format(fmts)) txtRec.append('"air=username,password"') txtRec.append('"UUID={0}"'.format(attrs['printer-uuid'].replace('urn:uuid:', ''))) txtRec.append('"TLS=1.2"') txtRec.append('"Transparent=F" "Binary=F"') printer_type = v['printer-type'] if (printer_type & cups.CUPS_PRINTER_FAX): txtRec.append('"Fax=T"') else: txtRec.append('"Fax=F"') if (printer_type & cups.CUPS_PRINTER_COLOR): txtRec.append('"Color=T"') else: txtRec.append('"Color=F"') if (printer_type & cups.CUPS_PRINTER_DUPLEX): txtRec.append('"Duplex=T"') else: txtRec.append('"Duplex=F"') if (printer_type & cups.CUPS_PRINTER_STAPLE): txtRec.append('"Staple=T"') else: txtRec.append('"Staple=F"') if (printer_type & cups.CUPS_PRINTER_COPIES): txtRec.append('"Copies=T"') else: txtRec.append('"Copies=F"') if (printer_type & cups.CUPS_PRINTER_COLLATE): txtRec.append('"Collate=T"') else: txtRec.append('"Collate=F"') if (printer_type & cups.CUPS_PRINTER_PUNCH): txtRec.append('"Punch=T"') else: txtRec.append('"Punch=F"') if (printer_type & cups.CUPS_PRINTER_BIND): txtRec.append('"Bind=T"') else: txtRec.append('"Bind=F"') if (printer_type & cups.CUPS_PRINTER_SORT): txtRec.append('"Sort=T"') else: txtRec.append('"Sort=F"') # if (printer_type & cups.CUPS_PRINTER_MFP): if (printer_type & 0x4000000): txtRec.append('"Scan=T"') else: txtRec.append('"Scan=F"') txtRec.append('"printer-state=3"') txtRec.append('"printer-type={0:#x}"'.format(v['printer-type'])) txtRec.append('"URF=DM3"') encodedLabel = self.encode(p) # print ipp records print('_ipp\t\t\tPTR\t{0}._ipp'.format(encodedLabel)) print('_cups._sub._ipp\t\tPTR\t{0}._ipp'.format(encodedLabel)) print('_universal._sub._ipp\tPTR\t{0}._ipp'.format(encodedLabel)) print('{0}._ipp\t\tSRV\t0 0 {1} {2}.'.format(encodedLabel, port_no, self.host)) sys.stdout.write('{0}._ipp\t\tTXT\t"txtvers=1" "qtotal=1" '.format(encodedLabel)) print(' '.join(txtRec)) print() # print ipps records print('_ipps\t\t\tPTR\t{0}._ipps'.format(encodedLabel)) print('_cups._sub._ipps\tPTR\t{0}._ipps'.format(encodedLabel)) print('_universal._sub._ipps\tPTR\t{0}._ipps'.format(encodedLabel)) print('{0}._ipps\t\tSRV\t0 0 {1} {2}.'.format(encodedLabel, port_no, self.host)) sys.stdout.write('{0}._ipps\t\tTXT\t"txtvers=1" "qtotal=1" '.format(encodedLabel)) print(' '.join(txtRec)) print()
def generate(self): collected_printers = list() # Collect shared printers from CUPS if applicable if self.usecups: if self.verbose: sys.stderr.write('Collecting shared printers from CUPS%s' % os.linesep) if not self.host: conn = cups.Connection() else: if not self.port: self.port = 631 conn = cups.Connection(self.host, self.port) printers = conn.getPrinters() for p, v in printers.items(): if v['printer-is-shared']: attrs = conn.getPrinterAttributes(p) uri = urlparse.urlparse(v['printer-uri-supported']) port_no = None if hasattr(uri, 'port'): port_no = uri.port if not port_no: port_no = self.port if not port_no: port_no = cups.getPort() if hasattr(uri, 'path'): rp = uri.path else: rp = uri[2] re_match = re.match(r'^//(.*):(\d+)(/.*)', rp) if re_match: rp = re_match.group(3) #Remove leading slashes from path #TODO XXX FIXME I'm worried this will match broken urlparse #results as well (for instance if they don't include a port) #the xml would be malform'd either way rp = re.sub(r'^/+', '', rp) pdl = Element('txt-record') fmts = [] defer = [] for a in attrs['document-format-supported']: if a in DOCUMENT_TYPES: if DOCUMENT_TYPES[a]: fmts.append(a) else: defer.append(a) if 'image/urf' not in fmts: sys.stderr.write( 'image/urf is not in mime types, %s may not be available on ios6 (see https://github.com/tjfontaine/airprint-generate/issues/5)%s' % (p, os.linesep)) fmts = ','.join(fmts + defer) dropped = [] # TODO XXX FIXME all fields should be checked for 255 limit while len('pdl=%s' % (fmts)) >= 255: (fmts, drop) = fmts.rsplit(',', 1) dropped.append(drop) if len(dropped) and self.verbose: sys.stderr.write('%s Losing support for: %s%s' % (p, ','.join(dropped), os.linesep)) collected_printers.append({ 'SOURCE': 'CUPS', 'name': p, 'host': self.host, 'address': None, 'port': port_no, 'domain': 'local', 'txt': { 'txtvers': '1', 'qtotal': '1', 'Transparent': 'T', 'URF': 'none', 'rp': rp, 'note': v['printer-info'], 'product': '(GPL Ghostscript)', 'printer-state': v['printer-state'], 'printer-type': v['printer-type'], 'adminurl': v['printer-uri-supported'], 'pdl': fmts, } }) # Collect networked printers using DNS-SD if applicable if (self.useavahi): if self.verbose: sys.stderr.write( 'Collecting networked printers using DNS-SD%s' % os.linesep) finder = avahisearch.AvahiPrinterFinder(verbose=self.verbose) for p in finder.Search(): p['SOURCE'] = 'DNS-SD' collected_printers.append(p) # Produce a .service file for each printer found for p in collected_printers: self.produce_settings_file(p)
def generate(self): if not self.host: conn = cups.Connection() else: if not self.port: self.port = 631 conn = cups.Connection(self.host, self.port) printers = conn.getPrinters() for p, v in list(printers.items()): if v['printer-is-shared']: attrs = conn.getPrinterAttributes(p) uri = urlparse.urlparse(v['printer-uri-supported']) tree = ElementTree() tree.parse(StringIO(XML_TEMPLATE.replace('\n', '').replace('\r', '').replace('\t', ''))) name = tree.find('name') name.text = 'AirPrint %s @ %%h' % (p) service = tree.find('service') port = service.find('port') port_no = None if hasattr(uri, 'port'): port_no = uri.port if not port_no: port_no = self.port if not port_no: port_no = cups.getPort() port.text = '%d' % port_no if hasattr(uri, 'path'): rp = uri.path else: rp = uri[2] re_match = re.match(r'^//(.*):(\d+)(/.*)', rp) if re_match: rp = re_match.group(3) #Remove leading slashes from path #TODO XXX FIXME I'm worried this will match broken urlparse #results as well (for instance if they don't include a port) #the xml would be malform'd either way rp = re.sub(r'^/+', '', rp) path = Element('txt-record') path.text = 'rp=%s' % (rp) service.append(path) desc = Element('txt-record') desc.text = 'note=%s' % (v['printer-info']) service.append(desc) product = Element('txt-record') product.text = 'product=(GPL Ghostscript)' service.append(product) state = Element('txt-record') state.text = 'printer-state=%s' % (v['printer-state']) service.append(state) ptype = Element('txt-record') ptype.text = 'printer-type=%s' % (hex(v['printer-type'])) service.append(ptype) pdl = Element('txt-record') fmts = [] defer = [] for a in attrs['document-format-supported']: if a in DOCUMENT_TYPES: if DOCUMENT_TYPES[a]: fmts.append(a) else: defer.append(a) if 'image/urf' not in fmts: sys.stderr.write('image/urf is not in mime types, %s may not be available on ios6 (see https://github.com/tjfontaine/airprint-generate/issues/5)%s' % (p, os.linesep)) fmts = ','.join(fmts+defer) dropped = [] # TODO XXX FIXME all fields should be checked for 255 limit while len('pdl=%s' % (fmts)) >= 255: (fmts, drop) = fmts.rsplit(',', 1) dropped.append(drop) if len(dropped) and self.verbose: sys.stderr.write('%s Losing support for: %s%s' % (p, ','.join(dropped), os.linesep)) pdl.text = 'pdl=%s' % (fmts) service.append(pdl) if self.adminurl: admin = Element('txt-record') admin.text = 'adminurl=%s' % (v['printer-uri-supported']) service.append(admin) fname = '%s%s.service' % (self.prefix, p) if self.directory: fname = os.path.join(self.directory, fname) f = open(fname, 'w') if etree: tree.write(f, pretty_print=True, xml_declaration=True, encoding="UTF-8") else: xmlstr = tostring(tree.getroot()) doc = parseString(xmlstr) dt= minidom.getDOMImplementation('').createDocumentType('service-group', None, 'avahi-service.dtd') doc.insertBefore(dt, doc.documentElement) doc.writexml(f) f.close() if self.verbose: sys.stderr.write('Created: %s%s' % (fname, os.linesep))
def genPrinterService(p,v,airprint): attrs = conn.getPrinterAttributes(p) uri = urlparse.urlparse(v['printer-uri-supported']) tree = ElementTree() tree.parse(StringIO(SERVICE_TEMPLATE.replace('\n', '').replace('\r', '').replace('\t', ''))) name = tree.find('name') name.text = '%s @ %%h' % (p if not airprint else 'AirPrint ' + p) service = tree.find('service') # Add subtype to AirPrint service definitions if airprint: path = Element('subtype') path.text = "_universal._sub._ipp._tcp" service.append(path) port = service.find('port') port_no = None if hasattr(uri, 'port'): port_no = uri.port if not port_no: port_no = self.port if not port_no: port_no = cups.getPort() port.text = '%d' % port_no if hasattr(uri, 'path'): rp = uri.path else: rp = uri[2] re_match = re.match(r'^//(.*):(\d+)(/.*)', rp) if re_match: rp = re_match.group(3) #Remove leading slashes from path #TODO XXX FIXME I'm worried this will match broken urlparse #results as well (for instance if they don't include a port) #the xml would be malform'd either way rp = re.sub(r'^/+', '', rp) path = Element('txt-record') path.text = 'rp=%s' % (rp) service.append(path) # This is reported to Apple products as the location if v['printer-location'] != '': desc = Element('txt-record') desc.text = 'note=%s' % (v['printer-location']) service.append(desc) # AirPrint and Non-AirPrint specific if airprint: product = Element('txt-record') product.text = 'product=(GPL Ghostscript)' service.append(product) else: product = Element('txt-record') product.text = 'product=(%s)' %(v['printer-make-and-model']) service.append(product) # Add name ptype = Element('txt-record') ptype.text = 'ty=%s' %(v['printer-make-and-model']) # USB manufacturer and model if usbdevs.get(v['device-uri'], None) is not None: usbdev = usbdevs.get(v['device-uri']).get('device-id',None) if usbdev is not None: manuf = None model = None # Tokenize values kvs = usbdev.split(';') for kv in kvs: if not kv.find(':') > 0: continue [k,val] = kv.split(':') if k == 'MFG': manuf = val elif k == 'MODEL' or k == 'MDL': model = val if manuf is not None and model is not None: man = Element('txt-record') man.text = 'usb_MFG=%s' %(manuf) service.append(man) man = Element('txt-record') man.text = 'usb_MDL=%s' %(model) service.append(man) # Color if attrs.get('color-supported', True): extTag = Element('txt-record') extTag.text = 'Color=T' service.append(extTag) # Duplex extTag = Element('txt-record') if 'sides' in attrs.get('job-creation-attributes-supported'): extTag.text = 'Duplex=T' else: extTag.text = 'Duplex=F' service.append(extTag) # Copies if 'copies' in attrs.get('job-creation-attributes-supported'): extTag = Element('txt-record') extTag.text = 'Copies=T' service.append(extTag) # Collate if 'separate-documents-collated-copies' in attrs.get('multiple-document-handling-supported'): extTag = Element('txt-record') extTag.text = 'Collate=T' service.append(extTag) # Printer type ptype = Element('txt-record') ptype.text = 'printer-type=%s' % (hex(v['printer-type'])) service.append(ptype) # Content types supported pdl = Element('txt-record') fmts = [] defer = [] for a in attrs['document-format-supported']: if airprint and a in DOCUMENT_TYPES and DOCUMENT_TYPES[a]: fmts.append(a) else: defer.append(a) fmts = ','.join(fmts+defer) dropped = [] # TODO XXX FIXME all fields should be checked for 255 limit while len('pdl=%s' % (fmts)) >= 255: (fmts, drop) = fmts.rsplit(',', 1) dropped.append(drop) pdl.text = 'pdl=%s' % (fmts) service.append(pdl) if airprint: p = 'AirPrint-' +p filename = SERVICE_FILENAME_TEMPLATE %(p) f = open(filename, 'w') tree.write(f, pretty_print=True, xml_declaration=True, encoding="UTF-8") f.close() return filename
def generate(self): if not self.host: conn = cups.Connection() else: if not self.port: self.port = 631 conn = cups.Connection(self.host, self.port) printers = conn.getPrinters() for p, v in printers.items(): if v["printer-is-shared"]: attrs = conn.getPrinterAttributes(p) uri = urlparse.urlparse(v["printer-uri-supported"]) tree = ElementTree() tree.parse(StringIO(XML_TEMPLATE.replace("\n", "").replace("\r", "").replace("\t", ""))) name = tree.find("name") name.text = "AirPrint %s @ %%h" % (p) service = tree.find("service") port = service.find("port") port_no = None if hasattr(uri, "port"): port_no = uri.port if not port_no: port_no = self.port if not port_no: port_no = cups.getPort() port.text = "%d" % port_no if hasattr(uri, "path"): rp = uri.path else: rp = uri[2] re_match = re.match(r"^//(.*):(\d+)(/.*)", rp) if re_match: rp = re_match.group(3) # Remove leading slashes from path # TODO XXX FIXME I'm worried this will match broken urlparse # results as well (for instance if they don't include a port) # the xml would be malform'd either way rp = re.sub(r"^/+", "", rp) path = Element("txt-record") path.text = "rp=%s" % (rp) service.append(path) desc = Element("txt-record") desc.text = "note=%s" % (v["printer-info"]) service.append(desc) product = Element("txt-record") product.text = "product=(GPL Ghostscript)" service.append(product) state = Element("txt-record") state.text = "printer-state=%s" % (v["printer-state"]) service.append(state) ptype = Element("txt-record") ptype.text = "printer-type=%s" % (hex(v["printer-type"])) service.append(ptype) pdl = Element("txt-record") fmts = [] defer = [] for a in attrs["document-format-supported"]: if a in DOCUMENT_TYPES: if DOCUMENT_TYPES[a]: fmts.append(a) else: defer.append(a) fmts = ",".join(fmts + defer) dropped = [] # TODO XXX FIXME all fields should be checked for 255 limit while len("pdl=%s" % (fmts)) >= 255: (fmts, drop) = fmts.rsplit(",", 1) dropped.append(drop) if len(dropped) and self.verbose: sys.stderr.write("%s Losing support for: %s%s" % (p, ",".join(dropped), os.linesep)) pdl.text = "pdl=%s" % (fmts) service.append(pdl) if self.adminurl: admin = Element("txt-record") admin.text = "adminurl=%s" % (v["printer-uri-supported"]) service.append(admin) fname = "%s%s.service" % (self.prefix, p) if self.directory: fname = os.path.join(self.directory, fname) f = open(fname, "w") if etree: tree.write(f, pretty_print=True, xml_declaration=True, encoding="UTF-8") else: xmlstr = tostring(tree.getroot()) doc = parseString(xmlstr) dt = minidom.getDOMImplementation("").createDocumentType("service-group", None, "avahi-service.dtd") doc.insertBefore(dt, doc.documentElement) doc.writexml(f) f.close() if self.verbose: sys.stderr.write("Created: %s%s" % (fname, os.linesep))
def generate(self): if not self.host: conn = cups.Connection() else: if not self.port: self.port = 631 conn = cups.Connection(self.host, self.port) printers = conn.getPrinters() for p, v in printers.items(): if v['printer-is-shared']: #pprint.pprint(v) # v contains keys: device-uri printer-info printer-is-shared printer-location printer-make-and-model printer-state printer-state-message printer-state-reasons printer-type printer-uri-supported attrs = conn.getPrinterAttributes(p) #pprint.pprint(attrs) uri = urlparse.urlparse(v['printer-uri-supported']) output = '_cups._sub._ipp._tcp\t\tIN\tPTR\t' + p + '._ipp._tcp\n' output += '_universal._sub._ipp._tcp\tIN\tPTR\t' + p + '._ipp._tcp\n' output += '_cups._sub._ipps._tcp\t\tIN\tPTR\t' + p + '._ipp._tcp\n' output += '_universal._sub._ipps._tcp\tIN\tPTR\t' + p + '._ipp._tcp\n' output += p + '._ipp._tcp\t\tIN\tSRV\t0 0 631 printserver\n' output += p + '._ipp._tcp\t\tIN\tTXT\t(' output += ' "txtvers=1"' output += ' "qtotal=1"' output += ' "rp=printers/' + p + '"' output += ' "adminurl=http://' + self.host + ':631/printers/' + p + '"' output += ' "ty=' + v['printer-info'] + '"' #s=v['printer-info'] #product = s[s.find("(")+1:s.find(")")] #output += ' "product=(' + product + ')"' output += ' "product=(GPL Ghostscript)"' output += ' "note=' + v['printer-location'] + '"' output += ' "transparent=t" "copies=t" "duplex=t" "color=f"' output += ' "pdl=application/octet-stream,application/pdf,application/postscript,image/jpeg,image/png,image/urf"' output += ' "URF=W8,SRGB24,CP1,RS600" )\n\n' print output tree = ElementTree() tree.parse( StringIO( XML_TEMPLATE.replace('\n', '').replace('\r', '').replace('\t', ''))) name = tree.find('name') name.text = 'AirPrint %s @ %%h' % (p) service = tree.find('service') port = service.find('port') port_no = None if hasattr(uri, 'port'): port_no = uri.port if not port_no: port_no = self.port if not port_no: port_no = cups.getPort() port.text = '%d' % port_no if hasattr(uri, 'path'): rp = uri.path else: rp = uri[2] re_match = re.match(r'^//(.*):(\d+)(/.*)', rp) if re_match: rp = re_match.group(3) #Remove leading slashes from path #TODO XXX FIXME I'm worried this will match broken urlparse #results as well (for instance if they don't include a port) #the xml would be malform'd either way rp = re.sub(r'^/+', '', rp) path = Element('txt-record') path.text = 'rp=%s' % (rp) service.append(path) desc = Element('txt-record') desc.text = 'note=%s' % (v['printer-info']) service.append(desc) product = Element('txt-record') product.text = 'product=(GPL Ghostscript)' service.append(product) state = Element('txt-record') state.text = 'printer-state=%s' % (v['printer-state']) service.append(state) ptype = Element('txt-record') ptype.text = 'printer-type=%s' % (hex(v['printer-type'])) service.append(ptype) pdl = Element('txt-record') fmts = [] defer = [] for a in attrs['document-format-supported']: if a in DOCUMENT_TYPES: if DOCUMENT_TYPES[a]: fmts.append(a) else: defer.append(a) if 'image/urf' not in fmts: sys.stderr.write( 'image/urf is not in mime types, %s may not be available on ios6 (see https://github.com/tjfontaine/airprint-generate/issues/5)%s' % (p, os.linesep)) fmts = ','.join(fmts + defer) # sys.stderr.write('formats %s' % (fmts)) dropped = [] # TODO XXX FIXME all fields should be checked for 255 limit while len('pdl=%s' % (fmts)) >= 255: (fmts, drop) = fmts.rsplit(',', 1) dropped.append(drop) if len(dropped) and self.verbose: sys.stderr.write('%s Losing support for: %s%s' % (p, ','.join(dropped), os.linesep)) pdl.text = 'pdl=%s' % (fmts) service.append(pdl) if self.adminurl: admin = Element('txt-record') admin.text = 'adminurl=%s' % (v['printer-uri-supported']) service.append(admin) fname = '%s%s.service' % (self.prefix, p) if self.directory: fname = os.path.join(self.directory, fname) #f = open(fname, 'w') #if etree: # tree.write(f, pretty_print=True, xml_declaration=True, encoding="UTF-8") #else: # xmlstr = tostring(tree.getroot()) # doc = parseString(xmlstr) # dt= minidom.getDOMImplementation('').createDocumentType('service-group', None, 'avahi-service.dtd') # doc.insertBefore(dt, doc.documentElement) # doc.writexml(f) #f.close() if self.verbose: sys.stderr.write('Created: %s%s' % (fname, os.linesep))
def generate(self): collected_printers = list() # Collect shared printers from CUPS if applicable if self.usecups: if self.verbose: sys.stderr.write('Collecting shared printers from CUPS%s' % os.linesep) if not self.host: conn = cups.Connection() else: if not self.port: self.port = 631 conn = cups.Connection(self.host, self.port) printers = conn.getPrinters() for p, v in printers.items(): if v['printer-is-shared']: attrs = conn.getPrinterAttributes(p) uri = urlparse.urlparse(v['printer-uri-supported']) port_no = None if hasattr(uri, 'port'): port_no = uri.port if not port_no: port_no = self.port if not port_no: port_no = cups.getPort() if hasattr(uri, 'path'): rp = uri.path else: rp = uri[2] re_match = re.match(r'^//(.*):(\d+)(/.*)', rp) if re_match: rp = re_match.group(3) #Remove leading slashes from path #TODO XXX FIXME I'm worried this will match broken urlparse #results as well (for instance if they don't include a port) #the xml would be malform'd either way rp = re.sub(r'^/+', '', rp) pdl = Element('txt-record') fmts = [] defer = [] for a in attrs['document-format-supported']: if a in DOCUMENT_TYPES: if DOCUMENT_TYPES[a]: fmts.append(a) else: defer.append(a) if 'image/urf' not in fmts: sys.stderr.write('image/urf is not in mime types, %s may not be available on ios6 (see https://github.com/tjfontaine/airprint-generate/issues/5)%s' % (p, os.linesep)) fmts = ','.join(fmts+defer) dropped = [] # TODO XXX FIXME all fields should be checked for 255 limit while len('pdl=%s' % (fmts)) >= 255: (fmts, drop) = fmts.rsplit(',', 1) dropped.append(drop) if len(dropped) and self.verbose: sys.stderr.write('%s Losing support for: %s%s' % (p, ','.join(dropped), os.linesep)) collected_printers.append( { 'SOURCE' : 'CUPS', 'name' : p, 'host' : None, # Could/should use self.host, but would break old behaviour 'address' : None, 'port' : port_no, 'domain' : 'local', 'txt' : { 'txtvers' : '1', 'qtotal' : '1', 'Transparent' : 'T', 'URF' : 'none', 'rp' : rp, 'note' : v['printer-info'], 'product' : '(GPL Ghostscript)', 'printer-state' : v['printer-state'], 'printer-type' : v['printer-type'], 'adminurl' : v['printer-uri-supported'], 'pdl' : fmts, } } ) # Collect networked printers using DNS-SD if applicable if (self.useavahi): if self.verbose: sys.stderr.write('Collecting networked printers using DNS-SD%s' % os.linesep) finder = avahisearch.AvahiPrinterFinder(verbose=self.verbose) for p in finder.Search(): p['SOURCE'] = 'DNS-SD' collected_printers.append(p) # Produce a .service file for each printer found for p in collected_printers: self.produce_settings_file(p)
def run(self): if self.host == None: self.host = cups.getServer() if self.port == None: self.port = cups.getPort() if self._encryption == None: self._encryption = cups.getEncryption() if self.user: cups.setUser(self.user) else: self.user = cups.getUser() cups.setPasswordCB2(self._auth) try: conn = cups.Connection(host=self.host, port=self.port, encryption=self._encryption) self._reply(None) except RuntimeError as e: conn = None self._error(e) while True: # Wait to find out what operation to try. debugprint("Awaiting further instructions") self.idle = self._queue.empty() item = self._queue.get() debugprint("Next task: %s" % repr(item)) if item == None: # Our signal to quit. self._queue.task_done() break elif self._destroyed: # Just mark all tasks done self._queue.task_done() continue self.idle = False (fn, args, kwds, rh, eh, ah) = item if rh != False: self._reply_handler = rh if eh != False: self._error_handler = eh if ah != False: self._auth_handler = ah if fn == True: # Our signal to change user and reconnect. self.user = args[0] cups.setUser(self.user) debugprint("Set user=%s; reconnecting..." % self.user) cups.setPasswordCB2(self._auth) try: conn = cups.Connection(host=self.host, port=self.port, encryption=self._encryption) debugprint("...reconnected") self._queue.task_done() self._reply(None) except RuntimeError as e: debugprint("...failed") self._queue.task_done() self._error(e) continue # Normal IPP operation. Try to perform it. try: debugprint("Call %s" % fn) result = fn(conn, *args, **kwds) if fn == cups.Connection.adminGetServerSettings.__call__: # Special case for a rubbish bit of API. if result == {}: # Authentication failed, but we aren't told that. raise cups.IPPError(cups.IPP_NOT_AUTHORIZED, "") debugprint("...success") self._reply(result) except Exception as e: debugprint("...failure (%s)" % repr(e)) self._error(e) self._queue.task_done() debugprint("Thread exiting") del self._conn # already destroyed del self._reply_handler del self._error_handler del self._auth_handler del self._queue del self._auth_queue del conn cups.setPasswordCB2(None)
def run (self): if self.host == None: self.host = cups.getServer () if self.port == None: self.port = cups.getPort () if self._encryption == None: self._encryption = cups.getEncryption () if self.user: cups.setUser (self.user) else: self.user = cups.getUser () cups.setPasswordCB2 (self._auth) try: conn = cups.Connection (host=self.host, port=self.port, encryption=self._encryption) self._reply (None) except RuntimeError as e: conn = None self._error (e) while True: # Wait to find out what operation to try. debugprint ("Awaiting further instructions") self.idle = self._queue.empty () item = self._queue.get () debugprint ("Next task: %s" % repr (item)) if item == None: # Our signal to quit. self._queue.task_done () break self.idle = False (fn, args, kwds, rh, eh, ah) = item if rh != False: self._reply_handler = rh if eh != False: self._error_handler = eh if ah != False: self._auth_handler = ah if fn == True: # Our signal to change user and reconnect. self.user = args[0] cups.setUser (self.user) debugprint ("Set user=%s; reconnecting..." % self.user) cups.setPasswordCB2 (self._auth) try: conn = cups.Connection (host=self.host, port=self.port, encryption=self._encryption) debugprint ("...reconnected") self._queue.task_done () self._reply (None) except RuntimeError as e: debugprint ("...failed") self._queue.task_done () self._error (e) continue # Normal IPP operation. Try to perform it. try: debugprint ("Call %s" % fn) result = fn (conn, *args, **kwds) if fn == cups.Connection.adminGetServerSettings.__call__: # Special case for a rubbish bit of API. if result == {}: # Authentication failed, but we aren't told that. raise cups.IPPError (cups.IPP_NOT_AUTHORIZED, '') debugprint ("...success") self._reply (result) except Exception as e: debugprint ("...failure (%s)" % repr (e)) self._error (e) self._queue.task_done () debugprint ("Thread exiting") self._destroyed = True del self._conn # already destroyed del self._reply_handler del self._error_handler del self._auth_handler del self._queue del self._auth_queue del conn cups.setPasswordCB2 (None)
def generate(self): if not self.host: conn = cups.Connection() else: if not self.port: self.port = 631 conn = cups.Connection(self.host, self.port) printers = conn.getPrinters() for p, v in printers.items(): if v['printer-is-shared']: attrs = conn.getPrinterAttributes(p) uri = urlparse.urlparse(v['printer-uri-supported']) tree = ElementTree() tree.parse(StringIO(XML_TEMPLATE.replace('\n', '').replace('\r', '').replace('\t', ''))) name = tree.find('name') name.text = 'AirPrint %s @ %%h' % (p) service = tree.find('service') port = service.find('port') port_no = None if hasattr(uri, 'port'): port_no = uri.port if not port_no: port_no = self.port if not port_no: port_no = cups.getPort() port.text = '%d' % port_no if hasattr(uri, 'path'): rp = uri.path else: rp = uri[2] re_match = re.match(r'^//(.*):(\d+)(/.*)', rp) if re_match: rp = re_match.group(3) #Remove leading slashes from path #TODO XXX FIXME I'm worried this will match broken urlparse #results as well (for instance if they don't include a port) #the xml would be malform'd either way rp = re.sub(r'^/+', '', rp) path = Element('txt-record') path.text = 'rp=%s' % (rp) service.append(path) desc = Element('txt-record') desc.text = 'note=%s' % (v['printer-info']) service.append(desc) product = Element('txt-record') product.text = 'product=(GPL Ghostscript)' service.append(product) state = Element('txt-record') state.text = 'printer-state=%s' % (v['printer-state']) service.append(state) ptype = Element('txt-record') ptype.text = 'printer-type=%s' % (hex(v['printer-type'])) service.append(ptype) pdl = Element('txt-record') fmts = [] defer = [] for a in attrs['document-format-supported']: if a in DOCUMENT_TYPES: if DOCUMENT_TYPES[a]: fmts.append(a) else: defer.append(a) if 'image/urf' not in fmts: sys.stderr.write('image/urf is not in mime types, %s may not be available on ios6 (see https://github.com/tjfontaine/airprint-generate/issues/5)%s' % (p, os.linesep)) fmts = ','.join(fmts+defer) dropped = [] # TODO XXX FIXME all fields should be checked for 255 limit while len('pdl=%s' % (fmts)) >= 255: (fmts, drop) = fmts.rsplit(',', 1) dropped.append(drop) if len(dropped) and self.verbose: sys.stderr.write('%s Losing support for: %s%s' % (p, ','.join(dropped), os.linesep)) pdl.text = 'pdl=%s' % (fmts) service.append(pdl) if self.adminurl: admin = Element('txt-record') admin.text = 'adminurl=%s' % (v['printer-uri-supported']) service.append(admin) fname = '%s%s.service' % (self.prefix, p) if self.directory: fname = os.path.join(self.directory, fname) f = open(fname, 'w') if etree: tree.write(f, pretty_print=True, xml_declaration=True, encoding="UTF-8") else: xmlstr = tostring(tree.getroot()) doc = parseString(xmlstr) dt= minidom.getDOMImplementation('').createDocumentType('service-group', None, 'avahi-service.dtd') doc.insertBefore(dt, doc.documentElement) doc.writexml(f) f.close() if self.verbose: sys.stderr.write('Created: %s%s' % (fname, os.linesep))