00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 from PyQt4.QtCore import *
00030 from PyQt4.QtNetwork import *
00031 
00032 from Koo.Common import Notifier
00033 from Koo.Common import Url
00034 
00035 from Cache import *
00036 import tiny_socket
00037 
00038 import xmlrpclib
00039 import base64
00040 import socket
00041 
00042 ConcurrencyCheckField = '__last_update'
00043 
00044 class RpcException(Exception):
00045         def __init__(self, info):
00046                 self.code = None
00047                 self.args = (info,)
00048                 self.info = info
00049                 self.backtrace = None
00050 
00051 class RpcProtocolException(RpcException):
00052         def __init__(self, backtrace):
00053                 self.code = None
00054                 self.args = (backtrace,)
00055                 self.info = unicode( str(backtrace), 'utf-8' )
00056                 self.backtrace = backtrace
00057 
00058 class RpcServerException(RpcException):
00059         def __init__(self, code, backtrace):
00060                 self.code = code
00061                 self.args = (backtrace,)
00062                 self.backtrace = backtrace
00063                 if hasattr(code, 'split'):
00064                         lines = code.split('\n')
00065 
00066                         self.type = lines[0].split(' -- ')[0]
00067                         self.info = ''
00068                         if len(lines[0].split(' -- ')) > 1:
00069                                 self.info = lines[0].split(' -- ')[1]
00070         
00071                         self.data = '\n'.join(lines[2:])
00072                 else:
00073                         self.type = 'error'
00074                         self.info = backtrace
00075                         self.data = backtrace
00076 
00077 
00078 
00079 
00080 class Connection:
00081         def __init__(self, url):
00082                 self.authorized = False
00083                 self.databaseName = None
00084                 self.uid = None
00085                 self.password = None
00086                 self.url = url
00087 
00088         def stringToUnicode(self, result): 
00089                 if isinstance(result, str):
00090                         return unicode( result, 'utf-8' )
00091                 elif isinstance(result, list):
00092                         return [self.stringToUnicode(x) for x in result]
00093                 elif isinstance(result, tuple):
00094                         return tuple([self.stringToUnicode(x) for x in result])
00095                 elif isinstance(result, dict):
00096                         newres = {}
00097                         for i in result.keys():
00098                                 newres[i] = self.stringToUnicode(result[i])
00099                         return newres
00100                 else:
00101                         return result
00102 
00103         def unicodeToString(self, result): 
00104                 if isinstance(result, unicode):
00105                         return result.encode( 'utf-8' )
00106                 elif isinstance(result, list):
00107                         return [self.unicodeToString(x) for x in result]
00108                 elif isinstance(result, tuple):
00109                         return tuple([self.unicodeToString(x) for x in result])
00110                 elif isinstance(result, dict):
00111                         newres = {}
00112                         for i in result.keys():
00113                                 newres[i] = self.unicodeToString(result[i])
00114                         return newres
00115                 else:
00116                         return result
00117 
00118         def connect(self, database, uid, password):
00119                 self.databaseName = database
00120                 self.uid = uid
00121                 self.password = password
00122 
00123         def call(self, url, method, *args ):
00124                 pass
00125 
00126 modules = []
00127 try:
00128         import Pyro.core
00129         modules.append( 'pyro' )
00130 except:
00131         pass
00132 
00133 
00134 
00135 
00136 class PyroConnection(Connection):
00137         def __init__(self, url):
00138                 Connection.__init__(self, url)
00139                 self.url += '/rpc'
00140                 self.proxy = Pyro.core.getProxyForURI( self.url )
00141 
00142         def singleCall(self, obj, method, *args):
00143                 encodedArgs = self.unicodeToString( args )
00144                 if self.authorized:
00145                         result = self.proxy.dispatch( obj[1:], method, self.databaseName, self.uid, self.password, *encodedArgs )
00146                 else:
00147                         result = self.proxy.dispatch( obj[1:], method, *encodedArgs )
00148                 return self.stringToUnicode( result )
00149 
00150         def call(self, obj, method, *args):
00151                 try:
00152                         try:
00153                                 
00154                                 
00155                                 
00156                                 result = self.singleCall( obj, method, *args )
00157                         except (Pyro.errors.ConnectionClosedError, Pyro.errors.ProtocolError), x:
00158                                 
00159                                 
00160                                 
00161                                 
00162                                 
00163                                 self.proxy = Pyro.core.getProxyForURI( self.url )
00164                                 result = self.singleCall( obj, method, *args )
00165                 except (Pyro.errors.ConnectionClosedError, Pyro.errors.ProtocolError), err:
00166                         raise RpcProtocolException( unicode( err ) )
00167                 except Exception, err:
00168                         if Pyro.util.getPyroTraceback(err):
00169                                 faultCode = err.message
00170                                 faultString = u''
00171                                 for x in Pyro.util.getPyroTraceback(err):
00172                                         faultString += unicode( x, 'utf-8', errors='ignore' )
00173                                 raise RpcServerException( faultCode, faultString )
00174                         raise
00175                 return result
00176 
00177 
00178 
00179 
00180 class SocketConnection(Connection):
00181         def call(self, obj, method, *args):
00182                 try:
00183                         s = tiny_socket.mysocket()
00184                         s.connect( self.url )
00185                 except socket.error, err:
00186                         raise RpcProtocolException( unicode(err) )
00187                 try:
00188                         
00189                         obj = obj[1:]
00190                         encodedArgs = self.unicodeToString( args )
00191                         if self.authorized:
00192                                 s.mysend( (obj, method, self.databaseName, self.uid, self.password) + encodedArgs )
00193                         else:
00194                                 s.mysend( (obj, method) + encodedArgs )
00195                         result = s.myreceive()
00196                 except socket.error, err:
00197                         raise RpcProtocolException( unicode(err) )
00198                 except tiny_socket.Myexception, err:
00199                         faultCode = unicode( err.faultCode, 'utf-8' )
00200                         faultString = unicode( err.faultString, 'utf-8' )
00201                         raise RpcServerException( faultCode, faultString )
00202                 finally:
00203                         s.disconnect()
00204                 return self.stringToUnicode( result )
00205 
00206 
00207 
00208 
00209 class XmlRpcConnection(Connection):
00210         def __init__(self, url):
00211                 Connection.__init__(self, url)
00212                 self.url += '/xmlrpc'
00213 
00214         def call(self, obj, method, *args ):
00215                 remote = xmlrpclib.ServerProxy(self.url + obj)
00216                 function = getattr(remote, method)
00217                 try:
00218                         if self.authorized:
00219                                 result = function(self.databaseName, self.uid, self.password, *args)
00220                         else:
00221                                 result = function( *args )
00222                 except socket.error, err:
00223                         raise RpcProtocolException( err )
00224                 except xmlrpclib.Fault, err:
00225                         raise RpcServerException( err.faultCode, err.faultString )
00226                 return result
00227 
00228 
00229 
00230 
00231 
00232 
00233 
00234 
00235 def createConnection(url):
00236         qUrl = QUrl( url )
00237         if qUrl.scheme() == 'socket':
00238                 con = SocketConnection( url )
00239         elif qUrl.scheme() == 'PYROLOC':
00240                 con = PyroConnection( url )
00241         else:
00242                 con = XmlRpcConnection( url )
00243         return con
00244 
00245 class AsynchronousSessionCall(QThread):
00246         def __init__(self, session, parent=None):
00247                 QThread.__init__(self, parent)
00248                 self.session = session.copy()
00249                 self.obj = None
00250                 self.method = None
00251                 self.args = None
00252                 self.result = None
00253                 self.callback = None
00254                 self.error = None
00255                 self.warning = None
00256                 self.exception = None
00257                 
00258                 
00259                 
00260                 self.useNotifications = False
00261 
00262         def execute(self, callback, obj, method, *args):
00263                 self.useNotifications = True
00264                 self.exception = None
00265                 self.callback = callback
00266                 self.obj = obj
00267                 self.method = method
00268                 self.args = args
00269                 self.connect( self, SIGNAL('finished()'), self.hasFinished )
00270                 self.start()
00271 
00272         def call(self, callback, obj, method, *args):
00273                 self.useNotifications = False
00274                 self.exception = None
00275                 self.callback = callback
00276                 self.obj = obj
00277                 self.method = method
00278                 self.args = args
00279                 self.connect( self, SIGNAL('finished()'), self.hasFinished )
00280                 self.start()
00281 
00282         def hasFinished(self):
00283                 if self.exception:
00284                         if self.useNotifications:
00285                                 
00286                                 
00287                                 if self.error:
00288                                         Notifier.notifyError(*self.error)
00289                                 elif self.warning:
00290                                         Notifier.notifyWarning(*self.warning)
00291                                 else: 
00292                                         raise self.exception
00293                         self.emit( SIGNAL('exception(PyQt_PyObject)'), self.exception )
00294                 else:
00295                         self.emit( SIGNAL('called(PyQt_PyObject)'), self.result )
00296 
00297                 if self.callback:
00298                         self.callback( self.result, self.exception )
00299 
00300                 
00301                 self.session = None
00302 
00303         def run(self):
00304                 
00305                 
00306                 if not self.useNotifications:
00307                         try:
00308                                 self.result = self.session.call( self.obj, self.method, *self.args )
00309                         except Exception, err:
00310                                 self.exception = err
00311                 else:
00312                         try:
00313                                 self.result = self.session.call( self.obj, self.method, *self.args )
00314                         except RpcProtocolException, err:
00315                                 self.exception = err
00316                                 self.error = (_('Connection Refused'), err.info, err.info)
00317                         except RpcServerException, err:
00318                                 self.exception = err
00319                                 if err.type in ('warning','UserError'):
00320                                         self.warning = (err.info, err.data)
00321                                 else:
00322                                         self.error = (_('Application Error'), _('View details'), err.backtrace )
00323 
00324 
00325 
00326 
00327 
00328 
00329 
00330 
00331 
00332 
00333 
00334 
00335 class Session:
00336         LoggedIn = 1
00337         Exception = 2
00338         InvalidCredentials = 3
00339         
00340         def __init__(self):
00341                 self.open = False
00342                 self.url = None
00343                 self.password = None
00344                 self.uid = None
00345                 self.context = {}
00346                 self.userName = None
00347                 self.databaseName = None
00348                 self.connection = None
00349                 self.cache = None
00350                 self.threads = []
00351 
00352         
00353         
00354         
00355         
00356         
00357         
00358         
00359         
00360         
00361         def appendThread(self, thread):
00362                 self.threads = [x for x in self.threads if x.isRunning()]
00363                 self.threads.append( thread )
00364 
00365         
00366         
00367         
00368         
00369         
00370         
00371         
00372         
00373         
00374         
00375         
00376         
00377         
00378         
00379         
00380         
00381         
00382         
00383         
00384         
00385         
00386         
00387         
00388         def callAsync( self, callback, obj, method, *args ):
00389                 caller = AsynchronousSessionCall( self )
00390                 caller.call( callback, obj, method, *args )
00391                 self.appendThread( caller )
00392                 return caller
00393 
00394         
00395         
00396         
00397         
00398         
00399         def executeAsync( self, callback, obj, method, *args ):
00400                 caller = AsynchronousSessionCall( self )
00401                 caller.execute( callback, obj, method, *args )
00402                 self.appendThread( caller )
00403                 return caller
00404 
00405         
00406         
00407         
00408         
00409         
00410         
00411         
00412         
00413         def call(self, obj, method, *args):
00414                 if not self.open:
00415                         raise RpcException(_('Not logged in'))
00416                 if self.cache:
00417                         if self.cache.exists( obj, method, *args ):
00418                                 return self.cache.get( obj, method, *args )
00419                 value = self.connection.call(obj, method, *args)
00420                 if self.cache:
00421                         self.cache.add( value, obj, method, *args )
00422                 return value
00423 
00424         
00425         
00426         
00427         
00428         
00429         def execute(self, obj, method, *args):
00430                 count = 1
00431                 while True:
00432                         try:
00433                                 return self.call(obj, method, *args)
00434                         except RpcProtocolException, err:
00435                                 if not Notifier.notifyLostConnection( count ):
00436                                         raise
00437                         except RpcServerException, err:
00438                                 if err.type in ('warning','UserError'):
00439                                         if err.info in ('ConcurrencyException') and len(args) > 4:
00440                                                 if Notifier.notifyConcurrencyError(args[0], args[2] and args[2][0], args[4]):
00441                                                         if ConcurrencyCheckField in args[4]:
00442                                                                 del args[4][ConcurrencyCheckField]
00443                                                         return self.execute(obj, method, *args)
00444                                         else:
00445                                                 Notifier.notifyWarning(err.info, err.data )
00446                                 else:
00447                                         Notifier.notifyError(_('Application Error'), _('View details'), err.backtrace )
00448                                 raise
00449                         count += 1
00450 
00451         
00452         
00453         
00454         
00455         
00456         def login(self, url, db):
00457                 url = QUrl( url )
00458                 _url = str( url.scheme() ) + '://' + str( url.host() ) + ':' + str( url.port() ) 
00459                 self.connection = createConnection( _url )
00460                 user = Url.decodeFromUrl( unicode( url.userName() ) )
00461                 password = Url.decodeFromUrl( unicode( url.password() ) )
00462                 try:
00463                         res = self.connection.call( '/common', 'login', db, user, password )
00464                 except socket.error, e:
00465                         return Session.Exception
00466                 if not res:
00467                         self.open=False
00468                         self.uid=False
00469                         return Session.InvalidCredentials
00470 
00471                 self.url = _url
00472                 self.open = True
00473                 self.uid = res
00474                 self.userName = user
00475                 self.password = password
00476                 self.databaseName = db
00477                 if self.cache:
00478                         self.cache.clear()
00479                 
00480                 self.connection.databaseName = self.databaseName
00481                 self.connection.password = self.password
00482                 self.connection.uid = self.uid
00483                 self.connection.authorized = True
00484 
00485                 self.reloadContext()
00486                 return Session.LoggedIn
00487 
00488         
00489         
00490         
00491         def reloadContext(self):
00492                 self.context = self.execute('/object', 'execute', 'res.users', 'context_get') or {}
00493 
00494         
00495         def logged(self):
00496                 return self.open
00497 
00498         
00499         def logout(self):
00500                 if self.open:
00501                         self.open = False
00502                         self.userName = None
00503                         self.uid = None
00504                         self.password = None
00505                         self.connection = None
00506                         if self.cache:
00507                                 self.cache.clear()
00508 
00509         
00510         
00511         def evaluateExpression(self, expression, context=None):
00512                 if context is None:
00513                         context = {}
00514                 context['uid'] = self.uid
00515                 if isinstance(expression, basestring):
00516                         expression = expression.replace("'active_id'","active_id")
00517                         return eval(expression, context)
00518                 else:
00519                         return expression 
00520 
00521         def copy(self):
00522                 new = Session()
00523                 new.open = self.open 
00524                 new.url = self.url 
00525                 new.password = self.password
00526                 new.uid = self.uid
00527                 new.context = self.context
00528                 new.userName = self.userName
00529                 new.databaseName = self.databaseName 
00530                 
00531                 
00532                 
00533                 new.connection = createConnection( new.url )
00534                 new.connection.databaseName = self.databaseName
00535                 new.connection.password = self.password
00536                 new.connection.uid = self.uid
00537                 new.connection.authorized = True
00538                 return new
00539 
00540 session = Session()
00541 session.cache = ActionViewCache()
00542 
00543 
00544 class Database:
00545         
00546         
00547         def list(self, url):
00548                 try:
00549                         return self.call( url, 'list' )
00550                 except:
00551                         return -1
00552 
00553         
00554         
00555         
00556         def call(self, url, method, *args):
00557                 con = createConnection( url )
00558                 return con.call( '/db', method, *args )
00559 
00560         
00561         
00562         def execute(self, url, method, *args):
00563                 res = False
00564                 try:
00565                         res = self.call(url, method, *args)
00566                 except socket.error, msg:
00567                         Notifier.notifyWarning('', _('Could not contact server!') )
00568                 return res
00569 
00570 database = Database()
00571 
00572 
00573 
00574 
00575 
00576 class RpcProxy(object):
00577         def __init__(self, resource, useExecute=True):
00578                 self.resource = resource
00579                 self.__attrs = {}
00580                 self.__useExecute = useExecute
00581 
00582         def __getattr__(self, name):
00583                 if not name in self.__attrs:
00584                         self.__attrs[name] = RpcFunction(self.resource, name, self.__useExecute)
00585                 return self.__attrs[name]
00586         
00587 
00588 class RpcFunction(object):
00589         def __init__(self, object, func_name, useExecute=True):
00590                 self.object = object
00591                 self.func = func_name
00592                 self.useExecute = useExecute
00593 
00594         def __call__(self, *args):
00595                 if self.useExecute:
00596                         return session.execute('/object', 'execute', self.object, self.func, *args)
00597                 else:
00598                         return session.call('/object', 'execute', self.object, self.func, *args)
00599 
00600 
00601 
00602 
00603 
00604 
00605 
00606 class RpcReply(QNetworkReply):
00607         def __init__(self, parent, url, operation):
00608                 QNetworkReply.__init__(self, parent)
00609 
00610                 path = unicode( url.path() )
00611                 path = path.split('/')
00612                 if len(path) >= 3:
00613                         model = unicode( url.host() )
00614                         function = path[1]
00615                         parameter = '/%s' % '/'.join( path[2:] )
00616 
00617                         try:
00618                                 self.content = session.call('/object','execute', model, function, parameter, session.context)
00619                         except:
00620                                 self.content = ''
00621                         if self.content:
00622                                 self.content = base64.decodestring( self.content )
00623                         else:
00624                                 self.content = ''
00625                 else:
00626                         self.content = ''
00627 
00628                 self.offset = 0
00629 
00630                 self.setHeader(QNetworkRequest.ContentTypeHeader, QVariant("text/html; charset=utf-8"))
00631                 self.setHeader(QNetworkRequest.ContentLengthHeader, QVariant(len(self.content)))
00632                 QTimer.singleShot(0, self, SIGNAL("readyRead()"))
00633                 QTimer.singleShot(0, self, SIGNAL("finished()"))
00634                 self.open(self.ReadOnly | self.Unbuffered)
00635                 self.setUrl(url)
00636 
00637         def abort(self):
00638                 pass
00639 
00640         def bytesAvailable(self):
00641                 return len(self.content) - self.offset
00642 
00643         def isSequential(self):
00644                 return True
00645 
00646         def readData(self, maxSize):
00647                 if self.offset < len(self.content):
00648                         end = min(self.offset + maxSize, len(self.content))
00649                         data = self.content[self.offset:end]
00650                         self.offset = end
00651                         return data
00652 
00653 
00654 
00655 
00656 class RpcNetworkAccessManager( QNetworkAccessManager ):
00657         def __init__(self, oldManager):
00658                 QNetworkAccessManager.__init__(self)
00659                 self.oldManager = oldManager
00660                 self.setCache(oldManager.cache())
00661                 self.setCookieJar(oldManager.cookieJar())
00662                 self.setProxy(oldManager.proxy())
00663                 self.setProxyFactory(oldManager.proxyFactory())
00664 
00665         def createRequest(self, operation, request, data):
00666                 if request.url().scheme() != 'openerp':
00667                         return QNetworkAccessManager.createRequest(self, operation, request, data)
00668 
00669                 if operation != self.GetOperation:
00670                         return QNetworkAccessManager.createRequest(self, operation, request, data)
00671 
00672                 return RpcReply(self, request.url(), self.GetOperation)
00673