diff --git a/.travis.yml b/.travis.yml index 580df32..d6ed783 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,14 +6,15 @@ python: install: - pip install -r requirements.txt -matrix: # allow Python 3 to fail until web.py is Py3-ready - allow_failures: - - python: "3.7" +git: + depth: 1 before_install: pip install --upgrade pip install: pip install flake8 pytest -r requirements.txt before_script: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics -script: - - cd tests/ - - bash main.sh + +# Running unittests requires a fully-working iRedMail server. +#script: +# - cd tests/ +# - bash main.sh diff --git a/backends/bk_iredmail_ldap.py b/backends/bk_iredmail_ldap.py index 54ad670..a0ce866 100644 --- a/backends/bk_iredmail_ldap.py +++ b/backends/bk_iredmail_ldap.py @@ -57,7 +57,7 @@ def __init__(self): try: # bind as vmailadmin self.conn.bind_s(settings.iredmail_ldap_bind_dn, settings.iredmail_ldap_bind_password) - except Exception, e: + except Exception as e: web.log_error('VMAILADMIN_INVALID_CREDENTIALS. Detail: %s' % repr(e)) def __del__(self): @@ -297,7 +297,7 @@ def __add_or_remove_attr_value(dn, attr, value, action, conn=None): conn.modify_s(dn, [(ldap.MOD_ADD, attr, str(v))]) except (ldap.NO_SUCH_OBJECT, ldap.TYPE_OR_VALUE_EXISTS): pass - except Exception, e: + except Exception as e: msg += str(e) elif action in ['del', 'delete', 'remove', 'disable']: for v in values: @@ -305,7 +305,7 @@ def __add_or_remove_attr_value(dn, attr, value, action, conn=None): conn.modify_s(dn, [(ldap.MOD_DELETE, attr, str(v))]) except ldap.NO_SUCH_ATTRIBUTE: pass - except Exception, e: + except Exception as e: msg += str(e) else: return (False, 'UNKNOWN_ACTION') @@ -344,7 +344,7 @@ def __get_primary_and_alias_domains(domain, with_primary_domain=True, conn=None) return (True, all_domains) else: return (False, 'INVALID_DOMAIN_NAME') - except Exception, e: + except Exception as e: return (False, repr(e)) @@ -411,7 +411,7 @@ def add_maillist(mail, form, conn=None): conn.add_s(dn_ml, ldif_ml) logger.info('Created: {0}.'.format(mail)) return (True, ) - except Exception, e: + except Exception as e: logger.error('Error while creating {0}: {1}'.format(mail, e)) return (False, repr(e)) @@ -434,7 +434,7 @@ def remove_maillist(mail, conn=None): return (True, ) except ldap.NO_SUCH_OBJECT: return (False, 'ACCOUNT_NOT_EXIST') - except Exception, e: + except Exception as e: logger.error("Error: {0}".format(e)) return (False, repr(e)) @@ -491,7 +491,7 @@ def update_maillist(mail, form, conn=None): return (True, ) except ldap.NO_SUCH_OBJECT: return (False, 'ACCOUNT_NOT_EXIST') - except Exception, e: + except Exception as e: return (False, repr(e)) else: return (True, ) @@ -532,5 +532,5 @@ def get_existing_maillists(domains=None, conn=None): existing_lists.update(_addresses) return (True, existing_lists) - except Exception, e: + except Exception as e: return (False, repr(e)) diff --git a/backends/bk_iredmail_sql.py b/backends/bk_iredmail_sql.py index d2b648b..cd3d7a5 100644 --- a/backends/bk_iredmail_sql.py +++ b/backends/bk_iredmail_sql.py @@ -56,7 +56,7 @@ def __init__(self): except (AttributeError, MySQLdb.OperationalError): # Reconnect if error raised: MySQL server has gone away. self.conn = self.__connect() - except Exception, e: + except Exception as e: logger.error("SQL error: {0}".format(e)) @@ -77,7 +77,7 @@ def __init__(self): user=settings.iredmail_sql_db_user, pw=settings.iredmail_sql_db_password) self.conn.supports_multiple_insert = True - except Exception, e: + except Exception as e: logger.error("SQL error: {0}".format(e)) @@ -121,7 +121,7 @@ def is_domain_exists(domain, conn=None): # Domain not exist return False - except Exception, e: + except Exception as e: # Return True as exist to not allow to create new domain/account. logger.error("SQL error: {0}".format(e)) return True @@ -159,7 +159,7 @@ def is_email_exists(mail, conn=None): return True return False - except Exception, e: + except Exception as e: logger.error("SQL error: {0}".format(e)) return True @@ -186,7 +186,7 @@ def is_maillist_exists(mail, conn=None): return True return False - except Exception, e: + except Exception as e: logger.error("SQL error: {0}".format(e)) return True @@ -305,7 +305,7 @@ def add_maillist(mail, form, conn=None): logger.info('Created: {0}.'.format(mail)) return (True, ) - except Exception, e: + except Exception as e: logger.error('Error while creating {0}: {1}'.format(mail, e)) return (False, repr(e)) @@ -331,7 +331,7 @@ def remove_maillist(mail, conn=None): where='address=$mail') return (True, ) - except Exception, e: + except Exception as e: logger.error("SQL error: {0}".format(e)) return (False, repr(e)) @@ -396,7 +396,7 @@ def update_maillist(mail, form, conn=None): conn.multiple_insert('moderators', records) return (True, ) - except Exception, e: + except Exception as e: return (False, repr(e)) @@ -432,5 +432,5 @@ def get_existing_maillists(domains=None, conn=None): existing_lists.add(_addr) return (True, list(existing_lists)) - except Exception, e: + except Exception as e: return (False, repr(e)) diff --git a/backends/bk_none.py b/backends/bk_none.py index 062a76e..d326e29 100644 --- a/backends/bk_none.py +++ b/backends/bk_none.py @@ -54,7 +54,7 @@ def get_existing_maillists(domains=None, *args, **kw): except OSError: # No such directory. pass - except Exception, e: + except Exception as e: return (False, repr(e)) for fn in fns: @@ -81,7 +81,7 @@ def get_existing_maillists(domains=None, *args, **kw): except OSError: # No such directory. pass - except Exception, e: + except Exception as e: return (False, repr(e)) all_lists.sort() diff --git a/libs/daemon.py b/libs/daemon.py index c9bab23..4ef87f6 100644 --- a/libs/daemon.py +++ b/libs/daemon.py @@ -52,7 +52,7 @@ # Default daemon parameters. # File mode creation mask of the daemon. -UMASK = 0077 +UMASK = 0o077 # Default working directory for the daemon. WORKDIR = "/" @@ -164,7 +164,7 @@ def daemonize(noClose=False): if not noClose: _redirectFileDescriptors() - except Exception, e: + except Exception as e: raise DaemonError('Error during daemonizing: {0} [{1}]'.format(e.strerror, e.errno)) @@ -175,7 +175,7 @@ def daemonize(noClose=False): def _fork(): try: return os.fork() - except OSError, e: + except OSError as e: raise DaemonError('Cannot fork: {0} [{1}]'.format(e.strerror, e.errno)) @@ -196,10 +196,10 @@ def _redirectFileDescriptors(): try: os.close(fd) - except OSError, e: + except OSError as e: # File descriptor wasn't open. Ignore. logging.info('Error in _redirectFileDescriptors 1: ({0}, {1})'.format(e.errno, e.strerror)) - except Exception, e: + except Exception as e: logging.info('Error in _redirectFileDescriptors 2: ({0}, {1})'.format(e.errno, e.strerror)) # Redirect standard input, output and error to something safe. diff --git a/libs/default_settings.py b/libs/default_settings.py index 82d29ec..c33d1eb 100644 --- a/libs/default_settings.py +++ b/libs/default_settings.py @@ -46,7 +46,7 @@ MLMMJ_SKEL_DIR = '/usr/share/mlmmj/text.skel' # Default file permission for created files/directories -MLMMJ_FILE_PERMISSION = 0700 +MLMMJ_FILE_PERMISSION = 0o700 # # Absolute path to mlmmj commands. If empty, will try /usr/bin and diff --git a/libs/mlmmj.py b/libs/mlmmj.py index 6e484e0..5c80f1e 100644 --- a/libs/mlmmj.py +++ b/libs/mlmmj.py @@ -51,7 +51,7 @@ def __remove_ml_sub_dir(mail, dirname): try: shutil.rmtree(_sub_dir) logger.debug("[{0}] {1}, removed sub-directory: {2}".format(web.ctx.ip, mail, _sub_dir)) - except Exception, e: + except Exception as e: logger.error("[{0}] {1}, error while removing sub-directory: {2}".format(web.ctx.ip, mail, _sub_dir)) return (False, repr(e)) @@ -65,7 +65,7 @@ def __set_file_permission(path): try: os.chown(path, _uid, _gid) return (True, ) - except Exception, e: + except Exception as e: return (False, repr(e)) @@ -75,7 +75,7 @@ def __copy_dir_files(src, dest, create_dest=True): if not os.path.exists(dest): try: os.makedirs(dest, mode=settings.MLMMJ_FILE_PERMISSION) - except Exception, e: + except Exception as e: return (False, repr(e)) for fn in os.listdir(src): @@ -126,7 +126,7 @@ def __remove_file(path): if os.path.exists(path): try: os.remove(path) - except Exception, e: + except Exception as e: logger.error("[{0}] error while removing parameter file: {1}, {2}".format(web.ctx.ip, path, e)) return (False, repr(e)) @@ -177,7 +177,7 @@ def __get_list_param_value(mail, param, is_email=False, param_file=None): except IOError: # No such file. pass - except Exception, e: + except Exception as e: logger.error('Error while getting (list) parameter value: {0} -> {1}'.format(param, e)) _values.sort() @@ -196,7 +196,7 @@ def __get_normal_param_value(mail, param, param_file=None): except IOError: # No such file. return '' - except Exception, e: + except Exception as e: logger.error("[{0}] {1}, error while getting parameter value: {2}, {3}".format(web.ctx.ip, mail, param, e)) return '' @@ -215,7 +215,7 @@ def __get_text_param_value(mail, param, param_file=None): except IOError: # No such file. return '' - except Exception, e: + except Exception as e: logger.error("[{0}] {1}, error while getting parameter value: {2}, {3}".format(web.ctx.ip, mail, param, e)) return '' @@ -323,7 +323,7 @@ def __update_boolean_param(mail, _f = __get_param_file(mail=mail, param='moderated') open(_f, 'a').close() - except Exception, e: + except Exception as e: logger.error("[{0}] {1}, error while updating (boolean) parameter: {2} -> {3}, {4}".format( web.ctx.ip, mail, param, value, e)) return (False, repr(e)) @@ -367,7 +367,7 @@ def __update_normal_param(mail, param, value, param_file=None, is_email=False): with open(param_file, 'w') as f: f.write(value + '\n') - except Exception, e: + except Exception as e: logger.error("[{0}] {1}, error while updating (normal) parameter: {2} -> {3}, {4}".format( web.ctx.ip, mail, param, value, e)) return (False, repr(e)) @@ -404,7 +404,7 @@ def __update_list_param(mail, param, value, param_file=None, is_email=False): f.write('\n'.join(_values) + '\n') logger.info("[{0}] {1}, updated: {2} -> {3}".format(web.ctx.ip, mail, param, ', '.join(_values))) - except Exception, e: + except Exception as e: logger.error("[{0}] {1}, error while updating (list) parameter: {2} -> {3}, {4}".format( web.ctx.ip, mail, param, value, e)) return (False, repr(e)) @@ -438,7 +438,7 @@ def __update_text_param(mail, # the characters will be a mess. with open(param_file, 'w') as f: f.write(value + '\n') - except Exception, e: + except Exception as e: logger.error("[{0}] {1}, error while updating (normal) parameter: {2} -> {3}, {4}".format( web.ctx.ip, mail, param, value, e)) return (False, repr(e)) @@ -619,7 +619,7 @@ def __archive_ml(mail): # Create archive directory try: os.makedirs(_new_dir, mode=settings.MLMMJ_FILE_PERMISSION) - except Exception, e: + except Exception as e: _msg = "error while creating directory under archive directory ({0}), {1}".format(_new_dir, repr(e)) logger.error("[{0}] {1}, {2}".format(web.ctx.ip, mail, _msg)) return (False, _msg) @@ -630,7 +630,7 @@ def __archive_ml(mail): # Return new directory path return (True, _new_dir) - except Exception, e: + except Exception as e: logger.error("[{0}] {1}, error while archiving: {2} ({3} -> {4})".format(web.ctx.ip, mail, e, _dir, _new_dir)) return (False, repr(e)) @@ -668,7 +668,7 @@ def __remove_lines_in_file(path, lines): return qr return (True, ) - except Exception, e: + except Exception as e: return (False, repr(e)) @@ -699,7 +699,7 @@ def __add_lines_in_file(f, lines): nf.write(''.join(file_lines)) return (True, ) - except Exception, e: + except Exception as e: return (False, repr(e)) @@ -757,7 +757,7 @@ def __add_subscribers_with_confirm(mail, subprocess.Popen(_new_cmd, stdout=subprocess.PIPE) logger.debug("[{0}] {1}, queued confirm mail for {2}.".format(web.ctx.ip, mail, addr)) - except Exception, e: + except Exception as e: logger.error("[{0}] {1}, error while subscribing {2}: {3}".format(web.ctx.ip, mail, addr, e)) _error[addr] = repr(e) @@ -893,7 +893,7 @@ def create_ml(mail, **kwargs): if not os.path.exists(_ml_dir): try: os.makedirs(_ml_dir, mode=settings.MLMMJ_FILE_PERMISSION) - except Exception, e: + except Exception as e: _msg = "error while creating base directory ({0}), {1}".format(_ml_dir, repr(e)) logger.error("[{0}] {1}, {2}".format(web.ctx.ip, mail, _msg)) return (False, _msg) @@ -904,7 +904,7 @@ def create_ml(mail, **kwargs): if not os.path.exists(_sub_dir): try: os.makedirs(_sub_dir, mode=settings.MLMMJ_FILE_PERMISSION) - except Exception, e: + except Exception as e: _msg = "error while creating sub-directory ({0}), {1}".format(_sub_dir, repr(e)) logger.error("[{0}] {1}, {2}".format(web.ctx.ip, mail, _msg)) return (False, _msg) @@ -955,7 +955,7 @@ def delete_ml(mail, archive=True): try: shutil.rmtree(_ml_dir) logger.info("[{0}] {1}, removed without archiving.".format(web.ctx.ip, mail)) - except Exception, e: + except Exception as e: return (False, repr(e)) else: logger.info("[{0}] {1}, removed (no data on file system).".format(web.ctx.ip, mail)) @@ -1053,7 +1053,7 @@ def remove_all_subscribers(mail): qr = __remove_file(path=_path) if not qr[0]: return qr - except Exception, e: + except Exception as e: return (False, repr(e)) return (True, ) diff --git a/mlmmjadmin.py b/mlmmjadmin.py index 95b29aa..d9b7f01 100644 --- a/mlmmjadmin.py +++ b/mlmmjadmin.py @@ -26,7 +26,7 @@ sys.exit("ERROR: directory doesn't exist or incorrect permission " "(check parent directories also): {0}.".format(_dir)) -os.umask(0077) +os.umask(0o077) # Get uid/gid of daemon user. uid = pwd.getpwnam(settings.run_as_user).pw_uid diff --git a/tests/__init__.py b/tests/__init__.py index 443692e..de458d3 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1,4 @@ +from __future__ import print_function import requests import settings @@ -32,4 +33,4 @@ def delete(url, data=None): def debug(*msg): - print '[DEBUG]', msg + print('[DEBUG]', msg) diff --git a/tools/maillist_admin.py b/tools/maillist_admin.py index 5fd6093..76aee1b 100644 --- a/tools/maillist_admin.py +++ b/tools/maillist_admin.py @@ -1,3 +1,4 @@ +from __future__ import print_function # TODO list all mailing list accounts # - if `backend` is `bk_none`, list all accounts under mlmmj spool directory. # - if `backend` is not `bk_none`, query from backend. @@ -70,7 +71,7 @@ """ if len(sys.argv) < 3: - print usage + print(usage) sys.exit() # Base url of API interface @@ -94,8 +95,8 @@ 'has_subscriber', 'subscribers', 'subscribed', 'add_subscribers', 'remove_subscribers']: - print ' Invalid action: {0}. Usage:'.format(action) - print usage + print(' Invalid action: {0}. Usage:'.format(action)) + print(usage) sys.exit() mail = sys.argv[2] @@ -122,44 +123,44 @@ if k in ['footer_text', 'footer_html', 'name', 'subject_prefix']: v = v.encode('utf-8') - print '{0}={1}'.format(k, v) + print('{0}={1}'.format(k, v)) else: - print "Error: {0}".format(_json['_msg']) + print("Error: {0}".format(_json['_msg'])) elif action == 'create': if run_backend_cli: # Create account in backend qr = backend.add_maillist(mail=mail, form=arg_kvs) if not qr[0]: - print "Error while interactive with backend:", qr[1] + print("Error while interactive with backend:", qr[1]) sys.exit() r = requests.post(api_url, data=arg_kvs, headers=api_headers, verify=verify_ssl) _json = r.json() if _json['_success']: - print "Created." + print("Created.") else: - print "Error: {0}".format(_json['_msg']) + print("Error: {0}".format(_json['_msg'])) elif action == 'update': if run_backend_cli: qr = backend.update_maillist(mail=mail, form=arg_kvs) if not qr[0]: - print "Error while interactive with backend:", qr[1] + print("Error while interactive with backend:", qr[1]) sys.exit() r = requests.put(api_url, data=arg_kvs, headers=api_headers, verify=verify_ssl) _json = r.json() if _json['_success']: - print "Updated." + print("Updated.") else: - print "Error: {0}".format(_json['_msg']) + print("Error: {0}".format(_json['_msg'])) elif action == 'delete': if run_backend_cli: qr = backend.remove_maillist(mail=mail) if not qr[0]: - print "Error while interactive with backend:", qr[1] + print("Error while interactive with backend:", qr[1]) sys.exit() api_url = api_url + '?' + urlencode(arg_kvs) @@ -167,11 +168,11 @@ _json = r.json() if _json['_success']: if arg_kvs.get('archive') in ['yes', None]: - print "Removed {0} (archived).".format(mail) + print("Removed {0} (archived).".format(mail)) else: - print "Removed {0} (without archive).".format(mail) + print("Removed {0} (without archive).".format(mail)) else: - print "Error: {0}".format(_json['_msg']) + print("Error: {0}".format(_json['_msg'])) elif action == 'has_subscriber': _subscriber = args[0] @@ -179,12 +180,12 @@ r = requests.get(url, headers=api_headers, verify=verify_ssl) _json = r.json() if _json['_success']: - print '[YES] Mailing list <{0}> has subscriber <{1}>.'.format(mail, _subscriber) + print('[YES] Mailing list <{0}> has subscriber <{1}>.'.format(mail, _subscriber)) else: if '_msg' in _json: - print "Error: {0}".format(_json['_msg']) + print("Error: {0}".format(_json['_msg'])) else: - print '[NO] Mailing list <{0}> does NOT have subscriber <{1}>.'.format(mail, _subscriber) + print('[NO] Mailing list <{0}> does NOT have subscriber <{1}>.'.format(mail, _subscriber)) elif action == 'subscribers': url = api_url + '/subscribers' @@ -192,9 +193,9 @@ _json = r.json() if _json['_success']: for i in _json['_data']: - print i['mail'], '(%s)' % i['subscription'] + print(i['mail'], '(%s)' % i['subscription']) else: - print "Error: {0}".format(_json['_msg']) + print("Error: {0}".format(_json['_msg'])) elif action == 'subscribed': url = api_subscriber_url + '/subscribed' + '?' + 'query_all_lists=yes' @@ -202,38 +203,38 @@ _json = r.json() if _json['_success']: for i in _json['_data']: - print i['mail'], '(%s)' % i['subscription'] + print(i['mail'], '(%s)' % i['subscription']) else: - print "Error: {0}".format(_json['_msg']) + print("Error: {0}".format(_json['_msg'])) elif action == 'add_subscribers': url = api_url + '/subscribers' _subscribers = set([str(i).lower() for i in args if is_email(i)]) if not _subscribers: - print "Error: No subscribers given." + print("Error: No subscribers given.") sys.exit() arg_kvs['add_subscribers'] = ','.join(_subscribers) r = requests.post(url, data=arg_kvs, headers=api_headers, verify=verify_ssl) _json = r.json() if _json['_success']: - print "Added." + print("Added.") else: - print "Error: {0}".format(_json['_msg']) + print("Error: {0}".format(_json['_msg'])) elif action == 'remove_subscribers': url = api_url + '/subscribers' _subscribers = set([str(i).lower() for i in args if is_email(i)]) if not _subscribers: - print "Error: No subscribers given." + print("Error: No subscribers given.") sys.exit() arg_kvs['remove_subscribers'] = ','.join(_subscribers) r = requests.post(url, data=arg_kvs, headers=api_headers, verify=verify_ssl) _json = r.json() if _json['_success']: - print "Removed." + print("Removed.") else: - print "Error: {0}".format(_json['_msg']) + print("Error: {0}".format(_json['_msg']))