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