Use JSON-RPC

This commit is contained in:
Cédric Krier committed 2011-08-06 14:30:52 +02:00
1 parent 25a3c46af2
commit d1daa78935
17 files changed
+386 -464

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* Use JSON-RPC
* Limit size of field when possible
* Add xalign and yalign as fields attributes
* Convert many popup to be no-modal
+11 -12
View File
@@ -200,7 +200,7 @@ def request_server(server_widget):
xoptions=gtk.FILL)
entry_port = gtk.Entry()
entry_port.set_max_length(5)
entry_port.set_text("8070")
entry_port.set_text("8000")
entry_port.set_activates_default(True)
entry_port.set_width_chars(16)
table.attach(entry_port, 1, 2, 1, 2, yoptions=False,
@@ -966,10 +966,9 @@ def to_xml(string):
PLOCK = Lock()
def process_exception(exception, *args):
global _USERNAME, _DATABASE, _SOCK
if isinstance(exception, TrytonError):
if exception.ex_type == 'BadFingerprint':
if exception.faultCode == 'BadFingerprint':
warning(
_('The server fingerprint has changed since last connection!\n'
'The application will stop connecting to this server '
@@ -977,13 +976,13 @@ def process_exception(exception, *args):
from tryton.gui.main import Main
Main.sig_quit()
sys.exit()
elif exception.ex_type == 'NotLogged':
if not rpc._SOCK:
elif exception.faultCode == 'NotLogged':
if rpc.CONNECTION is None:
message(_('Connection error!\n' \
'Unable to connect to the server!'))
return False
elif isinstance(exception, TrytonServerError):
if exception.ex_type == 'UserWarning':
if exception.faultCode == 'UserWarning':
msg = ''
if len(exception.args) > 4:
msg = exception.args[3]
@@ -1005,13 +1004,13 @@ def process_exception(exception, *args):
return process_exception(exception, *args)
return True
return False
elif exception.ex_type == 'UserError':
elif exception.faultCode == 'UserError':
msg = ''
if len(exception.args) > 3:
msg = exception.args[2]
warning(str(msg), str(exception.args[1]))
return False
elif exception.ex_type == 'ConcurrencyException':
elif exception.faultCode == 'ConcurrencyException':
if len(args) >= 6:
if concurrency(args[1], args[3][0], args[5]):
if '_timestamp' in args[5]:
@@ -1024,11 +1023,11 @@ def process_exception(exception, *args):
else:
message(_('Concurrency Exception'), msg_type=gtk.MESSAGE_ERROR)
return False
elif exception.ex_type == 'NotLogged':
elif exception.faultCode == 'NotLogged':
if not PLOCK.acquire(False):
return False
hostname = rpc._SOCK.hostname
port = rpc._SOCK.port
hostname = rpc._HOST
port = rpc._PORT
try:
while True:
password = ask(_('Password:'), visibility=False)
@@ -1060,7 +1059,7 @@ def process_exception(exception, *args):
return False
if isinstance(exception, TrytonServerError):
error_title, error_detail = exception.ex_type, exception.traceback
error_title, error_detail = exception.faultCode, exception.faultString
else:
error_title = str(exception)
error_detail = traceback.format_exc()
+1 -1
View File
@@ -57,7 +57,7 @@ class ConfigManager(object):
'login.profile': demo_server,
'login.login': 'demo',
'login.server': demo_server,
'login.port': '8070',
'login.port': '8000',
'login.db': demo_database,
'login.expanded': False,
'tip.autostart': False,
+4 -9
View File
@@ -1,16 +1,11 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from jsonrpc import Fault
class TrytonServerError(Exception):
def __init__(self, *args):
super(TrytonServerError, self).__init__(*args)
self.ex_type = str(args[0])
self.traceback = args[-1]
TrytonServerError = Fault
class TrytonError(Exception):
def __init__(self, ex_type):
self.ex_type = ex_type
def __init__(self, faultCode):
self.faultCode = faultCode
+5 -7
View File
@@ -19,21 +19,19 @@ class Fingerprints(dict):
for line in known_hosts:
line = line.strip()
try:
key, sha1 = line.split(' ')
host, port = key.rsplit(':', 1)
host, sha1 = line.split(' ')
except ValueError:
continue
self[(host, port)] = sha1
self[host] = sha1
def save(self):
lines = []
with open(KNOWN_HOSTS_PATH, 'w') as known_hosts:
known_hosts.writelines('%s:%s %s' % (host, port, sha1)
+ os.linesep for (host, port), sha1 in self.iteritems())
known_hosts.writelines('%s %s' % (host, sha1)
+ os.linesep for host, sha1 in self.iteritems())
def __setitem__(self, key, value):
assert isinstance(key, tuple)
assert len(key) == 2
assert isinstance(key, basestring)
assert len(value) == 59 # len of formated sha1
super(Fingerprints, self).__setitem__(key, value)
self.save()
+6 -10
View File
@@ -889,7 +889,7 @@ class Main(object):
try:
res = DBLogin().run()
except TrytonError, exception:
if exception.ex_type == 'QueryCanceled':
if exception.faultCode == 'QueryCanceled':
return False
except TrytonServerError, exception:
common.process_exception(exception)
@@ -982,12 +982,8 @@ class Main(object):
return True
def refresh_ssl(self):
if rpc.SECURE:
info = ''
if hasattr(rpc._SOCK.ssl_sock, 'server'):
info = str(rpc._SOCK.ssl_sock.server())
self.tooltips.set_tip(self.secure_img, _('SSL connection') + \
'\n' + info)
if rpc.CONNECTION is not None and rpc.CONNECTION.ssl:
self.tooltips.set_tip(self.secure_img, _('SSL connection'))
self.secure_img.show()
else:
self.secure_img.hide()
@@ -1044,7 +1040,7 @@ class Main(object):
return False
self.sb_username.set_text(prefs.get('status_bar', ''))
self.sb_servername.set_text('%s@%s:%d/%s' % (rpc._USERNAME,
rpc._SOCK.hostname, rpc._SOCK.port, rpc._DATABASE))
rpc._HOST, rpc._PORT, rpc._DATABASE))
if not prefs[menu_type]:
if quiet:
return False
@@ -1396,8 +1392,8 @@ class Main(object):
+ [CONFIG.defaults['login.port']])[:2]
database, path = (urlp.path[1:].split('/', 1) + [None])[:2]
if (not path or
hostname != rpc._SOCK.hostname or
int(port) != rpc._SOCK.port or
hostname != rpc._HOST or
int(port) != rpc._PORT or
database != rpc._DATABASE):
return
type_, path = (path.split('/', 1) + [''])[:2]
+3 -3
View File
@@ -29,7 +29,7 @@ class DBCreate(object):
self.entry_server_connection.modify_text(gtk.STATE_INSENSITIVE, \
gtk.gdk.color_parse(common.COLOR_SCHEMES["black"]))
self.tooltips.set_tip(self.entry_server_connection,_("This is the URL of " \
"the Tryton server. Use server 'localhost' and port '8070' if " \
"the Tryton server. Use server 'localhost' and port '8000' if " \
"the server is installed on this computer. Click on 'Change' to " \
"change the address."))
else:
@@ -50,7 +50,7 @@ class DBCreate(object):
"2. Find out on which address and port it is listen.\n" \
"3. If there is a firewall between the server and this " \
"client, make shure that the server address and port " \
"(usually 8070) are not blocked.\n" \
"(usually 8000) are not blocked.\n" \
"Click on 'Change' to change the address."))
return state
@@ -177,7 +177,7 @@ class DBCreate(object):
self.entry_server_connection.unset_flags(gtk.CAN_FOCUS)
self.entry_server_connection.set_editable(False)
self.tooltips.set_tip(self.entry_server_connection, _("This is the URL of " \
"the server. Use server 'localhost' and port '8070' if " \
"the server. Use server 'localhost' and port '8000' if " \
"the server is installed on this computer. Click on 'Change' to " \
"change the address."))
self.button_server_change = gtk.Button(_("C_hange"), stock=None,
+1 -1
View File
@@ -136,7 +136,7 @@ class DBBackupDrop(object):
self.entry_server_connection.unset_flags(gtk.CAN_FOCUS)
self.entry_server_connection.set_editable(False)
self.tooltips.set_tip(self.entry_server_connection, _("This is the " \
"URL of the server. Use server 'localhost' and port '8070' " \
"URL of the server. Use server 'localhost' and port '8000' " \
"if the server is installed on this computer. " \
"Click on 'Change' to change the address."))
table.attach(self.entry_server_connection, 1, 2, 2, 3,
+4 -4
View File
@@ -169,7 +169,7 @@ class DBListEditor(object):
for entryname in ('host', 'port', 'database', 'username'):
entry = getattr(self, '%s_entry' % entryname)
if entryname == 'port':
entry.set_text('8070')
entry.set_text('8000')
else:
entry.set_text('')
self.current_database = None
@@ -464,13 +464,13 @@ class DBLogin(object):
# Profile informations
self.profile_cfg = os.path.join(get_config_dir(), 'profiles.cfg')
self.profiles = ConfigParser.SafeConfigParser({'port': '8070'})
self.profiles = ConfigParser.SafeConfigParser({'port': '8000'})
if not os.path.exists(self.profile_cfg):
short_version = '.'.join(VERSION.split('.', 2)[:2])
name = 'demo%s.tryton.org' % short_version
self.profiles.add_section(name)
self.profiles.set(name, 'host', name)
self.profiles.set(name, 'port', '8070')
self.profiles.set(name, 'port', '8000')
self.profiles.set(name, 'database', 'demo%s' % short_version)
self.profiles.set(name, 'username', 'demo')
else:
@@ -565,7 +565,7 @@ class DBLogin(object):
if ':' in netloc:
return int(netloc.split(':')[1])
else:
return 8070
return 8000
def run(self):
profile_name = CONFIG['login.profile']
+1 -1
View File
@@ -83,7 +83,7 @@ class DBRestore(object):
self.entry_server_url.set_activates_default(True)
self.entry_server_url.set_width_chars(16)
self.tooltips.set_tip(self.entry_server_url, _("This is the URL of " \
"the server. Use server 'localhost' and port '8070' if " \
"the server. Use server 'localhost' and port '8000' if " \
"the server is installed on this computer. Click on " \
"'Change' to change the address."))
table.attach(self.entry_server_url, 1, 2, 0, 1, yoptions=gtk.FILL)
@@ -27,10 +27,7 @@ class Float(Integer):
self.entry.set_text('')
return False
self.digits = field.attrs.get('digits', (16, 2))
if isinstance(self.digits, str):
digits = record.expr_eval(self.digits)
else:
digits = self.digits
digits = record.expr_eval(self.digits)
self.entry.set_width_chars(sum(digits))
self.entry.set_text(locale.format('%.' + str(digits[1]) + 'f',
field.get(record) or 0.0, True))
@@ -54,10 +51,7 @@ class Float(Integer):
if new_value in ('-', decimal_point):
return
if isinstance(self.digits, str):
digits = self.record.expr_eval(self.digits)
else:
digits = self.digits
digits = self.record.expr_eval(self.digits)
try:
locale.atof(new_value)
@@ -28,9 +28,7 @@ class ProgressBar(WidgetInterface):
self.widget.set_fraction(0.0)
return False
value = float(field.get(record) or 0.0)
digits = field.attrs.get('digits', (16, 2))
if isinstance(self.digits, str):
digits = record.expr_eval(self.digits)
digits = record.expr_eval(field.attrs.get('digits', (16, 2)))
self.widget.set_text(locale.format('%.' + str(digits[1]) + 'f',
value, True))
self.widget.set_fraction(value / 100.0)
@@ -207,7 +207,8 @@ class ParserTree(ParserInterface):
'float_time'):
label = gtk.Label(node_attrs['sum'] + _(': '))
label_sum = gtk.Label()
if isinstance(fields[fname].attrs.get('digits'), str):
if isinstance(fields[fname].attrs.get('digits'),
basestring):
digits = 2
else:
digits = fields[fname].attrs.get('digits', (16, 2))[1]
@@ -456,18 +457,12 @@ class Float(Char):
super(Float, self).setter(column, cell, store, iter)
record = store.get_value(iter, 0)
field = record[self.field_name]
if isinstance(field.attrs.get('digits'), str):
digits = record.expr_eval(field.attrs['digits'])
else:
digits = field.attrs.get('digits', (16, 2))
digits = record.expr_eval(field.attrs.get('digits', (16, 2)))
cell.digits = digits
def get_textual_value(self, record):
field = record[self.field_name]
if isinstance(field.attrs.get('digits'), str):
digit = record.expr_eval(field.attrs['digits'])[1]
else:
digit = field.attrs.get('digits', (16, 2))[1]
digit = record.expr_eval(field.attrs.get('digits', (16, 2)))[1]
return locale.format('%.'+str(digit)+'f',
record[self.field_name].get_client(record) or 0.0, True)
@@ -870,10 +865,7 @@ class ProgressBar(object):
record = store.get_value(iter, 0)
value = float(self.get_textual_value(record) or 0.0)
cell.set_property('value', value)
if isinstance(self.attrs.get('digits'), str):
digit = record.expr_eval(self.attrs['digits'])[1]
else:
digit = self.attrs.get('digits', (16, 2))[1]
digit = record.expr_eval(self.attrs.get('digits', (16, 2)))[1]
text = locale.format('%.' + str(digit) + 'f', value, True)
cell.set_property('text', text + '%')
@@ -13,7 +13,7 @@ class Float(Integer):
def __init__(self, name, attrs=None, context=None, on_change=None):
super(Float, self).__init__(name, attrs=attrs, context=context,
on_change=on_change)
if isinstance(self.attrs.get('digits'), str):
if isinstance(self.attrs.get('digits'), basestring):
self.digits = (16, 2)
else:
self.digits = self.attrs.get('digits', (16, 2))
+272
View File
@@ -0,0 +1,272 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import xmlrpclib
import json
import ssl
import httplib
from decimal import Decimal
import datetime
import socket
import gzip
import StringIO
import hashlib
import sys
__all__ = ["ResponseError", "Fault", "ProtocolError", "Transport",
"ServerProxy"]
class ResponseError(xmlrpclib.ResponseError):
pass
class Fault(xmlrpclib.Fault):
pass
class ProtocolError(xmlrpclib.ProtocolError):
pass
def object_hook(dct):
if '__class__' in dct:
if dct['__class__'] == 'datetime':
return datetime.datetime(dct['year'], dct['month'], dct['day'],
dct['hour'], dct['minute'], dct['second'])
elif dct['__class__'] == 'date':
return datetime.date(dct['year'], dct['month'], dct['day'])
elif dct['__class__'] == 'Decimal':
return Decimal(dct['decimal'])
return dct
class JSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.date):
if isinstance(obj, datetime.datetime):
return {'__class__': 'datetime',
'year': obj.year,
'month': obj.month,
'day': obj.day,
'hour': obj.hour,
'minute': obj.minute,
'second': obj.second,
}
return {'__class__': 'date',
'year': obj.year,
'month': obj.month,
'day': obj.day,
}
elif isinstance(obj, Decimal):
return {'__class__': 'Decimal',
'decimal': str(obj),
}
return super(JSONEncoder, self).default(obj)
class JSONParser(object):
def __init__(self, target):
self.__targer = target
def feed(self, data):
self.__targer.feed(data)
def close(self):
pass
class JSONUnmarshaller(object):
data = ''
def feed(self, data):
self.data += data
def close(self):
return json.loads(self.data, object_hook=object_hook)
class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
accept_gzip_encoding = True
encode_threshold = 1400 # common MTU
def __init__(self, fingerprints=None, ca_certs=None):
xmlrpclib.Transport.__init__(self)
self.__connection = (None, None)
self.__fingerprints = fingerprints
self.__ca_certs = ca_certs
def getparser(self):
target = JSONUnmarshaller()
parser = JSONParser(target)
return parser, target
def get_host_info(self, host):
host, extra_headers, x509 = xmlrpclib.Transport.get_host_info(self, host)
if extra_headers is None:
extra_headers = []
extra_headers.append(('Connection', 'keep-alive'))
return host, extra_headers, x509
def send_content(self, connection, request_body):
connection.putheader("Content-Type", "text/json")
if (self.encode_threshold is not None and
self.encode_threshold < len(request_body) and
gzip):
connection.putheader("Content-Encoding", "gzip")
buffer = StringIO.StringIO()
output = gzip.GzipFile(mode='wb', fileobj=buffer)
output.write(request_body)
output.close()
buffer.seek(0)
request_body = buffer.getvalue()
connection.putheader("Content-Length", str(len(request_body)))
connection.endheaders()
if request_body:
connection.send(request_body)
def make_connection(self, host):
if self.__connection and host == self.__connection[0]:
return self.__connection[1]
fingerprint = None
host, extra_headers, x509 = self.get_host_info(host)
ca_certs = self.__ca_certs
cert_reqs = ssl.CERT_REQUIRED if ca_certs else ssl.CERT_NONE
class HTTPSConnection(httplib.HTTPSConnection):
def connect(self):
sock = socket.create_connection((self.host, self.port),
self.timeout)
if self._tunnel_host:
self.sock = sock
self._tunnel()
self.sock = ssl.wrap_socket(sock, self.key_file,
self.cert_file, ca_certs=ca_certs, cert_reqs=cert_reqs)
self.__connection = host, HTTPSConnection(host)
try:
self.__connection[1].connect()
sock = self.__connection[1].sock
try:
peercert = sock.getpeercert(True)
except socket.error:
peercert = None
def format_hash(value):
return reduce(lambda x, y: x + y[1].upper() +
((y[0] % 2 and y[0] + 1 < len(value)) and ':' or ''),
enumerate(value), '')
fingerprint = format_hash(hashlib.sha1(peercert).hexdigest())
except ssl.SSLError, e:
self.__connection = host, httplib.HTTPConnection(host)
if self.__fingerprints is not None:
if host in self.__fingerprints:
if self.__fingerprints[host] != fingerprint:
self.close()
raise ssl.SSLError('BadFingerprint')
elif fingerprint:
self.__fingerprints[host] = fingerprint
return self.__connection[1]
if sys.version_info[:2] <= (2, 6):
def request(self, host, handler, request_body, verbose=0):
h = self.make_connection(host)
if verbose:
h.set_debuglevel(1)
self.send_request(h, handler, request_body)
self.send_host(h, host)
self.send_user_agent(h)
self.send_content(h, request_body)
response = h.getresponse()
if response.status != 200:
raise ProtocolError(
host + handler,
response.status,
response.reason,
response.getheaders()
)
self.verbose = verbose
try:
sock = h._conn.sock
except AttributeError:
sock = None
if response.getheader("Content-Encoding", "") == "gzip":
response = gzip.GzipFile(mode="rb",
fileobj=StringIO.StringIO(response.read()))
sock = None
return self._parse_response(response, sock)
def send_request(self, connection, handler, request_body):
xmlrpclib.Transport.send_request(self, connection, handler,
request_body)
connection.putheader("Accept-Encoding", "gzip")
def close(self):
if self.__connection[1]:
self.__connection[1].close()
self.__connection = (None, None)
class ServerProxy(xmlrpclib.ServerProxy):
__id = 0
def __init__(self, host, port, database='', verbose=0,
fingerprints=None, ca_certs=None):
self.__host = '%s:%s' % (host, port)
self.__handler = '/' + database
self.__transport = Transport(fingerprints, ca_certs)
self.__verbose = verbose
def __request(self, methodname, params):
self.__id += 1
id_ = self.__id
request = json.dumps({
'id': id_,
'method': methodname,
'params': params,
}, cls=JSONEncoder)
try:
response = self.__transport.request(
self.__host,
self.__handler,
request,
verbose=self.__verbose
)
except socket.error, v:
# trap 'Broken pipe'
if v.args[0] != 32:
raise
# try one more time
self.__transport.close()
response = self.__transport.request(
self.__host,
self.__handler,
request,
verbose=self.__verbose
)
if response['id'] != id_:
raise ResponseError('Invalid response id (%s) excpected %s' %
(response['id'], id_))
if response.get('error'):
raise Fault(*response['error'])
return response['result']
def close(self):
self.__transport.close()
@property
def ssl(self):
return isinstance(self.__transport.make_connection(self.__host),
httplib.HTTPSConnection)
-255
View File
@@ -1,255 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import socket
# can't use/fall-back pickle due to different interface :-(
import cPickle
try:
import cStringIO as StringIO
except ImportError:
import StringIO
try:
import ssl
except ImportError:
ssl = None
import gzip
try:
import hashlib
except ImportError:
hashlib = None
import sha
import os
from tryton.exceptions import TrytonError, TrytonServerError
DNS_CACHE = {}
MAX_SIZE = 999999999
MAX_LENGHT = len(str(MAX_SIZE))
CONNECT_TIMEOUT = 5
TIMEOUT = 3600
GZIP_THRESHOLD = 1400 # common MTU
_ALLOWED_MODULES = {'datetime': ['datetime', 'date'], 'decimal': ['Decimal']}
def checkfunction(module, klass):
if module in _ALLOWED_MODULES and klass in _ALLOWED_MODULES[module]:
mod = __import__(module, {}, {}, ['__all__'])
_class = getattr(mod, klass)
return _class
raise ValueError('Not supported: %s/%s' % (module, klass))
class PySocket:
def __init__(self, sock=None, fingerprints=None, ca_certs=None):
self.sock = sock
self.host = None
self.hostname = None
self.port = None
self.ssl = False
self.ssl_sock = None
self.connected = False
self.buffer = ''
self.fingerprints = fingerprints
if ca_certs and os.path.isfile(ca_certs):
self.ca_certs = ca_certs
else:
self.ca_certs = None
def connect(self, host, port=False):
if not port:
buf = host.split('//')[1]
host, port = buf.rsplit(':', 1)
hostname = host
if host in DNS_CACHE:
host = DNS_CACHE[host]
self.sock = None
if socket.has_ipv6:
try:
socket.getaddrinfo(host, int(port), socket.AF_INET6)
self.sock = socket.socket(socket.AF_INET6,
socket.SOCK_STREAM)
self.sock.settimeout(CONNECT_TIMEOUT)
self.sock.connect((host, int(port)))
except socket.error:
self.sock = None
if self.sock is None:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(CONNECT_TIMEOUT)
self.sock.connect((host, int(port)))
DNS_CACHE[hostname], port = self.sock.getpeername()[:2]
try:
sock = None
if socket.has_ipv6:
try:
socket.getaddrinfo(host, int(port), socket.AF_INET6)
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.settimeout(CONNECT_TIMEOUT)
sock.connect((host, int(port)))
except socket.error:
sock = None
if sock is None:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(CONNECT_TIMEOUT)
sock.connect((host, int(port)))
if ssl:
ssl_sock = ssl.wrap_socket(sock)
self.ssl = True
elif hasattr(socket, 'ssl'):
ssl_sock = socket.ssl(sock)
self.ssl = True
except socket.error:
pass
self.sock.settimeout(TIMEOUT)
fingerprint = None
if self.ssl:
if ssl:
self.ssl_sock = ssl.wrap_socket(self.sock,
ca_certs=self.ca_certs, cert_reqs=(self.ca_certs and
ssl.CERT_REQUIRED or ssl.CERT_NONE))
elif hasattr(socket, 'ssl'):
self.ssl_sock = socket.ssl(self.sock, certfile=self.ca_certs)
try:
peercert = self.ssl_sock.getpeercert(True)
except socket.error:
peercert = None
def format_hash(value):
return reduce(lambda x, y: x + y[1].upper() +
((y[0] % 2 and y[0] + 1 < len(value)) and ':' or ''),
enumerate(value), '')
if peercert and hashlib:
fingerprint = format_hash(hashlib.sha1(peercert).hexdigest())
elif peercert:
fingerprint = format_hash(sha1.new(peercert).hexdigest())
self.host = host
self.hostname = hostname
self.port = port
self.connected = True
self.buffer = ''
if self.fingerprints is not None:
key = (self.hostname, str(self.port))
if key in self.fingerprints:
if self.fingerprints[key] != fingerprint:
self.disconnect()
raise TrytonError('BadFingerprint')
elif fingerprint:
self.fingerprints[key] = fingerprint
def disconnect(self):
try:
sock = self.sock
if self.ssl:
sock = self.ssl_sock
try:
shutdown_value = 2
if hasattr(socket, 'SHUT_RDWR'):
shutdown_value = socket.SHUT_RDWR
if hasattr(sock, 'sock_shutdown'):
sock.sock_shutdown(shutdown_value)
else:
sock.shutdown(shutdown_value)
except socket.error:
pass
sock.close()
except socket.error:
pass
self.sock = None
self.ssl = False
self.ssl_sock = None
self.connected = False
self.buffer = ''
def reconnect(self):
if self.hostname and self.port:
self.disconnect()
self.connect(self.hostname, self.port)
def send(self, msg, exception=False, traceback=None):
msg = cPickle.dumps([msg, traceback], protocol=2)
gzip_p = False
if len(msg) > GZIP_THRESHOLD:
buffer = StringIO.StringIO()
output = gzip.GzipFile(mode='wb', fileobj=buffer)
output.write(msg)
output.close()
buffer.seek(0)
msg = buffer.getvalue()
gzip_p = True
size = len(msg)
msg = str(size) + ' ' + (exception and "1" or "0") \
+ (gzip_p and "1" or "0") + msg
size = len(msg)
if self.ssl:
totalsent = 0
while totalsent < size:
sent = self.ssl_sock.write(msg[totalsent:])
if sent == 0:
raise RuntimeError("socket connection broken")
totalsent = totalsent + sent
else:
self.sock.sendall(msg)
def receive(self):
buf = self.buffer
L = []
size_remaining = MAX_LENGHT
while size_remaining:
chunk_size = min(size_remaining, 4096)
if self.ssl:
chunk = self.ssl_sock.read(chunk_size)
else:
chunk = self.sock.recv(chunk_size)
if chunk == '':
raise RuntimeError("socket connection broken")
L.append(chunk)
size_remaining -= len(chunk)
if ' ' in chunk:
break
if size_remaining < 0:
raise RuntimeError("socket connection broken")
buf += ''.join(L)
size, msg = buf.split(' ', 1)
size = int(size)
if size > MAX_SIZE:
raise RuntimeError("socket connection broken")
while len(msg) < 2:
chunk_size = min(size + 2, 4096)
if self.ssl:
msg += self.ssl_sock.read(chunk_size)
else:
msg += self.sock.recv(chunk_size)
if msg == '':
raise RuntimeError("socket connection broken")
exception = msg[0] != "0"
gzip_p = msg[1] != "0"
L = [msg[2:]]
size_remaining = size - len(L[0])
while size_remaining:
chunk_size = min(size_remaining, 4096)
if self.ssl:
chunk = self.ssl_sock.read(chunk_size)
else:
chunk = self.sock.recv(chunk_size)
if chunk == '':
raise RuntimeError("socket connection broken")
L.append(chunk)
size_remaining -= len(chunk)
msg = ''.join(L)
if len(msg) > size:
self.buffer = msg[size:]
msg = msg[:size]
else:
self.buffer = ''
msgio = StringIO.StringIO(msg)
if gzip_p:
output = gzip.GzipFile(mode='r', fileobj=msgio)
msgio = StringIO.StringIO(output.read(-1))
output.close()
unpickler = cPickle.Unpickler(msgio)
# cPickle mechanism to import instances (pickle differs here)
unpickler.find_global = checkfunction
res = unpickler.load()
if exception:
raise TrytonServerError(*(list(res[0]) + [res[1]]))
else:
return res[0]
+68 -136
View File
@@ -1,57 +1,43 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import itertools
import pysocket
import logging
import socket
import os
from threading import Semaphore
from functools import partial
from tryton.jsonrpc import ServerProxy, Fault
from tryton.fingerprints import Fingerprints
from tryton.config import get_config_dir
from tryton.ipc import Server as IPCServer
from tryton.exceptions import TrytonServerError
_SOCK = None
CONNECTION = None
_USER = None
_USERNAME = ''
_SESSION = ''
_HOST = ''
_PORT = None
_DATABASE = ''
CONTEXT = {}
_VIEW_CACHE = {}
TIMEZONE = 'utc'
SECURE = False
_SEMAPHORE = Semaphore()
_CA_CERTS = os.path.join(get_config_dir(), 'ca_certs')
if not os.path.isfile(_CA_CERTS):
_CA_CERTS = None
_FINGERPRINTS = Fingerprints()
ServerProxy = partial(ServerProxy, fingerprints=_FINGERPRINTS, ca_certs=_CA_CERTS)
def db_list(host, port):
global _SOCK, SECURE
_SEMAPHORE.acquire()
try:
try:
if _SOCK and (_SOCK.hostname != host or _SOCK.port != port):
_SOCK.disconnect()
if _SOCK is None:
_SOCK = pysocket.PySocket(fingerprints=Fingerprints(),
ca_certs=_CA_CERTS)
if not _SOCK.connected:
_SOCK.connect(host, port)
args = (None, None, None, 'common', 'db', 'list')
logging.getLogger('rpc.request').info(repr(args))
try:
_SOCK.send(args)
except socket.error, exception:
if exception.args[0] == 32:
_SOCK.reconnect()
_SOCK.send(args)
else:
raise
res = _SOCK.receive()
SECURE = _SOCK.ssl
finally:
_SEMAPHORE.release()
logging.getLogger('rpc.result').debug(repr(res))
return res
except TrytonServerError, exception:
connection = ServerProxy(host, port)
logging.getLogger('rpc.request').info('common.db.list(None, None)')
result = connection.common.db.list(None, None)
logging.getLogger('rpc.result').debug(repr(result))
return result
except Fault, exception:
if exception.args[0] == 'AccessDenied':
raise
else:
@@ -59,137 +45,90 @@ def db_list(host, port):
return None
def db_exec(host, port, method, *args):
global _SOCK, SECURE
_SEMAPHORE.acquire()
try:
if _SOCK and (_SOCK.hostname != host or _SOCK.port != port):
_SOCK.disconnect()
if _SOCK is None:
_SOCK= pysocket.PySocket(fingerprints=Fingerprints(),
ca_certs=_CA_CERTS)
if not _SOCK.connected:
_SOCK.connect(host, port)
args = (None, None, None, 'common', 'db', method) + args
logging.getLogger('rpc.request').info(repr(args))
try:
_SOCK.send(args)
except socket.error, exception:
if exception.args[0] == 32:
_SOCK.reconnect()
_SOCK.send(args)
else:
raise
res = _SOCK.receive()
SECURE = _SOCK.ssl
finally:
_SEMAPHORE.release()
logging.getLogger('rpc.result').debug(repr(res))
return res
connection = ServerProxy(host, port)
logging.getLogger('rpc.request').info('common.db.%s(None, None, %s)' %
(method, args))
result = getattr(connection.common.db, method)(None, None, *args)
logging.getLogger('rpc.result').debug(repr(result))
return result
def server_version(host, port):
global _SOCK, SECURE
_SEMAPHORE.acquire()
try:
try:
if _SOCK and (_SOCK.hostname != host or _SOCK.port != port):
_SOCK.disconnect()
if _SOCK is None:
_SOCK = pysocket.PySocket(fingerprints=Fingerprints(),
ca_certs=_CA_CERTS)
if not _SOCK.connected:
_SOCK.connect(host, port)
args = (None, None, None, 'common', None, 'version')
logging.getLogger('rpc.request').info(repr(args))
try:
_SOCK.send(args)
except socket.error, exception:
if exception.args[0] == 32:
_SOCK.reconnect()
_SOCK.send(args)
else:
raise
res = _SOCK.receive()
SECURE = _SOCK.ssl
finally:
_SEMAPHORE.release()
logging.getLogger('rpc.result').debug(repr(res))
return res
except (TrytonServerError, socket.error):
connection = ServerProxy(host, port)
logging.getLogger('rpc.request').info(
'common.server.version(None, None)')
result = connection.common.server.version(None, None)
logging.getLogger('rpc.result').debug(repr(result))
return result
except (Fault, socket.error):
logging.getLogger('rpc.result').debug(repr(None))
return None
def login(username, password, host, port, database):
global _SOCK, _USER, _USERNAME, _SESSION, _DATABASE, _VIEW_CACHE, SECURE
global CONNECTION, _USER, _USERNAME, _SESSION, _HOST, _PORT, _DATABASE, _VIEW_CACHE
_VIEW_CACHE = {}
SECURE = False
try:
_SEMAPHORE.acquire()
try:
if _SOCK and (_SOCK.hostname != host or _SOCK.port != port):
_SOCK.disconnect()
if _SOCK is None:
_SOCK = pysocket.PySocket(fingerprints=Fingerprints(),
ca_certs=_CA_CERTS)
if not _SOCK.connected:
_SOCK.connect(host, port)
args = (database, username, password, 'common', 'db', 'login')
logging.getLogger('rpc.request').info(repr(args))
_SOCK.send(args)
res = _SOCK.receive()
logging.getLogger('rpc.result').debug(repr(res))
connection = ServerProxy(host, port, database)
if str(connection) != str(CONNECTION):
if CONNECTION:
CONNECTION.close()
CONNECTION = connection
else:
connection = CONNECTION
logging.getLogger('rpc.request').info('common.db.login(%s, %s)' %
(username, 'x' * 10))
result = connection.common.db.login(username, password)
logging.getLogger('rpc.result').debug(repr(result))
finally:
_SEMAPHORE.release()
except (socket.error, RuntimeError):
try:
_SOCK.reconnect()
except (socket.error, RuntimeError):
pass
except socket.error:
_USER = None
_SESSION = ''
return -1
if not res:
if not result:
_USER = None
_SESSION = ''
return -2
_USER = res[0]
_USER = result[0]
_USERNAME = username
_SESSION = res[1]
_SESSION = result[1]
_HOST = host
_PORT = port
_DATABASE = database
SECURE = _SOCK.ssl
IPCServer(host, port, database).run()
return 1
def logout():
global _SOCK, _USER, _USERNAME, _SESSION, _DATABASE, _VIEW_CACHE, SECURE
global CONNECTION, _USER, _USERNAME, _SESSION, _HOST, _PORT, _DATABASE, _VIEW_CACHE
if IPCServer.instance:
IPCServer.instance.stop()
if _SOCK and _USER:
if CONNECTION is not None:
_SEMAPHORE.acquire()
try:
args = (_DATABASE, _USER, _SESSION, 'common', 'db', 'logout')
logging.getLogger('rpc.request').info(repr(args))
_SOCK.sock.settimeout(pysocket.CONNECT_TIMEOUT)
_SOCK.send(args)
res = _SOCK.receive()
logging.getLogger('rpc.result').debug(repr(res))
except socket.error:
logging.getLogger('rpc.request').info('common.db.logout(%s, %s)' %
(_USER, _SESSION))
CONNECTION.common.db.logout(_USER, _SESSION)
except (Fault, socket.error):
pass
finally:
_SEMAPHORE.release()
_SOCK.disconnect()
_SOCK = None
CONNECTION.close()
CONNECTION = None
_USER = None
_USERNAME = ''
_SESSION = ''
_HOST = ''
_PORT = None
_DATABASE = ''
_VIEW_CACHE = {}
SECURE = False
def context_reload():
global CONTEXT, TIMEZONE
global CONTEXT, TIMEZONE, _HOST, _PORT
try:
context = execute('model', 'res.user', 'get_preferences', True, {})
except TrytonServerError:
except Fault:
return
CONTEXT = {}
for i in context:
@@ -197,15 +136,15 @@ def context_reload():
CONTEXT[i] = value
if i == 'timezone':
try:
TIMEZONE = execute('common', None, 'timezone_get')
except TrytonServerError:
connection = ServerProxy(_HOST, _PORT)
TIMEZONE = connection.common.server.timezone_get(None, None)
except Fault:
pass
def _execute(blocking, *args):
global _SOCK, _DATABASE, _USER, _SESSION
if not _SOCK or not _SOCK.connected:
global CONNECTION, _USER, _SESSION
if CONNECTION is None:
raise TrytonServerError('NotLogged')
logging.getLogger('rpc.request').info(repr((args)))
key = False
if args[2] == 'fields_view_get':
args, ctx = args[:-1], args[-1]
@@ -223,17 +162,10 @@ def _execute(blocking, *args):
if not res:
return
try:
try:
_SOCK.send((_DATABASE, _USER, _SESSION) + args)
result = _SOCK.receive()
except (socket.error, RuntimeError):
try:
_SOCK.reconnect()
_SOCK.send((_DATABASE, _USER, _SESSION) + args)
result = _SOCK.receive()
except (socket.error, RuntimeError):
_SOCK.reconnect()
raise
name = '.'.join(args[:3])
args = (_USER, _SESSION) + args[3:]
logging.getLogger('rpc.request').info('%s%s' % (name, args))
result = getattr(CONNECTION, name)(*args)
finally:
_SEMAPHORE.release()
if key: