# HG changeset patch # User Pascal Volk # Date 1394391147 0 # Node ID aa346db76ee22e8d2933fe0e1633fc08da35e5a1 # Parent 935b4901d65261f528b6466bf88527757e278a66# Parent b65c3abf9ca8ffd395e5b65b5e8cbfb58eceeda8 merged changes from default(b65c3abf9ca8). diff -r b65c3abf9ca8 -r aa346db76ee2 INSTALL --- a/INSTALL Sun Mar 09 18:42:58 2014 +0000 +++ b/INSTALL Sun Mar 09 18:52:27 2014 +0000 @@ -1,26 +1,12 @@ Installation Prerequisites -You should already have installed and configured Postfix, Dovecot and -PostgreSQL. +You should already have installed and configured Postfix, Dovecot ≥ 1.2.0 +and PostgreSQL. The Virtual Mail Manager depends on: - - Python (>= 2.4.0) - - Psycopg 2¹ or pyPgSQL² + - Python (≥ 3.2) + - Psycopg¹ (≥ 2.0) -If you are using Python <= 2.5.0: - - if you want to store your users' passwords as PLAIN-MD4 digest in - the database, vmm will try to use Crypto.Hash.MD4 from PyCrypto³. - - if you are using Dovecot >= v1.1.0 and you want to store your users' - passwords as SHA256 or SSHA256 hashes, vmm will try to use - Crypto.Hash.SHA256 from PyCrypto². For SHA256/SSHA256 you should have - at least use PyCrypto in version 2.1.0alpha1. - - When the Crypto.Hash module couldn't be imported, vmm will use - dovecotpw/doveadm, if the misc.password_scheme setting in the vmm.cfg - is set to PLAIN-MD4, SHA256 or SSHA256 - -[1] Psycopg: (Debian: python-psycopg2) -[2] pyPgSQL: (Debian: python-pgsql) -[3] PyCrypto: (Debian: python-crypto) +[1] Psycopg: (Debian: python3-psycopg2) Create additionally a user and groups for improved security @@ -32,7 +18,7 @@ doveauth This will create the doveauth user and group. - For Dovecot >= 2.0 we create also the group `dovemail'. Dovecot will assign + For Dovecot ≥ 2.0 we create also the group `dovemail'. Dovecot will assign this group to all Dovecot processes. On a Debian GNU/Linux bases system run: @@ -43,7 +29,7 @@ (for more details see: http://vmm.localdomain.org/installation/postgresql_configuration.html) -* /etc/postgresql/8.4/main/pg_hba.conf +* /etc/postgresql/9.1/main/pg_hba.conf [ if you prefer to connect via TCP/IP ] # IPv4 local connections: host mailsys +mailsys 127.0.0.1/32 md5 @@ -52,7 +38,7 @@ local mailsys +mailsys md5 # reload configuration - /etc/init.d/postgresql-8.4 force-reload + /etc/init.d/postgresql force-reload * Create a database superuser if necessary: # as root run: su - postgres @@ -74,9 +60,7 @@ # connect to the new database psql mailsys vmm -W -h 127.0.0.1 - # either import the database structure for Dovecot v1.0.x/v1.1.x - \i vmm-y.x.z/pgsql/create_tables.pgsql - # or import the database structure for Dovecot v1.2.x/v2.x + # import the database structure for Dovecot ≥ 1.2.0 \i vmm-x.y.z/pgsql/create_tables-dovecot-1.2.x.pgsql # leave psql \q @@ -93,8 +77,8 @@ chmod 751 /srv/mail/* -For Dovecot >= 2.0 read the file Configure.Dovecot_2 -Configuring Dovecot v1.x +For Dovecot ≥ 2.0 read the file Configure.Dovecot_2 +Configuring Dovecot v1.2.x * /etc/dovecot/dovecot.conf # all your other settings @@ -137,8 +121,8 @@ Provide a root SETUID copy of Dovecot's deliver agent for Postfix -/!\ Only required with Dovecot v.1.x. - With Dovecot >= v2.0 use Dovecot's lmtp! +/!\ Only required with Dovecot v.1.2.x. + With Dovecot ≥ v2.0 use Dovecot's lmtp! mkdir -p /usr/local/lib/dovecot chmod 700 /usr/local/lib/dovecot @@ -154,7 +138,7 @@ Configuring Postfix's master.cf -/!\ Only required with Dovecot v.1.x. +/!\ Only required with Dovecot v.1.2.x. # Add Dovecot's deliver agent dovecot unix - n n - - pipe flags=DORhu user=nobody argv=/usr/local/lib/dovecot/deliver -f ${sender} @@ -181,7 +165,7 @@ virtual_mailbox_base = / virtual_mailbox_maps = ${proxysql}pgsql-virtual_mailbox_maps.cf - # dovecot LDA (only recommended with Dovecot v1.x) + # dovecot LDA (only recommended with Dovecot v1.2.x) #dovecot_destination_recipient_limit = 1 #virtual_transport = dovecot: @@ -219,7 +203,7 @@ # configure the Virtual Mail Manager # vmm.cfg(5) - configuration file for vmm # - # For Dovecot v1.x use 'dovecot:' as domain.transport + # For Dovecot v1.2.x use 'dovecot:' as domain.transport # When using Dovecot v2.x use 'lmtp:unix:private/dovecot-lmtp' as # domain.transport vmm configure diff -r b65c3abf9ca8 -r aa346db76ee2 README --- a/README Sun Mar 09 18:42:58 2014 +0000 +++ b/README Sun Mar 09 18:52:27 2014 +0000 @@ -18,7 +18,7 @@ • General features ‣ Unicode/UTF-8 capable (input/storage/output) - ‣ supports IDN_ (also ccTLDs/ccIDNs and 'new' gTLDs) + ‣ supports IDN_ (also `IDN ccTLDs`_/ccIDNs and 'new' gTLDs) ‣ supports the mailbox format Maildir_ and Dovecot's own high-performance mailbox formats single- and multi-\ dbox_ ‣ configurable basic mailbox structure, including sub-mailboxes @@ -76,8 +76,8 @@ Installation Prerequisites ========================== -You already should have installed and configured Postfix and Dovecot with -PostgreSQL support. You also need access to a local or remote PostgreSQL +You already should have installed and configured Postfix and Dovecot (≥ 2.0.0) +with PostgreSQL support. You also need access to a local or remote PostgreSQL server. To verify that your Dovecot and Postfix installation has support for @@ -90,22 +90,9 @@ hostname ~ # dovecot --build-options | grep postgresql SQL drivers: mysql postgresql sqlite -vmm depends on Python (≥ 2.4.0) and Psycopg_ (≥ 2.0) or pyPgSQL_ (≥ 2.5.1). -Psycopg and pyPgSQL are depending on parts of the *eGenix.com mx Base -Distribution* (mxDateTime_ and mxTools_). - -If you are using Python ≤ 2.5.0: - - • if you want to store your users' passwords as ``PLAIN-MD4`` digest in the - database, vmm will try to use ``Crypto.Hash.MD4`` from PyCrypto_ - • if you are using Dovecot ≥ v1.1.0 and you want to store your users' - passwords as ``SHA256`` or ``SSHA256`` hashes, vmm will try to use - ``Crypto.Hash.SHA256`` from PyCrypto. For ``SHA256``/``SSHA256`` you - should have installed PyCrypto, at least in version 2.1.0alpha1. - - When the Crypto.Hash module couldn't be imported, vmm will use - dovecotpw/doveadm, if the *misc.password_scheme* setting in your *vmm.cfg* - is set to ``PLAIN-MD4``, ``SHA256`` or ``SSHA256``. +Dependences +----------- +vmm (≥ 0.7.0) depends on Python (≥ 3.2) and Psycopg_ (≥ 2.0). Source code =========== @@ -138,18 +125,16 @@ details see the `COPYING` file. .. External references +.. _IDN ccTLDs: \ + http://en.wikipedia.org/wiki/Internationalized_country_code_top-level_domain .. _dbox: http://wiki2.dovecot.org/MailboxFormat/dbox .. _Dovecot: http://dovecot.org/ .. _IDN: http://en.wikipedia.org/wiki/Internationalized_domain_name .. _Maildir: http://wiki2.dovecot.org/MailboxFormat/Maildir .. _Mercurial: http://mercurial.selenic.com/ -.. _mxDateTime: http://www.egenix.com/products/python/mxBase/mxDateTime/ -.. _mxTools: http://www.egenix.com/products/python/mxBase/mxTools/ .. _Postfix: http://www.postfix.org/ .. _PostgreSQL: http://www.postgresql.org/ .. _Psycopg: http://initd.org/psycopg/ -.. _PyCrypto: http://www.pycrypto.org/ -.. _pyPgSQL: http://pypgsql.sourceforge.net/ .. _Python: http://www.python.org/ .. _relocated: http://www.postfix.org/relocated.5.html .. _transport: http://www.postfix.org/transport.5.html diff -r b65c3abf9ca8 -r aa346db76ee2 VirtualMailManager/__init__.py --- a/VirtualMailManager/__init__.py Sun Mar 09 18:42:58 2014 +0000 +++ b/VirtualMailManager/__init__.py Sun Mar 09 18:52:27 2014 +0000 @@ -32,4 +32,4 @@ locale.setlocale(locale.LC_ALL, 'C') ENCODING = locale.nl_langinfo(locale.CODESET) -gettext.install('vmm', '/usr/local/share/locale', unicode=1) +gettext.install('vmm', '/usr/local/share/locale') diff -r b65c3abf9ca8 -r aa346db76ee2 VirtualMailManager/account.py --- a/VirtualMailManager/account.py Sun Mar 09 18:42:58 2014 +0000 +++ b/VirtualMailManager/account.py Sun Mar 09 18:52:27 2014 +0000 @@ -43,7 +43,7 @@ Arguments: - `dbh` : pyPgSQL.PgSQL.Connection + `dbh` : psycopg2._psycopg.connection A database connection for the database access. `address` : VirtualMailManager.EmailAddress.EmailAddress The e-mail address of the (new) Account. @@ -57,7 +57,7 @@ # TP: Hm, what “quotation marks” should be used? # If you are unsure have a look at: # http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage - raise AErr(_(u"The domain '%s' does not exist.") % + raise AErr(_("The domain '%s' does not exist.") % self._addr.domainname, NO_SUCH_DOMAIN) self._uid = 0 self._mail = None @@ -69,7 +69,7 @@ self._new = True self._load() - def __nonzero__(self): + def __bool__(self): """Returns `True` if the Account is known, `False` if it's new.""" return not self._new @@ -86,11 +86,7 @@ self._uid, _mid, _qid, _ssid, _tid, _note = result def load_helper(ctor, own, field, dbresult): - # Py25: cur = None if own is None else getattr(own, field) - if own is None: - cur = None - else: - cur = getattr(own, field) + cur = None if own is None else getattr(own, field) if cur != dbresult: kwargs = {field: dbresult} if dbresult is None: @@ -120,8 +116,8 @@ information in the database. """ if maillocation.dovecot_version > cfg_dget('misc.dovecot_version'): - raise AErr(_(u"The mailbox format '%(mbfmt)s' requires Dovecot " - u">= v%(version)s.") % { + raise AErr(_("The mailbox format '%(mbfmt)s' requires Dovecot " + ">= v%(version)s.") % { 'mbfmt': maillocation.mbformat, 'version': version_str(maillocation.dovecot_version)}, INVALID_MAIL_LOCATION) @@ -137,7 +133,7 @@ `column` : basestring Name of the table column. Currently: qid, ssid and tid - `value` : long + `value` : int The referenced key """ if column not in ('qid', 'ssid', 'tid'): @@ -163,7 +159,7 @@ """Raise an AccountError if the Account is new - not yet saved in the database.""" if self._new: - raise AErr(_(u"The account '%s' does not exist.") % self._addr, + raise AErr(_("The account '%s' does not exist.") % self._addr, NO_SUCH_ACCOUNT) @property @@ -219,10 +215,10 @@ The password for the new Account. """ if not self._new: - raise AErr(_(u"The account '%s' already exists.") % self._addr, + raise AErr(_("The account '%s' already exists.") % self._addr, ACCOUNT_EXISTS) - if not isinstance(password, basestring) or not password: - raise AErr(_(u"Could not accept password: '%s'") % password, + if not isinstance(password, str) or not password: + raise AErr(_("Could not accept password: '%s'") % password, ACCOUNT_MISSING_PASSWORD) self._passwd = password @@ -234,36 +230,29 @@ `note` : basestring or None The note, or None to remove """ - assert note is None or isinstance(note, basestring) + assert note is None or isinstance(note, str) self._note = note def save(self): """Save the new Account in the database.""" if not self._new: - raise AErr(_(u"The account '%s' already exists.") % self._addr, + raise AErr(_("The account '%s' already exists.") % self._addr, ACCOUNT_EXISTS) if not self._passwd: - raise AErr(_(u"No password set for account: '%s'") % self._addr, + raise AErr(_("No password set for account: '%s'") % self._addr, ACCOUNT_MISSING_PASSWORD) self._prepare(MailLocation(self._dbh, mbfmt=cfg_dget('mailbox.format'), directory=cfg_dget('mailbox.root'))) dbc = self._dbh.cursor() - qid = ssid = tid = None - if self._qlimit: - qid = self._qlimit.qid - if self._services: - ssid = self._services.ssid - if self._transport: - tid = self._transport.tid dbc.execute('INSERT INTO users (local_part, passwd, uid, gid, mid, ' 'qid, ssid, tid, note) ' 'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)', (self._addr.localpart, pwhash(self._passwd, user=self._addr), self._uid, - self._domain.gid, self._mail.mid, qid, ssid, tid, -# self._qlimit.qid if self._qlimit else None, -# self._services.ssid if self._services else None, -# self._transport.tid if self._transport else None, + self._domain.gid, self._mail.mid, + self._qlimit.qid if self._qlimit else None, + self._services.ssid if self._services else None, + self._transport.tid if self._transport else None, self._note)) self._dbh.commit() dbc.close() @@ -272,25 +261,45 @@ def modify(self, field, value): """Update the Account's *field* to the new *value*. - Possible values for *field* are: 'name', 'password', 'note'. + Possible values for *field* are: 'name', 'note' and 'pwhash'. Arguments: - `field` : basestring - The attribute name: 'name', 'password' or 'note' - `value` : basestring + `field` : str + The attribute name: 'name', 'note' or 'pwhash' + `value` : str The new value of the attribute. """ - if field not in ('name', 'password', 'note'): - raise AErr(_(u"Unknown field: '%s'") % field, INVALID_ARGUMENT) + if field not in ('name', 'note', 'pwhash'): + raise AErr(_("Unknown field: '%s'") % field, INVALID_ARGUMENT) + if field == 'pwhash': + field = 'passwd' self._chk_state() dbc = self._dbh.cursor() - if field == 'password': - dbc.execute('UPDATE users SET passwd = %s WHERE uid = %s', - (pwhash(value, user=self._addr), self._uid)) - else: - dbc.execute('UPDATE users SET %s = %%s WHERE uid = %%s' % field, - (value, self._uid)) + dbc.execute('UPDATE users SET %s = %%s WHERE uid = %%s' % field, + (value, self._uid)) + if dbc.rowcount > 0: + self._dbh.commit() + dbc.close() + + def update_password(self, password, scheme=None): + """Update the Account's password. + + The given *password* will be hashed using password.pwhash. + When no *scheme* is specified, the configured scheme + (misc.password_scheme) will be used. + + Arguments: + + `password' : str + The Account's new plain text password + `scheme' : str + The password scheme used for password hashing; default None + """ + self._chk_state() + dbc = self._dbh.cursor() + dbc.execute('UPDATE users SET passwd = %s WHERE uid = %s', + (pwhash(password, scheme, self._addr), self.uid)) if dbc.rowcount > 0: self._dbh.commit() dbc.close() @@ -303,9 +312,6 @@ `quotalimit` : VirtualMailManager.quotalimit.QuotaLimit the new quota limit of the domain. """ - if cfg_dget('misc.dovecot_version') < 0x10102f00: - raise VMMError(_(u'PostgreSQL-based dictionary quota requires ' - u'Dovecot >= v1.1.2.'), VMM_ERROR) self._chk_state() if quotalimit == self._qlimit: return @@ -364,7 +370,7 @@ fmt = format_domain_default ret = {} - for service, state in services.iteritems(): + for service, state in services.items(): # TP: A service (e.g. pop3 or imap) may be enabled/usable or # disabled/unusable for a user. ret[service] = fmt((_('disabled'), _('enabled'))[state]) @@ -387,7 +393,7 @@ info = dbc.fetchone() dbc.close() if info: - info = dict(zip(('name', 'uq_bytes', 'uq_messages'), info)) + info = dict(list(zip(('name', 'uq_bytes', 'uq_messages'), info))) info.update(self._get_info_serviceset()) info['address'] = self._addr info['gid'] = self._domain.gid @@ -406,7 +412,7 @@ info['uid'] = self._uid return info # nearly impossible‽ - raise AErr(_(u"Could not fetch information for account: '%s'") % + raise AErr(_("Could not fetch information for account: '%s'") % self._addr, NO_SUCH_ACCOUNT) def get_aliases(self): @@ -450,8 +456,8 @@ a_count = self._count_aliases() if a_count > 0: dbc.close() - raise AErr(_(u"There are %(count)d aliases with the " - u"destination address '%(address)s'.") % + raise AErr(_("There are %(count)d aliases with the " + "destination address '%(address)s'.") % {'count': a_count, 'address': self._addr}, ALIAS_PRESENT) dbc.execute('DELETE FROM users WHERE uid = %s', (self._uid,)) @@ -471,17 +477,17 @@ Argument: - `uid` : long + `uid` : int The Account unique ID. - `dbh` : pyPgSQL.PgSQL.Connection + `dbh` : psycopg2._psycopg.connection a database connection for the database access. """ try: - uid = long(uid) + uid = int(uid) except ValueError: - raise AErr(_(u'UID must be an int/long.'), INVALID_ARGUMENT) + raise AErr(_('UID must be an integer.'), INVALID_ARGUMENT) if uid < 1: - raise AErr(_(u'UID must be greater than 0.'), INVALID_ARGUMENT) + raise AErr(_('UID must be greater than 0.'), INVALID_ARGUMENT) dbc = dbh.cursor() dbc.execute("SELECT local_part||'@'|| domain_name.domainname AS address, " "uid, users.gid, note FROM users LEFT JOIN domain_name ON " @@ -490,9 +496,9 @@ info = dbc.fetchone() dbc.close() if not info: - raise AErr(_(u"There is no account with the UID: '%d'") % uid, + raise AErr(_("There is no account with the UID: '%d'") % uid, NO_SUCH_ACCOUNT) - info = dict(zip(('address', 'uid', 'gid', 'note'), info)) + info = dict(list(zip(('address', 'uid', 'gid', 'note'), info))) return info del _, cfg_dget diff -r b65c3abf9ca8 -r aa346db76ee2 VirtualMailManager/alias.py --- a/VirtualMailManager/alias.py Sun Mar 09 18:42:58 2014 +0000 +++ b/VirtualMailManager/alias.py Sun Mar 09 18:52:27 2014 +0000 @@ -13,7 +13,6 @@ EmailAddress, DestinationEmailAddress as DestAddr from VirtualMailManager.errors import AliasError as AErr from VirtualMailManager.ext.postconf import Postconf -from VirtualMailManager.pycompat import all from VirtualMailManager.constants import \ ALIAS_EXCEEDS_EXPANSION_LIMIT, NO_SUCH_ALIAS, NO_SUCH_DOMAIN @@ -32,7 +31,7 @@ self._dbh = dbh self._gid = get_gid(self._dbh, self._addr.domainname) if not self._gid: - raise AErr(_(u"The domain '%s' does not exist.") % + raise AErr(_("The domain '%s' does not exist.") % self._addr.domainname, NO_SUCH_DOMAIN) self._dests = [] @@ -52,20 +51,20 @@ def _check_expansion(self, count_new): """Checks the current expansion limit of the alias.""" postconf = Postconf(cfg_dget('bin.postconf')) - limit = long(postconf.read('virtual_alias_expansion_limit')) + limit = int(postconf.read('virtual_alias_expansion_limit')) dcount = len(self._dests) failed = False if dcount == limit or dcount + count_new > limit: failed = True errmsg = _( -u"""Cannot add %(count_new)i new destination(s) to alias '%(address)s'. +"""Cannot add %(count_new)i new destination(s) to alias '%(address)s'. Currently this alias expands into %(count)i/%(limit)i recipients. %(count_new)i additional destination(s) will render this alias unusable. Hint: Increase Postfix' virtual_alias_expansion_limit""") elif dcount > limit: failed = True errmsg = _( -u"""Cannot add %(count_new)i new destination(s) to alias '%(address)s'. +"""Cannot add %(count_new)i new destination(s) to alias '%(address)s'. This alias already exceeds its expansion limit (%(count)i/%(limit)i). So its unusable, all messages addressed to this alias will be bounced. Hint: Delete some destination addresses.""") @@ -152,7 +151,7 @@ if not warnings is None: warnings.append(self._addr) if not self._dests: - raise AErr(_(u"The alias '%s' does not exist.") % self._addr, + raise AErr(_("The alias '%s' does not exist.") % self._addr, NO_SUCH_ALIAS) unknown = destinations.difference(set(self._dests)) if unknown: @@ -160,8 +159,8 @@ if not warnings is None: warnings.extend(unknown) if not destinations: - raise AErr(_(u"No suitable destinations left to remove from alias " - u"'%s'.") % self._addr, NO_SUCH_ALIAS) + raise AErr(_("No suitable destinations left to remove from alias " + "'%s'.") % self._addr, NO_SUCH_ALIAS) self._delete(destinations) for destination in destinations: self._dests.remove(destination) @@ -169,14 +168,14 @@ def get_destinations(self): """Returns an iterator for all destinations of the alias.""" if not self._dests: - raise AErr(_(u"The alias '%s' does not exist.") % self._addr, + raise AErr(_("The alias '%s' does not exist.") % self._addr, NO_SUCH_ALIAS) return iter(self._dests) def delete(self): """Deletes the alias with all its destinations.""" if not self._dests: - raise AErr(_(u"The alias '%s' does not exist.") % self._addr, + raise AErr(_("The alias '%s' does not exist.") % self._addr, NO_SUCH_ALIAS) self._delete() del self._dests[:] diff -r b65c3abf9ca8 -r aa346db76ee2 VirtualMailManager/aliasdomain.py --- a/VirtualMailManager/aliasdomain.py Sun Mar 09 18:42:58 2014 +0000 +++ b/VirtualMailManager/aliasdomain.py Sun Mar 09 18:52:27 2014 +0000 @@ -27,7 +27,7 @@ Arguments: - `dbh` : pyPgSQL.PgSQL.Connection + `dbh` : psycopg2._psycopg.connection a database connection for the database access `domainname` : basestring the name of the AliasDomain""" @@ -47,7 +47,7 @@ dbc.close() if result: if result[1]: - raise ADErr(_(u"The domain '%s' is a primary domain.") % + raise ADErr(_("The domain '%s' is a primary domain.") % self._name, ALIASDOMAIN_ISDOMAIN) self._gid = result[0] @@ -66,13 +66,13 @@ def save(self): """Stores information about the new AliasDomain in the database.""" if self._gid > 0: - raise ADErr(_(u"The alias domain '%s' already exists.") % + raise ADErr(_("The alias domain '%s' already exists.") % self._name, ALIASDOMAIN_EXISTS) if not self._domain: - raise ADErr(_(u'No destination domain set for the alias domain.'), + raise ADErr(_('No destination domain set for the alias domain.'), ALIASDOMAIN_NO_DOMDEST) if self._domain.gid < 1: - raise ADErr(_(u"The target domain '%s' does not exist.") % + raise ADErr(_("The target domain '%s' does not exist.") % self._domain.name, NO_SUCH_DOMAIN) dbc = self._dbh.cursor() dbc.execute('INSERT INTO domain_name (domainname, gid, is_primary) ' @@ -85,7 +85,7 @@ """Returns a dict (keys: "alias" and "domain") with the names of the AliasDomain and its primary domain.""" if self._gid < 1: - raise ADErr(_(u"The alias domain '%s' does not exist.") % + raise ADErr(_("The alias domain '%s' does not exist.") % self._name, NO_SUCH_ALIASDOMAIN) dbc = self._dbh.cursor() dbc.execute('SELECT domainname FROM domain_name WHERE gid = %s AND ' @@ -95,25 +95,25 @@ if domain: return {'alias': self._name, 'domain': domain[0]} else: # an almost unlikely case, isn't it? - raise ADErr(_(u'There is no primary domain for the alias domain ' - u"'%s'.") % self._name, NO_SUCH_DOMAIN) + raise ADErr(_('There is no primary domain for the alias domain ' + "'%s'.") % self._name, NO_SUCH_DOMAIN) def switch(self): """Switch the destination of the AliasDomain to the new destination, set with the method `set_destination()`. """ if not self._domain: - raise ADErr(_(u'No destination domain set for the alias domain.'), + raise ADErr(_('No destination domain set for the alias domain.'), ALIASDOMAIN_NO_DOMDEST) if self._domain.gid < 1: - raise ADErr(_(u"The target domain '%s' does not exist.") % + raise ADErr(_("The target domain '%s' does not exist.") % self._domain.name, NO_SUCH_DOMAIN) if self._gid < 1: - raise ADErr(_(u"The alias domain '%s' does not exist.") % + raise ADErr(_("The alias domain '%s' does not exist.") % self._name, NO_SUCH_ALIASDOMAIN) if self._gid == self._domain.gid: - raise ADErr(_(u"The alias domain '%(alias)s' is already assigned " - u"to the domain '%(domain)s'.") % + raise ADErr(_("The alias domain '%(alias)s' is already assigned " + "to the domain '%(domain)s'.") % {'alias': self._name, 'domain': self._domain.name}, ALIASDOMAIN_EXISTS) dbc = self._dbh.cursor() @@ -130,7 +130,7 @@ Raises an AliasDomainError if the AliasDomain doesn't exist. """ if self._gid < 1: - raise ADErr(_(u"The alias domain '%s' does not exist.") % + raise ADErr(_("The alias domain '%s' does not exist.") % self._name, NO_SUCH_ALIASDOMAIN) dbc = self._dbh.cursor() dbc.execute('DELETE FROM domain_name WHERE domainname = %s AND NOT ' diff -r b65c3abf9ca8 -r aa346db76ee2 VirtualMailManager/catchall.py --- a/VirtualMailManager/catchall.py Sun Mar 09 18:42:58 2014 +0000 +++ b/VirtualMailManager/catchall.py Sun Mar 09 18:52:27 2014 +0000 @@ -23,7 +23,6 @@ EmailAddress, DestinationEmailAddress as DestAddr from VirtualMailManager.errors import AliasError as AErr from VirtualMailManager.ext.postconf import Postconf -from VirtualMailManager.pycompat import all from VirtualMailManager.constants import \ ALIAS_EXCEEDS_EXPANSION_LIMIT, NO_SUCH_ALIAS, NO_SUCH_DOMAIN @@ -41,7 +40,7 @@ self._dbh = dbh self._gid = get_gid(self._dbh, self.domain) if not self._gid: - raise AErr(_(u"The domain '%s' does not exist.") % + raise AErr(_("The domain '%s' does not exist.") % self.domain, NO_SUCH_DOMAIN) self._dests = [] @@ -60,13 +59,13 @@ def _check_expansion(self, count_new): """Checks the current expansion limit of the alias.""" postconf = Postconf(cfg_dget('bin.postconf')) - limit = long(postconf.read('virtual_alias_expansion_limit')) + limit = int(postconf.read('virtual_alias_expansion_limit')) dcount = len(self._dests) failed = False if dcount == limit or dcount + count_new > limit: failed = True errmsg = _( -u"""Cannot add %(count_new)i new destination(s) to catch-all alias for +"""Cannot add %(count_new)i new destination(s) to catch-all alias for domain '%(domain)s'. Currently this alias expands into %(count)i/%(limit)i recipients. %(count_new)i additional destination(s) will render this alias unusable. @@ -74,7 +73,7 @@ elif dcount > limit: failed = True errmsg = _( -u"""Cannot add %(count_new)i new destination(s) to catch-all alias for domain +"""Cannot add %(count_new)i new destination(s) to catch-all alias for domain '%(domain)s'. This alias already exceeds its expansion limit \ (%(count)i/%(limit)i). So its unusable, all messages addressed to this alias will be bounced. @@ -148,16 +147,16 @@ if not warnings is None: assert isinstance(warnings, list) if not self._dests: - raise AErr(_(u"There are no catch-all aliases defined for " - u"domain '%s'.") % self._domain, NO_SUCH_ALIAS) + raise AErr(_("There are no catch-all aliases defined for " + "domain '%s'.") % self._domain, NO_SUCH_ALIAS) unknown = destinations.difference(set(self._dests)) if unknown: destinations.intersection_update(set(self._dests)) if not warnings is None: warnings.extend(unknown) if not destinations: - raise AErr(_(u"No suitable destinations left to remove from the " - u"catch-all alias of domain '%s'.") % self._domain, + raise AErr(_("No suitable destinations left to remove from the " + "catch-all alias of domain '%s'.") % self._domain, NO_SUCH_ALIAS) self._delete(destinations) for destination in destinations: @@ -166,15 +165,15 @@ def get_destinations(self): """Returns an iterator for all destinations of the catchall alias.""" if not self._dests: - raise AErr(_(u"There are no catch-all aliases defined for " - u"domain '%s'.") % self._domain, NO_SUCH_ALIAS) + raise AErr(_("There are no catch-all aliases defined for " + "domain '%s'.") % self._domain, NO_SUCH_ALIAS) return iter(self._dests) def delete(self): """Deletes all catchall destinations for the domain.""" if not self._dests: - raise AErr(_(u"There are no catch-all aliases defined for " - u"domain '%s'.") % self._domain, NO_SUCH_ALIAS) + raise AErr(_("There are no catch-all aliases defined for " + "domain '%s'.") % self._domain, NO_SUCH_ALIAS) self._delete() del self._dests[:] diff -r b65c3abf9ca8 -r aa346db76ee2 VirtualMailManager/cli/__init__.py --- a/VirtualMailManager/cli/__init__.py Sun Mar 09 18:42:58 2014 +0000 +++ b/VirtualMailManager/cli/__init__.py Sun Mar 09 18:52:27 2014 +0000 @@ -19,19 +19,20 @@ from VirtualMailManager.errors import VMMError -__all__ = ('prog', 'get_winsize', 'read_pass', 'w_err', 'w_std') +__all__ = ('get_winsize', 'read_pass', 'w_err', 'w_std') _ = lambda msg: msg _std_write = os.sys.stdout.write _err_write = os.sys.stderr.write -prog = os.path.basename(os.sys.argv[0]) def w_std(*args): """Writes a line for each arg of *args*, encoded in the current ENCODING, to stdout. """ - _std_write('\n'.join(a.encode(ENCODING, 'replace') for a in args) + '\n') + _std_write('\n'.join(arg.encode(ENCODING, 'replace').decode(ENCODING, + 'replace') + for arg in args) + '\n') def w_err(code, *args): @@ -40,7 +41,9 @@ This function optionally interrupts the program execution if *code* does not equal to 0. *code* will be used as the system exit status. """ - _err_write('\n'.join(a.encode(ENCODING, 'replace') for a in args) + '\n') + _err_write('\n'.join(arg.encode(ENCODING, 'replace').decode(ENCODING, + 'replace') + for arg in args) + '\n') if code: os.sys.exit(code) @@ -75,24 +78,24 @@ Throws a VMMError after the third failure. """ # TP: Please preserve the trailing space. - readp_msg0 = _(u'Enter new password: ').encode(ENCODING, 'replace') + readp_msg0 = _('Enter new password: ') # TP: Please preserve the trailing space. - readp_msg1 = _(u'Retype new password: ').encode(ENCODING, 'replace') + readp_msg1 = _('Retype new password: ') mismatched = True failures = 0 while mismatched: if failures > 2: - raise VMMError(_(u'Too many failures - try again later.'), + raise VMMError(_('Too many failures - try again later.'), VMM_TOO_MANY_FAILURES) clear0 = getpass(prompt=readp_msg0) clear1 = getpass(prompt=readp_msg1) if clear0 != clear1: failures += 1 - w_err(0, _(u'Sorry, passwords do not match.')) + w_err(0, _('Sorry, passwords do not match.')) continue if not clear0: failures += 1 - w_err(0, _(u'Sorry, empty passwords are not permitted.')) + w_err(0, _('Sorry, empty passwords are not permitted.')) continue mismatched = False return clear0 diff -r b65c3abf9ca8 -r aa346db76ee2 VirtualMailManager/cli/clihelp.py --- a/VirtualMailManager/cli/clihelp.py Sun Mar 09 18:42:58 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,261 +0,0 @@ -# -*- coding: UTF-8 -*- -# Copyright (c) 2012 - 2014, Pascal Volk -# See COPYING for distribution information. -""" - VirtualMailManager.cli.vmmhelp - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Virtual Mail Manager's command line help. -""" - -_ = lambda msg: msg - -help_msgs = { -# TP: There are some words enclosed within angle brackets '<'word'>'. They -# are used to indicate replaceable arguments. Please do not translate them. -# -# The descriptions of subcommands may contain the both keywords 'domain' -# and 'force', enclosed within single quotes. Please keep them as they are. -# - # TP: description of subcommand configget - 'configget': (_(u"""This subcommand is used to display the actual value -of the given configuration