Back to index

system-config-printer  1.3.9+20120706
asyncconn.py
Go to the documentation of this file.
00001 #!/usr/bin/python
00002 
00003 ## Copyright (C) 2007, 2008, 2009, 2010 Red Hat, Inc.
00004 ## Copyright (C) 2008 Novell, Inc.
00005 ## Authors: Tim Waugh <twaugh@redhat.com>, Vincent Untz
00006 
00007 ## This program is free software; you can redistribute it and/or modify
00008 ## it under the terms of the GNU General Public License as published by
00009 ## the Free Software Foundation; either version 2 of the License, or
00010 ## (at your option) any later version.
00011 
00012 ## This program is distributed in the hope that it will be useful,
00013 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 ## GNU General Public License for more details.
00016 
00017 ## You should have received a copy of the GNU General Public License
00018 ## along with this program; if not, write to the Free Software
00019 ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00020 
00021 import cups
00022 import gobject
00023 import gtk
00024 import os
00025 
00026 import asyncipp
00027 import asyncpk1
00028 import authconn
00029 import config
00030 from debug import *
00031 import debug
00032 
00033 ######
00034 ###### A class to keep track of what we're trying to achieve in order
00035 ###### to display that to the user if authentication is required.
00036 ######
00037 class SemanticOperations(object):
00038     def __init__ (self):
00039         self._operation_stack = []
00040 
00041     def _begin_operation (self, operation):
00042         self._operation_stack.append (operation)
00043 
00044     def _end_operation (self):
00045         self._operation_stack.pop ()
00046 
00047     def current_operation (self):
00048         try:
00049             return self._operation_stack[0]
00050         except IndexError:
00051             return None
00052 
00053 ######
00054 ###### Destructible method call.  Required so that all references can
00055 ###### be dropped when an asynchronous method call is destroyed.
00056 ######
00057 class _AsyncMethodCall:
00058     def __init__ (self, fn, reply_handler, error_handler, auth_handler):
00059         self._fn = fn
00060         self._reply_handler = reply_handler
00061         self._error_handler = error_handler
00062         self._auth_handler = auth_handler
00063         self._destroyed = False
00064         debugprint ("+%s" % self)
00065 
00066     def __del__ (self):
00067         debugprint ("-%s" % self)
00068 
00069     def destroy (self):
00070         if self._destroyed:
00071             return
00072 
00073         debugprint ("DESTROY: %s" % self)
00074         self._destroyed = True
00075         self._reply_handler = None
00076         self._error_handler = None
00077         self._auth_handler = None
00078         self._reply_data = None
00079         self._error_data = None
00080         self._auth_data = None
00081 
00082     def run (self, *args, **kwds):
00083         self._reply_data = kwds.get ('reply_handler')
00084         self._error_data = kwds.get ('error_handler')
00085         self._auth_data = kwds.get ('auth_handler')
00086         kwds['reply_handler'] = self.reply_handler
00087         kwds['error_handler'] = self.error_handler
00088         kwds['auth_handler'] = self.auth_handler
00089         self._fn (*args, **kwds)
00090 
00091     def reply_handler (self, *args):
00092         if not self._destroyed:
00093             self._reply_handler (self, self._reply_data, *args)
00094 
00095     def error_handler (self, *args):
00096         if not self._destroyed:
00097             self._error_handler (self, self._error_data, *args)
00098 
00099     def auth_handler (self, *args):
00100         if not self._destroyed:
00101             self._auth_handler (self, self.auth_data, *args)
00102 
00103 ######
00104 ###### An asynchronous libcups API using IPP or PolicyKit as
00105 ###### appropriate.
00106 ######
00107 
00108 class Connection(SemanticOperations):
00109     def __init__ (self, reply_handler=None, error_handler=None,
00110                   auth_handler=None, host=None, port=None, encryption=None,
00111                   parent=None, try_as_root=True, prompt_allowed=True):
00112         super (Connection, self).__init__ ()
00113 
00114         self._destroyed = False
00115 
00116         # Decide whether to use direct IPP or PolicyKit.
00117         if host == None:
00118             host = cups.getServer()
00119         use_pk = ((host.startswith ('/') or host == 'localhost') and
00120                   os.getuid () != 0)
00121 
00122         def subst_reply_handler (conn, reply):
00123             self._subst_reply_handler (None, reply_handler, reply)
00124 
00125         def subst_error_handler (conn, exc):
00126             self._subst_error_handler (None, error_handler, exc)
00127 
00128         def subst_auth_handler (prompt, conn, method, resource):
00129             self._subst_auth_handler (None, auth_handler, prompt, method, resource)
00130 
00131         if use_pk and try_as_root:
00132             debugprint ("Using polkit-1 connection class")
00133             c = asyncpk1.PK1Connection (reply_handler=subst_reply_handler,
00134                                         error_handler=subst_error_handler,
00135                                         host=host, port=port,
00136                                         encryption=encryption,
00137                                         parent=parent)
00138             self._conn = c
00139         else:
00140             debugprint ("Using IPP connection class")
00141             c = asyncipp.IPPAuthConnection (reply_handler=subst_reply_handler,
00142                                             error_handler=subst_error_handler,
00143                                             auth_handler=subst_auth_handler,
00144                                             host=host, port=port,
00145                                             encryption=encryption,
00146                                             parent=parent,
00147                                             try_as_root=try_as_root,
00148                                             prompt_allowed=prompt_allowed,
00149                                             semantic=self)
00150             self._conn = c
00151 
00152         methodtype = type (self._conn.getPrinters)
00153         instancemethodtype = type (self._conn.getDevices)
00154         bindings = []
00155         for fname in dir (self._conn):
00156             if fname.startswith ('_'):
00157                 continue
00158             fn = getattr (self._conn, fname)
00159             if type (fn) != methodtype and type (fn) != instancemethodtype:
00160                 continue
00161             if not hasattr (self, fname):
00162                 setattr (self, fname, self._make_binding (fn))
00163                 bindings.append (fname)
00164 
00165         self._bindings = bindings
00166         self._methodcalls = []
00167         debugprint ("+%s" % self)
00168 
00169     def __del__ (self):
00170         debug.debugprint ("-%s" % self)
00171 
00172     def destroy (self):
00173         debugprint ("DESTROY: %s" % self)
00174         self._destroyed = True
00175         try:
00176             self._conn.destroy ()
00177         except AttributeError:
00178             pass
00179 
00180         for methodcall in self._methodcalls:
00181             methodcall.destroy ()
00182 
00183         for binding in self._bindings:
00184             delattr (self, binding)
00185 
00186     def _make_binding (self, fn):
00187         return lambda *args, **kwds: self._call_function (fn, *args, **kwds)
00188 
00189     def _call_function (self, fn, *args, **kwds):
00190         methodcall = _AsyncMethodCall (fn,
00191                                        self._subst_reply_handler,
00192                                        self._subst_error_handler,
00193                                        self._subst_auth_handler)
00194         self._methodcalls.append (methodcall)
00195         methodcall.run (*args, **kwds)
00196 
00197     def _subst_reply_handler (self, methodcall, reply_handler, *args):
00198         if methodcall:
00199             methodcall.destroy ()
00200             i = self._methodcalls.index (methodcall)
00201             del self._methodcalls[i]
00202             args = args[1:]
00203         if reply_handler and not self._destroyed:
00204             reply_handler (self, *args)
00205 
00206     def _subst_error_handler (self, methodcall, error_handler, *args):
00207         if methodcall:
00208             methodcall.destroy ()
00209             i = self._methodcalls.index (methodcall)
00210             del self._methodcalls[i]
00211             args = args[1:]
00212         if error_handler and not self._destroyed:
00213             error_handler (self, *args)
00214 
00215     def _subst_auth_handler (self, methodcall, auth_handler, prompt, method, resource):
00216         if methodcall:
00217             methodcall.destroy ()
00218             i = self._methodcalls.index (methodcall)
00219             del self._methodcalls[i]
00220         if auth_handler and not self._destroyed:
00221             auth_handler (prompt, self, method, resource)
00222 
00223     def set_auth_info (self, password):
00224         """Call this from your auth_handler function."""
00225         self.thread.set_auth_info (password)
00226 
00227 if __name__ == "__main__":
00228     # Demo
00229     set_debugging (True)
00230     gobject.threads_init ()
00231 
00232     class Test:
00233         def __init__ (self, quit):
00234             self._conn = Connection ()
00235             self._quit = quit
00236             debugprint ("+%s" % self)
00237 
00238         def __del__ (self):
00239             debug.debugprint ("-%s" % self)
00240 
00241         def destroy (self):
00242             debugprint ("DESTROY: %s" % self)
00243             self._conn.destroy ()
00244             if self._quit:
00245                 loop.quit ()
00246 
00247         def getDevices (self):
00248             self._conn.getDevices (reply_handler=self.getDevices_reply,
00249                                    error_handler=self.getDevices_error)
00250 
00251         def getDevices_reply (self, conn, result):
00252             print conn, result
00253             self.destroy ()
00254 
00255         def getDevices_error (self, conn, exc):
00256             print repr (exc)
00257             self.destroy ()
00258 
00259     t = Test (False)
00260     loop = gobject.MainLoop ()
00261     t.getDevices ()
00262     t.destroy ()
00263 
00264     t = Test (True)
00265     t.getDevices ()
00266     loop.run ()