2006-12-07 13:41:40 +00:00
# -*- encoding: utf-8 -*-
##############################################################################
#
2008-11-03 18:27:16 +00:00
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
2008-06-16 07:24:04 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
##############################################################################
2006-12-07 13:41:40 +00:00
import base64 , os , string
import netsvc
import pooler , security , ir , tools
import logging
import threading , thread
import time
import base64
import addons
import sql_db
2008-07-08 08:13:12 +00:00
from tools . translate import _
2008-09-12 19:45:04 +00:00
import release
2006-12-07 13:41:40 +00:00
logging . basicConfig ( )
class db ( netsvc . Service ) :
2008-07-22 14:24:36 +00:00
def __init__ ( self , name = " db " ) :
netsvc . Service . __init__ ( self , name )
self . joinGroup ( " web-services " )
self . exportMethod ( self . create )
self . exportMethod ( self . get_progress )
self . exportMethod ( self . drop )
self . exportMethod ( self . dump )
self . exportMethod ( self . restore )
self . exportMethod ( self . list )
self . exportMethod ( self . list_lang )
self . exportMethod ( self . change_admin_password )
2008-08-20 08:20:19 +00:00
self . exportMethod ( self . server_version )
2008-07-22 14:24:36 +00:00
self . actions = { }
self . id = 0
self . id_protect = threading . Semaphore ( )
2008-09-02 17:53:29 +00:00
def create ( self , password , db_name , demo , lang , user_password = ' admin ' ) :
2008-07-22 14:24:36 +00:00
security . check_super ( password )
self . id_protect . acquire ( )
self . id + = 1
id = self . id
self . id_protect . release ( )
self . actions [ id ] = { ' clean ' : False }
db = sql_db . db_connect ( ' template1 ' , serialize = 1 )
cr = db . cursor ( )
2008-12-09 13:31:32 +00:00
cr . autocommit ( True )
2008-12-08 07:55:58 +00:00
time . sleep ( 0.2 )
2008-07-22 14:24:36 +00:00
cr . execute ( ' CREATE DATABASE ' + db_name + ' ENCODING \' unicode \' ' )
cr . close ( )
class DBInitialize ( object ) :
2008-09-02 17:53:29 +00:00
def __call__ ( self , serv , id , db_name , demo , lang , user_password = ' admin ' ) :
2008-07-22 14:24:36 +00:00
try :
serv . actions [ id ] [ ' progress ' ] = 0
clean = False
cr = sql_db . db_connect ( db_name ) . cursor ( )
tools . init_db ( cr )
cr . commit ( )
cr . close ( )
cr = None
2008-09-04 13:54:32 +00:00
pool = pooler . get_pool ( db_name , demo , serv . actions [ id ] ,
2008-07-22 14:24:36 +00:00
update_module = True )
2008-09-11 07:29:45 +00:00
2008-07-22 14:24:36 +00:00
cr = sql_db . db_connect ( db_name ) . cursor ( )
2008-09-11 07:29:45 +00:00
2008-09-04 13:54:32 +00:00
if lang :
modobj = pool . get ( ' ir.module.module ' )
mids = modobj . search ( cr , 1 , [ ( ' state ' , ' = ' , ' installed ' ) ] )
modobj . update_translations ( cr , 1 , mids , lang )
2008-09-02 17:53:29 +00:00
cr . execute ( ' UPDATE res_users SET password= %s , active=True WHERE login= %s ' , (
user_password , ' admin ' ) )
2008-09-04 13:54:32 +00:00
cr . execute ( ' SELECT login, password, name ' \
' FROM res_users ' \
' ORDER BY login ' )
2008-07-22 14:24:36 +00:00
serv . actions [ id ] [ ' users ' ] = cr . dictfetchall ( )
2008-09-04 13:54:32 +00:00
serv . actions [ id ] [ ' clean ' ] = True
2008-09-03 08:03:39 +00:00
cr . commit ( )
2008-07-22 14:24:36 +00:00
cr . close ( )
except Exception , e :
serv . actions [ id ] [ ' clean ' ] = False
serv . actions [ id ] [ ' exception ' ] = e
from StringIO import StringIO
import traceback
e_str = StringIO ( )
traceback . print_exc ( file = e_str )
traceback_str = e_str . getvalue ( )
e_str . close ( )
2008-12-10 22:11:04 +00:00
netsvc . Logger ( ) . notifyChannel ( ' web-services ' , netsvc . LOG_ERROR , ' CREATE DATABASE \n %s ' % ( traceback_str ) )
2008-07-22 14:24:36 +00:00
serv . actions [ id ] [ ' traceback ' ] = traceback_str
if cr :
cr . close ( )
logger = netsvc . Logger ( )
2008-12-10 22:11:04 +00:00
logger . notifyChannel ( " web-services " , netsvc . LOG_INFO , ' CREATE DATABASE: %s ' % ( db_name . lower ( ) ) )
2008-07-22 14:24:36 +00:00
dbi = DBInitialize ( )
create_thread = threading . Thread ( target = dbi ,
2008-09-02 17:53:29 +00:00
args = ( self , id , db_name , demo , lang , user_password ) )
2008-07-22 14:24:36 +00:00
create_thread . start ( )
self . actions [ id ] [ ' thread ' ] = create_thread
return id
def get_progress ( self , password , id ) :
security . check_super ( password )
if self . actions [ id ] [ ' thread ' ] . isAlive ( ) :
# return addons.init_progress[db_name]
return ( min ( self . actions [ id ] . get ( ' progress ' , 0 ) , 0.95 ) , [ ] )
else :
clean = self . actions [ id ] [ ' clean ' ]
if clean :
users = self . actions [ id ] [ ' users ' ]
del self . actions [ id ]
return ( 1.0 , users )
else :
e = self . actions [ id ] [ ' exception ' ]
del self . actions [ id ]
raise Exception , e
def drop ( self , password , db_name ) :
security . check_super ( password )
2008-12-09 13:31:32 +00:00
sql_db . close_db ( db_name )
2008-07-22 14:24:36 +00:00
logger = netsvc . Logger ( )
db = sql_db . db_connect ( ' template1 ' , serialize = 1 )
cr = db . cursor ( )
2008-12-09 13:31:32 +00:00
cr . autocommit ( True )
2008-07-22 14:24:36 +00:00
try :
try :
cr . execute ( ' DROP DATABASE ' + db_name )
2008-12-11 11:22:48 +00:00
except Exception , e :
2008-11-27 08:01:58 +00:00
logger . notifyChannel ( " web-services " , netsvc . LOG_ERROR ,
2008-12-11 11:22:48 +00:00
' DROP DB: %s failed: \n %s ' % ( db_name , e ) )
raise Exception ( " Couldn ' t drop database %s : %s " % ( db_name , e ) )
2008-07-22 14:24:36 +00:00
else :
logger . notifyChannel ( " web-services " , netsvc . LOG_INFO ,
' DROP DB: %s ' % ( db_name ) )
finally :
cr . close ( )
return True
def dump ( self , password , db_name ) :
security . check_super ( password )
logger = netsvc . Logger ( )
cmd = [ ' pg_dump ' , ' --format=c ' ]
if tools . config [ ' db_user ' ] :
cmd . append ( ' --username= ' + tools . config [ ' db_user ' ] )
if tools . config [ ' db_host ' ] :
cmd . append ( ' --host= ' + tools . config [ ' db_host ' ] )
if tools . config [ ' db_port ' ] :
cmd . append ( ' --port= ' + tools . config [ ' db_port ' ] )
cmd . append ( db_name )
stdin , stdout = tools . exec_pg_command_pipe ( * tuple ( cmd ) )
stdin . close ( )
data = stdout . read ( )
res = stdout . close ( )
if res :
2008-11-27 08:01:58 +00:00
logger . notifyChannel ( " web-services " , netsvc . LOG_ERROR ,
2008-07-22 14:24:36 +00:00
' DUMP DB: %s failed \n %s ' % ( db_name , data ) )
raise Exception , " Couldn ' t dump database "
logger . notifyChannel ( " web-services " , netsvc . LOG_INFO ,
' DUMP DB: %s ' % ( db_name ) )
return base64 . encodestring ( data )
def restore ( self , password , db_name , data ) :
security . check_super ( password )
logger = netsvc . Logger ( )
if self . db_exist ( db_name ) :
2008-11-27 08:01:58 +00:00
logger . notifyChannel ( " web-services " , netsvc . LOG_WARNING ,
2008-07-22 14:24:36 +00:00
' RESTORE DB: %s already exists ' % ( db_name , ) )
raise Exception , " Database already exists "
db = sql_db . db_connect ( ' template1 ' , serialize = 1 )
cr = db . cursor ( )
2008-12-09 13:31:32 +00:00
cr . autocommit ( True )
2008-07-22 14:24:36 +00:00
cr . execute ( ' CREATE DATABASE ' + db_name + ' ENCODING \' unicode \' ' )
cr . close ( )
cmd = [ ' pg_restore ' ]
if tools . config [ ' db_user ' ] :
cmd . append ( ' --username= ' + tools . config [ ' db_user ' ] )
if tools . config [ ' db_host ' ] :
cmd . append ( ' --host= ' + tools . config [ ' db_host ' ] )
if tools . config [ ' db_port ' ] :
cmd . append ( ' --port= ' + tools . config [ ' db_port ' ] )
cmd . append ( ' --dbname= ' + db_name )
args2 = tuple ( cmd )
buf = base64 . decodestring ( data )
if os . name == " nt " :
tmpfile = ( os . environ [ ' TMP ' ] or ' C: \\ ' ) + os . tmpnam ( )
file ( tmpfile , ' wb ' ) . write ( buf )
args2 = list ( args2 )
args2 . append ( ' ' + tmpfile )
args2 = tuple ( args2 )
stdin , stdout = tools . exec_pg_command_pipe ( * args2 )
if not os . name == " nt " :
stdin . write ( base64 . decodestring ( data ) )
stdin . close ( )
res = stdout . close ( )
if res :
raise Exception , " Couldn ' t restore database "
logger . notifyChannel ( " web-services " , netsvc . LOG_INFO ,
' RESTORE DB: %s ' % ( db_name ) )
return True
def db_exist ( self , db_name ) :
try :
db = sql_db . db_connect ( db_name )
return True
except :
return False
def list ( self ) :
db = sql_db . db_connect ( ' template1 ' )
try :
cr = db . cursor ( )
db_user = tools . config [ " db_user " ]
if not db_user and os . name == ' posix ' :
import pwd
db_user = pwd . getpwuid ( os . getuid ( ) ) [ 0 ]
if not db_user :
cr . execute ( " select usename from pg_user where usesysid=(select datdba from pg_database where datname= %s ) " , ( tools . config [ " db_name " ] , ) )
res = cr . fetchone ( )
db_user = res and res [ 0 ]
if db_user :
cr . execute ( " select datname from pg_database where datdba=(select usesysid from pg_user where usename= %s ) and datname not in ( ' template0 ' , ' template1 ' , ' postgres ' ) " , ( db_user , ) )
else :
cr . execute ( " select datname from pg_database where datname not in( ' template0 ' , ' template1 ' , ' postgres ' ) " )
res = [ name for ( name , ) in cr . fetchall ( ) ]
cr . close ( )
except :
res = [ ]
2008-09-08 22:52:36 +00:00
res . sort ( )
2008-07-22 14:24:36 +00:00
return res
def change_admin_password ( self , old_password , new_password ) :
security . check_super ( old_password )
tools . config [ ' admin_passwd ' ] = new_password
tools . config . save ( )
return True
2008-09-11 07:29:45 +00:00
2008-07-22 14:24:36 +00:00
def list_lang ( self ) :
return tools . scan_languages ( )
2008-08-20 08:20:19 +00:00
def server_version ( self ) :
""" Return the version of the server
Used by the client to verify the compatibility with its own version
"""
2008-09-12 19:45:04 +00:00
return release . version
2006-12-07 13:41:40 +00:00
db ( )
class common ( netsvc . Service ) :
2008-07-22 14:24:36 +00:00
def __init__ ( self , name = " common " ) :
netsvc . Service . __init__ ( self , name )
self . joinGroup ( " web-services " )
self . exportMethod ( self . ir_get )
self . exportMethod ( self . ir_set )
self . exportMethod ( self . ir_del )
self . exportMethod ( self . about )
self . exportMethod ( self . login )
2008-12-11 13:30:48 +00:00
self . exportMethod ( self . logout )
2008-07-22 14:24:36 +00:00
self . exportMethod ( self . timezone_get )
def ir_set ( self , db , uid , password , keys , args , name , value , replace = True , isobject = False ) :
security . check ( db , uid , password )
cr = pooler . get_db ( db ) . cursor ( )
res = ir . ir_set ( cr , uid , keys , args , name , value , replace , isobject )
cr . commit ( )
cr . close ( )
return res
def ir_del ( self , db , uid , password , id ) :
security . check ( db , uid , password )
cr = pooler . get_db ( db ) . cursor ( )
res = ir . ir_del ( cr , uid , id )
cr . commit ( )
cr . close ( )
return res
def ir_get ( self , db , uid , password , keys , args = None , meta = None , context = None ) :
if not args :
args = [ ]
if not context :
context = { }
security . check ( db , uid , password )
cr = pooler . get_db ( db ) . cursor ( )
res = ir . ir_get ( cr , uid , keys , args , meta , context )
cr . commit ( )
cr . close ( )
return res
def login ( self , db , login , password ) :
res = security . login ( db , login , password )
logger = netsvc . Logger ( )
msg = res and ' successful login ' or ' bad login or password '
2008-12-11 13:30:48 +00:00
logger . notifyChannel ( " web-service " , netsvc . LOG_INFO , " %s from ' %s ' using database ' %s ' " % ( msg , login , db . lower ( ) ) )
2008-07-22 14:24:36 +00:00
return res or False
2008-12-11 13:30:48 +00:00
def logout ( self , db , login , password ) :
2008-12-11 17:37:12 +00:00
# FIXME: WTF !!! what is this hardcoding ?
2008-12-11 13:30:48 +00:00
res = security . logout ( db , login , password )
service = netsvc . LocalService ( " object_proxy " )
fields = service . execute ( db , login , ' res.users ' , ' fields_get ' , { } )
try :
if ' current_status ' in fields . keys ( ) :
service . execute ( db , login , ' res.users ' , ' write ' , login , { ' current_status ' : False } )
emp_id = service . execute ( db , login , ' hr.employee ' , ' search ' , [ ( ' user_id ' , ' = ' , login ) ] )
emp = emp_id [ 0 ]
service . execute ( db , login , ' hr.attendance ' , ' create ' , { ' action ' : ' sign_out ' , ' employee_id ' : emp } )
cr = pooler . get_db ( db ) . cursor ( )
cr . execute ( " delete from time_sheet_remote_temp where user_id = ' %s ' " % ( res ) )
cr . commit ( )
cr . close ( )
except :
pass
logger = netsvc . Logger ( )
logger . notifyChannel ( " web-service " , netsvc . LOG_INFO , ' Logout=> %s from database %s ' % ( res , db . lower ( ) ) )
2008-12-11 17:37:12 +00:00
return True
2008-12-11 13:30:48 +00:00
2008-07-22 14:24:36 +00:00
2008-08-05 07:54:26 +00:00
def about ( self , extended = False ) :
""" Return information about the OpenERP Server.
@param extended : if True then return version info
@return string if extended is False else tuple
"""
info = _ ( '''
2006-12-07 13:41:40 +00:00
2008-04-09 11:33:09 +00:00
OpenERP is an ERP + CRM program for small and medium businesses .
2006-12-07 13:41:40 +00:00
The whole source code is distributed under the terms of the
GNU Public Licence .
( c ) 2003 - TODAY , Fabien Pinckaers - Tiny sprl ''' )
2007-04-30 10:16:11 +00:00
2008-08-05 07:54:26 +00:00
if extended :
2008-09-12 19:45:04 +00:00
return info , release . version
2008-08-05 07:54:26 +00:00
return info
2008-07-22 14:24:36 +00:00
def timezone_get ( self , db , login , password ) :
return time . tzname [ 0 ]
2006-12-07 13:41:40 +00:00
common ( )
class objects_proxy ( netsvc . Service ) :
2008-07-22 14:24:36 +00:00
def __init__ ( self , name = " object " ) :
netsvc . Service . __init__ ( self , name )
self . joinGroup ( ' web-services ' )
self . exportMethod ( self . execute )
self . exportMethod ( self . exec_workflow )
self . exportMethod ( self . obj_list )
2008-09-11 07:29:45 +00:00
2008-07-22 14:24:36 +00:00
def exec_workflow ( self , db , uid , passwd , object , method , id ) :
security . check ( db , uid , passwd )
service = netsvc . LocalService ( " object_proxy " )
res = service . exec_workflow ( db , uid , object , method , id )
return res
2008-09-11 07:29:45 +00:00
2008-07-22 14:24:36 +00:00
def execute ( self , db , uid , passwd , object , method , * args ) :
security . check ( db , uid , passwd )
service = netsvc . LocalService ( " object_proxy " )
res = service . execute ( db , uid , object , method , * args )
return res
def obj_list ( self , db , uid , passwd ) :
security . check ( db , uid , passwd )
service = netsvc . LocalService ( " object_proxy " )
res = service . obj_list ( )
return res
2006-12-07 13:41:40 +00:00
objects_proxy ( )
#
# Wizard ID: 1
# - None = end of wizard
#
# Wizard Type: 'form'
# - form
# - print
#
# Wizard datas: {}
# TODO: change local request to OSE request/reply pattern
#
class wizard ( netsvc . Service ) :
2008-07-22 14:24:36 +00:00
def __init__ ( self , name = ' wizard ' ) :
netsvc . Service . __init__ ( self , name )
self . joinGroup ( ' web-services ' )
self . exportMethod ( self . execute )
self . exportMethod ( self . create )
self . id = 0
self . wiz_datas = { }
self . wiz_name = { }
self . wiz_uid = { }
def _execute ( self , db , uid , wiz_id , datas , action , context ) :
self . wiz_datas [ wiz_id ] . update ( datas )
wiz = netsvc . LocalService ( ' wizard. ' + self . wiz_name [ wiz_id ] )
return wiz . execute ( db , uid , self . wiz_datas [ wiz_id ] , action , context )
def create ( self , db , uid , passwd , wiz_name , datas = None ) :
if not datas :
datas = { }
security . check ( db , uid , passwd )
2006-12-07 13:41:40 +00:00
#FIXME: this is not thread-safe
2008-07-22 14:24:36 +00:00
self . id + = 1
self . wiz_datas [ self . id ] = { }
self . wiz_name [ self . id ] = wiz_name
self . wiz_uid [ self . id ] = uid
return self . id
def execute ( self , db , uid , passwd , wiz_id , datas , action = ' init ' , context = None ) :
if not context :
context = { }
security . check ( db , uid , passwd )
if wiz_id in self . wiz_uid :
if self . wiz_uid [ wiz_id ] == uid :
return self . _execute ( db , uid , wiz_id , datas , action , context )
else :
raise Exception , ' AccessDenied '
else :
raise Exception , ' WizardNotFound '
2006-12-07 13:41:40 +00:00
wizard ( )
#
# TODO: set a maximum report number per user to avoid DOS attacks
#
# Report state:
# False -> True
#
class report_spool ( netsvc . Service ) :
2008-07-22 14:24:36 +00:00
def __init__ ( self , name = ' report ' ) :
netsvc . Service . __init__ ( self , name )
self . joinGroup ( ' web-services ' )
self . exportMethod ( self . report )
self . exportMethod ( self . report_get )
self . _reports = { }
self . id = 0
self . id_protect = threading . Semaphore ( )
def report ( self , db , uid , passwd , object , ids , datas = None , context = None ) :
if not datas :
datas = { }
if not context :
context = { }
security . check ( db , uid , passwd )
2008-09-11 07:29:45 +00:00
2008-07-22 14:24:36 +00:00
self . id_protect . acquire ( )
self . id + = 1
id = self . id
self . id_protect . release ( )
self . _reports [ id ] = { ' uid ' : uid , ' result ' : False , ' state ' : False , ' exception ' : None }
def go ( id , uid , ids , datas , context ) :
try :
cr = pooler . get_db ( db ) . cursor ( )
obj = netsvc . LocalService ( ' report. ' + object )
( result , format ) = obj . create ( cr , uid , ids , datas , context )
cr . close ( )
self . _reports [ id ] [ ' result ' ] = result
self . _reports [ id ] [ ' format ' ] = format
self . _reports [ id ] [ ' state ' ] = True
except Exception , exception :
import traceback
import sys
tb_s = reduce ( lambda x , y : x + y , traceback . format_exception (
sys . exc_type , sys . exc_value , sys . exc_traceback ) )
logger = netsvc . Logger ( )
2008-11-27 08:01:58 +00:00
logger . notifyChannel ( ' web-services ' , netsvc . LOG_ERROR ,
2008-07-22 14:24:36 +00:00
' Exception: %s \n %s ' % ( str ( exception ) , tb_s ) )
self . _reports [ id ] [ ' exception ' ] = exception
self . _reports [ id ] [ ' state ' ] = True
return True
thread . start_new_thread ( go , ( id , uid , ids , datas , context ) )
return id
def _check_report ( self , report_id ) :
result = self . _reports [ report_id ]
if result [ ' exception ' ] :
raise result [ ' exception ' ]
res = { ' state ' : result [ ' state ' ] }
if res [ ' state ' ] :
if tools . config [ ' reportgz ' ] :
import zlib
res2 = zlib . compress ( result [ ' result ' ] )
res [ ' code ' ] = ' zlib '
else :
#CHECKME: why is this needed???
if isinstance ( result [ ' result ' ] , unicode ) :
res2 = result [ ' result ' ] . encode ( ' latin1 ' , ' replace ' )
else :
res2 = result [ ' result ' ]
if res2 :
res [ ' result ' ] = base64 . encodestring ( res2 )
res [ ' format ' ] = result [ ' format ' ]
del self . _reports [ report_id ]
return res
def report_get ( self , db , uid , passwd , report_id ) :
security . check ( db , uid , passwd )
if report_id in self . _reports :
if self . _reports [ report_id ] [ ' uid ' ] == uid :
return self . _check_report ( report_id )
else :
raise Exception , ' AccessDenied '
else :
raise Exception , ' ReportNotFound '
2006-12-07 13:41:40 +00:00
report_spool ( )
2008-07-23 15:01:27 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: