merged changes from default(29295afafa91) v0.7.x
authorPascal Volk <user@localhost.localdomain.org>
Sun, 09 Dec 2012 15:03:33 +0000
branchv0.7.x
changeset 662 9ec7770193ad
parent 660 0bce7e1d1349 (diff)
parent 661 29295afafa91 (current diff)
child 663 de435d1aa1c0
merged changes from default(29295afafa91)
VirtualMailManager/account.py
--- a/VirtualMailManager/__init__.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/__init__.py	Sun Dec 09 15:03:33 2012 +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')
--- a/VirtualMailManager/account.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/account.py	Sun Dec 09 15:03:33 2012 +0000
@@ -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)
@@ -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()
@@ -282,7 +271,7 @@
           The new value of the attribute.
         """
         if field not in ('name', 'password', 'note'):
-            raise AErr(_(u"Unknown field: '%s'") % field, INVALID_ARGUMENT)
+            raise AErr(_("Unknown field: '%s'") % field, INVALID_ARGUMENT)
         self._chk_state()
         dbc = self._dbh.cursor()
         if field == 'password':
@@ -304,8 +293,8 @@
           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)
+            raise VMMError(_('PostgreSQL-based dictionary quota requires '
+                             'Dovecot >= v1.1.2.'), VMM_ERROR)
         self._chk_state()
         if quotalimit == self._qlimit:
             return
@@ -364,7 +353,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 +376,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 +395,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 +439,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,))
@@ -477,11 +466,11 @@
       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 int/long.'), 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 +479,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
--- a/VirtualMailManager/alias.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/alias.py	Sun Dec 09 15:03:33 2012 +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[:]
--- a/VirtualMailManager/aliasdomain.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/aliasdomain.py	Sun Dec 09 15:03:33 2012 +0000
@@ -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 '
--- a/VirtualMailManager/catchall.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/catchall.py	Sun Dec 09 15:03:33 2012 +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[:]
 
--- a/VirtualMailManager/cli/__init__.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/cli/__init__.py	Sun Dec 09 15:03:33 2012 +0000
@@ -31,7 +31,9 @@
     """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 +42,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 +79,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: ').encode(ENCODING, 'replace')
     # TP: Please preserve the trailing space.
-    readp_msg1 = _(u'Retype new password: ').encode(ENCODING, 'replace')
+    readp_msg1 = _('Retype new password: ').encode(ENCODING, 'replace')
     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
--- a/VirtualMailManager/cli/clihelp.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/cli/clihelp.py	Sun Dec 09 15:03:33 2012 +0000
@@ -18,242 +18,242 @@
 # 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
+    'configget': (_("""This subcommand is used to display the actual value
 of the given configuration <option>."""),),
     # TP: description of subcommand configset
