From c4c331d6450591877c5753060ed53b802bde56f4 Mon Sep 17 00:00:00 2001 From: Michele Comitini Date: Sat, 27 Jul 2013 16:27:12 +0200 Subject: [PATCH 1/4] pbkdf2 implemented ctypes wrapper around OpenSSL native implementation: 6x speedup --- gluon/contrib/pbkdf2_ctypes.py | 86 ++++++++++++++++++++++++++++++++++ gluon/utils.py | 5 +- 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 gluon/contrib/pbkdf2_ctypes.py diff --git a/gluon/contrib/pbkdf2_ctypes.py b/gluon/contrib/pbkdf2_ctypes.py new file mode 100644 index 00000000..4f55c5b9 --- /dev/null +++ b/gluon/contrib/pbkdf2_ctypes.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +""" + pbkdf2_ctypes + ~~~~~~ + + This module implements pbkdf2 for Python using crypto lib from + openssl. + + Note: This module is intended as a plugin replacement of pbkdf2.py + by Armin Ronacher. + + + :copyright: Copyright (c) 2013: Michele Comitini + :license: LGPLv3 + +""" + +import ctypes +import hashlib +import platform + + +try: # check that we have proper OpenSSL on the system. + if platform.system()=='Windows': + if platform.architecture()[0] == '64bit': + crypto = ctypes.CDLL('libeay64.dll') + else: + crypto = ctypes.CDLL('libeay32.dll') + else: # should work on most unix os'. + crypto = ctypes.CDLL('libcrypto.so') + + PKCS5_PBKDF2_HMAC = crypto.PKCS5_PBKDF2_HMAC + + hashlib_to_crypto_map = {hashlib.md5: crypto.EVP_md5, + hashlib.sha1: crypto.EVP_sha1, + hashlib.sha256: crypto.EVP_sha256, + hashlib.sha224: crypto.EVP_sha224, + hashlib.sha384: crypto.EVP_sha384, + hashlib.sha512: crypto.EVP_sha512 + } +except OSError, AttributeError: + raise ImportError('Cannot find a compatible OpenSSL installation on your system') + + +def pkcs5_pbkdf2_hmac(data, salt, iterations=1000, keylen=24, hashfunc=None): + c_pass = ctypes.c_char_p(data) + c_passlen = ctypes.c_int(len(data)) + c_salt = ctypes.c_char_p(salt) + c_saltlen = ctypes.c_int(len(salt)) + c_iter = ctypes.c_int(iterations) + c_keylen = ctypes.c_int(keylen) + if hashfunc: + crypto_hashfunc = hashlib_to_crypto_map.get(hashfunc) + crypto_hashfunc.restype = ctypes.c_void_p + if crypto_hashfunc is None: + raise ValueError('Unknown digest' + str(hashfunc)) + c_digest = ctypes.c_void_p(crypto_hashfunc()) + else: + crypto.EVP_sha1.restype = ctypes.c_void_p + c_digest = ctypes.c_void_p(crypto.EVP_sha1()) + c_buff = ctypes.create_string_buffer('\000' * (keylen + 1)) + err = PKCS5_PBKDF2_HMAC(c_pass, c_passlen, + c_salt, c_saltlen, + c_iter, + c_digest, + c_keylen, + c_buff) + + if err == 0: + raise ValueError('wrong parameters') + return c_buff.raw[:keylen] + +def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None): + return pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc).encode('hex') + +def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None): + return pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc) + + + + +if __name__ == '__main__': + crypto.SSLeay_version.restype = ctypes.c_char_p + print crypto.SSLeay_version(0) + for h in hashlib_to_crypto_map: + pkcs5_pbkdf2_hmac('secret' * 11, 'salt', hashfunc=h) diff --git a/gluon/utils.py b/gluon/utils.py index e8f1d294..848b80ad 100644 --- a/gluon/utils.py +++ b/gluon/utils.py @@ -44,7 +44,10 @@ except ImportError: import hmac try: - from contrib.pbkdf2 import pbkdf2_hex + try: + from contrib.pbkdf2_ctypes import pbkdf2_hex + except ImportError: + from contrib.pbkdf2 import pbkdf2_hex HAVE_PBKDF2 = True except ImportError: try: From abeb4541fecc56179712cf64707edba853c34ad6 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sun, 28 Jul 2013 05:47:36 -0500 Subject: [PATCH 2/4] better autocomplete in web2pu shell --- VERSION | 2 +- applications/admin/languages/it.py | 5 +++++ applications/welcome/languages/it.py | 3 ++- gluon/shell.py | 32 +++++++++++++++++++--------- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/VERSION b/VERSION index e0fb217f..2fd1a6ba 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.6.0-development+timestamp.2013.07.27.06.46.34 +Version 2.6.0-development+timestamp.2013.07.28.05.46.47 diff --git a/applications/admin/languages/it.py b/applications/admin/languages/it.py index 6a4973cb..c97b6b39 100644 --- a/applications/admin/languages/it.py +++ b/applications/admin/languages/it.py @@ -122,6 +122,8 @@ 'edit views:': 'modifica viste (view):', 'Editing bigul': 'Editing bigul', 'Editing file "%s"': 'Modifica del file "%s"', +'Editing italo': 'Editing italo', +'Editing italo3': 'Editing italo3', 'Editing Language file': 'Modifica file linguaggio', 'Enterprise Web Framework': 'Enterprise Web Framework', 'Error logs for "%(app)s"': 'Log degli errori per "%(app)s"', @@ -205,6 +207,7 @@ 'NO': 'NO', 'No databases in this application': 'Nessun database presente in questa applicazione', 'no match': 'nessuna corrispondenza', +'no package selected': 'no package selected', 'online designer': 'online designer', 'or alternatively': 'or alternatively', 'Or Get from URL:': 'Or Get from URL:', @@ -257,6 +260,7 @@ 'selected': 'selezionato', 'session': 'session', 'session expired': 'sessions scaduta', +'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'shell', 'Site': 'sito', 'some files could not be removed': 'non è stato possibile rimuovere alcuni files', @@ -269,6 +273,7 @@ 'Stylesheet': 'Foglio di stile (stylesheet)', 'Submit': 'Submit', 'submit': 'invia', +'successful': 'successful', 'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?', 'table': 'tabella', 'test': 'test', diff --git a/applications/welcome/languages/it.py b/applications/welcome/languages/it.py index 1f17ab79..8ba02131 100644 --- a/applications/welcome/languages/it.py +++ b/applications/welcome/languages/it.py @@ -23,7 +23,7 @@ 'Administrative Interface': 'Interfaccia Amministrativa', 'Administrative interface': 'Interfaccia amministrativa', 'Ajax Recipes': 'Ajax Recipes', -'An error occured, please %s the page': "È stato rilevato un errore, prego %s la pagina", +'An error occured, please %s the page': 'È stato rilevato un errore, prego %s la pagina', 'And': 'E', 'appadmin is disabled because insecure channel': 'Amministrazione (appadmin) disabilitata: comunicazione non sicura', 'Are you sure you want to delete this object?': 'Sicuro di voler cancellare questo oggetto ?', @@ -82,6 +82,7 @@ 'Edit This App': 'Modifica questa applicazione', 'Email and SMS': 'Email e SMS', 'Email non valida': 'Email non valida', +'enter a number between %(min)g and %(max)g': 'enter a number between %(min)g and %(max)g', 'enter an integer between %(min)g and %(max)g': 'inserisci un intero tra %(min)g e %(max)g', 'Errors': 'Errori', 'Errors in form, please check it out.': 'Errori nel form, ricontrollalo', diff --git a/gluon/shell.py b/gluon/shell.py index 2970e3f6..54a52f47 100644 --- a/gluon/shell.py +++ b/gluon/shell.py @@ -30,6 +30,25 @@ from dal import BaseAdapter logger = logging.getLogger("web2py") +def enable_autocomplete_and_history(adir,env): + try: + import rlcompleter + import atexit + import readline + except ImportError: + pass + else: + readline.parse_and_bind("bind ^I rl_complete" + if sys.platform == 'darwin' + else "tab: complete") + history_file = os.path.join(adir,'.pythonhistory') + try: + readline.read_history_file(history_file) + except IOError: + open(history_file, 'a').close() + atexit.register(readline.write_history_file, history_file) + readline.set_completer(rlcompleter.Completer(env).complete) + def exec_environment( pyfile='', @@ -159,8 +178,7 @@ def run( import_models=False, startfile=None, bpython=False, - python_code=False -): + python_code=False): """ Start interactive shell or run Python script (startfile) in web2py controller environment. appname is formatted like: @@ -174,6 +192,7 @@ def run( if not a: die(errmsg) adir = os.path.join('applications', a) + if not os.path.exists(adir): if sys.stdin and not sys.stdin.name == '/dev/null': confirm = raw_input( @@ -271,14 +290,7 @@ def run( except: logger.warning( 'import IPython error; use default python shell') - try: - import readline - import rlcompleter - except ImportError: - pass - else: - readline.set_completer(rlcompleter.Completer(_env).complete) - readline.parse_and_bind('tab:complete') + enable_autocomplete_and_history(adir,_env) code.interact(local=_env) From 0019dad19c98cf827f0fd1bf015171f6f71fa4b3 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Mon, 29 Jul 2013 07:51:36 -0500 Subject: [PATCH 3/4] fixed problem with alias and record versioning --- VERSION | 2 +- gluon/dal.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 2fd1a6ba..a59a1788 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.6.0-development+timestamp.2013.07.28.05.46.47 +Version 2.6.0-development+timestamp.2013.07.29.07.50.41 diff --git a/gluon/dal.py b/gluon/dal.py index 5a0f84d9..78839328 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -8348,10 +8348,10 @@ class Table(object): if is_active and is_active in fieldnames: self._before_delete.append( lambda qset: qset.update(is_active=False)) - newquery = lambda query, t=self: \ + newquery = lambda query, t=self, name=self._tablename: \ reduce(AND,[db[tn].is_active == True for tn in db._adapter.tables(query) - if tn==t.name or getattr(db[tn],'_ot',None)==t.name]) + if tn==name or getattr(db[tn],'_ot',None)==name]) query = self._common_filter if query: newquery = query & newquery @@ -8584,7 +8584,7 @@ class Table(object): for field in self: if field.type=='upload' and field.name in fields: value = fields[field.name] - if value and not isinstance(value,str): + if value is not None and not isinstance(value,str): if hasattr(value,'file') and hasattr(value,'filename'): new_name = field.store(value.file,filename=value.filename) elif hasattr(value,'read') and hasattr(value,'name'): From 60792d943fa67f65646ebccee6fd31fd7decb698 Mon Sep 17 00:00:00 2001 From: Michele Comitini Date: Mon, 29 Jul 2013 18:59:40 +0200 Subject: [PATCH 4/4] pbkdf2_ctypes updated from upstream --- gluon/contrib/pbkdf2_ctypes.py | 51 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/gluon/contrib/pbkdf2_ctypes.py b/gluon/contrib/pbkdf2_ctypes.py index 4f55c5b9..3627abed 100644 --- a/gluon/contrib/pbkdf2_ctypes.py +++ b/gluon/contrib/pbkdf2_ctypes.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -""" +""" pbkdf2_ctypes ~~~~~~ This module implements pbkdf2 for Python using crypto lib from - openssl. - + openssl. + Note: This module is intended as a plugin replacement of pbkdf2.py by Armin Ronacher. @@ -16,30 +16,35 @@ """ import ctypes +import ctypes.util import hashlib import platform +import os.path - -try: # check that we have proper OpenSSL on the system. - if platform.system()=='Windows': +try: # check that we have proper OpenSSL on the system. + system = platform.system() + if system == 'Windows': if platform.architecture()[0] == '64bit': - crypto = ctypes.CDLL('libeay64.dll') + crypto = ctypes.CDLL(os.path.basename( + ctypes.util.find_library('libeay64'))) else: - crypto = ctypes.CDLL('libeay32.dll') - else: # should work on most unix os'. - crypto = ctypes.CDLL('libcrypto.so') + crypto = ctypes.CDLL(os.path.basename( + ctypes.util.find_library('libeay32'))) + else: + crypto = ctypes.CDLL(os.path.basename( + ctypes.util.find_library('crypto'))) PKCS5_PBKDF2_HMAC = crypto.PKCS5_PBKDF2_HMAC - hashlib_to_crypto_map = {hashlib.md5: crypto.EVP_md5, - hashlib.sha1: crypto.EVP_sha1, - hashlib.sha256: crypto.EVP_sha256, - hashlib.sha224: crypto.EVP_sha224, - hashlib.sha384: crypto.EVP_sha384, - hashlib.sha512: crypto.EVP_sha512 - } + hashlib_to_crypto_map = {hashlib.md5: crypto.EVP_md5, + hashlib.sha1: crypto.EVP_sha1, + hashlib.sha256: crypto.EVP_sha256, + hashlib.sha224: crypto.EVP_sha224, + hashlib.sha384: crypto.EVP_sha384, + hashlib.sha512: crypto.EVP_sha512} except OSError, AttributeError: - raise ImportError('Cannot find a compatible OpenSSL installation on your system') + raise ImportError('Cannot find a compatible OpenSSL installation ' + 'on your system') def pkcs5_pbkdf2_hmac(data, salt, iterations=1000, keylen=24, hashfunc=None): @@ -58,7 +63,7 @@ def pkcs5_pbkdf2_hmac(data, salt, iterations=1000, keylen=24, hashfunc=None): else: crypto.EVP_sha1.restype = ctypes.c_void_p c_digest = ctypes.c_void_p(crypto.EVP_sha1()) - c_buff = ctypes.create_string_buffer('\000' * (keylen + 1)) + c_buff = ctypes.create_string_buffer('\000' * keylen) err = PKCS5_PBKDF2_HMAC(c_pass, c_passlen, c_salt, c_saltlen, c_iter, @@ -70,15 +75,15 @@ def pkcs5_pbkdf2_hmac(data, salt, iterations=1000, keylen=24, hashfunc=None): raise ValueError('wrong parameters') return c_buff.raw[:keylen] + def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None): - return pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc).encode('hex') + return pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc).\ + encode('hex') + def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None): return pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc) - - - if __name__ == '__main__': crypto.SSLeay_version.restype = ctypes.c_char_p print crypto.SSLeay_version(0)