Viewing file: cupspk.py (27.33 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# vim: set ts=4 sw=4 et: coding=UTF-8 # # Copyright (C) 2008 Novell, Inc. # Copyright (C) 2008, 2009, 2010 Red Hat, Inc. # Copyright (C) 2008, 2009, 2010 Tim Waugh <twaugh@redhat.com> # # Authors: Vincent Untz # # 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
# check FIXME/TODO here # check FIXME/TODO in cups-pk-helper # define fine-grained policy (more than one level of permission) # add missing methods
import os import sys
import tempfile
import cups import dbus import gtk from debug import debugprint
from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True)
CUPS_PK_NAME = 'org.opensuse.CupsPkHelper.Mechanism' CUPS_PK_PATH = '/' CUPS_PK_IFACE = 'org.opensuse.CupsPkHelper.Mechanism'
CUPS_PK_NEED_AUTH = 'org.opensuse.CupsPkHelper.Mechanism.NotPrivileged'
# we can't subclass cups.Connection, even when adding # Py_TPFLAGS_BASETYPE to cupsconnection.c # So we'll hack this... class Connection: def __init__(self, host, port, encryption): self._parent = None
try: self._session_bus = dbus.SessionBus() self._system_bus = dbus.SystemBus() except dbus.exceptions.DBusException: # One or other bus not running. self._session_bus = self._system_bus = None
self._connection = cups.Connection(host=host, port=port, encryption=encryption)
self._hack_subclass()
def _hack_subclass(self): # here's how to subclass without really subclassing. Just provide # the same methods methodtype = type(self._connection.getPrinters) for fname in dir(self._connection): if fname[0] == '_': continue fn = getattr(self._connection, fname) if type(fn) != methodtype: continue if not hasattr(self, fname): setattr(self, fname, fn.__call__)
def set_parent(self, parent): self._parent = parent
def _get_cups_pk(self): try: object = self._system_bus.get_object(CUPS_PK_NAME, CUPS_PK_PATH) return dbus.Interface(object, CUPS_PK_IFACE) except dbus.exceptions.DBusException: # Failed to get object or interface. return None except AttributeError: # No system D-Bus return None
def _call_with_pk_and_fallback(self, use_fallback, pk_function_name, pk_args, fallback_function, *args, **kwds): pk_function = None
if not use_fallback: cups_pk = self._get_cups_pk() if cups_pk: try: pk_function = cups_pk.get_dbus_method(pk_function_name) except dbus.exceptions.DBusException: pass
if use_fallback or not pk_function: return fallback_function(*args, **kwds)
pk_retval = 'PolicyKit communication issue'
while True: try: # FIXME: async call or not? pk_retval = pk_function(*pk_args)
# if the PK call has more than one return values, we pop the # first one as the error message if type(pk_retval) == tuple: retval = pk_retval[1:] # if there's no error, then we can safely return what we # got if pk_retval[0] == '': # if there's only one item left in the tuple, we don't # want to return the tuple, but the item if len(retval) == 1: return retval[0] else: return retval break except dbus.exceptions.DBusException, e: if e.get_dbus_name() == CUPS_PK_NEED_AUTH: raise cups.IPPError(cups.IPP_NOT_AUTHORIZED, 'pkcancel')
break
# The PolicyKit call did not work (either a PK-error and we got a dbus # exception that wasn't handled, or an error in the mechanism itself) if pk_retval != '': debugprint ('PolicyKit call to %s did not work: %s' % (pk_function_name, pk_retval)) return fallback_function(*args, **kwds)
def _args_to_tuple(self, types, *args): retval = [ False ]
if len(types) != len(args): retval[0] = True # We do this to have the right length for the returned value retval.extend(types) return tuple(types)
exception = False
for i in range(len(types)): if type(args[i]) != types[i]: if types[i] == str and type(args[i]) == unicode: # we accept a mix between unicode and str pass elif types[i] == str and type(args[i]) == int: # we accept a mix between int and str retval.append(str(args[i])) continue elif types[i] == str and type(args[i]) == float: # we accept a mix between float and str retval.append(str(args[i])) continue elif types[i] == str and type(args[i]) == bool: # we accept a mix between bool and str retval.append(str(args[i])) continue elif types[i] == str and args[i] == None: # None is an empty string for dbus retval.append('') continue elif types[i] == list and type(args[i]) == tuple: # we accept a mix between list and tuple retval.append(list(args[i])) continue elif types[i] == list and args[i] == None: # None is an empty list retval.append([]) continue else: exception = True retval.append(args[i])
retval[0] = exception
return tuple(retval)
def _kwds_to_vars(self, names, **kwds): ret = []
for name in names: if kwds.has_key(name): ret.append(kwds[name]) else: ret.append('')
return tuple(ret)
# getPrinters # getDests # getClasses # getPPDs # getServerPPD # getDocument
def getDevices(self, *args, **kwds): use_pycups = False
limit = 0 include_schemes = [] exclude_schemes = [] timeout = 0
if len(args) == 4: (use_pycups, limit, include_schemes, exclude_schemes, timeout) = self._args_to_tuple([int, str, str, int], *args) else: if kwds.has_key('timeout'): timeout = kwds['timeout']
if kwds.has_key('limit'): limit = kwds['limit']
if kwds.has_key('include_schemes'): include_schemes = kwds['include_schemes']
if kwds.has_key('exclude_schemes'): exclude_schemes = kwds['exclude_schemes']
pk_args = (timeout, limit, include_schemes, exclude_schemes)
try: result = self._call_with_pk_and_fallback(use_pycups, 'DevicesGet', pk_args, self._connection.getDevices, *args, **kwds) except TypeError: debugprint ("DevicesGet API exception; using old signature") if kwds.has_key ('timeout'): use_pycups = True
# Convert from list to string if len (include_schemes) > 0: include_schemes = reduce (lambda x, y: x + "," + y, include_schemes) else: include_schemes = ""
if len (exclude_schemes) > 0: exclude_schemes = reduce (lambda x, y: x + "," + y, exclude_schemes) else: exclude_schemes = ""
pk_args = (limit, include_schemes, exclude_schemes) result = self._call_with_pk_and_fallback(use_pycups, 'DevicesGet', pk_args, self._connection.getDevices, *args, **kwds)
# return 'result' if fallback was called if len (result.keys()) > 0 and type (result[result.keys()[0]]) == dict: return result
result_str = {} if result != None: for i in result.keys(): if type(i) == dbus.String: result_str[str(i)] = str(result[i]) else: result_str[i] = result[i]
# cups-pk-helper returns all devices in one dictionary. # Keys of different devices are distinguished by ':n' postfix.
devices = {} n = 0 postfix = ':' + str (n) device_keys = [x for x in result_str.keys() if x.endswith(postfix)] while len (device_keys) > 0:
device_uri = None device_dict = {} for i in device_keys: key = i[:len(i) - len(postfix)] if key != 'device-uri': device_dict[key] = result_str[i] else: device_uri = result_str[i]
if device_uri != None: devices[device_uri] = device_dict
n += 1 postfix = ':' + str (n) device_keys = [x for x in result_str.keys() if x.endswith(postfix)]
return devices
# getJobs # getJobAttributes
def cancelJob(self, *args, **kwds): (use_pycups, jobid) = self._args_to_tuple([int], *args) pk_args = (jobid, )
self._call_with_pk_and_fallback(use_pycups, 'JobCancel', pk_args, self._connection.cancelJob, *args, **kwds)
# cancelAllJobs # authenticateJob def setJobHoldUntil(self, *args, **kwds): (use_pycups, jobid, job_hold_until) = self._args_to_tuple([int, str], *args) pk_args = (jobid, job_hold_until, )
self._call_with_pk_and_fallback(use_pycups, 'JobSetHoldUntil', pk_args, self._connection.setJobHoldUntil, *args, **kwds)
def restartJob(self, *args, **kwds): (use_pycups, jobid) = self._args_to_tuple([int], *args) pk_args = (jobid, ) self._call_with_pk_and_fallback(use_pycups, 'JobRestart', pk_args, self._connection.restartJob, *args, **kwds)
def getFile(self, *args, **kwds): ''' Keeping this as an alternative for the code. We don't use it because it's not possible to know if the call was a PK-one (and so we push the content of a temporary filename to fd or file) or a non-PK-one (in which case nothing should be done).
filename = None fd = None file = None if use_pycups: if len(kwds) != 1: use_pycups = True elif kwds.has_key('filename'): filename = kwds['filename'] elif kwds.has_key('fd'): fd = kwds['fd'] elif kwds.has_key('file'): file = kwds['file'] else: use_pycups = True
if fd or file: '''
file_object = None fd = None if len(args) == 2: (use_pycups, resource, filename) = self._args_to_tuple([str, str], *args) else: (use_pycups, resource) = self._args_to_tuple([str], *args) if kwds.has_key('filename'): filename = kwds['filename'] elif kwds.has_key('fd'): fd = kwds['fd'] elif kwds.has_key('file'): file_object = kwds['file'] else: if not use_pycups: raise TypeError() else: filename = None
if (not use_pycups) and (fd != None or file_object != None): # Create the temporary file in /tmp to ensure that # cups-pk-helper-mechanism is able to write to it. (tmpfd, tmpfname) = tempfile.mkstemp(dir="/tmp") os.close (tmpfd)
pk_args = (resource, tmpfname) self._call_with_pk_and_fallback(use_pycups, 'FileGet', pk_args, self._connection.getFile, *args, **kwds)
tmpfd = os.open (tmpfname, os.O_RDONLY) tmpfile = os.fdopen (tmpfd, 'r') tmpfile.seek (0)
if fd != None: os.lseek (fd, 0, os.SEEK_SET) line = tmpfile.readline() while line != '': os.write (fd, line) line = tmpfile.readline() else: file_object.seek (0) line = tmpfile.readline() while line != '': file_object.write (line) line = tmpfile.readline()
tmpfile.close () os.remove (tmpfname) else: pk_args = (resource, filename)
self._call_with_pk_and_fallback(use_pycups, 'FileGet', pk_args, self._connection.getFile, *args, **kwds)
def putFile(self, *args, **kwds): if len(args) == 2: (use_pycups, resource, filename) = self._args_to_tuple([str, str], *args) else: (use_pycups, resource) = self._args_to_tuple([str], *args) if kwds.has_key('filename'): filename = kwds['filename'] elif kwds.has_key('fd'): fd = kwds['fd'] elif kwds.has_key('file'): file_object = kwds['file'] else: if not use_pycups: raise TypeError() else: filename = None
if (not use_pycups) and (fd != None or file_object != None): (tmpfd, tmpfname) = tempfile.mkstemp() os.lseek (tmpfd, 0, os.SEEK_SET)
if fd != None: os.lseek (fd, 0, os.SEEK_SET) buf = os.read (fd, 512) while buf != '': os.write (tmpfd, buf) buf = os.read (fd, 512) else: file_object.seek (0) line = file_object.readline () while line != '': os.write (tmpfd, line) line = file_object.readline ()
os.close (tmpfd)
pk_args = (resource, tmpfname)
self._call_with_pk_and_fallback(use_pycups, 'FilePut', pk_args, self._connection.putFile, *args, **kwds)
os.remove (tmpfname) else:
pk_args = (resource, filename)
self._call_with_pk_and_fallback(use_pycups, 'FilePut', pk_args, self._connection.putFile, *args, **kwds)
def addPrinter(self, *args, **kwds): (use_pycups, name) = self._args_to_tuple([str], *args) (filename, ppdname, info, location, device, ppd) = self._kwds_to_vars(['filename', 'ppdname', 'info', 'location', 'device', 'ppd'], **kwds)
need_unlink = False if not ppdname and not filename and ppd: (fd, filename) = tempfile.mkstemp () ppd.writeFd(fd) os.close(fd) need_unlink = True
if filename and not ppdname: pk_args = (name, device, filename, info, location) self._call_with_pk_and_fallback(use_pycups, 'PrinterAddWithPpdFile', pk_args, self._connection.addPrinter, *args, **kwds) if need_unlink: os.unlink(filename) else: pk_args = (name, device, ppdname, info, location) self._call_with_pk_and_fallback(use_pycups, 'PrinterAdd', pk_args, self._connection.addPrinter, *args, **kwds)
def setPrinterDevice(self, *args, **kwds): (use_pycups, name, device) = self._args_to_tuple([str, str], *args) pk_args = (name, device)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetDevice', pk_args, self._connection.setPrinterDevice, *args, **kwds)
def setPrinterInfo(self, *args, **kwds): (use_pycups, name, info) = self._args_to_tuple([str, str], *args) pk_args = (name, info)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetInfo', pk_args, self._connection.setPrinterInfo, *args, **kwds)
def setPrinterLocation(self, *args, **kwds): (use_pycups, name, location) = self._args_to_tuple([str, str], *args) pk_args = (name, location)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetLocation', pk_args, self._connection.setPrinterLocation, *args, **kwds)
def setPrinterShared(self, *args, **kwds): (use_pycups, name, shared) = self._args_to_tuple([str, bool], *args) pk_args = (name, shared)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetShared', pk_args, self._connection.setPrinterShared, *args, **kwds)
def setPrinterJobSheets(self, *args, **kwds): (use_pycups, name, start, end) = self._args_to_tuple([str, str, str], *args) pk_args = (name, start, end)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetJobSheets', pk_args, self._connection.setPrinterJobSheets, *args, **kwds)
def setPrinterErrorPolicy(self, *args, **kwds): (use_pycups, name, policy) = self._args_to_tuple([str, str], *args) pk_args = (name, policy)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetErrorPolicy', pk_args, self._connection.setPrinterErrorPolicy, *args, **kwds)
def setPrinterOpPolicy(self, *args, **kwds): (use_pycups, name, policy) = self._args_to_tuple([str, str], *args) pk_args = (name, policy)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetOpPolicy', pk_args, self._connection.setPrinterOpPolicy, *args, **kwds)
def setPrinterUsersAllowed(self, *args, **kwds): (use_pycups, name, users) = self._args_to_tuple([str, list], *args) pk_args = (name, users)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetUsersAllowed', pk_args, self._connection.setPrinterUsersAllowed, *args, **kwds)
def setPrinterUsersDenied(self, *args, **kwds): (use_pycups, name, users) = self._args_to_tuple([str, list], *args) pk_args = (name, users)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetUsersDenied', pk_args, self._connection.setPrinterUsersDenied, *args, **kwds)
def addPrinterOptionDefault(self, *args, **kwds): # The values can be either a single string, or a list of strings, so # we have to handle this (use_pycups, name, option, value) = self._args_to_tuple([str, str, str], *args) # success if not use_pycups: values = (value,) # okay, maybe we directly have values else: (use_pycups, name, option, values) = self._args_to_tuple([str, str, list], *args) pk_args = (name, option, values)
self._call_with_pk_and_fallback(use_pycups, 'PrinterAddOptionDefault', pk_args, self._connection.addPrinterOptionDefault, *args, **kwds)
def deletePrinterOptionDefault(self, *args, **kwds): (use_pycups, name, option) = self._args_to_tuple([str, str], *args) pk_args = (name, option)
self._call_with_pk_and_fallback(use_pycups, 'PrinterDeleteOptionDefault', pk_args, self._connection.deletePrinterOptionDefault, *args, **kwds)
def deletePrinter(self, *args, **kwds): (use_pycups, name) = self._args_to_tuple([str], *args) pk_args = (name,)
self._call_with_pk_and_fallback(use_pycups, 'PrinterDelete', pk_args, self._connection.deletePrinter, *args, **kwds)
# getPrinterAttributes
def addPrinterToClass(self, *args, **kwds): (use_pycups, printer, name) = self._args_to_tuple([str, str], *args) pk_args = (name, printer)
self._call_with_pk_and_fallback(use_pycups, 'ClassAddPrinter', pk_args, self._connection.addPrinterToClass, *args, **kwds)
def deletePrinterFromClass(self, *args, **kwds): (use_pycups, printer, name) = self._args_to_tuple([str, str], *args) pk_args = (name, printer)
self._call_with_pk_and_fallback(use_pycups, 'ClassDeletePrinter', pk_args, self._connection.deletePrinterFromClass, *args, **kwds)
def deleteClass(self, *args, **kwds): (use_pycups, name) = self._args_to_tuple([str], *args) pk_args = (name,)
self._call_with_pk_and_fallback(use_pycups, 'ClassDelete', pk_args, self._connection.deleteClass, *args, **kwds)
# getDefault
def setDefault(self, *args, **kwds): (use_pycups, name) = self._args_to_tuple([str], *args) pk_args = (name,)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetDefault', pk_args, self._connection.setDefault, *args, **kwds)
# getPPD
def enablePrinter(self, *args, **kwds): (use_pycups, name) = self._args_to_tuple([str], *args) pk_args = (name, True)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetEnabled', pk_args, self._connection.enablePrinter, *args, **kwds)
def disablePrinter(self, *args, **kwds): (use_pycups, name) = self._args_to_tuple([str], *args) pk_args = (name, False)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetEnabled', pk_args, self._connection.disablePrinter, *args, **kwds)
def acceptJobs(self, *args, **kwds): (use_pycups, name) = self._args_to_tuple([str], *args) pk_args = (name, True, '')
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetAcceptJobs', pk_args, self._connection.acceptJobs, *args, **kwds)
def rejectJobs(self, *args, **kwds): (use_pycups, name) = self._args_to_tuple([str], *args) (reason,) = self._kwds_to_vars(['reason'], **kwds) pk_args = (name, False, reason)
self._call_with_pk_and_fallback(use_pycups, 'PrinterSetAcceptJobs', pk_args, self._connection.rejectJobs, *args, **kwds)
# printTestPage
def adminGetServerSettings(self, *args, **kwds): use_pycups = False pk_args = ()
result = self._call_with_pk_and_fallback(use_pycups, 'ServerGetSettings', pk_args, self._connection.adminGetServerSettings, *args, **kwds) settings = {} if result != None: for i in result.keys(): if type(i) == dbus.String: settings[str(i)] = str(result[i]) else: settings[i] = result[i]
return settings
def adminSetServerSettings(self, *args, **kwds): (use_pycups, settings) = self._args_to_tuple([dict], *args) pk_args = (settings,)
self._call_with_pk_and_fallback(use_pycups, 'ServerSetSettings', pk_args, self._connection.adminSetServerSettings, *args, **kwds)
# getSubscriptions # createSubscription # getNotifications # cancelSubscription # renewSubscription # printFile # printFiles
|