1 ##############################################################################
3 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
7 # WARNING: This program as such is intended to be used by professional
8 # programmers who take the whole responsability of assessing all potential
9 # consequences resulting from its eventual inadequacies and bugs
10 # End users who are looking for a ready-to-use solution with commercial
11 # garantees and support are strongly adviced to contract a Free Software
14 # This program is Free Software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; either version 2
17 # of the License, or (at your option) any later version.
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 ##############################################################################
43 class rpc_int_exception(Exception):
47 class rpc_exception(Exception):
48 def __init__(self
, code
, backtrace
):
52 if hasattr(code
, 'split'):
53 lines
= code
.split('\n')
55 self
.type = lines
[0].split(' -- ')[0]
57 if len(lines
[0].split(' -- ')) > 1:
58 self
.message
= lines
[0].split(' -- ')[1]
60 self
.data
= '\n'.join(lines
[2:])
63 self
.message
= backtrace
66 self
.backtrace
= backtrace
68 log
= logging
.getLogger('rpc.exception')
69 log
.warning('CODE %s: %s' % (str(code
), self
.message
))
71 class gw_inter(object):
72 __slots__
= ('_url', '_db', '_uid', '_passwd', '_sock', '_obj')
73 def __init__(self
, url
, db
, uid
, passwd
, obj
='/object'):
79 def exec_auth(method
, *args
):
81 def execute(method
, *args
):
84 class xmlrpc_gw(gw_inter
):
85 __slots__
= ('_url', '_db', '_uid', '_passwd', '_sock', '_obj')
86 def __init__(self
, url
, db
, uid
, passwd
, obj
='/object'):
87 gw_inter
.__init
__(self
, url
, db
, uid
, passwd
, obj
)
88 self
._sock
= xmlrpclib
.ServerProxy(url
+obj
)
89 def exec_auth(self
, method
, *args
):
90 logging
.getLogger('rpc.request').info(str((method
, self
._db
, self
._uid
, self
._passwd
, args
)))
91 res
= self
.execute(method
, self
._uid
, self
._passwd
, *args
)
92 logging
.getLogger('rpc.result').debug(str(res
))
95 def __convert(self
, result
):
96 if type(result
)==type(u
''):
97 return result
.encode('utf-8')
98 elif type(result
)==type([]):
99 return map(self
.__convert
, result
)
100 elif type(result
)==type({}):
102 for i
in result
.keys():
103 newres
[i
] = self
.__convert
(result
[i
])
108 def execute(self
, method
, *args
):
109 result
= getattr(self
._sock
,method
)(self
._db
, *args
)
110 return self
.__convert
(result
)
112 class tinySocket_gw(gw_inter
):
113 __slots__
= ('_url', '_db', '_uid', '_passwd', '_sock', '_obj')
114 def __init__(self
, url
, db
, uid
, passwd
, obj
='/object'):
115 gw_inter
.__init
__(self
, url
, db
, uid
, passwd
, obj
)
116 self
._sock
= tiny_socket
.mysocket()
118 def exec_auth(self
, method
, *args
):
119 logging
.getLogger('rpc.request').info(str((method
, self
._db
, self
._uid
, self
._passwd
, args
)))
120 res
= self
.execute(method
, self
._uid
, self
._passwd
, *args
)
121 logging
.getLogger('rpc.result').debug(str(res
))
123 def execute(self
, method
, *args
):
124 self
._sock
.connect(self
._url
)
125 self
._sock
.mysend((self
._obj
, method
, self
._db
)+args
)
126 res
= self
._sock
.myreceive()
127 self
._sock
.disconnect()
130 class rpc_session(object):
131 __slots__
= ('_open', '_url', 'uid', 'uname', '_passwd', '_gw', 'db', 'context', 'timezone')
141 self
.timezone
= 'utc'
143 def rpc_exec(self
, obj
, method
, *args
):
145 sock
= self
._gw
(self
._url
, self
.db
, self
.uid
, self
._passwd
, obj
)
146 return sock
.execute(method
, *args
)
147 except socket
.error
, e
:
148 common
.error(_('Connection refused !'), str(e
), str(e
))
149 raise rpc_exception(69, _('Connection refused!'))
150 except xmlrpclib
.Fault
, err
:
151 raise rpc_exception(err
.faultCode
, err
.faultString
)
153 def rpc_exec_auth_try(self
, obj
, method
, *args
):
155 sock
= self
._gw
(self
._url
, self
.db
, self
.uid
, self
._passwd
, obj
)
156 return sock
.exec_auth(method
, *args
)
158 raise rpc_exception(1, 'not logged')
160 def rpc_exec_auth_wo(self
, obj
, method
, *args
):
162 sock
= self
._gw
(self
._url
, self
.db
, self
.uid
, self
._passwd
, obj
)
163 return sock
.exec_auth(method
, *args
)
164 except xmlrpclib
.Fault
, err
:
165 a
= rpc_exception(err
.faultCode
, err
.faultString
)
166 except tiny_socket
.Myexception
, err
:
167 a
= rpc_exception(err
.faultCode
, err
.faultString
)
168 if a
.code
in ('warning', 'UserError'):
169 common
.warning(a
.data
, a
.message
)
173 def rpc_exec_auth(self
, obj
, method
, *args
):
176 sock
= self
._gw
(self
._url
, self
.db
, self
.uid
, self
._passwd
, obj
)
177 return sock
.exec_auth(method
, *args
)
178 except socket
.error
, e
:
179 common
.error(_('Connection refused !'), str(e
), str(e
))
180 raise rpc_exception(69, 'Connection refused!')
182 if isinstance(e
, xmlrpclib
.Fault
) \
183 or isinstance(e
, tiny_socket
.Myexception
):
184 a
= rpc_exception(e
.faultCode
, e
.faultString
)
185 if a
.type in ('warning','UserError'):
186 if a
.message
in ('ConcurrencyException') and len(args
) > 4:
187 if common
.concurrency(args
[0], args
[2][0], args
[4]):
188 if 'read_delta' in args
[4]:
189 del args
[4]['read_delta']
190 return self
.rpc_exec_auth(obj
, method
, *args
)
192 common
.warning(a
.data
, a
.message
)
194 common
.error(_('Application Error'), e
.faultCode
, e
.faultString
)
196 common
.error(_('Application Error'), _('View details'), str(e
))
197 #TODO Must propagate the exception?
200 raise rpc_exception(1, 'not logged')
202 def login(self
, uname
, passwd
, url
, port
, protocol
, db
):
204 if _protocol
== 'http://' or _protocol
== 'https://':
205 _url
= _protocol
+ url
+':'+str(port
)+'/xmlrpc'
206 _sock
= xmlrpclib
.ServerProxy(_url
+'/common')
209 res
= _sock
.login(db
or '', uname
or '', passwd
or '')
210 except socket
.error
,e
:
217 _url
= _protocol
+url
+':'+str(port
)
218 _sock
= tiny_socket
.mysocket()
219 self
._gw
= tinySocket_gw
221 _sock
.connect(url
, int(port
))
222 _sock
.mysend(('common', 'login', db
or '', uname
or '', passwd
or ''))
223 res
= _sock
.myreceive()
225 except socket
.error
,e
:
235 self
._passwd
= passwd
238 #CHECKME: is this useful? maybe it's used to see if there is no
240 sock
= self
._gw
(self
._url
, self
.db
, self
.uid
, self
._passwd
)
241 self
.context_reload()
244 def list_db(self
, url
):
245 m
= re
.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url
or '')
248 if m
.group(1) == 'http://' or m
.group(1) == 'https://':
249 sock
= xmlrpclib
.ServerProxy(url
+ '/xmlrpc/db')
255 sock
= tiny_socket
.mysocket()
257 sock
.connect(m
.group(2), int(m
.group(3)))
258 sock
.mysend(('db', 'list'))
259 res
= sock
.myreceive()
265 def db_exec_no_except(self
, url
, method
, *args
):
266 m
= re
.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url
or '')
267 if m
.group(1) == 'http://' or m
.group(1) == 'https://':
268 sock
= xmlrpclib
.ServerProxy(url
+ '/xmlrpc/db')
269 return getattr(sock
, method
)(*args
)
271 sock
= tiny_socket
.mysocket()
272 sock
.connect(m
.group(2), int(m
.group(3)))
273 sock
.mysend(('db', method
)+args
)
274 res
= sock
.myreceive()
278 def db_exec(self
, url
, method
, *args
):
281 res
= self
.db_exec_no_except(url
, method
, *args
)
282 except socket
.error
, msg
:
283 common
.warning('Could not contact server!')
286 def context_reload(self
):
288 self
.timezone
= 'utc'
290 self
.context
= self
.rpc_exec_auth('/object', 'execute', 'res.users', 'context_get')
291 if 'lang' in self
.context
:
293 translate
.setlang(self
.context
['lang'])
294 options
.options
['client.lang']=self
.context
['lang']
295 ids
= self
.rpc_exec_auth('/object', 'execute', 'res.lang', 'search', [('code', '=', self
.context
['lang'])])
297 l
= self
.rpc_exec_auth('/object', 'execute', 'res.lang', 'read', ids
, ['direction'])
298 if l
and 'direction' in l
[0]:
299 common
.DIRECTION
= l
[0]['direction']
301 if common
.DIRECTION
== 'rtl':
302 gtk
.widget_set_default_direction(gtk
.TEXT_DIR_RTL
)
304 gtk
.widget_set_default_direction(gtk
.TEXT_DIR_LTR
)
305 if 'tz' in self
.context
:
306 self
.timezone
= self
.rpc_exec_auth('/common', 'timezone_get')
310 common
.warning('You select a timezone but tinyERP could not find pytz library !\nThe timezone functionality will be disable.')
324 session
= rpc_session()
327 class RPCProxy(object):
329 def __init__(self
, resource
):
330 self
.resource
= resource
333 def __getattr__(self
, name
):
334 if not name
in self
.__attrs
:
335 self
.__attrs
[name
] = RPCFunction(self
.resource
, name
)
336 return self
.__attrs
[name
]
339 class RPCFunction(object):
341 def __init__(self
, object, func_name
):
343 self
.func
= func_name
345 def __call__(self
, *args
):
346 return session
.rpc_exec_auth('/object', 'execute', self
.object, self
.func
, *args
)