-    'configset': (_(u"""Use this subcommand to set or update a single
+    'configset': (_("""Use this subcommand to set or update a single
 configuration option's value. <option> is the configuration option, <value>
 is the <option>'s new value."""),
-_(u"""Note: This subcommand will create a new vmm.cfg without any comments.
+_("""Note: This subcommand will create a new vmm.cfg without any comments.
 Your current configuration file will be backed as vmm.cfg.bak."""),),
     # TP: description of subcommand configure
-    'configure': (_(u"""Starts the interactive configuration for all
+    'configure': (_("""Starts the interactive configuration for all
 configuration sections."""),
-_(u"""In this process the currently set value of each option will be displayed
+_("""In this process the currently set value of each option will be displayed
 in square brackets. If no value is configured, the default value of each
 option will be displayed in square brackets. Press the return key, to accept
 the displayed value."""),
-_(u"""If the optional argument <section> is given, only the configuration
+_("""If the optional argument <section> is given, only the configuration
 options from the given section will be displayed and will be configurable.
 The following sections are available:
 """),
 """    account, bin, database, domain, mailbox, misc""",
-_(u"""All configuration options are described in vmm.cfg(5)."""),
-_(u"""Note: This subcommand will create a new vmm.cfg without any comments.
+_("""All configuration options are described in vmm.cfg(5)."""),
+_("""Note: This subcommand will create a new vmm.cfg without any comments.
 Your current configuration file will be backed as vmm.cfg.bak."""),),
     # TP: description of subcommand getuser
-    'getuser': (_(u"""If only the <uid> is available, for example from process
+    'getuser': (_("""If only the <uid> is available, for example from process
 list, the subcommand getuser will show the user's address."""),),
     # TP: description of subcommand listaddresses
-    'listaddresses': (_(u"""This command lists all defined addresses.
+    'listaddresses': (_("""This command lists all defined addresses.
 Addresses belonging to alias-domains are prefixed with a '-', addresses of
 regular domains with a '+'. Additionally, the letters 'u', 'a', and 'r'
 indicate the type of each address: user, alias and relocated respectively.
 The output can be limited with an optional <pattern>."""),
-_(u"""To perform a wild card search, the % character can be used at the start
+_("""To perform a wild card search, the % character can be used at the start
 and/or the end of the <pattern>."""),),
     # TP: description of subcommand listaliases
-    'listaliases': (_(u"""This command lists all defined aliases. Aliases
+    'listaliases': (_("""This command lists all defined aliases. Aliases
 belonging to alias-domains are prefixed with a '-', addresses of regular
 domains with a '+'. The output can be limited with an optional <pattern>."""),
-_(u"""To perform a wild card search, the % character can be used at the start
+_("""To perform a wild card search, the % character can be used at the start
 and/or the end of the <pattern>."""),),
     # TP: description of subcommand listdomains
-    'listdomains': (_(u"""This subcommand lists all available domains. All
+    'listdomains': (_("""This subcommand lists all available domains. All
 domain names will be prefixed either with `[+]', if the domain is a primary
 domain, or with `[-]', if it is an alias domain name. The output can be
 limited with an optional <pattern>."""),
-_(u"""To perform a wild card search, the % character can be used at the start
+_("""To perform a wild card search, the % character can be used at the start
 and/or the end of the <pattern>."""),),
     # TP: description of subcommand listpwschemes
-    'listpwschemes': (_(u"""This subcommand lists all password schemes which
+    'listpwschemes': (_("""This subcommand lists all password schemes which
 could be used in the vmm.cfg as value of the misc.password_scheme option.
 The output varies, depending on the used Dovecot version and the system's
 libc."""),
-_(u"""When your Dovecot installation isn't too old, you will see additionally
+_("""When your Dovecot installation isn't too old, you will see additionally
 a few usable encoding suffixes. One of them can be appended to the password
 scheme."""),),
     # TP: description of subcommand listrelocated
-    'listrelocated': (_(u"""This command lists all defined relocated addresses.
+    'listrelocated': (_("""This command lists all defined relocated addresses.
 Relocated entries belonging to alias-domains are prefixed with a '-', addresses
 of regular domains with a '+'. The output can be limited with an optional
 <pattern>."""),
-_(u"""To perform a wild card search, the % character can be used at the start
+_("""To perform a wild card search, the % character can be used at the start
 and/or the end of the <pattern>."""),),
     # TP: description of subcommand listusers
-    'listusers': (_(u"""This command lists all user accounts. User accounts
+    'listusers': (_("""This command lists all user accounts. User accounts
 belonging to alias-domains are prefixed with a '-', addresses of regular
 domains with a '+'. The output can be limited with an optional <pattern>."""),
-_(u"""To perform a wild card search, the % character can be used at the start
+_("""To perform a wild card search, the % character can be used at the start
 and/or the end of the pattern."""),),
     # TP: description of subcommand version
-    'version': (_(u"""Prints vmm's version and copyright information to stdout.
+    'version': (_("""Prints vmm's version and copyright information to stdout.
 After this vmm exits."""),),
     # TP: description of subcommand domainadd
-    'domainadd': (_(u"""Adds the new domain into the database and creates the
+    'domainadd': (_("""Adds the new domain into the database and creates the
 domain directory."""),
-_(u"""If the optional argument <transport> is given, it will override the
+_("""If the optional argument <transport> is given, it will override the
 default transport (domain.transport) from vmm.cfg. The specified <transport>
 will be the default transport for all new accounts in this domain."""),
-_(u"""Configuration-related behavior:"""),
-u""" * domain.auto_postmaster""",
-_(u"""When that option is set to true (default) vmm will automatically create
+_("""Configuration-related behavior:"""),
+""" * domain.auto_postmaster""",
+_("""When that option is set to true (default) vmm will automatically create
 the postmaster account for the new domain and prompt for postmaster@<fqdn>'s
 password."""),
-u""" * account.random_password""",
-_(u"""When the value of that option is also set to true, vmm will automatically
+""" * account.random_password""",
+_("""When the value of that option is also set to true, vmm will automatically
 create the postmaster account for the new domain and print the generated
 postmaster password to stdout."""),),
     # TP: description of subcommand domaindelete
-    'domaindelete': (_(u"""This subcommand deletes the domain specified by
+    'domaindelete': (_("""This subcommand deletes the domain specified by
 <fqdn>."""),
-_(u"""If there are accounts, aliases and/or relocated users assigned to the
+_("""If there are accounts, aliases and/or relocated users assigned to the
 given domain, vmm will abort the requested operation and show an error
 message. If you know, what you are doing, you can specify the optional keyword
 'force'."""),
-_(u"""If you really always know what you are doing, edit your vmm.cfg and set
+_("""If you really always know what you are doing, edit your vmm.cfg and set
 the option domain.force_deletion to true."""),),
     # TP: description of subcommand domaininfo
-    'domaininfo': (_(u"""This subcommand shows some information about the
+    'domaininfo': (_("""This subcommand shows some information about the
 given domain."""),
-_(u"""For a more detailed information about the domain the optional argument
+_("""For a more detailed information about the domain the optional argument
 <details> can be specified. A possible <details> value can be one of the
 following six keywords:"""),
 """    accounts, aliasdomains, aliases, catchall, relocated, full""",),
     # TP: description of subcommand domainquota
-    'domainquota': (_(u"""This subcommand is used to configure a new quota
+    'domainquota': (_("""This subcommand is used to configure a new quota
 limit for the accounts of the domain - not for the domain itself."""),
-_(u"""The default quota limit for accounts is defined in the vmm.cfg
+_("""The default quota limit for accounts is defined in the vmm.cfg
 (domain.quota_bytes and domain.quota_messages)."""),
-_(u"""The new quota limit will affect only those accounts for which the limit
+_("""The new quota limit will affect only those accounts for which the limit
 has not been overridden. If you want to restore the default to all accounts,
 you may pass the keyword 'force'. When the argument <messages> was omitted the
 default number of messages 0 (zero) will be applied."""),),
     # TP: description of subcommand domainservices
-    'domainservices': (_(u"""To define which services could be used by the
+    'domainservices': (_("""To define which services could be used by the
 users of the domain — with the given <fqdn> — use this subcommand."""),
-_(u"""Each specified <service> will be enabled/usable. All other services
+_("""Each specified <service> will be enabled/usable. All other services
 will be deactivated/unusable. Possible <service> names are:"""),
-u"""    imap, pop3, sieve, smtp""",
-_(u"""The new service set will affect only those accounts for which the set has
+"""    imap, pop3, sieve, smtp""",
+_("""The new service set will affect only those accounts for which the set has
 not been overridden. If you want to restore the default to all accounts, you
 may pass the keyword 'force'."""),),
     # TP: description of subcommand domaintransport
-    'domaintransport': (_(u"""A new transport for the indicated domain can be
+    'domaintransport': (_("""A new transport for the indicated domain can be
 set with this subcommand."""),
-_(u"""The new transport will affect only those accounts for which the transport
+_("""The new transport will affect only those accounts for which the transport
 has not been overridden. If you want to restore the default to all accounts,
 you may pass the keyword 'force'."""),),
     # TP: description of subcommand domainnote
-    'domainnote': (_(u"""With this subcommand, it is possible to attach a
+    'domainnote': (_("""With this subcommand, it is possible to attach a
 note to the specified domain. Without an argument, an existing note is
 removed."""),),
     # TP: description of subcommand aliasdomainadd
-    'aliasdomainadd': (_(u"""This subcommand adds the new alias domain
+    'aliasdomainadd': (_("""This subcommand adds the new alias domain
 (<fqdn>) to the destination <domain> that should be aliased."""),),
     # TP: description of subcommand aliasdomaindelete
-    'aliasdomaindelete': (_(u"""Use this subcommand if the alias domain
+    'aliasdomaindelete': (_("""Use this subcommand if the alias domain
 <fqdn> should be removed."""),),
     # TP: description of subcommand aliasdomaininfo
-    'aliasdomaininfo': (_(u"""This subcommand shows to which domain the alias
+    'aliasdomaininfo': (_("""This subcommand shows to which domain the alias
 domain <fqdn> is assigned to."""),),
     # TP: description of subcommand aliasdomainswitch
-    'aliasdomainswitch': (_(u"""If the destination of the existing alias
+    'aliasdomainswitch': (_("""If the destination of the existing alias
 domain <fqdn> should be switched to another <destination> use this
 subcommand."""),),
     # TP: description of subcommand useradd
-    'useradd': (_(u"""Use this subcommand to create a new e-mail account for
+    'useradd': (_("""Use this subcommand to create a new e-mail account for
 the given <address>."""),
-_(u"""If the <password> is not provided, vmm will prompt for it interactively.
+_("""If the <password> is not provided, vmm will prompt for it interactively.
 When no <password> is provided and account.random_password is set to true, vmm
 will generate a random password and print it to stdout after the account has
 been created."""),),
     # TP: description of subcommand userdelete
-    'userdelete': (_(u"""Use this subcommand to delete the account with the
+    'userdelete': (_("""Use this subcommand to delete the account with the
 given <address>."""),
-_(u"""If there are one or more aliases with an identical destination address,
+_("""If there are one or more aliases with an identical destination address,
 vmm will abort the requested operation and show an error message. To prevent
 this, specify the optional keyword 'force'."""),),
     # TP: description of subcommand userinfo
-    'userinfo': (_(u"""This subcommand displays some information about the
+    'userinfo': (_("""This subcommand displays some information about the
 account specified by <address>."""),
-_(u"""If the optional argument <details> is given some more information will be
+_("""If the optional argument <details> is given some more information will be
 displayed. Possible values for <details> are:"""),
-u"""    aliases, du. full""",),
+"""    aliases, du. full""",),
     # TP: description of subcommand username
-    'username': (_(u"""The user's real <name> can be set/updated with this
+    'username': (_("""The user's real <name> can be set/updated with this
 subcommand."""),
-_(u"""If no <name> is given, the value stored for the account is erased."""),
+_("""If no <name> is given, the value stored for the account is erased."""),
 ),
     # TP: description of subcommand userpassword
-    'userpassword': (_(u"""The password of an account can be updated with this
+    'userpassword': (_("""The password of an account can be updated with this
 subcommand."""),
-_(u"""If no <password> was provided, vmm will prompt for it interactively."""),
+_("""If no <password> was provided, vmm will prompt for it interactively."""),
 ),
     # TP: description of subcommand usernote
-    'usernote': (_(u"""With this subcommand, it is possible to attach a note
+    'usernote': (_("""With this subcommand, it is possible to attach a note
 to the specified account. Without an argument, an existing note is
 removed."""),),
     # TP: description of subcommand userquota
-    'userquota': (_(u"""This subcommand is used to set a new quota limit for
+    'userquota': (_("""This subcommand is used to set a new quota limit for
 the given account."""),
-_(u"""When the argument <messages> was omitted the default number of messages
+_("""When the argument <messages> was omitted the default number of messages
 0 (zero) will be applied."""),
-_(u"""Instead of <storage> pass the keyword 'domain' to remove the
+_("""Instead of <storage> pass the keyword 'domain' to remove the
 account-specific override, causing the domain's value to be in effect."""),),
     # TP: description of subcommand userservices
-    'userservices': (_(u"""To grant a user access to the specified services,
+    'userservices': (_("""To grant a user access to the specified services,
 use this command."""),
-_(u"""All omitted services will be deactivated/unusable for the user with the
+_("""All omitted services will be deactivated/unusable for the user with the
 given <address>."""),
-_(u"""Instead of <service> pass 'domain' to remove the account-specific
+_("""Instead of <service> pass 'domain' to remove the account-specific
 override, causing the domain's value to be in effect."""),),
     # TP: description of subcommand usertransport
-    'usertransport': (_(u"""A different <transport> for an account can be
+    'usertransport': (_("""A different <transport> for an account can be
 specified with this subcommand."""),
-_(u"""Instead of <transport> pass 'domain' to remove the account-specific
+_("""Instead of <transport> pass 'domain' to remove the account-specific
 override, causing the domain's value to be in effect."""),),
     # TP: description of subcommand aliasadd
-    'aliasadd': (_(u"""This subcommand is used to create a new alias
+    'aliasadd': (_("""This subcommand is used to create a new alias
 <address> with one or more <destination> addresses."""),
-_(u"""Within the destination address, the placeholders '%n', '%d', and '%='
+_("""Within the destination address, the placeholders '%n', '%d', and '%='
 will be replaced by the local part, the domain, or the email address with '@'
 replaced by '=' respectively. In combination with alias domains, this enables
 domain-specific destinations."""),),
     # TP: description of subcommand aliasdelete
-    'aliasdelete': (_(u"""This subcommand is used to delete one or multiple
+    'aliasdelete': (_("""This subcommand is used to delete one or multiple
 <destination>s from the alias with the given <address>."""),
-_(u"""When no <destination> address was specified the alias with all its
+_("""When no <destination> address was specified the alias with all its
 destinations will be deleted."""),),
     # TP: description of subcommand aliasinfo
-    'aliasinfo': (_(u"""Information about the alias with the given <address>
+    'aliasinfo': (_("""Information about the alias with the given <address>
 can be displayed with this subcommand."""),),
     # TP: description of subcommand relocatedadd
-    'relocatedadd': (_(u"""A new relocated user can be created with this
+    'relocatedadd': (_("""A new relocated user can be created with this
 subcommand."""),
-_(u"""<address> is the user's ex-email address, for example
+_("""<address> is the user's ex-email address, for example
 b.user@example.com, and <newaddress> points to the new email address where
 the user can be reached."""),),
     # TP: description of subcommand relocatedinfo
-    'relocatedinfo': (_(u"""This subcommand shows the new address of the
+    'relocatedinfo': (_("""This subcommand shows the new address of the
 relocated user with the given <address>."""),),
     # TP: description of subcommand relocateddelete
-    'relocateddelete': (_(u"""Use this subcommand in order to delete the
+    'relocateddelete': (_("""Use this subcommand in order to delete the
 relocated user with the given <address>."""),),
     # TP: description of subcommand catchalladd
-    'catchalladd': (_(u"""This subcommand allows to specify destination
+    'catchalladd': (_("""This subcommand allows to specify destination
 addresses for a domain, which shall receive mail addressed to unknown
 local-parts within that domain. Those catch-all aliases hence "catch all" mail
 to any address in the domain (unless a more specific alias, mailbox or
 relocated user exists)."""),
-_(u"""WARNING: Catch-all addresses can cause mail server flooding because
+_("""WARNING: Catch-all addresses can cause mail server flooding because
 spammers like to deliver mail to all possible combinations of names, e.g.
 to all addresses between abba@example.org and zztop@example.org."""),),
     # TP: description of subcommand catchallinfo
-    'catchallinfo': (_(u"""This subcommand displays information about catch-all
+    'catchallinfo': (_("""This subcommand displays information about catch-all
 aliases defined for the domain <fqdn>."""),),
     # TP: description of subcommand catchalldelete
-    'catchalldelete': (_(u"""With this subcommand, catch-all aliases defined
+    'catchalldelete': (_("""With this subcommand, catch-all aliases defined
 for a domain can be removed, either all of them, or those <destination>s which
 were specified explicitly."""),),
 }
--- a/VirtualMailManager/cli/config.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/cli/config.py	Sun Dec 09 15:03:33 2012 +0000
@@ -8,8 +8,9 @@
     Adds some interactive stuff to the Config class.
 """
 
-from ConfigParser import RawConfigParser
+from configparser import RawConfigParser
 from shutil import copy2
+from string import Template
 
 from VirtualMailManager import ENCODING
 from VirtualMailManager.config import Config, ConfigValueError, LazyConfig
@@ -29,33 +30,35 @@
     def configure(self, sections):
         """Interactive method for configuring all options of the given
         iterable ``sections`` object."""
-        input_fmt = _(u'Enter new value for option %(option)s '
-                      u'[%(current_value)s]: ')
+        input_tpl = Template(_('Enter new value for option $option '
+                               '[$current_value]: '))
         failures = 0
 
-        w_std(_(u'Using configuration file: %s\n') % self._cfg_filename)
+        w_std(_('Using configuration file: %s\n') % self._cfg_filename)
         for section in sections:
-            w_std(_(u"* Configuration section: '%s'") % section)
+            w_std(_("* Configuration section: '%s'") % section)
             for opt, val in self.items(section):
                 failures = 0
                 while True:
-                    newval = raw_input(input_fmt.encode(ENCODING, 'replace') %
-                                       {'option': opt, 'current_value': val})
+                    if isinstance(val, str):
+                        val = val.encode(ENCODING, 'replace').decode(ENCODING)
+                    newval = input(input_tpl.substitute(option=opt,
+                                                        current_value=val))
                     if newval and newval != val:
                         try:
                             LazyConfig.set(self, '%s.%s' % (section, opt),
                                            newval)
                             break
-                        except (ValueError, ConfigValueError, VMMError), err:
-                            w_err(0, _(u'Warning: %s') % err)
+                        except (ValueError, ConfigValueError, VMMError) as err:
+                            w_err(0, _('Warning: %s') % err)
                             failures += 1
                             if failures > 2:
-                                raise ConfigError(_(u'Too many failures - try '
-                                                    u'again later.'),
+                                raise ConfigError(_('Too many failures - try '
+                                                    'again later.'),
                                                   VMM_TOO_MANY_FAILURES)
                     else:
                         break
-            print
+            print()
         if self._modified:
             self._save_changes()
 
@@ -72,7 +75,7 @@
             val = self._cfg[section][option_].cls(value)
             if self._cfg[section][option_].validate:
                 val = self._cfg[section][option_].validate(val)
-        except (ValueError, ConfigValueError), err:
+        except (ValueError, ConfigValueError) as err:
             raise ConfigError(str(err), CONF_ERROR)
         # Do not write default values also skip identical values
         if not self._cfg[section][option_].default is None:
@@ -89,8 +92,7 @@
     def _save_changes(self):
         """Writes changes to the configuration file."""
         copy2(self._cfg_filename, self._cfg_filename + '.bak')
-        self._cfg_file = open(self._cfg_filename, 'w')
-        self.write(self._cfg_file)
-        self._cfg_file.close()
+        with open(self._cfg_filename, 'w', encoding='utf-8') as self._cfg_file:
+            self.write(self._cfg_file)
 
 del _
--- a/VirtualMailManager/cli/handler.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/cli/handler.py	Sun Dec 09 15:03:33 2012 +0000
@@ -63,7 +63,7 @@
         elif self._cfg.has_section(section):
             self._cfg.configure([section])
         else:
-            raise VMMError(_(u"Invalid section: '%s'") % section,
+            raise VMMError(_("Invalid section: '%s'") % section,
                            INVALID_SECTION)
 
     def user_add(self, emailaddress, password=None):
@@ -74,7 +74,7 @@
         """
         acc = self._get_account(emailaddress)
         if acc:
-            raise VMMError(_(u"The account '%s' already exists.") %
+            raise VMMError(_("The account '%s' already exists.") %
                            acc.address, ACCOUNT_EXISTS)
         self._is_other_address(acc.address, TYPE_ACCOUNT)
         rand_pass = self._cfg.dget('account.random_password')
@@ -90,9 +90,9 @@
         password dialog."""
         acc = self._get_account(emailaddress)
         if not acc:
-            raise VMMError(_(u"The account '%s' does not exist.") %
+            raise VMMError(_("The account '%s' does not exist.") %
                            acc.address, NO_SUCH_ACCOUNT)
-        if not isinstance(password, basestring) or not password:
+        if not isinstance(password, str) or not password:
             password = read_pass()
         acc.modify('password', password)
 
--- a/VirtualMailManager/cli/main.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/cli/main.py	Sun Dec 09 15:03:33 2012 +0000
@@ -8,7 +8,7 @@
     VirtualMailManager's command line interface.
 """
 
-from ConfigParser import NoOptionError, NoSectionError
+from configparser import NoOptionError, NoSectionError
 
 from VirtualMailManager import ENCODING, errors
 from VirtualMailManager.config import BadOptionError, ConfigValueError
@@ -28,8 +28,8 @@
     try:
         handler = CliHandler()
     except (errors.NotRootError, errors.PermissionError, errors.VMMError,
-            errors.ConfigError), err:
-        w_err(err.code, _(u'Error: %s') % err.msg)
+            errors.ConfigError) as err:
+        w_err(err.code, _('Error: %s') % err.msg)
     else:
         handler.cfg_install()
         return handler
@@ -38,19 +38,19 @@
 def run(argv):
     update_cmd_map()
     if len(argv) < 2:
-        usage(EX_MISSING_ARGS, _(u"You must specify a subcommand at least."))
+        usage(EX_MISSING_ARGS, _("You must specify a subcommand at least."))
 
     sub_cmd = argv[1].lower()
     if sub_cmd in cmd_map:
         cmd_func = cmd_map[sub_cmd].func
     else:
-        for cmd in cmd_map.itervalues():
+        for cmd in cmd_map.values():
             if cmd.alias == sub_cmd:
                 cmd_func = cmd.func
                 sub_cmd = cmd.name
                 break
         else:
-            usage(EX_UNKNOWN_COMMAND, _(u"Unknown subcommand: '%s'") % sub_cmd)
+            usage(EX_UNKNOWN_COMMAND, _("Unknown subcommand: '%s'") % sub_cmd)
 
     handler = _get_handler()
     run_ctx = RunContext(argv, handler, sub_cmd)
@@ -59,24 +59,24 @@
     except (EOFError, KeyboardInterrupt):
         # TP: We have to cry, because root has killed/interrupted vmm
         # with Ctrl+C or Ctrl+D.
-        w_err(EX_USER_INTERRUPT, '', _(u'Ouch!'), '')
-    except errors.VMMError, err:
+        w_err(EX_USER_INTERRUPT, '', _('Ouch!'), '')
+    except errors.VMMError as err:
         if err.code != DATABASE_ERROR:
             if handler.has_warnings():
-                w_err(0, _(u'Warnings:'), *handler.get_warnings())
-            w_err(err.code, _(u'Error: %s') % err.msg)
-        w_err(err.code, unicode(err.msg, ENCODING, 'replace'))
-    except (BadOptionError, ConfigValueError), err:
-        w_err(INVALID_ARGUMENT, _(u'Error: %s') % err)
-    except NoSectionError, err:
+                w_err(0, _('Warnings:'), *handler.get_warnings())
+            w_err(err.code, _('Error: %s') % err.msg)
+        w_err(err.code, str(err.msg, ENCODING, 'replace'))
+    except (BadOptionError, ConfigValueError) as err:
+        w_err(INVALID_ARGUMENT, _('Error: %s') % err)
+    except NoSectionError as err:
         w_err(INVALID_ARGUMENT,
-              _(u"Error: Unknown section: '%s'") % err.section)
-    except NoOptionError, err:
+              _("Error: Unknown section: '%s'") % err.section)
+    except NoOptionError as err:
         w_err(INVALID_ARGUMENT,
-              _(u"Error: No option '%(option)s' in section: '%(section)s'") %
+              _("Error: No option '%(option)s' in section: '%(section)s'") %
               {'option': err.option, 'section': err.section})
     if handler.has_warnings():
-        w_err(0, _(u'Warnings:'), *handler.get_warnings())
+        w_err(0, _('Warnings:'), *handler.get_warnings())
     return EX_SUCCESS
 
 del _
--- a/VirtualMailManager/cli/subcommands.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/cli/subcommands.py	Sun Dec 09 15:03:33 2012 +0000
@@ -79,7 +79,7 @@
     @property
     def usage(self):
         """the command's usage info."""
-        return u'%s %s %s' % (prog, self.name, self.args)
+        return '%s %s %s' % (prog, self.name, self.args)
 
     def help_(self):
         """Print the Command's help message to stdout."""
@@ -98,19 +98,19 @@
             [w_std(txt_wrpr.fill(_(para)) + '\n') for para
                     in help_msgs[self.name]]
         except KeyError:
-            w_err(1, _(u"Subcommand '%s' is not yet documented." % self.name),
+            w_err(1, _("Subcommand '%s' is not yet documented." % self.name),
                   'see also: vmm(1)')
 
 
 class RunContext(object):
     """Contains all information necessary to run a subcommand."""
     __slots__ = ('argc', 'args', 'cget', 'hdlr', 'scmd')
-    plan_a_b = _(u'Plan A failed ... trying Plan B: %(subcommand)s %(object)s')
+    plan_a_b = _('Plan A failed ... trying Plan B: %(subcommand)s %(object)s')
 
     def __init__(self, argv, handler, command):
         """Create a new RunContext"""
         self.argc = len(argv)
-        self.args = [unicode(arg, ENCODING) for arg in argv]
+        self.args = argv[:]  # will be moved to argparse
         self.cget = handler.cfg_dget
         self.hdlr = handler
         self.scmd = command
@@ -119,17 +119,17 @@
 def alias_add(ctx):
     """create a new alias e-mail address"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing alias address and destination.'),
+        usage(EX_MISSING_ARGS, _('Missing alias address and destination.'),
               ctx.scmd)
     elif ctx.argc < 4:
-        usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing destination address.'), ctx.scmd)
     ctx.hdlr.alias_add(ctx.args[2].lower(), *ctx.args[3:])
 
 
 def alias_delete(ctx):
     """delete the specified alias e-mail address or one of its destinations"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing alias address.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing alias address.'), ctx.scmd)
     elif ctx.argc < 4:
         ctx.hdlr.alias_delete(ctx.args[2].lower())
     else:
@@ -139,18 +139,18 @@
 def alias_info(ctx):
     """show the destination(s) of the specified alias"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing alias address.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing alias address.'), ctx.scmd)
     address = ctx.args[2].lower()
     try:
         _print_aliase_info(address, ctx.hdlr.alias_info(address))
-    except VMMError, err:
+    except VMMError as err:
         if err.code is ACCOUNT_EXISTS:
-            w_err(0, ctx.plan_a_b % {'subcommand': u'userinfo',
+            w_err(0, ctx.plan_a_b % {'subcommand': 'userinfo',
                   'object': address})
             ctx.scmd = ctx.args[1] = 'userinfo'
             user_info(ctx)
         elif err.code is RELOCATED_EXISTS:
-            w_err(0, ctx.plan_a_b % {'subcommand': u'relocatedinfo',
+            w_err(0, ctx.plan_a_b % {'subcommand': 'relocatedinfo',
                   'object': address})
             ctx.scmd = ctx.args[1] = 'relocatedinfo'
             relocated_info(ctx)
@@ -161,10 +161,10 @@
 def aliasdomain_add(ctx):
     """create a new alias for an existing domain"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing alias domain name and destination '
-                                 u'domain name.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing alias domain name and destination '
+                                 'domain name.'), ctx.scmd)
     elif ctx.argc < 4:
-        usage(EX_MISSING_ARGS, _(u'Missing destination domain name.'),
+        usage(EX_MISSING_ARGS, _('Missing destination domain name.'),
               ctx.scmd)
     ctx.hdlr.aliasdomain_add(ctx.args[2].lower(), ctx.args[3].lower())
 
@@ -172,19 +172,19 @@
 def aliasdomain_delete(ctx):
     """delete the specified alias domain"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing alias domain name.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing alias domain name.'), ctx.scmd)
     ctx.hdlr.aliasdomain_delete(ctx.args[2].lower())
 
 
 def aliasdomain_info(ctx):
     """show the destination of the given alias domain"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing alias domain name.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing alias domain name.'), ctx.scmd)
     try:
         _print_aliasdomain_info(ctx.hdlr.aliasdomain_info(ctx.args[2].lower()))
-    except VMMError, err:
+    except VMMError as err:
         if err.code is ALIASDOMAIN_ISDOMAIN:
-            w_err(0, ctx.plan_a_b % {'subcommand': u'domaininfo',
+            w_err(0, ctx.plan_a_b % {'subcommand': 'domaininfo',
                   'object': ctx.args[2].lower()})
             ctx.scmd = ctx.args[1] = 'domaininfo'
             domain_info(ctx)
@@ -195,10 +195,10 @@
 def aliasdomain_switch(ctx):
     """assign the given alias domain to an other domain"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing alias domain name and destination '
-                                 u'domain name.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing alias domain name and destination '
+                                 'domain name.'), ctx.scmd)
     elif ctx.argc < 4:
-        usage(EX_MISSING_ARGS, _(u'Missing destination domain name.'),
+        usage(EX_MISSING_ARGS, _('Missing destination domain name.'),
               ctx.scmd)
     ctx.hdlr.aliasdomain_switch(ctx.args[2].lower(), ctx.args[3].lower())
 
@@ -206,17 +206,17 @@
 def catchall_add(ctx):
     """create a new catchall alias e-mail address"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing domain and destination.'),
+        usage(EX_MISSING_ARGS, _('Missing domain and destination.'),
               ctx.scmd)
     elif ctx.argc < 4:
-        usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing destination address.'), ctx.scmd)
     ctx.hdlr.catchall_add(ctx.args[2].lower(), *ctx.args[3:])
 
 
 def catchall_delete(ctx):
     """delete the specified destination or all of the catchall destination"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing domain name.'), ctx.scmd)
     elif ctx.argc < 4:
         ctx.hdlr.catchall_delete(ctx.args[2].lower())
     else:
@@ -226,7 +226,7 @@
 def catchall_info(ctx):
     """show the catchall destination(s) of the specified domain"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing domain name.'), ctx.scmd)
     address = ctx.args[2].lower()
     _print_catchall_info(address, ctx.hdlr.catchall_info(address))
 
@@ -234,7 +234,7 @@
 def config_get(ctx):
     """show the actual value of the configuration option"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u"Missing option name."), ctx.scmd)
+        usage(EX_MISSING_ARGS, _("Missing option name."), ctx.scmd)
 
     noop = lambda option: option
     opt_formater = {
@@ -250,9 +250,9 @@
 def config_set(ctx):
     """set a new value for the configuration option"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing option and new value.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing option and new value.'), ctx.scmd)
     if ctx.argc < 4:
-        usage(EX_MISSING_ARGS, _(u'Missing new configuration value.'),
+        usage(EX_MISSING_ARGS, _('Missing new configuration value.'),
               ctx.scmd)
     ctx.hdlr.cfg_set(ctx.args[2].lower(), ctx.args[3])
 
@@ -268,15 +268,15 @@
 def domain_add(ctx):
     """create a new domain"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing domain name.'), ctx.scmd)
     elif ctx.argc < 4:
         ctx.hdlr.domain_add(ctx.args[2].lower())
     else:
         ctx.hdlr.domain_add(ctx.args[2].lower(), ctx.args[3])
     if ctx.cget('domain.auto_postmaster'):
-        w_std(_(u'Creating account for postmaster@%s') % ctx.args[2].lower())
+        w_std(_('Creating account for postmaster@%s') % ctx.args[2].lower())
         ctx.scmd = 'useradd'
-        ctx.args = [prog, ctx.scmd, u'postmaster@' + ctx.args[2].lower()]
+        ctx.args = [prog, ctx.scmd, 'postmaster@' + ctx.args[2].lower()]
         ctx.argc = 3
         user_add(ctx)
 
@@ -284,84 +284,82 @@
 def domain_delete(ctx):
     """delete the given domain and all its alias domains"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing domain name.'), ctx.scmd)
     elif ctx.argc < 4:
         ctx.hdlr.domain_delete(ctx.args[2].lower())
     elif ctx.args[3].lower() == 'force':
         ctx.hdlr.domain_delete(ctx.args[2].lower(), True)
     else:
-        usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[3],
+        usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % ctx.args[3],
               ctx.scmd)
 
 
 def domain_info(ctx):
     """display information about the given domain"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing domain name.'), ctx.scmd)
     if ctx.argc < 4:
         details = None
     else:
         details = ctx.args[3].lower()
         if details not in ('accounts', 'aliasdomains', 'aliases', 'full',
                            'relocated', 'catchall'):
-            usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % details,
+            usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % details,
                   ctx.scmd)
     try:
         info = ctx.hdlr.domain_info(ctx.args[2].lower(), details)
-    except VMMError, err:
+    except VMMError as err:
         if err.code is DOMAIN_ALIAS_EXISTS:
-            w_err(0, ctx.plan_a_b % {'subcommand': u'aliasdomaininfo',
+            w_err(0, ctx.plan_a_b % {'subcommand': 'aliasdomaininfo',
                   'object': ctx.args[2].lower()})
             ctx.scmd = ctx.args[1] = 'aliasdomaininfo'
             aliasdomain_info(ctx)
         else:
             raise
     else:
-        q_limit = u'Storage: %(bytes)s; Messages: %(messages)s'
+        q_limit = 'Storage: %(bytes)s; Messages: %(messages)s'
         if not details:
             info['bytes'] = human_size(info['bytes'])
-            info['messages'] = locale.format('%d', info['messages'],
-                                             True).decode(ENCODING, 'replace')
+            info['messages'] = locale.format('%d', info['messages'], True)
             info['quota limit/user'] = q_limit % info
-            _print_info(ctx, info, _(u'Domain'))
+            _print_info(ctx, info, _('Domain'))
         else:
             info[0]['bytes'] = human_size(info[0]['bytes'])
             info[0]['messages'] = locale.format('%d', info[0]['messages'],
-                                                True).decode(ENCODING,
-                                                             'replace')
+                                                True)
             info[0]['quota limit/user'] = q_limit % info[0]
-            _print_info(ctx, info[0], _(u'Domain'))
-            if details == u'accounts':
-                _print_list(info[1], _(u'accounts'))
-            elif details == u'aliasdomains':
-                _print_list(info[1], _(u'alias domains'))
-            elif details == u'aliases':
-                _print_list(info[1], _(u'aliases'))
-            elif details == u'relocated':
-                _print_list(info[1], _(u'relocated users'))
-            elif details == u'catchall':
-                _print_list(info[1], _(u'catch-all destinations'))
+            _print_info(ctx, info[0], _('Domain'))
+            if details == 'accounts':
+                _print_list(info[1], _('accounts'))
+            elif details == 'aliasdomains':
+                _print_list(info[1], _('alias domains'))
+            elif details == 'aliases':
+                _print_list(info[1], _('aliases'))
+            elif details == 'relocated':
+                _print_list(info[1], _('relocated users'))
+            elif details == 'catchall':
+                _print_list(info[1], _('catch-all destinations'))
             else:
-                _print_list(info[1], _(u'alias domains'))
-                _print_list(info[2], _(u'accounts'))
-                _print_list(info[3], _(u'aliases'))
-                _print_list(info[4], _(u'relocated users'))
-                _print_list(info[5], _(u'catch-all destinations'))
+                _print_list(info[1], _('alias domains'))
+                _print_list(info[2], _('accounts'))
+                _print_list(info[3], _('aliases'))
+                _print_list(info[4], _('relocated users'))
+                _print_list(info[5], _('catch-all destinations'))
 
 
 def domain_quota(ctx):
     """update the quota limit of the specified domain"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing domain name and storage value.'),
+        usage(EX_MISSING_ARGS, _('Missing domain name and storage value.'),
               ctx.scmd)
     if ctx.argc < 4:
-        usage(EX_MISSING_ARGS, _(u'Missing storage value.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing storage value.'), ctx.scmd)
     messages = 0
     force = None
     try:
         bytes_ = size_in_bytes(ctx.args[3])
     except (ValueError, TypeError):
-        usage(INVALID_ARGUMENT, _(u"Invalid storage value: '%s'") %
+        usage(INVALID_ARGUMENT, _("Invalid storage value: '%s'") %
               ctx.args[3], ctx.scmd)
     if ctx.argc < 5:
         pass
@@ -371,18 +369,18 @@
         except ValueError:
             if ctx.args[4].lower() != 'force':
                 usage(INVALID_ARGUMENT,
-                      _(u"Neither a valid number of messages nor the keyword "
-                        u"'force': '%s'") % ctx.args[4], ctx.scmd)
+                      _("Neither a valid number of messages nor the keyword "
+                        "'force': '%s'") % ctx.args[4], ctx.scmd)
             force = 'force'
     else:
         try:
             messages = int(ctx.args[4])
         except ValueError:
             usage(INVALID_ARGUMENT,
-                  _(u"Not a valid number of messages: '%s'") % ctx.args[4],
+                  _("Not a valid number of messages: '%s'") % ctx.args[4],
                   ctx.scmd)
         if ctx.args[5].lower() != 'force':
-            usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[5],
+            usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % ctx.args[5],
                   ctx.scmd)
         force = 'force'
     ctx.hdlr.domain_quotalimit(ctx.args[2].lower(), bytes_, messages, force)
@@ -391,7 +389,7 @@
 def domain_services(ctx):
     """allow all named service and block the uncredited."""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing domain name.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing domain name.'), ctx.scmd)
     services = []
     force = False
     if ctx.argc is 3:
@@ -403,7 +401,7 @@
         elif arg == 'force':
             force = True
         else:
-            usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % arg,
+            usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % arg,
                   ctx.scmd)
     else:
         services.extend([service.lower() for service in ctx.args[3:-1]])
@@ -414,7 +412,7 @@
             services.append(arg)
         unknown = [service for service in services if service not in SERVICES]
         if unknown:
-            usage(INVALID_ARGUMENT, _(u'Invalid service arguments: %s') %
+            usage(INVALID_ARGUMENT, _('Invalid service arguments: %s') %
                   ' '.join(unknown), ctx.scmd)
     ctx.hdlr.domain_services(ctx.args[2].lower(), (None, 'force')[force],
                              *services)
@@ -423,16 +421,16 @@
 def domain_transport(ctx):
     """update the transport of the specified domain"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing domain name and new transport.'),
+        usage(EX_MISSING_ARGS, _('Missing domain name and new transport.'),
               ctx.scmd)
     if ctx.argc < 4:
-        usage(EX_MISSING_ARGS, _(u'Missing new transport.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing new transport.'), ctx.scmd)
     if ctx.argc < 5:
         ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3])
     else:
         force = ctx.args[4].lower()
         if force != 'force':
-            usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % force,
+            usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % force,
                   ctx.scmd)
         ctx.hdlr.domain_transport(ctx.args[2].lower(), ctx.args[3], force)
 
@@ -440,7 +438,7 @@
 def domain_note(ctx):
     """update the note of the given domain"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing domain name.'),
+        usage(EX_MISSING_ARGS, _('Missing domain name.'),
               ctx.scmd)
     elif ctx.argc < 4:
         note = None
@@ -452,8 +450,8 @@
 def get_user(ctx):
     """get the address of the user with the given UID"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing UID.'), ctx.scmd)
-    _print_info(ctx, ctx.hdlr.user_by_uid(ctx.args[2]), _(u'Account'))
+        usage(EX_MISSING_ARGS, _('Missing UID.'), ctx.scmd)
+    _print_info(ctx, ctx.hdlr.user_by_uid(ctx.args[2]), _('Account'))
 
 
 def help_(ctx):
@@ -463,14 +461,14 @@
         if hlptpc in cmd_map:
             topic = hlptpc
         else:
-            for scmd in cmd_map.itervalues():
+            for scmd in cmd_map.values():
                 if scmd.alias == hlptpc:
                     topic = scmd.name
                     break
             else:
-                usage(INVALID_ARGUMENT, _(u"Unknown help topic: '%s'") %
+                usage(INVALID_ARGUMENT, _("Unknown help topic: '%s'") %
                       ctx.args[2], ctx.scmd)
-        if topic != u'help':
+        if topic != 'help':
             return cmd_map[topic].help_()
 
     old_ii = txt_wrpr.initial_indent
@@ -478,10 +476,9 @@
     txt_wrpr.initial_indent = ' '
     # len(max(_overview.iterkeys(), key=len)) #Py25
     txt_wrpr.subsequent_indent = 20 * ' '
-    order = cmd_map.keys()
-    order.sort()
+    order = sorted(list(cmd_map.keys()))
 
-    w_std(_(u'List of available subcommands:') + '\n')
+    w_std(_('List of available subcommands:') + '\n')
     for key in order:
         w_std('\n'.join(txt_wrpr.wrap('%-18s %s' % (key, cmd_map[key].descr))))
 
@@ -502,19 +499,14 @@
 
 def list_pwschemes(ctx_unused):
     """Prints all usable password schemes and password encoding suffixes."""
-    # TODO: Remove trailing colons from keys.
-    # For now it is to late, the translators has stared their work
-    keys = (_(u'Usable password schemes:'), _(u'Usable encoding suffixes:'))
+    keys = (_('Usable password schemes'), _('Usable encoding suffixes'))
     old_ii, old_si = txt_wrpr.initial_indent, txt_wrpr.subsequent_indent
     txt_wrpr.initial_indent = txt_wrpr.subsequent_indent = '\t'
     txt_wrpr.width = txt_wrpr.width - 8
 
     for key, value in zip(keys, list_schemes()):
-        if key.endswith(':'):  # who knows … (see TODO above)
-            #key = key.rpartition(':')[0]
-            key = key[:-1]  # This one is for Py24
         w_std(key, len(key) * '-')
-        w_std('\n'.join(txt_wrpr.wrap(' '.join(value))), '')
+        w_std('\n'.join(txt_wrpr.wrap(' '.join(sorted(value)))), '')
 
     txt_wrpr.initial_indent, txt_wrpr.subsequent_indent = old_ii, old_si
     txt_wrpr.width = txt_wrpr.width + 8
@@ -554,35 +546,35 @@
     """create a new record for a relocated user"""
     if ctx.argc < 3:
         usage(EX_MISSING_ARGS,
-              _(u'Missing relocated address and destination.'), ctx.scmd)
+              _('Missing relocated address and destination.'), ctx.scmd)
     elif ctx.argc < 4:
-        usage(EX_MISSING_ARGS, _(u'Missing destination address.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing destination address.'), ctx.scmd)
     ctx.hdlr.relocated_add(ctx.args[2].lower(), ctx.args[3])
 
 
 def relocated_delete(ctx):
     """delete the record of the relocated user"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing relocated address.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing relocated address.'), ctx.scmd)
     ctx.hdlr.relocated_delete(ctx.args[2].lower())
 
 
 def relocated_info(ctx):
     """print information about a relocated user"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing relocated address.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing relocated address.'), ctx.scmd)
     relocated = ctx.args[2].lower()
     try:
         _print_relocated_info(addr=relocated,
                               dest=ctx.hdlr.relocated_info(relocated))
-    except VMMError, err:
+    except VMMError as err:
         if err.code is ACCOUNT_EXISTS:
-            w_err(0, ctx.plan_a_b % {'subcommand': u'userinfo',
+            w_err(0, ctx.plan_a_b % {'subcommand': 'userinfo',
                   'object': relocated})
             ctx.scmd = ctx.args[1] = 'userinfoi'
             user_info(ctx)
         elif err.code is ALIAS_EXISTS:
-            w_err(0, ctx.plan_a_b % {'subcommand': u'aliasinfo',
+            w_err(0, ctx.plan_a_b % {'subcommand': 'aliasinfo',
                   'object': relocated})
             ctx.scmd = ctx.args[1] = 'aliasinfo'
             alias_info(ctx)
@@ -593,50 +585,50 @@
 def user_add(ctx):
     """create a new e-mail user with the given address"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing e-mail address.'), ctx.scmd)
     elif ctx.argc < 4:
         password = None
     else:
         password = ctx.args[3]
     gen_pass = ctx.hdlr.user_add(ctx.args[2].lower(), password)
     if ctx.argc < 4 and gen_pass:
-        w_std(_(u"Generated password: %s") % gen_pass)
+        w_std(_("Generated password: %s") % gen_pass)
 
 
 def user_delete(ctx):
     """delete the specified user"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing e-mail address.'), ctx.scmd)
     elif ctx.argc < 4:
         ctx.hdlr.user_delete(ctx.args[2].lower())
     elif ctx.args[3].lower() == 'force':
         ctx.hdlr.user_delete(ctx.args[2].lower(), True)
     else:
-        usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % ctx.args[3],
+        usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % ctx.args[3],
               ctx.scmd)
 
 
 def user_info(ctx):
     """display information about the given address"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing e-mail address.'), ctx.scmd)
     if ctx.argc < 4:
         details = None
     else:
         details = ctx.args[3].lower()
         if details not in ('aliases', 'du', 'full'):
-            usage(INVALID_ARGUMENT, _(u"Invalid argument: '%s'") % details,
+            usage(INVALID_ARGUMENT, _("Invalid argument: '%s'") % details,
                   ctx.scmd)
     try:
         info = ctx.hdlr.user_info(ctx.args[2].lower(), details)
-    except VMMError, err:
+    except VMMError as err:
         if err.code is ALIAS_EXISTS:
-            w_err(0, ctx.plan_a_b % {'subcommand': u'aliasinfo',
+            w_err(0, ctx.plan_a_b % {'subcommand': 'aliasinfo',
                   'object': ctx.args[2].lower()})
             ctx.scmd = ctx.args[1] = 'aliasinfo'
             alias_info(ctx)
         elif err.code is RELOCATED_EXISTS:
-            w_err(0, ctx.plan_a_b % {'subcommand': u'relocatedinfo',
+            w_err(0, ctx.plan_a_b % {'subcommand': 'relocatedinfo',
                   'object': ctx.args[2].lower()})
             ctx.scmd = ctx.args[1] = 'relocatedinfo'
             relocated_info(ctx)
@@ -650,7 +642,7 @@
                 _format_quota_usage(info['ql_messages'],
                                     info['uq_messages'],
                                     domaindefault=info['ql_domaindefault'])
-            _print_info(ctx, info, _(u'Account'))
+            _print_info(ctx, info, _('Account'))
         else:
             info[0]['quota storage'] = _format_quota_usage(info[0]['ql_bytes'],
                     info[0]['uq_bytes'], True, info[0]['ql_domaindefault'])
@@ -658,14 +650,14 @@
                 _format_quota_usage(info[0]['ql_messages'],
                                     info[0]['uq_messages'],
                                     domaindefault=info[0]['ql_domaindefault'])
-            _print_info(ctx, info[0], _(u'Account'))
-            _print_list(info[1], _(u'alias addresses'))
+            _print_info(ctx, info[0], _('Account'))
+            _print_list(info[1], _('alias addresses'))
 
 
 def user_name(ctx):
     """set or update the real name for an address"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u"Missing e-mail address and user's name."),
+        usage(EX_MISSING_ARGS, _("Missing e-mail address and user's name."),
               ctx.scmd)
     elif ctx.argc < 4:
         name = None
@@ -677,7 +669,7 @@
 def user_password(ctx):
     """update the password for the given address"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing e-mail address.'), ctx.scmd)
     elif ctx.argc < 4:
         password = None
     else:
@@ -688,7 +680,7 @@
 def user_note(ctx):
     """update the note of the given address"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'),
+        usage(EX_MISSING_ARGS, _('Missing e-mail address.'),
               ctx.scmd)
     elif ctx.argc < 4:
         note = None
@@ -700,15 +692,15 @@
 def user_quota(ctx):
     """update the quota limit for the given address"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing e-mail address and storage value.'),
+        usage(EX_MISSING_ARGS, _('Missing e-mail address and storage value.'),
               ctx.scmd)
     elif ctx.argc < 4:
-        usage(EX_MISSING_ARGS, _(u'Missing storage value.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing storage value.'), ctx.scmd)
     if ctx.args[3] != 'domain':
         try:
             bytes_ = size_in_bytes(ctx.args[3])
         except (ValueError, TypeError):
-            usage(INVALID_ARGUMENT, _(u"Invalid storage value: '%s'") %
+            usage(INVALID_ARGUMENT, _("Invalid storage value: '%s'") %
                   ctx.args[3], ctx.scmd)
     else:
         bytes_ = ctx.args[3]
@@ -719,7 +711,7 @@
             messages = int(ctx.args[4])
         except ValueError:
             usage(INVALID_ARGUMENT,
-                  _(u"Not a valid number of messages: '%s'") % ctx.args[4],
+                  _("Not a valid number of messages: '%s'") % ctx.args[4],
                   ctx.scmd)
     ctx.hdlr.user_quotalimit(ctx.args[2].lower(), bytes_, messages)
 
@@ -727,13 +719,13 @@
 def user_services(ctx):
     """allow all named service and block the uncredited."""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing e-mail address.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing e-mail address.'), ctx.scmd)
     services = []
     if ctx.argc >= 4:
         services.extend([service.lower() for service in ctx.args[3:]])
         unknown = [service for service in services if service not in SERVICES]
         if unknown and ctx.args[3] != 'domain':
-            usage(INVALID_ARGUMENT, _(u'Invalid service arguments: %s') %
+            usage(INVALID_ARGUMENT, _('Invalid service arguments: %s') %
                   ' '.join(unknown), ctx.scmd)
     ctx.hdlr.user_services(ctx.args[2].lower(), *services)
 
@@ -741,10 +733,10 @@
 def user_transport(ctx):
     """update the transport of the given address"""
     if ctx.argc < 3:
-        usage(EX_MISSING_ARGS, _(u'Missing e-mail address and transport.'),
+        usage(EX_MISSING_ARGS, _('Missing e-mail address and transport.'),
               ctx.scmd)
     if ctx.argc < 4:
-        usage(EX_MISSING_ARGS, _(u'Missing transport.'), ctx.scmd)
+        usage(EX_MISSING_ARGS, _('Missing transport.'), ctx.scmd)
     ctx.hdlr.user_transport(ctx.args[2].lower(), ctx.args[3])
 
 
@@ -753,8 +745,8 @@
     When errno > 0, sys,exit(errno) will interrupt the program.
     """
     if subcommand and subcommand in cmd_map:
-        w_err(errno, _(u"Error: %s") % errmsg,
-              _(u"usage: ") + cmd_map[subcommand].usage)
+        w_err(errno, _("Error: %s") % errmsg,
+              _("usage: ") + cmd_map[subcommand].usage)
 
     # TP: Please adjust translated words like the original text.
     # (It's a table header.) Extract from usage text:
@@ -764,16 +756,15 @@
     #
     #   da    domainadd           fqdn [transport]
     #   dd    domaindelete        fqdn [force]
-    u_head = _(u"""usage: %s subcommand arguments
+    u_head = _("""usage: %s subcommand arguments
   short long
   subcommand                arguments\n""") % prog
-    order = cmd_map.keys()
-    order.sort()
+    order = sorted(list(cmd_map.keys()))
     w_err(0, u_head)
     for key in order:
         scmd = cmd_map[key]
         w_err(0, '  %-5s %-19s %s' % (scmd.alias, scmd.name, scmd.args))
-    w_err(errno, '', _(u"Error: %s") % errmsg)
+    w_err(errno, '', _("Error: %s") % errmsg)
 
 
 def version(ctx_unused):
@@ -783,12 +774,12 @@
     # the version information, e.g.:
     # vmm, version 0.5.2 (from 09/09/09)
     # Python 2.5.4 on FreeBSD
-        _(u'version'), __version__, _(u'from'),
+        _('version'), __version__, _('from'),
         strftime(locale.nl_langinfo(locale.D_FMT),
-            strptime(__date__, '%Y-%m-%d')).decode(ENCODING, 'replace'),
-        os.sys.version.split()[0], _(u'on'), os.uname()[0],
+            strptime(__date__, '%Y-%m-%d')),
+        os.sys.version.split()[0], _('on'), os.uname()[0],
         __copyright__, prog,
-        _(u'is free software and comes with ABSOLUTELY NO WARRANTY.')))
+        _('is free software and comes with ABSOLUTELY NO WARRANTY.')))
 
 
 def update_cmd_map():
@@ -797,119 +788,119 @@
     cmd_map.update({
     # Account commands
     'getuser': cmd('getuser', 'gu', get_user, 'uid',
-                   _(u'get the address of the user with the given UID')),
+                   _('get the address of the user with the given UID')),
     'useradd': cmd('useradd', 'ua', user_add, 'address [password]',
-                   _(u'create a new e-mail user with the given address')),
+                   _('create a new e-mail user with the given address')),
     'userdelete': cmd('userdelete', 'ud', user_delete, 'address [force]',
-                      _(u'delete the specified user')),
+                      _('delete the specified user')),
     'userinfo': cmd('userinfo', 'ui', user_info, 'address [details]',
-                    _(u'display information about the given address')),
+                    _('display information about the given address')),
     'username': cmd('username', 'un', user_name, 'address [name]',
-                    _(u'set, update or delete the real name for an address')),
+                    _('set, update or delete the real name for an address')),
     'userpassword': cmd('userpassword', 'up', user_password,
                         'address [password]',
-                        _(u'update the password for the given address')),
+                        _('update the password for the given address')),
     'userquota': cmd('userquota', 'uq', user_quota,
                      'address storage [messages] | address domain',
-                     _(u'update the quota limit for the given address')),
+                     _('update the quota limit for the given address')),
     'userservices': cmd('userservices', 'us', user_services,
                         'address [service ...] | address domain',
-                        _(u'enables the specified services and disables all '
-                          u'not specified services')),
+                        _('enables the specified services and disables all '
+                          'not specified services')),
     'usertransport': cmd('usertransport', 'ut', user_transport,
                          'address transport | address domain',
-                         _(u'update the transport of the given address')),
+                         _('update the transport of the given address')),
     'usernote': cmd('usernote', 'uo', user_note, 'address [note]',
-                    _(u'set, update or delete the note of the given address')),
+                    _('set, update or delete the note of the given address')),
     # Alias commands
     'aliasadd': cmd('aliasadd', 'aa', alias_add, 'address destination ...',
-                    _(u'create a new alias e-mail address with one or more '
-                      u'destinations')),
+                    _('create a new alias e-mail address with one or more '
+                      'destinations')),
     'aliasdelete': cmd('aliasdelete', 'ad', alias_delete,
                        'address [destination ...]',
-                       _(u'delete the specified alias e-mail address or one '
-                         u'of its destinations')),
+                       _('delete the specified alias e-mail address or one '
+                         'of its destinations')),
     'aliasinfo': cmd('aliasinfo', 'ai', alias_info, 'address',
-                     _(u'show the destination(s) of the specified alias')),
+                     _('show the destination(s) of the specified alias')),
     # AliasDomain commands
     'aliasdomainadd': cmd('aliasdomainadd', 'ada', aliasdomain_add,
                           'fqdn destination',
-                          _(u'create a new alias for an existing domain')),
+                          _('create a new alias for an existing domain')),
     'aliasdomaindelete': cmd('aliasdomaindelete', 'add', aliasdomain_delete,
-                             'fqdn', _(u'delete the specified alias domain')),
+                             'fqdn', _('delete the specified alias domain')),
     'aliasdomaininfo': cmd('aliasdomaininfo', 'adi', aliasdomain_info, 'fqdn',
-                         _(u'show the destination of the given alias domain')),
+                         _('show the destination of the given alias domain')),
     'aliasdomainswitch': cmd('aliasdomainswitch', 'ads', aliasdomain_switch,
-                             'fqdn destination', _(u'assign the given alias '
+                             'fqdn destination', _('assign the given alias '
                              'domain to an other domain')),
     # CatchallAlias commands
     'catchalladd': cmd('catchalladd', 'caa', catchall_add,
                        'fqdn destination ...',
-                       _(u'add one or more catch-all destinations for a '
-                         u'domain')),
+                       _('add one or more catch-all destinations for a '
+                         'domain')),
     'catchalldelete': cmd('catchalldelete', 'cad', catchall_delete,
                        'fqdn [destination ...]',
-                       _(u'delete the specified catch-all destination or all '
-                         u'of a domain\'s destinations')),
+                       _('delete the specified catch-all destination or all '
+                         'of a domain\'s destinations')),
     'catchallinfo': cmd('catchallinfo', 'cai', catchall_info, 'fqdn',
-                        _(u'show the catch-all destination(s) of the '
-                          u'specified domain')),
+                        _('show the catch-all destination(s) of the '
+                          'specified domain')),
     # Domain commands
     'domainadd': cmd('domainadd', 'da', domain_add, 'fqdn [transport]',
-                     _(u'create a new domain')),
+                     _('create a new domain')),
     'domaindelete': cmd('domaindelete', 'dd', domain_delete, 'fqdn [force]',
-                      _(u'delete the given domain and all its alias domains')),
+                      _('delete the given domain and all its alias domains')),
     'domaininfo': cmd('domaininfo', 'di', domain_info, 'fqdn [details]',
-                      _(u'display information about the given domain')),
+                      _('display information about the given domain')),
     'domainquota': cmd('domainquota', 'dq', domain_quota,
                        'fqdn storage [messages] [force]',
-                       _(u'update the quota limit of the specified domain')),
+                       _('update the quota limit of the specified domain')),
     'domainservices': cmd('domainservices', 'ds', domain_services,
                           'fqdn [service ...] [force]',
-                          _(u'enables the specified services and disables all '
-                            u'not specified services of the given domain')),
+                          _('enables the specified services and disables all '
+                            'not specified services of the given domain')),
     'domaintransport': cmd('domaintransport', 'dt', domain_transport,
                            'fqdn transport [force]',
-                           _(u'update the transport of the specified domain')),
+                           _('update the transport of the specified domain')),
     'domainnote': cmd('domainnote', 'do', domain_note, 'fqdn [note]',
-                     _(u'set, update or delete the note of the given domain')),
+                     _('set, update or delete the note of the given domain')),
     # List commands
     'listdomains': cmd('listdomains', 'ld', list_domains, '[pattern]',
-                      _(u'list all domains or search for domains by pattern')),
+                      _('list all domains or search for domains by pattern')),
     'listaddresses': cmd('listaddresses', 'll', list_addresses, '[pattern]',
-                         _(u'list all addresses or search for addresses by '
-                           u'pattern')),
+                         _('list all addresses or search for addresses by '
+                           'pattern')),
     'listusers': cmd('listusers', 'lu', list_users, '[pattern]',
-                     _(u'list all user accounts or search for accounts by '
-                       u'pattern')),
+                     _('list all user accounts or search for accounts by '
+                       'pattern')),
     'listaliases': cmd('listaliases', 'la', list_aliases, '[pattern]',
-                      _(u'list all aliases or search for aliases by pattern')),
+                      _('list all aliases or search for aliases by pattern')),
     'listrelocated': cmd('listrelocated', 'lr', list_relocated, '[pattern]',
-                         _(u'list all relocated users or search for relocated '
-                           u'users by pattern')),
+                         _('list all relocated users or search for relocated '
+                           'users by pattern')),
     # Relocated commands
     'relocatedadd': cmd('relocatedadd', 'ra', relocated_add,
                         'address newaddress',
-                        _(u'create a new record for a relocated user')),
+                        _('create a new record for a relocated user')),
     'relocateddelete': cmd('relocateddelete', 'rd', relocated_delete,
                            'address',
-                           _(u'delete the record of the relocated user')),
+                           _('delete the record of the relocated user')),
     'relocatedinfo': cmd('relocatedinfo', 'ri', relocated_info, 'address',
-                         _(u'print information about a relocated user')),
+                         _('print information about a relocated user')),
     # cli commands
     'configget': cmd('configget', 'cg', config_get, 'option',
                      _('show the actual value of the configuration option')),
     'configset': cmd('configset', 'cs', config_set, 'option value',
                       _('set a new value for the configuration option')),
     'configure': cmd('configure', 'cf', configure, '[section]',
-                     _(u'start interactive configuration mode')),
+                     _('start interactive configuration mode')),
     'listpwschemes': cmd('listpwschemes', 'lp', list_pwschemes, '',
-                         _(u'lists all usable password schemes and password '
-                           u'encoding suffixes')),
+                         _('lists all usable password schemes and password '
+                           'encoding suffixes')),
     'help': cmd('help', 'h', help_, '[subcommand]',
-                _(u'show a help overview or help for the given subcommand')),
+                _('show a help overview or help for the given subcommand')),
     'version': cmd('version', 'v', version, '',
-                   _(u'show version and copyright information')),
+                   _('show version and copyright information')),
     })
 
 
@@ -918,26 +909,26 @@
     get a dict from the handler."""
     order = ()
     if ctx.scmd == 'domaininfo':
-        order = ((u'domain name', 0), (u'gid', 1), (u'domain directory', 0),
-                 (u'quota limit/user', 0), (u'active services', 0),
-                 (u'transport', 0), (u'alias domains', 0), (u'accounts', 0),
-                 (u'aliases', 0), (u'relocated', 0), (u'catch-all dests', 0))
+        order = (('domain name', 0), ('gid', 1), ('domain directory', 0),
+                 ('quota limit/user', 0), ('active services', 0),
+                 ('transport', 0), ('alias domains', 0), ('accounts', 0),
+                 ('aliases', 0), ('relocated', 0), ('catch-all dests', 0))
     elif ctx.scmd == 'userinfo':
-        if ctx.argc == 4 and ctx.args[3] != u'aliases' or \
+        if ctx.argc == 4 and ctx.args[3] != 'aliases' or \
            ctx.cget('account.disk_usage'):
-            order = ((u'address', 0), (u'name', 0), (u'uid', 1), (u'gid', 1),
-                     (u'home', 0), (u'mail_location', 0),
-                     (u'quota storage', 0), (u'quota messages', 0),
-                     (u'disk usage', 0), (u'transport', 0), (u'smtp', 1),
-                     (u'pop3', 1), (u'imap', 1), (u'sieve', 1))
+            order = (('address', 0), ('name', 0), ('uid', 1), ('gid', 1),
+                     ('home', 0), ('mail_location', 0),
+                     ('quota storage', 0), ('quota messages', 0),
+                     ('disk usage', 0), ('transport', 0), ('smtp', 1),
+                     ('pop3', 1), ('imap', 1), ('sieve', 1))
         else:
-            order = ((u'address', 0), (u'name', 0), (u'uid', 1), (u'gid', 1),
-                     (u'home', 0), (u'mail_location', 0),
-                     (u'quota storage', 0), (u'quota messages', 0),
-                     (u'transport', 0), (u'smtp', 1), (u'pop3', 1),
-                     (u'imap', 1), (u'sieve', 1))
+            order = (('address', 0), ('name', 0), ('uid', 1), ('gid', 1),
+                     ('home', 0), ('mail_location', 0),
+                     ('quota storage', 0), ('quota messages', 0),
+                     ('transport', 0), ('smtp', 1), ('pop3', 1),
+                     ('imap', 1), ('sieve', 1))
     elif ctx.scmd == 'getuser':
-        order = ((u'uid', 1), (u'gid', 1), (u'address', 0))
+        order = (('uid', 1), ('gid', 1), ('address', 0))
     return order
 
 
@@ -950,43 +941,37 @@
         }
     else:
         q_usage = {
-            'used': locale.format('%d', used, True).decode(ENCODING,
-                                                           'replace'),
-            'limit': locale.format('%d', limit, True).decode(ENCODING,
-                                                             'replace'),
+            'used': locale.format('%d', used, True),
+            'limit': locale.format('%d', limit, True),
         }
     if limit:
         q_usage['percent'] = locale.format('%6.2f', 100. / limit * used, True)
     else:
         q_usage['percent'] = locale.format('%6.2f', 0, True)
-    #  Py25: fmt = format_domain_default if domaindefault else lambda s: s
-    if domaindefault:
-        fmt = format_domain_default
-    else:
-        fmt = lambda s: s
+    fmt = format_domain_default if domaindefault else lambda s: s
     # TP: e.g.: [  0.00%] 21.09 KiB/1.00 GiB
-    return fmt(_(u'[%(percent)s%%] %(used)s/%(limit)s') % q_usage)
+    return fmt(_('[%(percent)s%%] %(used)s/%(limit)s') % q_usage)
 
 
 def _print_info(ctx, info, title):
     """Print info dicts."""
     # TP: used in e.g. 'Domain information' or 'Account information'
-    msg = u'%s %s' % (title, _(u'information'))
-    w_std(msg, u'-' * len(msg))
+    msg = '%s %s' % (title, _('information'))
+    w_std(msg, '-' * len(msg))
     for key, upper in _get_order(ctx):
         if upper:
-            w_std(u'\t%s: %s' % (key.upper().ljust(17, u'.'), info[key]))
+            w_std('\t%s: %s' % (key.upper().ljust(17, '.'), info[key]))
         else:
-            w_std(u'\t%s: %s' % (key.title().ljust(17, u'.'), info[key]))
-    print
+            w_std('\t%s: %s' % (key.title().ljust(17, '.'), info[key]))
+    print()
     note = info.get('note')
     if note:
         _print_note(note + '\n')
 
 
 def _print_note(note):
-    msg = _(u'Note')
-    w_std(msg, u'-' * len(msg))
+    msg = _('Note')
+    w_std(msg, '-' * len(msg))
     old_ii = txt_wrpr.initial_indent
     old_si = txt_wrpr.subsequent_indent
     txt_wrpr.initial_indent = txt_wrpr.subsequent_indent = '\t'
@@ -1001,63 +986,61 @@
 def _print_list(alist, title):
     """Print a list."""
     # TP: used in e.g. 'Existing alias addresses' or 'Existing accounts'
-    msg = u'%s %s' % (_(u'Existing'), title)
-    w_std(msg, u'-' * len(msg))
+    msg = '%s %s' % (_('Existing'), title)
+    w_std(msg, '-' * len(msg))
     if alist:
-        if title != _(u'alias domains'):
-            w_std(*(u'\t%s' % item for item in alist))
+        if title != _('alias domains'):
+            w_std(*('\t%s' % item for item in alist))
         else:
             for domain in alist:
                 if not domain.startswith('xn--'):
-                    w_std(u'\t%s' % domain)
+                    w_std('\t%s' % domain)
                 else:
-                    w_std(u'\t%s (%s)' % (domain, domain.decode('idna')))
-        print
+                    w_std('\t%s (%s)' % (domain,
+                                        domain.encode('utf-8').decode('idna')))
+        print()
     else:
-        w_std(_(u'\tNone'), '')
+        w_std(_('\tNone'), '')
 
 
 def _print_aliase_info(alias, destinations):
     """Print the alias address and all its destinations"""
-    title = _(u'Alias information')
-    w_std(title, u'-' * len(title))
-    w_std(_(u'\tMail for %s will be redirected to:') % alias)
-    w_std(*(u'\t     * %s' % dest for dest in destinations))
-    print
+    title = _('Alias information')
+    w_std(title, '-' * len(title))
+    w_std(_('\tMail for %s will be redirected to:') % alias)
+    w_std(*('\t     * %s' % dest for dest in destinations))
+    print()
 
 
 def _print_catchall_info(domain, destinations):
     """Print the catchall destinations of a domain"""
-    title = _(u'Catch-all information')
-    w_std(title, u'-' * len(title))
-    w_std(_(u'\tMail to unknown local-parts in domain %s will be sent to:')
+    title = _('Catch-all information')
+    w_std(title, '-' * len(title))
+    w_std(_('\tMail to unknown local-parts in domain %s will be sent to:')
           % domain)
-    w_std(*(u'\t     * %s' % dest for dest in destinations))
-    print
+    w_std(*('\t     * %s' % dest for dest in destinations))
+    print()
 
 
 def _print_relocated_info(**kwargs):
     """Print the old and new addresses of a relocated user."""
-    title = _(u'Relocated information')
-    w_std(title, u'-' * len(title))
-    w_std(_(u"\tUser '%(addr)s' has moved to '%(dest)s'") % kwargs, '')
+    title = _('Relocated information')
+    w_std(title, '-' * len(title))
+    w_std(_("\tUser '%(addr)s' has moved to '%(dest)s'") % kwargs, '')
 
 
 def _format_domain(domain, main=True):
     """format (prefix/convert) the domain name."""
     if domain.startswith('xn--'):
-        domain = u'%s (%s)' % (domain, domain.decode('idna'))
+        domain = '%s (%s)' % (domain, domain.encode('utf-8').decode('idna'))
     if main:
-        return u'\t[+] %s' % domain
-    return u'\t[-]     %s' % domain
+        return '\t[+] %s' % domain
+    return '\t[-]     %s' % domain
 
 
 def _print_domain_list(dids, domains, matching):
     """Print a list of (matching) domains/alias domains."""
-    if matching:
-        title = _(u'Matching domains')
-    else:
-        title = _(u'Existing domains')
+    title = _('Matching domains') if matching else _('Existing domains')
     w_std(title, '-' * len(title))
     if domains:
         for did in dids:
@@ -1067,7 +1050,7 @@
                 w_std(*(_format_domain(a, False) for a in domains[did][1:]))
     else:
         w_std(_('\tNone'))
-    print
+    print()
 
 
 def _print_address_list(which, dids, addresses, matching):
@@ -1083,9 +1066,9 @@
     }
     try:
         if matching:
-            title = _(u'Matching %s') % _trans[which]
+            title = _('Matching %s') % _trans[which]
         else:
-            title = _(u'Existing %s') % _trans[which]
+            title = _('Existing %s') % _trans[which]
         w_std(title, '-' * len(title))
     except KeyError:
         raise VMMError(_("Invalid address type for list: '%s'") % which,
@@ -1111,15 +1094,16 @@
                 w_std('\t%s %s' % (leader, addr))
     else:
         w_std(_('\tNone'))
-    print
+    print()
 
 
 def _print_aliasdomain_info(info):
     """Print alias domain information."""
-    title = _(u'Alias domain information')
+    title = _('Alias domain information')
     for key in ('alias', 'domain'):
         if info[key].startswith('xn--'):
-            info[key] = u'%s (%s)' % (info[key], info[key].decode('idna'))
+            info[key] = '%s (%s)' % (info[key],
+                                     info[key].encode(ENCODING).decode('idna'))
     w_std(title, '-' * len(title),
           _('\tThe alias domain %(alias)s belongs to:\n\t    * %(domain)s') %
           info, '')
--- a/VirtualMailManager/common.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/common.py	Sun Dec 09 15:03:33 2012 +0000
@@ -36,9 +36,9 @@
 
 def get_unicode(string):
     """Converts `string` to `unicode`, if necessary."""
-    if isinstance(string, unicode):
+    if isinstance(string, str):
         return string
-    return unicode(string, ENCODING, 'replace')
+    return str(string, ENCODING, 'replace')
 
 
 def lisdir(path):
@@ -60,19 +60,19 @@
     """
     binary = expand_path(binary)
     if not os.path.isfile(binary):
-        raise VMMError(_(u"No such file: '%s'") % get_unicode(binary),
+        raise VMMError(_("No such file: '%s'") % get_unicode(binary),
                        NO_SUCH_BINARY)
     if not os.access(binary, os.X_OK):
-        raise VMMError(_(u"File is not executable: '%s'") %
+        raise VMMError(_("File is not executable: '%s'") %
                        get_unicode(binary), NOT_EXECUTABLE)
     return binary
 
 
 def human_size(size):
     """Converts the `size` in bytes in human readable format."""
-    if not isinstance(size, (long, int)):
+    if not isinstance(size, int):
         try:
-            size = long(size)
+            size = int(size)
         except ValueError:
             raise TypeError("'size' must be a positive long or int.")
     if size < 0:
@@ -80,14 +80,14 @@
     if size < 1024:
         return str(size)
     # TP: abbreviations of gibibyte, tebibyte kibibyte and mebibyte
-    prefix_multiply = ((_(u'TiB'), 1 << 40), (_(u'GiB'), 1 << 30),
-                       (_(u'MiB'), 1 << 20), (_(u'KiB'), 1 << 10))
+    prefix_multiply = ((_('TiB'), 1 << 40), (_('GiB'), 1 << 30),
+                       (_('MiB'), 1 << 20), (_('KiB'), 1 << 10))
     for prefix, multiply in prefix_multiply:
         if size >= multiply:
             # TP: e.g.: '%(size)s %(prefix)s' -> '118.30 MiB'
-            return _(u'%(size)s %(prefix)s') % {
+            return _('%(size)s %(prefix)s') % {
                     'size': locale.format('%.2f', float(size) / multiply,
-                                          True).decode(ENCODING, 'replace'),
+                                          True),
                     'prefix': prefix}
 
 
@@ -97,7 +97,7 @@
     The string `size` can be suffixed with *b* (bytes), *k* (kilobytes),
     *M* (megabytes) or *G* (gigabytes).
     """
-    if not isinstance(size, basestring) or not size:
+    if not isinstance(size, str) or not size:
         raise TypeError('size must be a non empty string.')
     if size[-1].upper() in ('B', 'K', 'M', 'G'):
         try:
@@ -108,11 +108,11 @@
         if unit == 'B':
             return num
         elif unit == 'K':
-            return num << 10L
+            return num << 10
         elif unit == 'M':
-            return num << 20L
+            return num << 20
         else:
-            return num << 30L
+            return num << 30
     else:
         try:
             num = int(size)
@@ -136,8 +136,8 @@
     """
     if transport.transport in ('virtual', 'virtual:') and \
       not maillocation.postfix:
-        raise VMMError(_(u"Invalid transport '%(transport)s' for mailbox "
-                         u"format '%(mbfmt)s'.") %
+        raise VMMError(_("Invalid transport '%(transport)s' for mailbox "
+                         "format '%(mbfmt)s'.") %
                        {'transport': transport.transport,
                         'mbfmt': maillocation.mbformat}, INVALID_MAIL_LOCATION)
 
@@ -189,7 +189,7 @@
     global _version_cache
     if version in _version_cache:
         return _version_cache[version]
-    if not isinstance(version, (int, long)):
+    if not isinstance(version, int):
         raise TypeError('Argument is not a int/long: %r', version)
     major = (version >> 28) & 0xFF
     minor = (version >> 20) & 0xFF
@@ -197,7 +197,8 @@
     level = (version >> 8) & 0x0F
     serial = version & 0xFF
 
-    levels = dict(zip(_version_level.values(), _version_level.keys()))
+    levels = dict(list(zip(list(_version_level.values()),
+                  list(_version_level.keys()))))
     if level == 0xF and not serial:
         version_string = '%u.%u.%u' % (major, minor, patch)
     elif level in levels and not patch:
@@ -214,7 +215,7 @@
     # TP: [domain default] indicates that a user's setting is the same as
     # configured in the user's domain.
     # e.g.: [  0.84%] 42/5,000 [domain default]
-    return _(u'%s [domain default]') % domaindata
+    return _('%s [domain default]') % domaindata
 
 
 def search_addresses(dbh, typelimit=None, lpattern=None, llike=False,
--- a/VirtualMailManager/config.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/config.py	Sun Dec 09 15:03:33 2012 +0000
@@ -8,10 +8,10 @@
     VMM's configuration module for simplified configuration access.
 """
 
-from ConfigParser import \
+from configparser import \
      Error, MissingSectionHeaderError, NoOptionError, NoSectionError, \
      ParsingError, RawConfigParser
-from cStringIO import StringIO
+from io import StringIO
 
 from VirtualMailManager.common import VERSION_RE, \
      exec_ok, expand_path, get_unicode, lisdir, size_in_bytes, version_hex
@@ -19,6 +19,7 @@
 from VirtualMailManager.errors import ConfigError, VMMError
 from VirtualMailManager.maillocation import known_format
 from VirtualMailManager.password import verify_scheme as _verify_scheme
+import collections
 
 DB_MODULES = ('psycopg2', 'pypgsql')
 DB_SSL_MODES = ('allow', 'disabled', 'prefer', 'require', 'verify-ca',
@@ -83,10 +84,10 @@
         """
         if isinstance(value, bool):
             return value
-        if value.lower() in self._boolean_states:
-            return self._boolean_states[value.lower()]
+        if value.lower() in self.BOOLEAN_STATES:
+            return self.BOOLEAN_STATES[value.lower()]
         else:
-            raise ConfigValueError(_(u"Not a boolean: '%s'") %
+            raise ConfigValueError(_("Not a boolean: '%s'") %
                                    get_unicode(value))
 
     def getboolean(self, section, option):
@@ -103,9 +104,9 @@
         tmp = self.get(section, option)
         if isinstance(tmp, bool):
             return tmp
-        if not tmp.lower() in self._boolean_states:
+        if not tmp.lower() in self.BOOLEAN_STATES:
             raise ValueError('Not a boolean: %s' % tmp)
-        return self._boolean_states[tmp.lower()]
+        return self.BOOLEAN_STATES[tmp.lower()]
 
     def _get_section_option(self, section_option):
         """splits ``section_option`` (section.option) in two parts and
@@ -124,8 +125,8 @@
         sect_opt = section_option.lower().split('.')
         # TODO: cache it
         if len(sect_opt) != 2 or not sect_opt[0] or not sect_opt[1]:
-            raise BadOptionError(_(u"Bad format: '%s' - expected: "
-                                   u"section.option") %
+            raise BadOptionError(_("Bad format: '%s' - expected: "
+                                   "section.option") %
                                  get_unicode(section_option))
         if not sect_opt[0] in self._cfg:
             raise NoSectionError(sect_opt[0])
@@ -143,14 +144,14 @@
             raise NoSectionError(section)
         else:
             return ((k, self._cfg[section][k].default)
-                    for k in self._cfg[section].iterkeys())
+                    for k in self._cfg[section].keys())
         # still here? Get defaults and merge defaults with configured setting
         defaults = dict((k, self._cfg[section][k].default)
-                        for k in self._cfg[section].iterkeys())
+                        for k in self._cfg[section].keys())
         defaults.update(sect)
         if '__name__' in defaults:
             del defaults['__name__']
-        return defaults.iteritems()
+        return iter(defaults.items())
 
     def dget(self, option):
         """Returns the value of the `option`.
@@ -216,7 +217,7 @@
 
     def sections(self):
         """Returns an iterator object for all configuration sections."""
-        return self._cfg.iterkeys()
+        return iter(self._cfg.keys())
 
 
 class LazyConfigOption(object):
@@ -246,15 +247,12 @@
           check the value, when `LazyConfig.set()` is called.
         """
         self.__cls = cls
-        if not default is None:  # enforce the type of the default value
-            self.__default = self.__cls(default)
-        else:
-            self.__default = default
-        if not callable(getter):
+        self.__default = default if default is None else self.__cls(default)
+        if not isinstance(getter, collections.Callable):
             raise TypeError('getter has to be a callable, got a %r' %
                             getter.__class__.__name__)
         self.__getter = getter
-        if validate and not callable(validate):
+        if validate and not isinstance(validate, collections.Callable):
             raise TypeError('validate has to be callable or None, got a %r' %
                             validate.__class__.__name__)
         self.__validate = validate
@@ -337,9 +335,9 @@
             },
             'mailbox': {
                 'folders': LCO(str, 'Drafts:Sent:Templates:Trash',
-                               self.unicode),
+                               self.str),
                 'format': LCO(str, 'maildir', self.get, check_mailbox_format),
-                'root': LCO(str, 'Maildir', self.unicode),
+                'root': LCO(str, 'Maildir', self.str),
                 'subscribe': LCO(bool_t, True, self.getboolean),
             },
             'misc': {
@@ -360,12 +358,11 @@
         Raises a ConfigError if the configuration syntax is
         invalid.
         """
-        self._cfg_file = open(self._cfg_filename, 'r')
-        try:
-            self.readfp(self._cfg_file)
-        except (MissingSectionHeaderError, ParsingError), err:
-            raise ConfigError(str(err), CONF_ERROR)
-        self._cfg_file.close()
+        with open(self._cfg_filename, 'r', encoding='utf-8') as self._cfg_file:
+            try:
+                self.readfp(self._cfg_file)
+            except (MissingSectionHeaderError, ParsingError) as err:
+                raise ConfigError(str(err), CONF_ERROR)
 
     def check(self):
         """Performs a configuration check.
@@ -374,9 +371,9 @@
         Or some settings have a invalid value.
         """
         def iter_dict():
-            for section, options in self._missing.iteritems():
-                errmsg.write(_(u'* Section: %s\n') % section)
-                errmsg.writelines(u'    %s\n' % option for option in options)
+            for section, options in self._missing.items():
+                errmsg.write(_('* Section: %s\n') % section)
+                errmsg.writelines('    %s\n' % option for option in options)
             self._missing.clear()
 
         errmsg = None
@@ -385,19 +382,19 @@
                     'dovecot_version' in self._missing['misc']
         if self._missing:
             errmsg = StringIO()
-            errmsg.write(_(u'Check of configuration file %s failed.\n') %
+            errmsg.write(_('Check of configuration file %s failed.\n') %
                          self._cfg_filename)
-            errmsg.write(_(u'Missing options, which have no default value.\n'))
+            errmsg.write(_('Missing options, which have no default value.\n'))
             iter_dict()
         self._chk_possible_values(miss_vers)
         if self._missing:
             if not errmsg:
                 errmsg = StringIO()
-                errmsg.write(_(u'Check of configuration file %s failed.\n') %
+                errmsg.write(_('Check of configuration file %s failed.\n') %
                              self._cfg_filename)
-                errmsg.write(_(u'Invalid configuration values.\n'))
+                errmsg.write(_('Invalid configuration values.\n'))
             else:
-                errmsg.write('\n' + _(u'Invalid configuration values.\n'))
+                errmsg.write('\n' + _('Invalid configuration values.\n'))
             iter_dict()
         if errmsg:
             raise ConfigError(errmsg.getvalue(), CONF_ERROR)
@@ -412,7 +409,7 @@
         value to a long"""
         return size_in_bytes(self.get(section, option))
 
-    def unicode(self, section, option):
+    def str(self, section, option):
         """Returns the value of the `option` from `section`, converted
         to Unicode."""
         return get_unicode(self.get(section, option))
@@ -421,9 +418,9 @@
         """Checks all section's options for settings w/o a default
         value. Missing items will be stored in _missing.
         """
-        for section in self._cfg.iterkeys():
+        for section in self._cfg.keys():
             missing = []
-            for option, value in self._cfg[section].iteritems():
+            for option, value in self._cfg[section].items():
                 if (value.default is None and
                     not RawConfigParser.has_option(self, section, option)):
                     missing.append(option)
@@ -436,30 +433,30 @@
             value = self.get('misc', 'dovecot_version')
             if not VERSION_RE.match(value):
                 self._missing['misc'] = ['version: ' +
-                        _(u"Not a valid Dovecot version: '%s'") % value]
+                        _("Not a valid Dovecot version: '%s'") % value]
         # section database
         db_err = []
         value = self.dget('database.module').lower()
         if value not in DB_MODULES:
             db_err.append('module: ' +
-                          _(u"Unsupported database module: '%s'") % value)
+                          _("Unsupported database module: '%s'") % value)
         if value == 'psycopg2':
             value = self.dget('database.sslmode')
             if value not in DB_SSL_MODES:
                 db_err.append('sslmode: ' +
-                              _(u"Unknown pgsql SSL mode: '%s'") % value)
+                              _("Unknown pgsql SSL mode: '%s'") % value)
         if db_err:
             self._missing['database'] = db_err
         # section mailbox
         value = self.dget('mailbox.format')
         if not known_format(value):
             self._missing['mailbox'] = ['format: ' +
-                              _(u"Unsupported mailbox format: '%s'") % value]
+                              _("Unsupported mailbox format: '%s'") % value]
         # section domain
         try:
             value = self.dget('domain.quota_bytes')
-        except (ValueError, TypeError), err:
-            self._missing['domain'] = [u'quota_bytes: ' + str(err)]
+        except (ValueError, TypeError) as err:
+            self._missing['domain'] = ['quota_bytes: ' + str(err)]
 
 
 def is_dir(path):
@@ -470,14 +467,14 @@
     path = expand_path(path)
     if lisdir(path):
         return path
-    raise ConfigValueError(_(u"No such directory: %s") % get_unicode(path))
+    raise ConfigValueError(_("No such directory: %s") % get_unicode(path))
 
 
 def check_db_module(module):
     """Check if the *module* is a supported pgsql module."""
     if module.lower() in DB_MODULES:
         return module
-    raise ConfigValueError(_(u"Unsupported database module: '%s'") %
+    raise ConfigValueError(_("Unsupported database module: '%s'") %
                            get_unicode(module))
 
 
@@ -485,7 +482,7 @@
     """Check if the *ssl_mode* is one of the SSL modes, known by pgsql."""
     if ssl_mode in DB_SSL_MODES:
         return ssl_mode
-    raise ConfigValueError(_(u"Unknown pgsql SSL mode: '%s'") %
+    raise ConfigValueError(_("Unknown pgsql SSL mode: '%s'") %
                            get_unicode(ssl_mode))
 
 
@@ -498,7 +495,7 @@
     format = format.lower()
     if known_format(format):
         return format
-    raise ConfigValueError(_(u"Unsupported mailbox format: '%s'") %
+    raise ConfigValueError(_("Unsupported mailbox format: '%s'") %
                            get_unicode(format))
 
 
@@ -508,8 +505,8 @@
     Otherwise a `ConfigValueError` will be raised."""
     try:
         tmp = size_in_bytes(value)
-    except (TypeError, ValueError), err:
-        raise ConfigValueError(_(u"Not a valid size value: '%s'") %
+    except (TypeError, ValueError) as err:
+        raise ConfigValueError(_("Not a valid size value: '%s'") %
                                get_unicode(value))
     return value
 
@@ -520,7 +517,7 @@
     Otherwise a `ConfigValueError` will be raised.
     """
     if not VERSION_RE.match(version_string):
-        raise ConfigValueError(_(u"Not a valid Dovecot version: '%s'") %
+        raise ConfigValueError(_("Not a valid Dovecot version: '%s'") %
                                get_unicode(version_string))
     return version_string
 
@@ -531,7 +528,7 @@
     """
     try:
         scheme, encoding = _verify_scheme(scheme)
-    except VMMError, err:  # 'cast' it
+    except VMMError as err:  # 'cast' it
         raise ConfigValueError(err.msg)
     if not encoding:
         return scheme
--- a/VirtualMailManager/domain.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/domain.py	Sun Dec 09 15:03:33 2012 +0000
@@ -18,7 +18,6 @@
 from VirtualMailManager.common import validate_transport
 from VirtualMailManager.errors import VMMError, DomainError as DomErr
 from VirtualMailManager.maillocation import MailLocation
-from VirtualMailManager.pycompat import all, any
 from VirtualMailManager.quotalimit import QuotaLimit
 from VirtualMailManager.serviceset import ServiceSet
 from VirtualMailManager.transport import Transport
@@ -79,7 +78,7 @@
         dbc.close()
         if result:
             if not result[5]:
-                raise DomErr(_(u"The domain '%s' is an alias domain.") %
+                raise DomErr(_("The domain '%s' is an alias domain.") %
                              self._name, DOMAIN_ALIAS_EXISTS)
             self._gid, self._directory = result[0], result[4]
             self._qlimit = QuotaLimit(self._dbh, qid=result[1])
@@ -114,9 +113,9 @@
         result = result[0]
         if any(result):
             keys = ('account_count', 'alias_count', 'relocated_count')
-            raise DomErr(_(u'There are %(account_count)u accounts, '
-                           u'%(alias_count)u aliases and %(relocated_count)u '
-                           u'relocated users.') % dict(zip(keys, result)),
+            raise DomErr(_('There are %(account_count)u accounts, '
+                           '%(alias_count)u aliases and %(relocated_count)u '
+                           'relocated users.') % dict(list(zip(keys, result))),
                          ACCOUNT_AND_ALIAS_PRESENT)
 
     def _chk_state(self, must_exist=True):
@@ -126,10 +125,10 @@
           - or *must_exist* is `False` and the domain exists
         """
         if must_exist and self._new:
-            raise DomErr(_(u"The domain '%s' does not exist.") % self._name,
+            raise DomErr(_("The domain '%s' does not exist.") % self._name,
                          NO_SUCH_DOMAIN)
         elif not must_exist and not self._new:
-            raise DomErr(_(u"The domain '%s' already exists.") % self._name,
+            raise DomErr(_("The domain '%s' already exists.") % self._name,
                          DOMAIN_EXISTS)
 
     def _update_tables(self, column, value):
@@ -264,7 +263,7 @@
           The note, or None to remove
         """
         self._chk_state(False)
-        assert note is None or isinstance(note, basestring)
+        assert note is None or isinstance(note, str)
         self._note = note
 
     def save(self):
@@ -326,8 +325,8 @@
           enforce new quota limit for all accounts, default `False`
         """
         if cfg_dget('misc.dovecot_version') < 0x10102f00:
-            raise VMMError(_(u'PostgreSQL-based dictionary quota requires '
-                             u'Dovecot >= v1.1.2.'), VMM_ERROR)
+            raise VMMError(_('PostgreSQL-based dictionary quota requires '
+                             'Dovecot >= v1.1.2.'), VMM_ERROR)
         self._chk_state()
         assert isinstance(quotalimit, QuotaLimit)
         if not force and quotalimit == self._qlimit:
@@ -389,7 +388,7 @@
           the new note
         """
         self._chk_state()
-        assert note is None or isinstance(note, basestring)
+        assert note is None or isinstance(note, str)
         if note == self._note:
             return
         self._update_tables('note', note)
@@ -406,7 +405,7 @@
         dbc.close()
         keys = ('alias domains', 'accounts', 'aliases', 'relocated',
                 'catch-all dests')
-        info = dict(zip(keys, info))
+        info = dict(list(zip(keys, info)))
         info['gid'] = self._gid
         info['domain name'] = self._name
         info['transport'] = self._transport.transport
@@ -433,7 +432,7 @@
         dbc.close()
         accounts = []
         if users:
-            addr = u'@'.join
+            addr = '@'.join
             _dom = self._name
             accounts = [addr((account[0], _dom)) for account in users]
         return accounts
@@ -448,7 +447,7 @@
         dbc.close()
         aliases = []
         if addresses:
-            addr = u'@'.join
+            addr = '@'.join
             _dom = self._name
             aliases = [addr((alias[0], _dom)) for alias in addresses]
         return aliases
@@ -463,7 +462,7 @@
         dbc.close()
         relocated = []
         if addresses:
-            addr = u'@'.join
+            addr = '@'.join
             _dom = self._name
             relocated = [addr((address[0], _dom)) for address in addresses]
         return relocated
@@ -500,11 +499,11 @@
 
     """
     if not RE_DOMAIN.match(domainname):
-        domainname = domainname.encode('idna')
+        domainname = domainname.encode('idna').decode()
     if len(domainname) > 255:
-        raise DomErr(_(u'The domain name is too long'), DOMAIN_TOO_LONG)
+        raise DomErr(_('The domain name is too long'), DOMAIN_TOO_LONG)
     if not RE_DOMAIN.match(domainname):
-        raise DomErr(_(u"The domain name '%s' is invalid") % domainname,
+        raise DomErr(_("The domain name '%s' is invalid") % domainname,
                      DOMAIN_INVALID)
     return domainname
 
--- a/VirtualMailManager/emailaddress.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/emailaddress.py	Sun Dec 09 15:03:33 2012 +0000
@@ -26,7 +26,7 @@
 
     def __init__(self, address, _validate=True):
         """Creates a new instance from the string/unicode ``address``."""
-        assert isinstance(address, basestring)
+        assert isinstance(address, str)
         self._localpart = None
         self._domainname = None
         if _validate:
@@ -70,16 +70,16 @@
         parts = address.split('@')
         p_len = len(parts)
         if p_len < 2:
-            raise EAErr(_(u"Missing the '@' sign in address: '%s'") % address,
+            raise EAErr(_("Missing the '@' sign in address: '%s'") % address,
                         INVALID_ADDRESS)
         elif p_len > 2:
-            raise EAErr(_(u"Too many '@' signs in address: '%s'") % address,
+            raise EAErr(_("Too many '@' signs in address: '%s'") % address,
                         INVALID_ADDRESS)
         if not parts[0]:
-            raise EAErr(_(u"Missing local-part in address: '%s'") % address,
+            raise EAErr(_("Missing local-part in address: '%s'") % address,
                         LOCALPART_INVALID)
         if not parts[1]:
-            raise EAErr(_(u"Missing domain name in address: '%s'") % address,
+            raise EAErr(_("Missing domain name in address: '%s'") % address,
                         DOMAIN_NO_NAME)
         self._localpart = check_localpart(parts[0])
         self._domainname = check_domainname(parts[1])
@@ -105,7 +105,7 @@
         if not _validate:
             try:
                 self._chk_address(address)
-            except DomainError, err:
+            except DomainError as err:
                 if err.code is DOMAIN_INVALID and \
                    address.split('@')[1] == 'localhost':
                     self._localhost = True
@@ -142,13 +142,13 @@
     invalid characters.
     """
     if len(localpart) > 64:
-        raise EAErr(_(u"The local-part '%s' is too long.") % localpart,
+        raise EAErr(_("The local-part '%s' is too long.") % localpart,
                     LOCALPART_TOO_LONG)
     invalid_chars = set(RE_LOCALPART.findall(localpart))
     if invalid_chars:
-        i_chars = u''.join((u'"%s" ' % c for c in invalid_chars))
-        raise EAErr(_(u"The local-part '%(l_part)s' contains invalid "
-                      u"characters: %(i_chars)s") % {'l_part': localpart,
+        i_chars = ''.join(('"%s" ' % c for c in invalid_chars))
+        raise EAErr(_("The local-part '%(l_part)s' contains invalid "
+                      "characters: %(i_chars)s") % {'l_part': localpart,
                     'i_chars': i_chars}, LOCALPART_INVALID)
     return localpart
 
--- a/VirtualMailManager/ext/postconf.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/ext/postconf.py	Sun Dec 09 15:03:33 2012 +0000
@@ -53,7 +53,7 @@
         stderr = Popen((self._bin, '-e', parameter + '=' + str(value)),
                        stderr=PIPE).communicate()[1]
         if stderr:
-            raise VMMError(stderr.strip(), VMM_ERROR)
+            raise VMMError(stderr.strip().decode(), VMM_ERROR)
 
     def read(self, parameter, expand_vars=True):
         """Returns the parameters value.
@@ -81,8 +81,8 @@
         """Check that the `parameter` looks like a configuration parameter.
         If not, a VMMError will be raised."""
         if not self.__class__._parameter_re.match(parameter):
-            raise VMMError(_(u"The value '%s' does not look like a valid "
-                             u"Postfix configuration parameter name.") %
+            raise VMMError(_("The value '%s' does not look like a valid "
+                             "Postfix configuration parameter name.") %
                            parameter, VMM_ERROR)
 
     def _expand_vars(self):
@@ -99,7 +99,7 @@
 
     def _expand_multi_vars(self, old_new):
         """Replace all $vars in self._val with their values."""
-        for old, new in old_new.iteritems():
+        for old, new in old_new.items():
             self._val = self._val.replace('$' + old, new)
 
     def _read(self, parameter):
@@ -107,8 +107,8 @@
         stdout, stderr = Popen([self._bin, '-h', parameter], stdout=PIPE,
                                stderr=PIPE).communicate()
         if stderr:
-            raise VMMError(stderr.strip(), VMM_ERROR)
-        return stdout.strip()
+            raise VMMError(stderr.strip().decode(), VMM_ERROR)
+        return stdout.strip().decode()
 
     def _read_multi(self, parameters):
         """Ask postconf for multiple configuration parameters. Returns a dict
@@ -117,9 +117,9 @@
         cmd.extend(parameter[1:] for parameter in parameters)
         stdout, stderr = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate()
         if stderr:
-            raise VMMError(stderr.strip(), VMM_ERROR)
+            raise VMMError(stderr.strip().decode(), VMM_ERROR)
         par_val = {}
-        for line in stdout.splitlines():
+        for line in stdout.decode().splitlines():
             par, val = line.split(' = ')
             par_val[par] = val
         return par_val
--- a/VirtualMailManager/handler.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/handler.py	Sun Dec 09 15:03:33 2012 +0000
@@ -16,6 +16,7 @@
 import re
 
 from shutil import rmtree
+from stat import S_IRGRP, S_IROTH, S_IWGRP, S_IWOTH
 from subprocess import Popen, PIPE
 
 from VirtualMailManager.account import Account
@@ -37,7 +38,6 @@
 from VirtualMailManager.errors import \
      DomainError, NotRootError, PermissionError, VMMError
 from VirtualMailManager.mailbox import new as new_mailbox
-from VirtualMailManager.pycompat import all, any
 from VirtualMailManager.quotalimit import QuotaLimit
 from VirtualMailManager.relocated import Relocated
 from VirtualMailManager.serviceset import ServiceSet, SERVICES
@@ -51,9 +51,9 @@
 CFG_PATH = '/root:/usr/local/etc:/etc'
 RE_DOMAIN_SEARCH = """^[a-z0-9-\.]+$"""
 OTHER_TYPES = {
-    TYPE_ACCOUNT: (_(u'an account'), ACCOUNT_EXISTS),
-    TYPE_ALIAS: (_(u'an alias'), ALIAS_EXISTS),
-    TYPE_RELOCATED: (_(u'a relocated user'), RELOCATED_EXISTS),
+    TYPE_ACCOUNT: (_('an account'), ACCOUNT_EXISTS),
+    TYPE_ALIAS: (_('an alias'), ALIAS_EXISTS),
+    TYPE_RELOCATED: (_('a relocated user'), RELOCATED_EXISTS),
 }
 
 
@@ -79,7 +79,7 @@
         self._db_connect = None
 
         if os.geteuid():
-            raise NotRootError(_(u"You are not root.\n\tGood bye!\n"),
+            raise NotRootError(_("You are not root.\n\tGood bye!\n"),
                                CONF_NOPERM)
         if self._check_cfg_file():
             self._cfg = Cfg(self._cfg_fname)
@@ -99,22 +99,24 @@
                 self._cfg_fname = tmp
                 break
         if not self._cfg_fname:
-            raise VMMError(_(u"Could not find '%(cfg_file)s' in: "
-                             u"'%(cfg_path)s'") % {'cfg_file': CFG_FILE,
+            raise VMMError(_("Could not find '%(cfg_file)s' in: "
+                             "'%(cfg_path)s'") % {'cfg_file': CFG_FILE,
                            'cfg_path': CFG_PATH}, CONF_NOFILE)
 
     def _check_cfg_file(self):
         """Checks the configuration file, returns bool"""
+        GRPRW = S_IRGRP | S_IWGRP
+        OTHRW = S_IROTH | S_IWOTH
         self._find_cfg_file()
         fstat = os.stat(self._cfg_fname)
-        fmode = int(oct(fstat.st_mode & 0777))
-        if fmode % 100 and fstat.st_uid != fstat.st_gid or \
-           fmode % 10 and fstat.st_uid == fstat.st_gid:
+        if (fstat.st_uid == fstat.st_gid and fstat.st_mode & OTHRW) or \
+           (fstat.st_uid != fstat.st_gid and fstat.st_mode & (GRPRW | OTHRW)):
             # TP: Please keep the backticks around the command. `chmod 0600 …`
-            raise PermissionError(_(u"wrong permissions for '%(file)s': "
-                                    u"%(perms)s\n`chmod 0600 %(file)s` would "
-                                    u"be great.") % {'file': self._cfg_fname,
-                                  'perms': fmode}, CONF_WRONGPERM)
+            raise PermissionError(_("wrong permissions for '%(file)s': "
+                                    "%(perms)s\n`chmod 0600 %(file)s` would "
+                                    "be great.") % {'file': self._cfg_fname,
+                                  'perms': oct(fstat.st_mode)[-4:]},
+                                  CONF_WRONGPERM)
         else:
             return True
 
@@ -125,23 +127,23 @@
         dir_created = False
         basedir = self._cfg.dget('misc.base_directory')
         if not os.path.exists(basedir):
-            old_umask = os.umask(0006)
-            os.makedirs(basedir, 0771)
+            old_umask = os.umask(0o006)
+            os.makedirs(basedir, 0o771)
             os.chown(basedir, 0, 0)
             os.umask(old_umask)
             dir_created = True
         if not dir_created and not lisdir(basedir):
-            raise VMMError(_(u"'%(path)s' is not a directory.\n(%(cfg_file)s: "
-                             u"section 'misc', option 'base_directory')") %
+            raise VMMError(_("'%(path)s' is not a directory.\n(%(cfg_file)s: "
+                             "section 'misc', option 'base_directory')") %
                            {'path': basedir, 'cfg_file': self._cfg_fname},
                            NO_SUCH_DIRECTORY)
         for opt, val in self._cfg.items('bin'):
             try:
                 exec_ok(val)
-            except VMMError, err:
+            except VMMError as err:
                 if err.code in (NO_SUCH_BINARY, NOT_EXECUTABLE):
-                    raise VMMError(err.msg + _(u"\n(%(cfg_file)s: section "
-                                   u"'bin', option '%(option)s')") %
+                    raise VMMError(err.msg + _("\n(%(cfg_file)s: section "
+                                   "'bin', option '%(option)s')") %
                                    {'cfg_file': self._cfg_fname,
                                     'option': opt}, err.code)
                 else:
@@ -154,14 +156,14 @@
             try:
                 _db_mod = __import__('psycopg2')
             except ImportError:
-                raise VMMError(_(u"Unable to import database module '%s'.") %
+                raise VMMError(_("Unable to import database module '%s'.") %
                                'psycopg2', VMM_ERROR)
             self._db_connect = self._psycopg2_connect
         else:
             try:
                 tmp = __import__('pyPgSQL', globals(), locals(), ['PgSQL'])
             except ImportError:
-                raise VMMError(_(u"Unable to import database module '%s'.") %
+                raise VMMError(_("Unable to import database module '%s'.") %
                                'pyPgSQL', VMM_ERROR)
             _db_mod = tmp.PgSQL
             self._db_connect = self._pypgsql_connect
@@ -181,7 +183,7 @@
                 dbc = self._dbh.cursor()
                 dbc.execute("SET NAMES 'UTF8'")
                 dbc.close()
-            except _db_mod.libpq.DatabaseError, err:
+            except _db_mod.libpq.DatabaseError as err:
                 raise VMMError(str(err), DATABASE_ERROR)
 
     def _psycopg2_connect(self):
@@ -198,11 +200,10 @@
                         user=self._cfg.pget('database.user'),
                         password=self._cfg.pget('database.pass'))
                 self._dbh.set_client_encoding('utf8')
-                _db_mod.extensions.register_type(_db_mod.extensions.UNICODE)
                 dbc = self._dbh.cursor()
                 dbc.execute("SET NAMES 'UTF8'")
                 dbc.close()
-            except _db_mod.DatabaseError, err:
+            except _db_mod.DatabaseError as err:
                 raise VMMError(str(err), DATABASE_ERROR)
 
     def _chk_other_address_types(self, address, exclude):
@@ -240,7 +241,7 @@
             return False
         # TP: %(a_type)s will be one of: 'an account', 'an alias' or
         # 'a relocated user'
-        msg = _(u"There is already %(a_type)s with the address '%(address)s'.")
+        msg = _("There is already %(a_type)s with the address '%(address)s'.")
         raise VMMError(msg % {'a_type': OTHER_TYPES[other][0],
                               'address': address}, OTHER_TYPES[other][1])
 
@@ -282,7 +283,7 @@
         """
         if lisdir(directory):
             return Popen([self._cfg.dget('bin.du'), "-hs", directory],
-                         stdout=PIPE).communicate()[0].split('\t')[0]
+                         stdout=PIPE).communicate()[0].decode().split('\t')[0]
         else:
             self._warnings.append(_('No such directory: %s') % directory)
             return 0
@@ -293,16 +294,16 @@
         hashdir, domdir = domain.directory.split(os.path.sep)[-2:]
         dir_created = False
         os.chdir(self._cfg.dget('misc.base_directory'))
-        old_umask = os.umask(0022)
+        old_umask = os.umask(0o022)
         if not os.path.exists(hashdir):
-            os.mkdir(hashdir, 0711)
+            os.mkdir(hashdir, 0o711)
             os.chown(hashdir, 0, 0)
             dir_created = True
         if not dir_created and not lisdir(hashdir):
-            raise VMMError(_(u"'%s' is not a directory.") % hashdir,
+            raise VMMError(_("'%s' is not a directory.") % hashdir,
                            NO_SUCH_DIRECTORY)
         if os.path.exists(domain.directory):
-            raise VMMError(_(u"The file/directory '%s' already exists.") %
+            raise VMMError(_("The file/directory '%s' already exists.") %
                            domain.directory, VMM_ERROR)
         os.mkdir(os.path.join(hashdir, domdir),
                  self._cfg.dget('domain.directory_mode'))
@@ -315,7 +316,7 @@
         domdir = account.domain.directory
         if not lisdir(domdir):
             self._make_domain_dir(account.domain)
-        os.umask(0007)
+        os.umask(0o007)
         uid = account.uid
         os.chdir(domdir)
         os.mkdir('%s' % uid, self._cfg.dget('account.directory_mode'))
@@ -332,7 +333,7 @@
             bad = mailbox.add_boxes(folders,
                                     self._cfg.dget('mailbox.subscribe'))
             if bad:
-                self._warnings.append(_(u"Skipped mailbox folders:") +
+                self._warnings.append(_("Skipped mailbox folders:") +
                                       '\n\t- ' + '\n\t- '.join(bad))
         os.chdir(oldpwd)
 
@@ -349,29 +350,29 @@
         `gid` : int/long
           The user's GID (commonly AccountObj.gid)
         """
-        assert all(isinstance(xid, (long, int)) for xid in (uid, gid)) and \
-                isinstance(domdir, basestring)
+        assert all(isinstance(xid, int) for xid in (uid, gid)) and \
+                isinstance(domdir, str)
         if uid < MIN_UID or gid < MIN_GID:
-            raise VMMError(_(u"UID '%(uid)u' and/or GID '%(gid)u' are less "
-                             u"than %(min_uid)u/%(min_gid)u.") % {'uid': uid,
+            raise VMMError(_("UID '%(uid)u' and/or GID '%(gid)u' are less "
+                             "than %(min_uid)u/%(min_gid)u.") % {'uid': uid,
                            'gid': gid, 'min_gid': MIN_GID, 'min_uid': MIN_UID},
                            MAILDIR_PERM_MISMATCH)
         if domdir.count('..'):
-            raise VMMError(_(u'Found ".." in domain directory path: %s') %
+            raise VMMError(_('Found ".." in domain directory path: %s') %
                            domdir, FOUND_DOTS_IN_PATH)
         if not lisdir(domdir):
-            raise VMMError(_(u"No such directory: %s") % domdir,
+            raise VMMError(_("No such directory: %s") % domdir,
                            NO_SUCH_DIRECTORY)
         os.chdir(domdir)
         userdir = '%s' % uid
         if not lisdir(userdir):
-            self._warnings.append(_(u"No such directory: %s") %
+            self._warnings.append(_("No such directory: %s") %
                                   os.path.join(domdir, userdir))
             return
         mdstat = os.lstat(userdir)
         if (mdstat.st_uid, mdstat.st_gid) != (uid, gid):
-            raise VMMError(_(u'Detected owner/group mismatch in home '
-                             u'directory.'), MAILDIR_PERM_MISMATCH)
+            raise VMMError(_('Detected owner/group mismatch in home '
+                             'directory.'), MAILDIR_PERM_MISMATCH)
         rmtree(userdir, ignore_errors=True)
 
     def _delete_domain_dir(self, domdir, gid):
@@ -384,21 +385,21 @@
         `gid` : int/long
           The domain's GID (commonly DomainObj.gid)
         """
-        assert isinstance(domdir, basestring) and isinstance(gid, (long, int))
+        assert isinstance(domdir, str) and isinstance(gid, int)
         if gid < MIN_GID:
-            raise VMMError(_(u"GID '%(gid)u' is less than '%(min_gid)u'.") %
+            raise VMMError(_("GID '%(gid)u' is less than '%(min_gid)u'.") %
                            {'gid': gid, 'min_gid': MIN_GID},
                            DOMAINDIR_GROUP_MISMATCH)
         if domdir.count('..'):
-            raise VMMError(_(u'Found ".." in domain directory path: %s') %
+            raise VMMError(_('Found ".." in domain directory path: %s') %
                            domdir, FOUND_DOTS_IN_PATH)
         if not lisdir(domdir):
             self._warnings.append(_('No such directory: %s') % domdir)
             return
         dirst = os.lstat(domdir)
         if dirst.st_gid != gid:
-            raise VMMError(_(u'Detected group mismatch in domain directory: '
-                             u'%s') % domdir, DOMAINDIR_GROUP_MISMATCH)
+            raise VMMError(_('Detected group mismatch in domain directory: '
+                             '%s') % domdir, DOMAINDIR_GROUP_MISMATCH)
         rmtree(domdir, ignore_errors=True)
 
     def has_warnings(self):
@@ -426,9 +427,9 @@
     def cfg_install(self):
         """Installs the cfg_dget method as ``cfg_dget`` into the built-in
         namespace."""
-        import __builtin__
-        assert 'cfg_dget' not in __builtin__.__dict__
-        __builtin__.__dict__['cfg_dget'] = self._cfg.dget
+        import builtins
+        assert 'cfg_dget' not in builtins.__dict__
+        builtins.__dict__['cfg_dget'] = self._cfg.dget
 
     def domain_add(self, domainname, transport=None):
         """Wrapper around Domain's set_quotalimit, set_transport and save."""
@@ -439,7 +440,7 @@
         else:
             dom.set_transport(Transport(self._dbh, transport=transport))
         dom.set_quotalimit(QuotaLimit(self._dbh,
-                           bytes=long(self._cfg.dget('domain.quota_bytes')),
+                           bytes=int(self._cfg.dget('domain.quota_bytes')),
                            messages=self._cfg.dget('domain.quota_messages')))
         dom.set_serviceset(ServiceSet(self._dbh,
                                       imap=self._cfg.dget('domain.imap'),
@@ -452,11 +453,11 @@
 
     def domain_quotalimit(self, domainname, bytes_, messages=0, force=None):
         """Wrapper around Domain.update_quotalimit()."""
-        if not all(isinstance(i, (int, long)) for i in (bytes_, messages)):
+        if not all(isinstance(i, int) for i in (bytes_, messages)):
             raise TypeError("'bytes_' and 'messages' have to be "
                             "integers or longs.")
         if force is not None and force != 'force':
-            raise DomainError(_(u"Invalid argument: '%s'") % force,
+            raise DomainError(_("Invalid argument: '%s'") % force,
                               INVALID_ARGUMENT)
         dom = self._get_domain(domainname)
         quotalimit = QuotaLimit(self._dbh, bytes=bytes_, messages=messages)
@@ -469,11 +470,11 @@
         """Wrapper around Domain.update_serviceset()."""
         kwargs = dict.fromkeys(SERVICES, False)
         if force is not None and force != 'force':
-            raise DomainError(_(u"Invalid argument: '%s'") % force,
+            raise DomainError(_("Invalid argument: '%s'") % force,
                               INVALID_ARGUMENT)
         for service in set(services):
             if service not in SERVICES:
-                raise DomainError(_(u"Unknown service: '%s'") % service,
+                raise DomainError(_("Unknown service: '%s'") % service,
                                   UNKNOWN_SERVICE)
             kwargs[service] = True
 
@@ -484,7 +485,7 @@
     def domain_transport(self, domainname, transport, force=None):
         """Wrapper around Domain.update_transport()"""
         if force is not None and force != 'force':
-            raise DomainError(_(u"Invalid argument: '%s'") % force,
+            raise DomainError(_("Invalid argument: '%s'") % force,
                               INVALID_ARGUMENT)
         dom = self._get_domain(domainname)
         trsp = Transport(self._dbh, transport=transport)
@@ -518,13 +519,13 @@
         Domain.get_relocated."""
         if details not in [None, 'accounts', 'aliasdomains', 'aliases', 'full',
                            'relocated', 'catchall']:
-            raise VMMError(_(u"Invalid argument: '%s'") % details,
+            raise VMMError(_("Invalid argument: '%s'") % details,
                            INVALID_ARGUMENT)
         dom = self._get_domain(domainname)
         dominfo = dom.get_info()
         if dominfo['domain name'].startswith('xn--'):
             dominfo['domain name'] += ' (%s)' % \
-                                      dominfo['domain name'].decode('idna')
+                         dominfo['domain name'].encode('utf-8').decode('idna')
         if details is None:
             return dominfo
         elif details == 'accounts':
@@ -597,8 +598,8 @@
         if pattern and (pattern.startswith('%') or pattern.endswith('%')):
             like = True
             if not re.match(RE_DOMAIN_SEARCH, pattern.strip('%')):
-                raise VMMError(_(u"The pattern '%s' contains invalid "
-                                 u"characters.") % pattern, DOMAIN_INVALID)
+                raise VMMError(_("The pattern '%s' contains invalid "
+                                 "characters.") % pattern, DOMAIN_INVALID)
         self._db_connect()
         return search(self._dbh, pattern=pattern, like=like)
 
@@ -616,13 +617,10 @@
                 dpattern = parts[1]
                 dlike = dpattern.startswith('%') or dpattern.endswith('%')
 
-                if llike:
-                    checkp = lpattern.strip('%')
-                else:
-                    checkp = lpattern
+                checkp = lpattern.strip('%') if llike else lpattern
                 if len(checkp) > 0 and re.search(RE_LOCALPART, checkp):
-                    raise VMMError(_(u"The pattern '%s' contains invalid "
-                                     u"characters.") % pattern,
+                    raise VMMError(_("The pattern '%s' contains invalid "
+                                     "characters.") % pattern,
                                    LOCALPART_INVALID)
             else:
                 # else just match on domains
@@ -630,13 +628,10 @@
                 dpattern = parts[0]
                 dlike = dpattern.startswith('%') or dpattern.endswith('%')
 
-            if dlike:
-                checkp = dpattern.strip('%')
-            else:
-                checkp = dpattern
+            checkp = dpattern.strip('%') if dlike else dpattern
             if len(checkp) > 0 and not re.match(RE_DOMAIN_SEARCH, checkp):
-                raise VMMError(_(u"The pattern '%s' contains invalid "
-                                 u"characters.") % pattern, DOMAIN_INVALID)
+                raise VMMError(_("The pattern '%s' contains invalid "
+                                 "characters.") % pattern, DOMAIN_INVALID)
         self._db_connect()
         from VirtualMailManager.common import search_addresses
         return search_addresses(self._dbh, typelimit=typelimit,
@@ -647,7 +642,7 @@
         """Wrapper around Account.set_password() and Account.save()."""
         acc = self._get_account(emailaddress)
         if acc:
-            raise VMMError(_(u"The account '%s' already exists.") %
+            raise VMMError(_("The account '%s' already exists.") %
                            acc.address, ACCOUNT_EXISTS)
         self._is_other_address(acc.address, TYPE_ACCOUNT)
         acc.set_password(password)
@@ -670,8 +665,8 @@
         for destination in destinations:
             if destination.gid and \
                not self._chk_other_address_types(destination, TYPE_RELOCATED):
-                self._warnings.append(_(u"The destination account/alias '%s' "
-                                        u"does not exist.") % destination)
+                self._warnings.append(_("The destination account/alias '%s' "
+                                        "does not exist.") % destination)
 
     def user_delete(self, emailaddress, force=False):
         """Wrapper around Account.delete(...)"""
@@ -679,7 +674,7 @@
             raise TypeError('force must be a bool')
         acc = self._get_account(emailaddress)
         if not acc:
-            raise VMMError(_(u"The account '%s' does not exist.") %
+            raise VMMError(_("The account '%s' does not exist.") %
                            acc.address, NO_SUCH_ACCOUNT)
         uid = acc.uid
         gid = acc.gid
@@ -689,10 +684,10 @@
         if self._cfg.dget('account.delete_directory'):
             try:
                 self._delete_home(dom_dir, uid, gid)
-            except VMMError, err:
+            except VMMError as err:
                 if err.code in (FOUND_DOTS_IN_PATH, MAILDIR_PERM_MISMATCH,
                                 NO_SUCH_DIRECTORY):
-                    warning = _(u"""\
+                    warning = _("""\
 The account has been successfully deleted from the database.
     But an error occurred while deleting the following directory:
     '%(directory)s'
@@ -708,7 +703,7 @@
         if alias:
             return alias.get_destinations()
         if not self._is_other_address(alias.address, TYPE_ALIAS):
-            raise VMMError(_(u"The alias '%s' does not exist.") %
+            raise VMMError(_("The alias '%s' does not exist.") %
                            alias.address, NO_SUCH_ALIAS)
 
     def alias_delete(self, aliasaddress, targetaddresses=None):
@@ -725,7 +720,7 @@
             warnings = []
             try:
                 alias.del_destinations(destinations, warnings)
-            except VMMError, err:
+            except VMMError as err:
                 error = err
             if warnings:
                 self._warnings.append(_('Ignored destination addresses:'))
@@ -747,8 +742,8 @@
         for destination in destinations:
             if destination.gid and \
                not self._chk_other_address_types(destination, TYPE_RELOCATED):
-                self._warnings.append(_(u"The destination account/alias '%s' "
-                                        u"does not exist.") % destination)
+                self._warnings.append(_("The destination account/alias '%s' "
+                                        "does not exist.") % destination)
 
     def catchall_info(self, domain):
         """Returns an iterator object for all destinations (`EmailAddress`
@@ -769,7 +764,7 @@
             warnings = []
             try:
                 catchall.del_destinations(destinations, warnings)
-            except VMMError, err:
+            except VMMError as err:
                 error = err
             if warnings:
                 self._warnings.append(_('Ignored destination addresses:'))
@@ -780,12 +775,12 @@
     def user_info(self, emailaddress, details=None):
         """Wrapper around Account.get_info(...)"""
         if details not in (None, 'du', 'aliases', 'full'):
-            raise VMMError(_(u"Invalid argument: '%s'") % details,
+            raise VMMError(_("Invalid argument: '%s'") % details,
                            INVALID_ARGUMENT)
         acc = self._get_account(emailaddress)
         if not acc:
             if not self._is_other_address(acc.address, TYPE_ACCOUNT):
-                raise VMMError(_(u"The account '%s' does not exist.") %
+                raise VMMError(_("The account '%s' does not exist.") %
                                acc.address, NO_SUCH_ACCOUNT)
         info = acc.get_info()
         if self._cfg.dget('account.disk_usage') or details in ('du', 'full'):
@@ -806,12 +801,12 @@
 
     def user_password(self, emailaddress, password):
         """Wrapper for Account.modify('password' ...)."""
-        if not isinstance(password, basestring) or not password:
-            raise VMMError(_(u"Could not accept password: '%s'") % password,
+        if not isinstance(password, str) or not password:
+            raise VMMError(_("Could not accept password: '%s'") % password,
                            INVALID_ARGUMENT)
         acc = self._get_account(emailaddress)
         if not acc:
-            raise VMMError(_(u"The account '%s' does not exist.") %
+            raise VMMError(_("The account '%s' does not exist.") %
                            acc.address, NO_SUCH_ACCOUNT)
         acc.modify('password', password)
 
@@ -819,7 +814,7 @@
         """Wrapper for Account.modify('name', ...)."""
         acc = self._get_account(emailaddress)
         if not acc:
-            raise VMMError(_(u"The account '%s' does not exist.") %
+            raise VMMError(_("The account '%s' does not exist.") %
                            acc.address, NO_SUCH_ACCOUNT)
         acc.modify('name', name)
 
@@ -827,7 +822,7 @@
         """Wrapper for Account.modify('note', ...)."""
         acc = self._get_account(emailaddress)
         if not acc:
-            raise VMMError(_(u"The account '%s' does not exist.") %
+            raise VMMError(_("The account '%s' does not exist.") %
                            acc.address, NO_SUCH_ACCOUNT)
         acc.modify('note', note)
 
@@ -835,12 +830,12 @@
         """Wrapper for Account.update_quotalimit(QuotaLimit)."""
         acc = self._get_account(emailaddress)
         if not acc:
-            raise VMMError(_(u"The account '%s' does not exist.") %
+            raise VMMError(_("The account '%s' does not exist.") %
                         acc.address, NO_SUCH_ACCOUNT)
         if bytes_ == 'domain':
             quotalimit = None
         else:
-            if not all(isinstance(i, (int, long)) for i in (bytes_, messages)):
+            if not all(isinstance(i, int) for i in (bytes_, messages)):
                 raise TypeError("'bytes_' and 'messages' have to be "
                                 "integers or longs.")
             quotalimit = QuotaLimit(self._dbh, bytes=bytes_,
@@ -849,24 +844,22 @@
 
     def user_transport(self, emailaddress, transport):
         """Wrapper for Account.update_transport(Transport)."""
-        if not isinstance(transport, basestring) or not transport:
-            raise VMMError(_(u"Could not accept transport: '%s'") % transport,
+        if not isinstance(transport, str) or not transport:
+            raise VMMError(_("Could not accept transport: '%s'") % transport,
                            INVALID_ARGUMENT)
         acc = self._get_account(emailaddress)
         if not acc:
-            raise VMMError(_(u"The account '%s' does not exist.") %
+            raise VMMError(_("The account '%s' does not exist.") %
                            acc.address, NO_SUCH_ACCOUNT)
-        if transport == 'domain':
-            transport = None
-        else:
-            transport = Transport(self._dbh, transport=transport)
+        transport = None if transport == 'domain' \
+                         else Transport(self._dbh, transport=transport)
         acc.update_transport(transport)
 
     def user_services(self, emailaddress, *services):
         """Wrapper around Account.update_serviceset()."""
         acc = self._get_account(emailaddress)
         if not acc:
-            raise VMMError(_(u"The account '%s' does not exist.") %
+            raise VMMError(_("The account '%s' does not exist.") %
                         acc.address, NO_SUCH_ACCOUNT)
         if len(services) == 1 and services[0] == 'domain':
             serviceset = None
@@ -874,7 +867,7 @@
             kwargs = dict.fromkeys(SERVICES, False)
             for service in set(services):
                 if service not in SERVICES:
-                    raise VMMError(_(u"Unknown service: '%s'") % service,
+                    raise VMMError(_("Unknown service: '%s'") % service,
                                 UNKNOWN_SERVICE)
                 kwargs[service] = True
             serviceset = ServiceSet(self._dbh, **kwargs)
@@ -891,8 +884,8 @@
         relocated.set_destination(destination)
         if destination.gid and \
            not self._chk_other_address_types(destination, TYPE_RELOCATED):
-            self._warnings.append(_(u"The destination account/alias '%s' "
-                                    u"does not exist.") % destination)
+            self._warnings.append(_("The destination account/alias '%s' "
+                                    "does not exist.") % destination)
 
     def relocated_info(self, emailaddress):
         """Returns the target address of the relocated user with the given
@@ -901,7 +894,7 @@
         if relocated:
             return relocated.get_info()
         if not self._is_other_address(relocated.address, TYPE_RELOCATED):
-            raise VMMError(_(u"The relocated user '%s' does not exist.") %
+            raise VMMError(_("The relocated user '%s' does not exist.") %
                            relocated.address, NO_SUCH_RELOCATED)
 
     def relocated_delete(self, emailaddress):
--- a/VirtualMailManager/mailbox.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/mailbox.py	Sun Dec 09 15:03:33 2012 +0000
@@ -14,6 +14,7 @@
 from binascii import a2b_base64, b2a_base64
 from subprocess import Popen, PIPE
 
+from VirtualMailManager import ENCODING
 from VirtualMailManager.account import Account
 from VirtualMailManager.common import lisdir
 from VirtualMailManager.errors import VMMError
@@ -29,13 +30,14 @@
 
 def _mbase64_encode(inp, dest):
     if inp:
-        mb64 = b2a_base64(''.join(inp).encode('utf-16be'))
+        mb64 = b2a_base64(''.join(inp).encode('utf-16be')).decode()
         dest.append('&%s-' % mb64.rstrip('\n=').replace('/', ','))
         del inp[:]
 
 
 def _mbase64_to_unicode(mb64):
-    return unicode(a2b_base64(mb64.replace(',', '/') + '==='), 'utf-16be')
+    return str(a2b_base64(mb64.replace(',', '/').encode() + b'==='),
+               'utf-16be')
 
 
 def utf8_to_mutf7(src):
@@ -86,7 +88,7 @@
 class Mailbox(object):
     """Base class of all mailbox classes."""
     __slots__ = ('_boxes', '_root', '_sep', '_user')
-    FILE_MODE = 0600
+    FILE_MODE = 0o600
     _ctrl_chr_re = re.compile('[\x00-\x1F\x7F-\x9F]')
     _box_name_re = re.compile('^[\x20-\x25\x27-\x7E]+$')
 
@@ -206,11 +208,9 @@
         """Writes all created mailboxes to the subscriptions file."""
         if not self._boxes:
             return
-        subscriptions = open('subscriptions', 'w')
-        subscriptions.write('\n'.join(self._boxes))
-        subscriptions.write('\n')
-        subscriptions.flush()
-        subscriptions.close()
+        with open('subscriptions', 'w') as subscriptions:
+            subscriptions.write('\n'.join(self._boxes))
+            subscriptions.write('\n')
         os.chown('subscriptions', self._user.uid, self._user.gid)
         os.chmod('subscriptions', self.__class__.FILE_MODE)
         del self._boxes[:]
@@ -257,8 +257,8 @@
         process = Popen(cmd_args, stderr=PIPE)
         stderr = process.communicate()[1]
         if process.returncode:
-            e_msg = _(u'Failed to create mailboxes: %r\n') % mailboxes
-            raise VMMError(e_msg + stderr.strip(), VMM_ERROR)
+            e_msg = _('Failed to create mailboxes: %r\n') % mailboxes
+            raise VMMError(e_msg + stderr.strip().decode(ENCODING), VMM_ERROR)
 
     def create(self):
         """Create a dbox INBOX"""
--- a/VirtualMailManager/maillocation.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/maillocation.py	Sun Dec 09 15:03:33 2012 +0000
@@ -12,7 +12,6 @@
 
 from VirtualMailManager.constants import MAILLOCATION_INIT
 from VirtualMailManager.errors import MailLocationError as MLErr
-from VirtualMailManager.pycompat import all
 
 
 __all__ = ('MailLocation', 'known_format')
@@ -56,29 +55,29 @@
         self._mbfmt = None
         self._mid = 0
 
-        for key in kwargs.iterkeys():
+        for key in kwargs.keys():
             if key not in self.__class__._kwargs:
                 raise ValueError('unrecognized keyword: %r' % key)
         mid = kwargs.get('mid')
         if mid:
-            assert isinstance(mid, (int, long))
+            assert isinstance(mid, int)
             self._load_by_mid(mid)
         else:
             args = kwargs.get('mbfmt'), kwargs.get('directory')
-            assert all(isinstance(arg, basestring) for arg in args)
+            assert all(isinstance(arg, str) for arg in args)
             if args[0].lower() not in _format_info:
-                raise MLErr(_(u"Unsupported mailbox format: '%s'") % args[0],
+                raise MLErr(_("Unsupported mailbox format: '%s'") % args[0],
                             MAILLOCATION_INIT)
             directory = args[1].strip()
             if not directory:
-                raise MLErr(_(u"Empty directory name"), MAILLOCATION_INIT)
+                raise MLErr(_("Empty directory name"), MAILLOCATION_INIT)
             if len(directory) > 20:
-                raise MLErr(_(u"Directory name is too long: '%s'") % directory,
+                raise MLErr(_("Directory name is too long: '%s'") % directory,
                             MAILLOCATION_INIT)
             self._load_by_names(args[0].lower(), directory)
 
     def __str__(self):
-        return u'%s:~/%s' % (self._mbfmt, self._directory)
+        return '%s:~/%s' % (self._mbfmt, self._directory)
 
     @property
     def directory(self):
--- a/VirtualMailManager/network.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/network.py	Sun Dec 09 15:03:33 2012 +0000
@@ -81,7 +81,7 @@
     `(address_family, address_as_long)` will be returned. The
     `address_family`will be either `socket.AF_INET` or `socket.AF_INET6`.
     """
-    if not isinstance(ip_address, basestring) or not ip_address:
+    if not isinstance(ip_address, str) or not ip_address:
         raise TypeError('ip_address must be a non empty string.')
     if not ip_address.count(':'):
         family = socket.AF_INET
@@ -97,4 +97,4 @@
             address = socket.inet_pton(family, ip_address)
         except socket.error:
             raise ValueError('Not a valid IPv6 address: %r' % ip_address)
-    return (family, long(address.encode('hex'), 16))
+    return (family, int(address.encode('hex'), 16))
--- a/VirtualMailManager/password.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/password.py	Sun Dec 09 15:03:33 2012 +0000
@@ -15,22 +15,20 @@
         schemes, encodings = list_schemes()
 """
 
+import hashlib
+
+from base64 import b64encode
+from binascii import b2a_hex
 from crypt import crypt
 from random import SystemRandom
 from subprocess import Popen, PIPE
 
-try:
-    import hashlib
-except ImportError:
-    from VirtualMailManager.pycompat import hashlib
-
 from VirtualMailManager import ENCODING
 from VirtualMailManager.emailaddress import EmailAddress
 from VirtualMailManager.common import get_unicode, version_str
 from VirtualMailManager.constants import VMM_ERROR
 from VirtualMailManager.errors import VMMError
 
-COMPAT = hasattr(hashlib, 'compat')
 SALTCHARS = './0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
 PASSWDCHARS = '._-+#*23456789abcdefghikmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
 DEFAULT_B64 = (None, 'B64', 'BASE64')
@@ -55,7 +53,7 @@
 cfg_dget = lambda option: None
 _sys_rand = SystemRandom()
 _choice = _sys_rand.choice
-_get_salt = lambda s_len: ''.join(_choice(SALTCHARS) for x in xrange(s_len))
+_get_salt = lambda s_len: ''.join(_choice(SALTCHARS) for x in range(s_len))
 
 
 def _dovecotpw(password, scheme, encoding):
@@ -71,8 +69,8 @@
     process = Popen(cmd_args, stdout=PIPE, stderr=PIPE)
     stdout, stderr = process.communicate()
     if process.returncode:
-        raise VMMError(stderr.strip(), VMM_ERROR)
-    hashed = stdout.strip()
+        raise VMMError(stderr.strip().decode(ENCODING), VMM_ERROR)
+    hashed = stdout.strip().decode(ENCODING)
     if not hashed.startswith('{%s}' % scheme):
         raise VMMError('Unexpected result from %s: %s' %
                        (cfg_dget('bin.dovecotpw'), hashed), VMM_ERROR)
@@ -80,33 +78,13 @@
 
 
 def _md4_new():
-    """Returns an new MD4-hash object if supported by the hashlib or
-    provided by PyCrypto - other `None`.
+    """Returns an new MD4-hash object if supported by the hashlib -
+    otherwise `None`.
     """
     try:
         return hashlib.new('md4')
-    except ValueError, err:
-        if str(err) == 'unsupported hash type':
-            if not COMPAT:
-                try:
-                    from Crypto.Hash import MD4
-                    return MD4.new()
-                except ImportError:
-                    return None
-        else:
-            raise
-
-
-def _sha256_new(data=''):
-    """Returns a new sha256 object from the hashlib.
-
-    Returns `None` if the PyCrypto in pycompat.hashlib is too old."""
-    if not COMPAT:
-        return hashlib.sha256(data)
-    try:
-        return hashlib.new('sha256', data)
-    except ValueError, err:
-        if str(err) == 'unsupported hash type':
+    except ValueError as err:
+        if err.args[0].startswith('unsupported hash type'):
             return None
         else:
             raise
@@ -121,13 +99,14 @@
 
 def _clear_hash(password, scheme, encoding):
     """Generates a (encoded) CLEARTEXT/PLAIN 'hash'."""
+    password = password.decode(ENCODING)
     if encoding:
         if encoding == 'HEX':
-            password = password.encode('hex')
+            password = b2a_hex(password.encode()).decode()
         else:
-            password = password.encode('base64').replace('\n', '')
+            password = b64encode(password.encode()).decode()
         return _format_digest(password, scheme, encoding)
-    return get_unicode('{%s}%s' % (scheme, password))
+    return '{%s}%s' % (scheme, password)
 
 
 def _get_crypt_blowfish_salt():
@@ -174,12 +153,12 @@
         salt = _get_crypt_sha2_salt(CRYPT_ID_SHA256)
     else:
         salt = _get_crypt_sha2_salt(CRYPT_ID_SHA512)
-    encrypted = crypt(password, salt)
+    encrypted = crypt(password.decode(ENCODING), salt)
     if encoding:
         if encoding == 'HEX':
-            encrypted = encrypted.encode('hex')
+            encrypted = b2a_hex(encrypted.encode()).decode()
         else:
-            encrypted = encrypted.encode('base64').replace('\n', '')
+            encrypted = b64encode(encrypted.encode()).decode()
     if scheme in ('BLF-CRYPT', 'SHA256-CRYPT', 'SHA512-CRYPT') and \
        cfg_dget('misc.dovecot_version') < 0x20000b06:
         scheme = 'CRYPT'
@@ -194,7 +173,7 @@
         if encoding in DEFAULT_HEX:
             digest = md4.hexdigest()
         else:
-            digest = md4.digest().encode('base64').rstrip()
+            digest = b64encode(md4.digest()).decode()
         return _format_digest(digest, scheme, encoding)
     return _dovecotpw(password, scheme, encoding)
 
@@ -210,15 +189,17 @@
         #       http://dovecot.org/list/dovecot-news/2009-March/000103.html
         #       http://hg.dovecot.org/dovecot-1.1/rev/2b0043ba89ae
         if cfg_dget('misc.dovecot_version') >= 0x1010cf00:
-            md5.update('%s:%s:' % (user.localpart, user.domainname))
+            md5.update(user.localpart.encode() + b':' +
+                       user.domainname.encode() + b':')
         else:
-            md5.update('%s::' % user)
+            raise VMMError('You will need Dovecot >= v1.2.0 for proper '
+                           'functioning digest-md5 authentication.', VMM_ERROR)
     md5.update(password)
     if (scheme in ('PLAIN-MD5', 'DIGEST-MD5') and encoding in DEFAULT_HEX) or \
        (scheme == 'LDAP-MD5' and encoding == 'HEX'):
         digest = md5.hexdigest()
     else:
-        digest = md5.digest().encode('base64').rstrip()
+        digest = b64encode(md5.digest()).decode()
     return _format_digest(digest, scheme, encoding)
 
 
@@ -226,12 +207,13 @@
     """Generates NTLM hashes."""
     md4 = _md4_new()
     if md4:
-        password = ''.join('%s\x00' % c for c in password)
+        password = b''.join(bytes(x)
+                            for x in zip(password, bytes(len(password))))
         md4.update(password)
         if encoding in DEFAULT_HEX:
             digest = md4.hexdigest()
         else:
-            digest = md4.digest().encode('base64').rstrip()
+            digest = b64encode(md4.digest()).decode()
         return _format_digest(digest, scheme, encoding)
     return _dovecotpw(password, scheme, encoding)
 
@@ -240,7 +222,7 @@
     """Generates SHA1 aka SHA hashes."""
     sha1 = hashlib.sha1(password)
     if encoding in DEFAULT_B64:
-        digest = sha1.digest().encode('base64').rstrip()
+        digest = b64encode(sha1.digest()).decode()
     else:
         digest = sha1.hexdigest()
     return _format_digest(digest, scheme, encoding)
@@ -248,78 +230,69 @@
 
 def _sha256_hash(password, scheme, encoding):
     """Generates SHA256 hashes."""
-    sha256 = _sha256_new(password)
-    if sha256:
-        if encoding in DEFAULT_B64:
-            digest = sha256.digest().encode('base64').rstrip()
-        else:
-            digest = sha256.hexdigest()
-        return _format_digest(digest, scheme, encoding)
-    return _dovecotpw(password, scheme, encoding)
+    sha256 = hashlib.sha256(password)
+    if encoding in DEFAULT_B64:
+        digest = b64encode(sha256.digest()).decode()
+    else:
+        digest = sha256.hexdigest()
+    return _format_digest(digest, scheme, encoding)
 
 
 def _sha512_hash(password, scheme, encoding):
     """Generates SHA512 hashes."""
-    if not COMPAT:
-        sha512 = hashlib.sha512(password)
-        if encoding in DEFAULT_B64:
-            digest = sha512.digest().encode('base64').replace('\n', '')
-        else:
-            digest = sha512.hexdigest()
-        return _format_digest(digest, scheme, encoding)
-    return _dovecotpw(password, scheme, encoding)
+    sha512 = hashlib.sha512(password)
+    if encoding in DEFAULT_B64:
+        digest = b64encode(sha512.digest()).decode()
+    else:
+        digest = sha512.hexdigest()
+    return _format_digest(digest, scheme, encoding)
 
 
 def _smd5_hash(password, scheme, encoding):
     """Generates SMD5 (salted PLAIN-MD5) hashes."""
     md5 = hashlib.md5(password)
-    salt = _get_salt(SALTED_ALGO_SALT_LEN)
+    salt = _get_salt(SALTED_ALGO_SALT_LEN).encode()
     md5.update(salt)
     if encoding in DEFAULT_B64:
-        digest = (md5.digest() + salt).encode('base64').rstrip()
+        digest = b64encode(md5.digest() + salt).decode()
     else:
-        digest = md5.hexdigest() + salt.encode('hex')
+        digest = md5.hexdigest() + b2a_hex(salt).decode()
     return _format_digest(digest, scheme, encoding)
 
 
 def _ssha1_hash(password, scheme, encoding):
     """Generates SSHA (salted SHA/SHA1) hashes."""
     sha1 = hashlib.sha1(password)
-    salt = _get_salt(SALTED_ALGO_SALT_LEN)
+    salt = _get_salt(SALTED_ALGO_SALT_LEN).encode()
     sha1.update(salt)
     if encoding in DEFAULT_B64:
-        digest = (sha1.digest() + salt).encode('base64').rstrip()
+        digest = b64encode(sha1.digest() + salt).decode()
     else:
-        digest = sha1.hexdigest() + salt.encode('hex')
+        digest = sha1.hexdigest() + b2a_hex(salt).decode()
     return _format_digest(digest, scheme, encoding)
 
 
 def _ssha256_hash(password, scheme, encoding):
     """Generates SSHA256 (salted SHA256) hashes."""
-    sha256 = _sha256_new(password)
-    if sha256:
-        salt = _get_salt(SALTED_ALGO_SALT_LEN)
-        sha256.update(salt)
-        if encoding in DEFAULT_B64:
-            digest = (sha256.digest() + salt).encode('base64').rstrip()
-        else:
-            digest = sha256.hexdigest() + salt.encode('hex')
-        return _format_digest(digest, scheme, encoding)
-    return _dovecotpw(password, scheme, encoding)
+    sha256 = hashlib.sha256(password)
+    salt = _get_salt(SALTED_ALGO_SALT_LEN).encode()
+    sha256.update(salt)
+    if encoding in DEFAULT_B64:
+        digest = b64encode(sha256.digest() + salt).decode()
+    else:
+        digest = sha256.hexdigest() + b2a_hex(salt).decode()
+    return _format_digest(digest, scheme, encoding)
 
 
 def _ssha512_hash(password, scheme, encoding):
     """Generates SSHA512 (salted SHA512) hashes."""
-    if not COMPAT:
-        salt = _get_salt(SALTED_ALGO_SALT_LEN)
-        sha512 = hashlib.sha512(password + salt)
-        if encoding in DEFAULT_B64:
-            digest = (sha512.digest() + salt).encode('base64').replace('\n',
-                                                                       '')
-        else:
-            digest = sha512.hexdigest() + salt.encode('hex')
-        return _format_digest(digest, scheme, encoding)
-    return _dovecotpw(password, scheme, encoding)
+    salt = _get_salt(SALTED_ALGO_SALT_LEN).encode()
+    sha512 = hashlib.sha512(password + salt)
+    if encoding in DEFAULT_B64:
+        digest = b64encode(sha512.digest() + salt).decode()
+    else:
+        digest = sha512.hexdigest() + b2a_hex(salt).decode()
+    return _format_digest(digest, scheme, encoding)
 
 _scheme_info = {
     'CLEARTEXT': (_clear_hash, 0x10000f00),
@@ -359,11 +332,8 @@
     be empty.
     """
     dcv = cfg_dget('misc.dovecot_version')
-    schemes = (k for (k, v) in _scheme_info.iteritems() if v[1] <= dcv)
-    if dcv >= 0x10100a01:
-        encodings = ('.B64', '.BASE64', '.HEX')
-    else:
-        encodings = ()
+    schemes = (k for (k, v) in _scheme_info.items() if v[1] <= dcv)
+    encodings = ('.B64', '.BASE64', '.HEX') if dcv >= 0x10100a01 else ()
     return schemes, encodings
 
 
@@ -382,23 +352,23 @@
       * depends on a newer Dovecot version
       * has a unknown encoding suffix
     """
-    assert isinstance(scheme, basestring), 'Not a str/unicode: %r' % scheme
+    assert isinstance(scheme, str), 'Not a str: {!r}'.format(scheme)
     scheme_encoding = scheme.upper().split('.')
     scheme = scheme_encoding[0]
     if scheme not in _scheme_info:
-        raise VMMError(_(u"Unsupported password scheme: '%s'") % scheme,
+        raise VMMError(_("Unsupported password scheme: '%s'") % scheme,
                        VMM_ERROR)
     if cfg_dget('misc.dovecot_version') < _scheme_info[scheme][1]:
-        raise VMMError(_(u"The password scheme '%(scheme)s' requires Dovecot "
-                         u">= v%(version)s.") % {'scheme': scheme,
+        raise VMMError(_("The password scheme '%(scheme)s' requires Dovecot "
+                         ">= v%(version)s.") % {'scheme': scheme,
                        'version': version_str(_scheme_info[scheme][1])},
                        VMM_ERROR)
     if len(scheme_encoding) > 1:
         if cfg_dget('misc.dovecot_version') < 0x10100a01:
-            raise VMMError(_(u'Encoding suffixes for password schemes require '
-                             u'Dovecot >= v1.1.alpha1.'), VMM_ERROR)
+            raise VMMError(_('Encoding suffixes for password schemes require '
+                             'Dovecot >= v1.1.alpha1.'), VMM_ERROR)
         if scheme_encoding[1] not in ('B64', 'BASE64', 'HEX'):
-            raise VMMError(_(u"Unsupported password encoding: '%s'") %
+            raise VMMError(_("Unsupported password encoding: '%s'") %
                            scheme_encoding[1], VMM_ERROR)
         encoding = scheme_encoding[1]
     else:
@@ -413,11 +383,9 @@
     be used for the hash generation.  When 'DIGEST-MD5' is used as scheme,
     also an EmailAddress instance must be given as *user* argument.
     """
-    if not isinstance(password, basestring):
+    if not isinstance(password, str):
         raise TypeError('Password is not a string: %r' % password)
-    if isinstance(password, unicode):
-        password = password.encode(ENCODING)
-    password = password.strip()
+    password = password.encode(ENCODING).strip()
     if not password:
         raise ValueError("Could not accept empty password.")
     if scheme is None:
--- a/VirtualMailManager/pycompat/__init__.py	Sun Dec 09 14:59:46 2012 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-# -*- coding: UTF-8 -*-
-# Copyright (c) 2010 - 2012, Pascal Volk
-# See COPYING for distribution information.
-
-"""
-    VirtualMailManager.pycompat
-
-    VirtualMailManager's compatibility stuff for Python 2.4
-"""
-
-# http://docs.python.org/library/functions.html#all
-try:
-    all = all
-except NameError:
-    def all(iterable):
-        """Return True if all elements of the *iterable* are true
-        (or if the iterable is empty).
-
-        """
-        for element in iterable:
-            if not element:
-                return False
-        return True
-
-
-# http://docs.python.org/library/functions.html#any
-try:
-    any = any
-except NameError:
-    def any(iterable):
-        """Return True if any element of the *iterable* is true.  If the
-        iterable is empty, return False.
-
-        """
-        for element in iterable:
-            if element:
-                return True
-        return False
--- a/VirtualMailManager/pycompat/hashlib.py	Sun Dec 09 14:59:46 2012 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-# -*- coding: UTF-8 -*-
-# Copyright (c) 2010 - 2012, Pascal Volk
-# See COPYING for distribution information.
-
-"""
-    VirtualMailManager.pycompat.hashlib
-
-    VirtualMailManager's minimal hashlib emulation for Python 2.4
-
-    hashlib.md5(...), hashlib.sha1(...), hashlib.new('md5', ...) and
-    hashlib.new('sha1', ...) will work always.
-
-    When the PyCrypto module <http://www.pycrypto.org/> could be found in
-    sys.path hashlib.new('md4', ...) will also work.
-
-    With PyCrypto >= 2.1.0alpha1 hashlib.new('sha256', ...) and
-    hashlib.sha256(...) becomes functional.
-"""
-
-
-import md5 as _md5
-import sha as _sha1
-
-try:
-    import Crypto
-except ImportError:
-    _md4 = None
-    SHA256 = None
-else:
-    from Crypto.Hash import MD4 as _md4
-    if hasattr(Crypto, 'version_info'):  # <- Available since v2.1.0alpha1
-        from Crypto.Hash import SHA256   # SHA256 works since v2.1.0alpha1
-        sha256 = SHA256.new
-    else:
-        SHA256 = None
-    del Crypto
-
-
-compat = 0x01
-md5 = _md5.new
-sha1 = _sha1.new
-
-
-def new(name, string=''):
-    """Return a new hashing object using the named algorithm, optionally
-    initialized with the provided string.
-    """
-    if name in ('md5', 'MD5'):
-        return _md5.new(string)
-    if name in ('sha1', 'SHA1'):
-        return _sha1.new(string)
-    if not _md4:
-        raise ValueError('unsupported hash type')
-    if name in ('md4', 'MD4'):
-        return _md4.new(string)
-    if name in ('sha256', 'SHA256') and SHA256:
-        return SHA256.new(string)
-    raise ValueError('unsupported hash type')
--- a/VirtualMailManager/quotalimit.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/quotalimit.py	Sun Dec 09 15:03:33 2012 +0000
@@ -9,8 +9,6 @@
     for domains and accounts.
 """
 
-from VirtualMailManager.pycompat import all
-
 _ = lambda msg: msg
 
 
@@ -44,24 +42,18 @@
         self._bytes = 0
         self._messages = 0
 
-        for key in kwargs.iterkeys():
+        for key in kwargs.keys():
             if key not in self.__class__._kwargs:
                 raise ValueError('unrecognized keyword: %r' % key)
         qid = kwargs.get('qid')
         if qid is not None:
-            assert isinstance(qid, (int, long))
+            assert isinstance(qid, int)
             self._load_by_qid(qid)
         else:
             bytes_, msgs = kwargs.get('bytes'), kwargs.get('messages')
-            assert all(isinstance(i, (int, long)) for i in (bytes_, msgs))
-            if bytes_ < 0:
-                self._bytes = -bytes_
-            else:
-                self._bytes = bytes_
-            if msgs < 0:
-                self._messages = -msgs
-            else:
-                self._messages = msgs
+            assert all(isinstance(i, int) for i in (bytes_, msgs))
+            self._bytes = -bytes_ if bytes_ < 0 else bytes_
+            self._messages = -msgs if msgs < 0 else msgs
             self._load_by_limit()
 
     @property
--- a/VirtualMailManager/relocated.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/relocated.py	Sun Dec 09 15:03:33 2012 +0000
@@ -36,13 +36,13 @@
         self._dbh = dbh
         self._gid = get_gid(self._dbh, self._addr.domainname)
         if not self._gid:
-            raise RErr(_(u"The domain '%s' does not exist.") %
+            raise RErr(_("The domain '%s' does not exist.") %
                        self._addr.domainname, NO_SUCH_DOMAIN)
         self._dest = None
 
         self._load()
 
-    def __nonzero__(self):
+    def __bool__(self):
         """Returns `True` if the Relocated is known, `False` if it's new."""
         return self._dest is not None
 
@@ -59,8 +59,8 @@
         if destination:
             destination = DestinationEmailAddress(destination[0], self._dbh)
             if destination.at_localhost:
-                raise RErr(_(u"The destination address' domain name must not "
-                             u"be localhost."), DOMAIN_INVALID)
+                raise RErr(_("The destination address' domain name must not "
+                             "be localhost."), DOMAIN_INVALID)
             self._dest = destination
 
     @property
@@ -73,14 +73,14 @@
         update = False
         assert isinstance(destination, DestinationEmailAddress)
         if destination.at_localhost:
-            raise RErr(_(u"The destination address' domain name must not be "
-                         u"localhost."), DOMAIN_INVALID)
+            raise RErr(_("The destination address' domain name must not be "
+                         "localhost."), DOMAIN_INVALID)
         if self._addr == destination:
-            raise RErr(_(u'Address and destination are identical.'),
+            raise RErr(_('Address and destination are identical.'),
                        RELOCATED_ADDR_DEST_IDENTICAL)
         if self._dest:
             if self._dest == destination:
-                raise RErr(_(u"The relocated user '%s' already exists.") %
+                raise RErr(_("The relocated user '%s' already exists.") %
                            self._addr, RELOCATED_EXISTS)
             else:
                 self._dest = destination
@@ -103,14 +103,14 @@
     def get_info(self):
         """Returns the address to which mails should be sent."""
         if not self._dest:
-            raise RErr(_(u"The relocated user '%s' does not exist.") %
+            raise RErr(_("The relocated user '%s' does not exist.") %
                        self._addr, NO_SUCH_RELOCATED)
         return self._dest
 
     def delete(self):
         """Deletes the relocated entry from the database."""
         if not self._dest:
-            raise RErr(_(u"The relocated user '%s' does not exist.") %
+            raise RErr(_("The relocated user '%s' does not exist.") %
                        self._addr, NO_SUCH_RELOCATED)
         dbc = self._dbh.cursor()
         dbc.execute('DELETE FROM relocated WHERE gid = %s AND address = %s',
--- a/VirtualMailManager/serviceset.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/serviceset.py	Sun Dec 09 15:03:33 2012 +0000
@@ -65,12 +65,12 @@
         else:
             self._sieve_col = 'sieve'
 
-        for key in kwargs.iterkeys():
+        for key in kwargs.keys():
             if key not in self.__class__._kwargs:
                 raise ValueError('unrecognized keyword: %r' % key)
             if key == 'ssid':
                 assert not isinstance(kwargs[key], bool) and \
-                       isinstance(kwargs[key], (int, long)) and kwargs[key] > 0
+                       isinstance(kwargs[key], int) and kwargs[key] > 0
                 self._load_by_ssid(kwargs[key])
                 break
             else:
@@ -101,13 +101,13 @@
 
     def __repr__(self):
         return '%s(%s, %s)' % (self.__class__.__name__, self._dbh,
-                  ', '.join('%s=%r' % s for s in self._services.iteritems()))
+                  ', '.join('%s=%r' % s for s in self._services.items()))
 
     def _load_by_services(self):
         """Try to load the service_set by it's service combination."""
         sql = ('SELECT ssid FROM service_set WHERE %s' %
                ' AND '.join('%s = %s' %
-               (k, str(v).upper()) for k, v in self._services.iteritems()))
+               (k, str(v).upper()) for k, v in self._services.items()))
         if self._sieve_col == 'managesieve':
             sql = sql.replace('sieve', self._sieve_col)
         dbc = self._dbh.cursor()
@@ -131,10 +131,7 @@
         self._ssid = result[0]
         #self._services.update(zip(SERVICES, result[1:]))
         for key, value in zip(SERVICES, result[1:]):  # pyPgSQL compatible
-            if value:
-                self._services[key] = True
-            else:
-                self._services[key] = False
+            self._services[key] = True if value else False
 
     def _save(self):
         """Store a new service_set in the database."""
--- a/VirtualMailManager/transport.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/VirtualMailManager/transport.py	Sun Dec 09 15:03:33 2012 +0000
@@ -9,8 +9,6 @@
     domains and accounts.
 """
 
-from VirtualMailManager.pycompat import any
-
 _ = lambda msg: msg
 
 
@@ -34,10 +32,10 @@
         self._tid = 0
         assert any((tid, transport))
         if tid:
-            assert not isinstance(tid, bool) and isinstance(tid, (int, long))
+            assert not isinstance(tid, bool) and isinstance(tid, int)
             self._load_by_id(tid)
         else:
-            assert isinstance(transport, basestring)
+            assert isinstance(transport, str)
             self._transport = transport
             self._load_by_name()
 
--- a/pgsql/set-permissions.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/pgsql/set-permissions.py	Sun Dec 09 15:03:33 2012 +0000
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # coding: utf-8
 # Copyright 2012, Pascal Volk
 # See COPYING for distribution information.
@@ -119,10 +119,10 @@
                 (dc_rw_tbls, db['dovecot']))
     dbc.execute('GRANT SELECT ON %s TO %s' % (dc_ro_tbls, db['dovecot']))
     dbc.execute('GRANT SELECT ON %s TO %s' % (pf_ro_tbls, db['postfix']))
-    for table, columns in db['dovecot_tbls'].iteritems():
+    for table, columns in db['dovecot_tbls'].items():
         dbc.execute('GRANT SELECT (%s) ON %s TO %s' % (columns, table,
                                                        db['dovecot']))
-    for table, columns in db['postfix_tbls'].iteritems():
+    for table, columns in db['postfix_tbls'].items():
         dbc.execute('GRANT SELECT (%s) ON %s TO %s' % (columns, table,
                                                        db['postfix']))
     dbc.close()
--- a/setup.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/setup.py	Sun Dec 09 15:03:33 2012 +0000
@@ -1,11 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 # Copyright 2007 - 2012, Pascal Volk
 # See COPYING for distribution information.
 
 import os
 from distutils.core import setup
-from distutils.dist import DistributionMetadata
 
 VERSION = '0.6.1'
 
@@ -20,7 +19,6 @@
     'VirtualMailManager',
     'VirtualMailManager.cli',
     'VirtualMailManager.ext',
-    'VirtualMailManager.pycompat',
 ]
 # http://pypi.python.org/pypi?%3Aaction=list_classifiers
 classifiers = ['Development Status :: 5 - Production/Stable',
@@ -44,7 +42,7 @@
                'Topic :: Utilities']
 
 # sucessfuly tested on:
-platforms = ['freebsd7', 'linux2', 'openbsd4']
+platforms = ['freebsd7', 'linux2', 'openbsd5']
 
 # remove existing MANIFEST
 if os.path.exists('MANIFEST'):
@@ -58,12 +56,10 @@
               'author': 'Pascal Volk',
               'author_email': 'user+vmm@localhost.localdomain.org',
               'license': 'BSD License',
+              'requires': ['psycopg2 (>=2.0)'],
               'url': 'http://vmm.localdomain.org/',
-              'download_url':'http://sf.net/projects/vmm/files/',
+              'download_url': 'http://sf.net/projects/vmm/files/',
               'platforms': platforms,
               'classifiers': classifiers}
 
-if 'requires' in DistributionMetadata._METHOD_BASENAMES:
-    setup_args['requires'] = ['psycopg2 (>=2.0)', 'pyPgSQL (>=2.5.1)']
-
 setup(**setup_args)
--- a/update_config.py	Sun Dec 09 14:59:46 2012 +0000
+++ b/update_config.py	Sun Dec 09 15:03:33 2012 +0000
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # -*- coding: UTF-8 -*-
 # Copyright (c) 2008 - 2012, Pascal Volk
 # See COPYING for distribution information.
@@ -6,7 +6,7 @@
 import os
 os.sys.path.remove(os.sys.path[0])
 from time import time
-from ConfigParser import ConfigParser
+from configparser import ConfigParser
 from shutil import copy2
 
 pre_060 = False
@@ -161,18 +161,20 @@
     if len(sect_opt):
         update_cfg_file(cp, cf)
         sect_opt.sort()
-        print 'Please have a look at your configuration: %s' % cf
-        print 'This are your Modified/Renamed/New settings:'
+        print('Please have a look at your configuration: %s' % cf)
+        print('This are your Modified/Renamed/New settings:')
         for s_o, st in sect_opt:
-            print '%s   %s = %s' % (st, s_o, get_option(cp, s_o))
+            print('%s   %s = %s' % (st, s_o, get_option(cp, s_o)))
         if had_config:
-            print '\nRemoved section "config" with option "done" (obsolte)'
+            print('\nRemoved section "config" with option "done" (obsolte)')
         if had_gid_mail:
-            print '\nRemoved option "gid_mail" from section "misc" (obsolte)\n'
+            print('\nRemoved option "gid_mail" from section "misc"',
+                  '(obsolte)\n')
         os.sys.exit(0)
     if had_config or had_gid_mail:
         update_cfg_file(cp, cf)
         if had_config:
-            print '\nRemoved section "config" with option "done" (obsolte)'
+            print('\nRemoved section "config" with option "done" (obsolte)')
         if had_gid_mail:
-            print '\nRemoved option "gid_mail" from section "misc" (obsolte)\n'
+            print('\nRemoved option "gid_mail" from section "misc"',
+                  '(obsolte)\n')
--- a/vmm	Sun Dec 09 14:59:46 2012 +0000
+++ b/vmm	Sun Dec 09 15:03:33 2012 +0000
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # -*- coding: UTF-8 -*-
 # Copyright 2007 - 2012, Pascal Volk
 # See COPYING for distribution information.