Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Evergreen:11.1
system-config-printer
system-config-printer-policykit.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File system-config-printer-policykit.patch of Package system-config-printer
diff --git a/Makefile.am b/Makefile.am index cb62cbd..fee240e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -60,6 +60,7 @@ nobase_pkgdata_DATA= \ authconn.py \ config.py \ contextmenu.py \ + cupspk.py \ debug.py \ errordialogs.py \ jobviewer.py \ diff --git a/authconn.py b/authconn.py index 1b37e34..bfcb13f 100644 --- a/authconn.py +++ b/authconn.py @@ -18,6 +18,7 @@ ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. import cups +import cupspk import gtk from debug import * @@ -120,8 +121,15 @@ class Connection: def _connect (self): cups.setUser (self._use_user) + + self._use_pk = self._server[0] == '/' or self._server == 'localhost' + if self._use_pk: + create_object = cupspk.Connection + else: + create_object = cups.Connection + try: - self._connection = cups.Connection (host=self._server, + self._connection = create_object (host=self._server, port=self._port, encryption=self._encryption) except TypeError: @@ -129,16 +137,20 @@ class Connection: cups.setServer (self._server) cups.setPort (self._port) cups.setEncryption (self._encryption) - self._connection = cups.Connection () + self._connection = create_object () + + if self._use_pk: + self._connection.set_parent(self._parent) self._user = self._use_user debugprint ("Connected as user %s" % self._user) - methodtype = type (self._connection.getPrinters) + methodtype_lambda = type (self._connection.getPrinters) + methodtype_real = type (self._connection.addPrinter) for fname in dir (self._connection): if fname[0] == '_': continue fn = getattr (self._connection, fname) - if type (fn) != methodtype: + if not type (fn) in [methodtype_lambda, methodtype_real]: continue setattr (self, fname, self._make_binding (fname, fn)) @@ -164,6 +176,8 @@ class Connection: raise cups.IPPError (cups.IPP_NOT_AUTHORIZED, '') break except cups.IPPError, (e, m): + if self._use_pk and m == 'pkcancel': + raise if not self._cancel and (e == cups.IPP_NOT_AUTHORIZED or e == cups.IPP_FORBIDDEN): self._failed () diff --git a/cupspk.py b/cupspk.py new file mode 100644 index 0000000..b04d1b8 --- /dev/null +++ b/cupspk.py @@ -0,0 +1,625 @@ +# vim: set ts=4 sw=4 et: coding=UTF-8 +# +# Copyright (C) 2008 Novell, Inc. +# +# 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 dbus.mainloop.glib import DBusGMainLoop +DBusGMainLoop(set_as_default=True) + +PK_AUTH_NAME = 'org.freedesktop.PolicyKit.AuthenticationAgent' +PK_AUTH_PATH = '/' +PK_AUTH_IFACE = 'org.freedesktop.PolicyKit.AuthenticationAgent' + +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' + + +pk_auth_ret = False +pk_auth_error = None +pk_auth_running = False +pk_auth_done = False + +def _pk_auth_reply_handler(ret): + global pk_auth_ret + global pk_auth_done + + pk_auth_ret = ret + pk_auth_done = True + +def _pk_auth_error_handler(e): + global pk_auth_error + global pk_auth_done + + pk_auth_error = str(e) + pk_auth_done = True + + +# 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 + self._session_bus = dbus.SessionBus() + self._system_bus = dbus.SystemBus() + + 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: + return None + + + def _obtain_auth(self, action, xid = 0): + global pk_auth_ret + global pk_auth_error + global pk_auth_done + global pk_auth_running + + if pk_auth_running: + # FIXME: raise an exception: this should really never happen + return False + + pk_auth_ret = False + pk_auth_error = None + pk_auth_done = False + + pk_auth_object = self._session_bus.get_object(PK_AUTH_NAME, PK_AUTH_PATH) + pk_auth = dbus.Interface(pk_auth_object, PK_AUTH_IFACE) + + # We're doing this dbus call asynchronously because we want to not + # freeze the UI while a menu is being destroyed. And the windows might + # need some repainting while the authorization dialog is displayed. + pk_auth_running = True + + pk_auth.ObtainAuthorization(action, dbus.UInt32(xid), dbus.UInt32(os.getpid()), reply_handler=_pk_auth_reply_handler, error_handler=_pk_auth_error_handler) + + while not pk_auth_done: + gtk.main_iteration(True) + + pk_auth_running = False + + if pk_auth_error != None: + raise dbus.exceptions.DBusException(pk_auth_error) + + if not type(pk_auth_ret) == dbus.Boolean: + return False + + return pk_auth_ret != 0 + + + def _handle_exception_with_auth(self, e): + if e.get_dbus_name() != CUPS_PK_NEED_AUTH: + return False + + tokens = e.get_dbus_message().split(' ', 2) + if len(tokens) != 3: + return False + + try: + xid = 0 + if self._parent and getattr(self._parent, 'window') and getattr(self._parent.window, 'xid'): + xid = self._parent.window.xid + + # FIXME: is xid working? + ret = self._obtain_auth(tokens[0], xid) + except dbus.exceptions.DBusException: + return False + + if not ret: + raise cups.IPPError(cups.IPP_NOT_AUTHORIZED, 'pkcancel') + + return True + + + 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 not self._handle_exception_with_auth(e): + 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 != '': + print >>sys.stderr, '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]) == 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 +# getDevices +# getJobs +# getJobAttributes +# cancelJob +# cancelAllJobs +# authenticateJob +# setJobHoldUntil +# restartJob + + 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: + ''' + + 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'] + else: + if not use_pycups: + raise TypeError() + else: + filename = None + + 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'] + else: + if not use_pycups: + raise TypeError() + else: + filename = None + + 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.enablePrinter, + *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 +# adminGetServerSettings + + + def adminGetServerSettings(self, *args, **kwds): + use_pycups = False + pk_args = () + + return self._call_with_pk_and_fallback(use_pycups, + 'ServerGetSettings', pk_args, + self._connection.adminGetServerSettings, + *args, **kwds) + + + 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 diff --git a/system-config-printer.py b/system-config-printer.py index 2520e73..b908f7b 100755 --- a/system-config-printer.py +++ b/system-config-printer.py @@ -1538,7 +1538,7 @@ class GUI(GtkGUI, monitor.Watcher): return True self.changed = set() # of options - if not self.__dict__.has_key ("server_settings"): + if not self.cups._use_pk and not self.__dict__.has_key ("server_settings"): # We can authenticate with the server correctly at this point, # but we have never fetched the server settings to see whether # the server is publishing shared printers. Fetch the settings
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor