#!/usr/bin/python3 ## Copyright (C) 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. ## Authors: ## Tim Waugh <twaugh@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. import asyncconn import cups from gi.repository import GLib from gi.repository import Gdk from gi.repository import Gtk import os from shutil import copyfileobj from tempfile import NamedTemporaryFile from debug import * cups.require ("1.9.50") class PPDCache: def __init__ (self, host=None, port=None, encryption=None): self._cups = None self._exc = None self._cache = dict() self._modtimes = dict() self._host = host self._port = port self._encryption = encryption self._queued = list() self._connecting = False debugprint ("+%s" % self) def __del__ (self): debugprint ("-%s" % self) if self._cups: self._cups.destroy () def fetch_ppd (self, name, callback, check_uptodate=True): if check_uptodate and name in self._modtimes: # We have getPPD3 so we can check whether the PPD is up to # date. debugprint ("%s: check if %s is up to date" % (self, name)) self._cups.getPPD3 (name, modtime=self._modtimes[name], reply_handler=lambda c, r: self._got_ppd3 (c, name, r, callback), error_handler=lambda c, r: self._got_ppd3 (c, name, r, callback)) return try: f = self._cache[name] except RuntimeError as e: self._schedule_callback (callback, name, None, e) return except KeyError: if not self._cups: self._queued.append ((name, callback)) if not self._connecting: self._connect () return debugprint ("%s: fetch PPD for %s" % (self, name)) self._cups.getPPD3 (name, reply_handler=lambda c, r: self._got_ppd3 (c, name, r, callback), error_handler=lambda c, r: self._got_ppd3 (c, name, r, callback)) return # Copy from our file object to a new temporary file, create a # PPD object from it, then remove the file. This way we don't # leave temporary files around even though we are caching... f.seek (0) with NamedTemporaryFile () as tmpf: copyfileobj (f, tmpf) try: ppd = cups.PPD (tmpf.file) self._schedule_callback (callback, name, ppd, None) except Exception as e: self._schedule_callback (callback, name, None, e) def _connect (self, callback=None): self._connecting = True asyncconn.Connection (host=self._host, port=self._port, encryption=self._encryption, reply_handler=self._connected, error_handler=self._connected) def _got_ppd (self, connection, name, result, callback): if isinstance (result, Exception): self._schedule_callback (callback, name, none, result) else: # Store an open file object, then remove the actual file. # This way we don't leave temporary files around. try: self._cache[name] = open (result, "rb") debugprint ("%s: caching %s (fd %d)" % (self, result, self._cache[name].fileno())) os.unlink (result) except IOError as exc: self._schedule_callback (callback, name, None, exc) return self.fetch_ppd (name, callback) def _got_ppd3 (self, connection, name, result, callback): (status, modtime, filename) = result if status in [cups.HTTP_OK, cups.HTTP_NOT_MODIFIED]: if status == cups.HTTP_NOT_MODIFIED: # The file is no newer than the one we already have. # CUPS before 1.5.3 created a temporary file in error # in this situation (STR #4018) so remove that. try: os.unlink (filename) except OSError: pass elif status == cups.HTTP_OK: # Our version of the file was older. Cache the new version. # Store an open file object, then remove the actual # file. This way we don't leave temporary files # around. try: self._cache[name] = open (filename, "rb") debugprint ("%s: caching %s (fd %d) " "(%s) - %s" % (self, filename, self._cache[name].fileno (), modtime, status)) os.unlink (filename) self._modtimes[name] = modtime except IOError as exc: # File disappeared? debugprint ("%s: file %s disappeared? Unable to cache it" % (self, filename)) self._schedule_callback (callback, name, None, exc) return # Now fetch it from our own cache. self.fetch_ppd (name, callback, check_uptodate=False) else: self._schedule_callback (callback, name, None, cups.HTTPError (status)) def _connected (self, connection, exc): self._connecting = False if isinstance (exc, Exception): self._cups = None self._exc = exc else: self._cups = connection queued = self._queued self._queued = list() for name, callback in queued: self.fetch_ppd (name, callback) def _schedule_callback (self, callback, name, result, exc): def cb_func (callback, name, result, exc): Gdk.threads_enter () callback (name, result, exc) Gdk.threads_leave () return False GLib.idle_add (cb_func, callback, name, result, exc) if __name__ == "__main__": import sys from debug import * from gi.repository import GObject set_debugging (True) Gdk.threads_init () loop = GObject.MainLoop () def signal (name, result, exc): debugprint ("**** %s" % name) debugprint (repr (result)) debugprint (repr (exc)) c = cups.Connection () printers = c.getPrinters () del c cache = PPDCache () p = None for p in printers: cache.fetch_ppd (p, signal) if p: GLib.timeout_add_seconds (1, cache.fetch_ppd, p, signal) GLib.timeout_add_seconds (5, loop.quit) loop.run ()
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
__pycache__ | Folder | 0755 |
|
|
icons | Folder | 0755 |
|
|
troubleshoot | Folder | 0755 |
|
|
ui | Folder | 0755 |
|
|
xml | Folder | 0755 |
|
|
HIG.py | File | 57 B | 0644 |
|
OpenPrintingRequest.py | File | 6.81 KB | 0644 |
|
PhysicalDevice.py | File | 16.19 KB | 0644 |
|
SearchCriterion.py | File | 1.32 KB | 0644 |
|
ToolbarSearchEntry.py | File | 6.96 KB | 0644 |
|
applet.py | File | 16.39 KB | 0755 |
|
asyncconn.py | File | 9.6 KB | 0644 |
|
asyncipp.py | File | 26.13 KB | 0644 |
|
asyncpk1.py | File | 25.85 KB | 0644 |
|
authconn.py | File | 18.85 KB | 0644 |
|
check-device-ids.py | File | 11.16 KB | 0755 |
|
config.py | File | 1.19 KB | 0644 |
|
cupspk.py | File | 27.48 KB | 0644 |
|
debug.py | File | 1.62 KB | 0644 |
|
dnssdresolve.py | File | 4.66 KB | 0644 |
|
errordialogs.py | File | 3.12 KB | 0644 |
|
firewallsettings.py | File | 9.38 KB | 0644 |
|
gtkinklevel.py | File | 4.71 KB | 0644 |
|
gui.py | File | 2.13 KB | 0644 |
|
install-printerdriver.py | File | 5.34 KB | 0755 |
|
installpackage.py | File | 2.2 KB | 0644 |
|
jobviewer.py | File | 97.36 KB | 0644 |
|
killtimer.py | File | 2.08 KB | 0644 |
|
monitor.py | File | 32.36 KB | 0644 |
|
newprinter.py | File | 178.53 KB | 0644 |
|
options.py | File | 18.97 KB | 0644 |
|
optionwidgets.py | File | 8.89 KB | 0644 |
|
ppdcache.py | File | 7.57 KB | 0644 |
|
ppdippstr.py | File | 16.19 KB | 0644 |
|
ppdsloader.py | File | 10.98 KB | 0644 |
|
printerproperties.py | File | 78.11 KB | 0644 |
|
probe_printer.py | File | 15.82 KB | 0644 |
|
pysmb.py | File | 6.81 KB | 0755 |
|
scp-dbus-service.py | File | 22.22 KB | 0755 |
|
serversettings.py | File | 21.83 KB | 0644 |
|
smburi.py | File | 3.22 KB | 0644 |
|
statereason.py | File | 6.83 KB | 0644 |
|
system-config-printer.py | File | 86.21 KB | 0755 |
|
timedops.py | File | 8.21 KB | 0644 |
|
userdefault.py | File | 6.2 KB | 0644 |
|