diff --git a/conf.py b/conf.py
index 9a111b6..6b5d71f 100755
--- a/conf.py
+++ b/conf.py
@@ -1,47 +1,47 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
 #
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 """
     Kolab configuration utility.
 """
 
 from __future__ import print_function
 
 import logging
 import os
 import sys
 
 sys.path.append('.')
 
 from pykolab.translate import _
 
 try:
     import pykolab.logger
-except ImportError, e:
+except ImportError as e:
     print(_("Cannot load pykolab/logger.py:"), file=sys.stderr)
     print("%s" % e, file=sys.stderr)
     sys.exit(1)
 
 import pykolab
 
 if __name__ == "__main__":
     pykolab = pykolab.Conf()
     pykolab.finalize_conf()
     pykolab.run()
diff --git a/cyruslib.py b/cyruslib.py
index 0edbfdb..5c2ff1f 100644
--- a/cyruslib.py
+++ b/cyruslib.py
@@ -1,833 +1,833 @@
 # -*- coding: utf-8 -*-
 #
 # Cyruslib v0.8.5-20090401
 # Copyright (C) 2007-2009 Reinaldo de Carvalho <reinaldoc@gmail.com>
 # Copyright (C) 2003-2006 Gianluigi Tiesi <sherpya@netfarm.it>
 # Copyright (C) 2003-2006 NetFarm S.r.l. [http://www.netfarm.it]
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 2 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 # Requires python >= 2.6
 #
 
 from __future__ import print_function
 
 __version__ = '0.8.5'
 __all__ = [ 'CYRUS' ]
 __doc__ = """Cyrus admin wrapper
 Adds cyrus-specific commands to imaplib IMAP4 Class
 and defines new CYRUS class for cyrus imapd commands
 
 """
 
 from sys import exit, stdout
 
 try:
     import imaplib
     import re
     from binascii import b2a_base64
 except ImportError, e:
     print(e)
     exit(1)
 
 Commands = {
         'RECONSTRUCT'  : ('AUTH',),
         'DUMP'         : ('AUTH',), # To check admin status
         'ID'           : ('AUTH',), # Only one ID allowed in non auth mode
         'GETANNOTATION': ('AUTH',),
         'SETANNOTATION': ('AUTH',),
         'XFER'         : ('AUTH',)
     }
 
 imaplib.Commands.update(Commands)
 
 DEFAULT_SEP = '.'
 QUOTE       = '"'
 DQUOTE      = '""'
 
 re_ns  = re.compile(r'.*\(\(\".*(\.|/)\"\)\).*')
 re_q0  = re.compile(r'(.*)\s\(\)')
 re_q   = re.compile(r'(.*)\s\(STORAGE (\d+) (\d+)\)')
 re_mb  = re.compile(r'\((.*)\)\s\".\"\s(.*)')
 re_url = re.compile(r'^(imaps?)://(.+?):?(\d{0,5})$')
 
 def ok(res):
     return res.upper().startswith('OK')
 
 def quote(text, qchar=QUOTE):
     return text.join([qchar, qchar])
 
 def unquote(text, qchar=QUOTE):
     return ''.join(text.split(qchar))
 
 def getflags(test):
     flags = []
     for flag in test.split('\\'):
         flag = flag.strip()
         if len(flag): flags.append(flag)
     return flags
 
 ### A smart function to return an array of split strings
 ### and honours quoted strings
 def splitquote(text):
     data = text.split(QUOTE)
     if len(data) == 1: # no quotes
         res = data[0].split()
     else:
         res = []
         for match in data:
             if len(match.strip()) == 0: continue
             if match[0] == ' ':
                 res = res + match.strip().split()
             else:
                 res.append(match)
     return res
 
 ### return a dictionary from a cyrus info response
 def res2dict(data):
     data = splitquote(data)
     datalen = len(data)
     if datalen % 2: # Unmatched pair
         return False, {}
     res = {}
     for i in range(0, datalen, 2):
         res[data[i]] = data[i+1]
     return True, res
 
 
 class CYRUSError(Exception): pass
 
 class IMAP4(imaplib.IMAP4):
     def getsep(self):
         """Get mailbox separator"""
         ### yes, ugly but cyradm does it in the same way
         ### also more realable then calling NAMESPACE
         ### and it should be also compatibile with other servers
         try:
             return unquote(self.list(DQUOTE, DQUOTE)[1][0]).split()[1]
         except:
             return DEFAULT_SEP
 
     def isadmin(self):
         ### A trick to check if the user is admin or not
         ### normal users cannot use dump command
         try:
             res, msg = self._simple_command('DUMP', 'NIL')
             if msg[0].lower().find('denied') == -1:
                 return True
         except:
             pass
         return False
 
     def id(self):
         try:
             typ, dat = self._simple_command('ID', '("name" "PyKolab/Kolab")')
             res, dat = self._untagged_response(typ, dat, 'ID')
         except:
             return False, dat[0]
         return ok(res), dat[0]
 
     def getannotation(self, mailbox, pattern='*', shared=None):
         if shared == None:
             typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('*'))
         elif shared:
             typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('value.shared'))
         else:
             typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('value.priv'))
 
         return self._untagged_response(typ, dat, 'ANNOTATION')
 
     def setannotation(self, mailbox, desc, value, shared=False):
         if value:
             value = quote(value)
         else:
             value = "NIL"
         if shared:
             typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.shared'), value) )
         else:
             typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.priv'), value) )
 
         return self._untagged_response(typ, dat, 'ANNOTATION')
 
     def setquota(self, mailbox, limit):
         """Set quota of a mailbox"""
         if limit == 0:
             quota = '()'
         else:
             quota = '(STORAGE %s)' % limit
         return self._simple_command('SETQUOTA', mailbox, quota)
 
     ### Overridden to support partition
     ### Pychecker will complain about non matching signature
     def create(self, mailbox, partition=None):
         """Create a mailbox, partition is optional"""
         if partition is not None:
             return self._simple_command('CREATE', mailbox, partition)
         else:
             return self._simple_command('CREATE', mailbox)
 
     ### Overridden to support partition
     ### Pychecker: same here
     def rename(self, from_mailbox, to_mailbox, partition=None):
         """Rename a from_mailbox to to_mailbox, partition is optional"""
         if partition is not None:
             return self._simple_command('RENAME', from_mailbox, to_mailbox, partition)
         else:
             return self._simple_command('RENAME', from_mailbox, to_mailbox)
 
     def reconstruct(self, mailbox):
         return self._simple_command('RECONSTRUCT', mailbox)
 
 class IMAP4_SSL(imaplib.IMAP4_SSL):
     def getsep(self):
         """Get mailbox separator"""
         ### yes, ugly but cyradm does it in the same way
         ### also more realable then calling NAMESPACE
         ### and it should be also compatibile with other servers
         try:
             return unquote(self.list(DQUOTE, DQUOTE)[1][0]).split()[1]
         except:
             return DEFAULT_SEP
 
     def isadmin(self):
         ### A trick to check if the user is admin or not
         ### normal users cannot use dump command
         try:
             res, msg = self._simple_command('DUMP', 'NIL')
             if msg[0].lower().find('denied') == -1:
                 return True
         except:
             pass
         return False
 
     def id(self):
         try:
             typ, dat = self._simple_command('ID', '("name" "PyKolab/Kolab")')
             res, dat = self._untagged_response(typ, dat, 'ID')
         except:
             return False, dat[0]
         return ok(res), dat[0]
 
     def getannotation(self, mailbox, pattern='*', shared=None):
         if shared == None:
             typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('*'))
         elif shared:
             typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('value.shared'))
         else:
             typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('value.priv'))
 
         return self._untagged_response(typ, dat, 'ANNOTATION')
 
     def setannotation(self, mailbox, desc, value, shared=False):
         if value:
             value = quote(value)
         else:
             value = "NIL"
         if shared:
             typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.shared'), value) )
         else:
             typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.priv'), value) )
 
         return self._untagged_response(typ, dat, 'ANNOTATION')
 
     def setquota(self, mailbox, limit):
         """Set quota of a mailbox"""
         if limit == 0:
             quota = '()'
         else:
             quota = '(STORAGE %s)' % limit
         return self._simple_command('SETQUOTA', mailbox, quota)
 
     ### Overridden to support partition
     ### Pychecker will complain about non matching signature
     def create(self, mailbox, partition=None):
         """Create a mailbox, partition is optional"""
         if partition is not None:
             return self._simple_command('CREATE', mailbox, partition)
         else:
             return self._simple_command('CREATE', mailbox)
 
     ### Overridden to support partition
     ### Pychecker: same here
     def rename(self, from_mailbox, to_mailbox, partition=None):
         """Rename a from_mailbox to to_mailbox, partition is optional"""
         if partition is not None:
             return self._simple_command('RENAME', from_mailbox, to_mailbox, partition)
         else:
             return self._simple_command('RENAME', from_mailbox, to_mailbox)
 
     def reconstruct(self, mailbox):
         return self._simple_command('RECONSTRUCT', mailbox)
 
     def login_plain(self, admin, password, asUser):
         if asUser:
             encoded = b2a_base64("%s\0%s\0%s" % (asUser, admin, password)).strip()
         else:
             encoded = b2a_base64("%s\0%s\0%s" % (admin, admin, password)).strip()
 
         res, data = self._simple_command('AUTHENTICATE', 'PLAIN', encoded)
         self.AUTH = True
         if ok(res):
             self.state = 'AUTH'
         return res, data
 
 
 class CYRUS:
     ERROR = {}
     ERROR["CONNECT"]     = [0,  "Connection error"]
     ERROR["INVALID_URL"] = [1,  "Invalid URL"]
     ERROR["ENCODING"]    = [3,  "Invalid encondig"]
     ERROR["MBXNULL"]     = [5,  "Mailbox is Null"]
     ERROR["NOAUTH"]      = [7,  "Connection is not authenticated"]
     ERROR["LOGIN"]       = [10, "User or password is wrong"]
     ERROR["ADMIN"]       = [11, "User is not cyrus administrator"]
     ERROR["AUTH"]        = [12, "Connection already authenticated"]
     ERROR["LOGINPLAIN"]  = [15, "Encryption needed to use mechanism"]
     ERROR["LOGIN_PLAIN"] = [16, "User or password is wrong"]
     ERROR["CREATE"]      = [20, "Unable create mailbox"]
     ERROR["DELETE"]      = [25, "Unable delete mailbox"]
     ERROR["GETACL"]      = [30, "Unable parse GETACL result"]
     ERROR["SETQUOTA"]    = [40, "Invalid integer argument"]
     ERROR["GETQUOTA"]    = [45, "Quota root does not exist"]
     ERROR["RENAME"]      = [50, "Unable rename mailbox"]
     ERROR["RECONSTRUCT"] = [60, "Unable reconstruct mailbox"]
     ERROR["SUBSCRIBE"]   = [70, "User is cyrus administrator, normal user required"]
     ERROR["UNSUBSCRIBE"] = [75, "User is cyrus administrator, normal user required"]
     ERROR["LSUB"]        = [77, "User is cyrus administrator, normal user required"]
     ERROR["UNKCMD"]      = [98, "Command not implemented"]
     ERROR["IMAPLIB"]     = [99, "Generic imaplib error"]
 
     ENCODING_LIST = ['imap', 'utf-8', 'iso-8859-1']
 
     def __init__(self, url = 'imap://localhost:143'):
         self.VERBOSE = False
         self.AUTH = False
         self.ADMIN = None
         self.AUSER = None
         self.ADMINACL = 'c'
         self.SEP = DEFAULT_SEP
         self.ENCODING = 'imap'
         self.LOGFD = stdout
         match = re_url.match(url)
         if match:
             host = match.group(2)
             if match.group(3):
                 port = int(match.group(3))
             else:
                 port = 143
         else:
             self.__doraise("INVALID_URL")
         try:
             if match.group(1) == 'imap':
                 self.ssl = False
                 self.m = IMAP4(host, port)
             else:
                 self.ssl = True
                 self.m = IMAP4_SSL(host, port)
         except:
             self.__doraise("CONNECT")
 
     def __del__(self):
         if self.AUTH:
             self.logout()
 
     def __verbose(self, msg):
         if self.VERBOSE:
             print(msg, file=self.LOGFD)
 
     def __doexception(self, function, msg=None, *args):
         if msg is None:
             try:
                 msg = self.ERROR.get(function.upper())[1]
             except:
                 msg = self.ERROR.get("IMAPLIB")[1]
         value = ""
         for arg in args:
             if arg is not None:
                 value = "%s %s" % (value, arg)
 
         self.__verbose( '[%s%s] %s: %s' % (function.upper(), value, "BAD", msg) )
         self.__doraise( function.upper(), msg )
 
     def __doraise(self, mode, msg=None):
         idError = self.ERROR.get(mode)
         if idError:
             if msg is None:
                 msg = idError[1]
         else:
             idError = [self.ERROR.get("IMAPLIB")[0]]
         raise CYRUSError( idError[0], mode, msg )
 
 
     def __prepare(self, command, mailbox=True):
         if not self.AUTH:
             self.__doexception(command, self.ERROR.get("NOAUTH")[1])
         elif not mailbox:
             self.__doexception(command, self.ERROR.get("MBXNULL")[1])
 
     def __docommand(self, function, *args):
         wrapped = getattr(self.m, function, None)
         if wrapped is None:
             raise self.__doraise("UNKCMD")
         try:
             res, msg = wrapped(*args)
             if ok(res):
                 return res, msg
-        except Exception, info:
+        except Exception as info:
             error = str(info).split(':').pop().strip()
             if error.upper().startswith('BAD'):
                 error = error.split('BAD', 1).pop().strip()
                 error = unquote(error[1:-1], '\'')
             self.__doexception(function, error, *args)
         self.__doexception(function, msg[0], *args)
 
     def xfer(self, mailbox, server):
         """Xfer a mailbox to server"""
         return self.m._simple_command('XFER', mailbox, server)
 
     def id(self):
         self.__prepare('id')
         res, data = self.m.id()
         data = data.strip()
         if not res or (len(data) < 3): return False, {}
         data = data[1:-1] # Strip ()
         res, rdata = res2dict(data)
         if not res:
             self.__verbose( '[ID] Umatched pairs in result' )
         return res, rdata
 
     def login(self, username, password):
         if self.AUTH:
             self.__doexception("LOGIN", self.ERROR.get("AUTH")[1])
         try:
             res, msg = self.m.login(username, password)
             self.AUTH = True
             self.id()
             admin = self.m.isadmin()
-        except Exception, info:
+        except Exception as info:
             self.AUTH = False
             error = str(info).split(':').pop().strip()
             self.__doexception("LOGIN", error)
 
         if admin:
             self.ADMIN = username
 
         self.SEP = self.m.getsep()
         self.__verbose( '[LOGIN %s] %s: %s' % (username, res, msg[0]) )
 
     def login_plain(self, username, password, asUser = None):
         if self.AUTH:
             self.__doexception("LOGINPLAIN", self.ERROR.get("AUTH")[1])
         if not self.ssl:
             self.__doexception("LOGINPLAIN", self.ERROR.get("LOGINPLAIN")[1])
         res, msg = self.__docommand("login_plain", username, password, asUser)
         self.__verbose( '[AUTHENTICATE PLAIN %s] %s: %s' % (username, res, msg[0]) )
 
         if ok(res):
             self.AUTH = True
             self.id()
             if asUser is None:
                 if self.m.isadmin():
                     self.ADMIN = admin
             else:
                 self.ADMIN = asUser
                 self.AUSER = asUser
             self.SEP = self.m.getsep()
 
     def logout(self):
         try:
             res, msg = self.m.logout()
-        except Exception, info:
+        except Exception as info:
             error = str(info).split(':').pop().strip()
             self.__doexception("LOGOUT", error)
         self.AUTH = False
         self.ADMIN = None
         self.AUSER = None
         self.__verbose( '[LOGOUT] %s: %s' % (res, msg[0]) )
 
     def getEncoding(self):
         """Get current input/output codification"""
         return self.ENCODING
 
     def setEncoding(self, enc = None):
         """Set current input/output codification"""
         if enc is None:
             self.ENCODING = 'imap'
         elif enc in self.ENCODING_LIST:
             self.ENCODING = enc
         else:
             raise self.__doraise("ENCODING")
 
     def __encode(self, text):
         if re.search("&", text):
             text = re.sub("/", "+AC8-", text)
             text = re.sub("&", "+", text)
             text = unicode(text, 'utf-7').encode(self.ENCODING)
         return text
 
     def encode(self, text):
         if self.ENCODING == 'imap':
             return text
         elif self.ENCODING in self.ENCODING_LIST:
             return self.__encode(text)
 
     def __decode(self, text):
         text = re.sub("/", "-&", text)
         text = re.sub(" ", "-@", text)
         text = unicode(text, self.ENCODING).encode('utf-7')
         text = re.sub("-@", " ", text)
         text = re.sub("-&", "/", text)
         text = re.sub("\+", "&", text)
         return text
 
     def decode(self, text):
         if self.ENCODING == 'imap':
             return text
         elif self.ENCODING in self.ENCODING_LIST:
             return self.__decode(text)
 
     def lm(self, pattern="*"):
         """
         List mailboxes, returns dict with list of mailboxes
 
         To list all mailboxes                       lm()
         To list users top mailboxes                 lm("user/%")
         To list all users mailboxes                 lm("user/*")
         To list users mailboxes startwith a word    lm("user/word*")
         To list global top folders                  lm("%")
         To list global startwith a word             unsupported by server
           suggestion                                lm("word*")
 
         """
         self.__prepare('LIST')
         if pattern == '': pattern = "*"
         if pattern == '%':
             res, ml = self.__docommand('list', '', '%')
         else:
             res, ml = self.__docommand('list', '""', self.decode(pattern))
 
         if not ok(res):
             self.__verbose( '[LIST] %s: %s' % (res, ml) )
             return []
 
         if (len(ml) == 1) and ml[0] is None:
             self.__verbose( '[LIST] No results' )
             return []
 
         mb = []
         for mailbox in ml:
             res = re_mb.match(mailbox)
             if res is None: continue
             mbe = unquote(res.group(2))
             if 'Noselect' in getflags(res.group(1)): continue
             mb.append(self.encode(mbe))
         return mb
 
     def cm(self, mailbox, partition=None):
         """Create mailbox"""
         self.__prepare('CREATE', mailbox)
         res, msg = self.__docommand('create', self.decode(mailbox), partition)
         self.__verbose( '[CREATE %s partition=%s] %s: %s' % (mailbox, partition, res, msg[0]) )
 
     def __dm(self, mailbox):
         if not mailbox:
             return True
         self.__docommand("setacl", self.decode(mailbox), self.ADMIN, self.ADMINACL)
         res, msg = self.__docommand("delete", self.decode(mailbox))
         self.__verbose( '[DELETE %s] %s: %s' % (mailbox, res, msg[0]) )
 
     def dm(self, mailbox, recursive=True):
         """Delete mailbox"""
         self.__prepare('DELETE', mailbox)
         mbxTmp = mailbox.split(self.SEP)
         # Cyrus is not recursive for user subfolders and global folders
         if (recursive and mbxTmp[0] != "user") or (len(mbxTmp) > 2):
             mbxList = self.lm("%s%s*" % (mailbox, self.SEP))
             mbxList.reverse()
             for mbox in mbxList:
                 self.__dm(mbox)
         self.__dm(mailbox)
 
     def rename(self, fromMbx, toMbx, partition=None):
         """Rename or change partition"""
         self.__prepare('RENAME', fromMbx)
         # Rename is recursive! Amen!
         res, msg = self.__docommand("rename", self.decode(fromMbx), self.decode(toMbx), partition)
         self.__verbose( '[RENAME %s %s] %s: %s' % (fromMbx, toMbx, res, msg[0]) )
 
     def lam(self, mailbox):
         """List ACLs"""
         self.__prepare('GETACL', mailbox)
         res, acl = self.__docommand("getacl", self.decode(mailbox))
         acls = {}
         aclList = splitquote(acl.pop().strip())
         del aclList[0] # mailbox
         for i in range(0, len(aclList), 2):
             try:
                 userid = self.encode(aclList[i])
                 rights = aclList[i + 1]
-            except Exception, info:
+            except Exception as info:
                 self.__verbose( '[GETACL %s] BAD: %s' % (mailbox, info.args[0]) )
                 raise self.__doraise("GETACL")
             self.__verbose( '[GETACL %s] %s %s' % (mailbox, userid, rights) )
             acls[userid] = rights
         return acls
 
     def sam(self, mailbox, userid, rights):
         """Set ACL"""
         self.__prepare('SETACL', mailbox)
         res, msg = self.__docommand("setacl", self.decode(mailbox), userid, rights)
         self.__verbose( '[SETACL %s %s %s] %s: %s' % (mailbox, userid, rights, res, msg[0]) )
 
     def lq(self, mailbox):
         """List Quota"""
         self.__prepare('GETQUOTA', mailbox)
         res, msg = self.__docommand("getquota", self.decode(mailbox))
         match = re_q0.match(msg[0])
         if match:
             self.__verbose( '[GETQUOTA %s] QUOTA (Unlimited)' % mailbox )
             return None, None
         match = re_q.match(msg[0])
         if match is None:
             self.__verbose( '[GETQUOTA %s] BAD: RegExp not matched, please report' % mailbox )
             return None, None
         try:
             used = int(match.group(2))
             quota = int(match.group(3))
             self.__verbose( '[GETQUOTA %s] %s: QUOTA (%d/%d)' % (mailbox, res, used, quota) )
             return used, quota
         except:
             self.__verbose( '[GETQUOTA %s] BAD: Error while parsing results' % mailbox )
             return None, None
 
     def lqr(self, mailbox):
         """List Quota Root"""
         self.__prepare('GETQUOTAROOT', mailbox)
         res, msg = self.__docommand("getquotaroot", self.decode(mailbox))
         (_mailbox, _root) = msg[0][0].split()
 
         match = re_q0.match(msg[1][0])
         if match:
             self.__verbose( '[GETQUOTAROOT %s] QUOTAROOT (Unlimited)' % mailbox )
             return _root, None, None
 
         match = re_q.match(msg[1][0])
         try:
             used = int(match.group(2))
             quota = int(match.group(3))
             self.__verbose( '[GETQUOTAROOT %s] %s: QUOTA (%d/%d)' % (mailbox, res, used, quota) )
             return _root, used, quota
         except:
             self.__verbose( '[GETQUOTAROOT %s] BAD: Error while parsing results' % mailbox )
             return _root, None, None
 
     def sq(self, mailbox, limit):
         """Set Quota"""
         self.__prepare('SETQUOTA', mailbox)
         try:
             limit = int(limit)
-        except ValueError, e:
+        except ValueError:
             self.__verbose( '[SETQUOTA %s] BAD: %s %s' % (mailbox, self.ERROR.get("SETQUOTA")[1], limit) )
             raise self.__doraise("SETQUOTA")
         res, msg = self.__docommand("setquota", self.decode(mailbox), limit)
         self.__verbose( '[SETQUOTA %s %s] %s: %s' % (mailbox, limit, res, msg[0]) )
 
     def getannotation(self, mailbox, pattern='*'):
         """Get Annotation"""
         self.__prepare('GETANNOTATION')
         res, data = self.__docommand('getannotation', self.decode(mailbox), pattern)
 
         if (len(data) == 1) and data[0] is None:
             self.__verbose( '[GETANNOTATION %s] No results' % (mailbox) )
             return {}
 
         ann = {}
         annotations = []
         empty_values = [ "NIL", '" "', None, '', ' ' ]
 
         concat_items = []
         for item in data:
             if isinstance(item, tuple):
                 item = ' '.join([str(x) for x in item])
 
             if len(concat_items) > 0:
                 concat_items.append(item)
 
                 if ''.join(concat_items).count('(') == ''.join(concat_items).count(')'):
                     annotations.append(''.join(concat_items))
                     concat_items = []
                     continue
             else:
 
                 if item.count('(') == item.count(')'):
                     annotations.append(item)
                     continue
                 else:
                     concat_items.append(item)
                     continue
 
         for annotation in annotations:
             annotation = annotation.strip()
 
             if not annotation[0] == '"':
                 folder = annotation.split('"')[0].replace('"','').strip()
                 key = annotation.split('"')[1].replace('"','').replace("'","").strip()
                 _annot = annotation.split('(')[1].split(')')[0].strip()
             else:
                 folder = annotation.split('"')[1].replace('"','').strip()
                 key = annotation.split('"')[3].replace('"','').replace("'","").strip()
                 _annot = annotation.split('(')[1].split(')')[0].strip()
 
             if not ann.has_key(folder):
                 ann[folder] = {}
 
             try:
                 value_priv = _annot[(_annot.index('"value.priv"')+len('"value.priv"')):_annot.index('"size.priv"')].strip()
-            except ValueError, errmsg:
+            except ValueError:
                 value_priv = None
 
             try:
                 size_priv = _annot[(_annot.index('"size.priv"')+len('"size.priv"')):].strip().split('"')[1].strip()
                 try:
                     value_priv = value_priv[value_priv.index('{%s}' % (size_priv))+len('{%s}' % (size_priv)):].strip()
-                except Exception, errmsg:
+                except Exception:
                     pass
-            except Exception, errmsg:
+            except Exception:
                 pass
 
             if value_priv in empty_values:
                 value_priv = None
             else:
                 try:
                     value_priv = value_priv[:value_priv.index('"content-type.priv"')].strip()
                 except:
                     pass
 
                 try:
                     value_priv = value_priv[:value_priv.index('"modifiedsince.priv"')].strip()
                 except:
                     pass
 
                 if value_priv.startswith('"'):
                     value_priv = value_priv[1:]
 
                 if value_priv.endswith('"'):
                     value_priv = value_priv[:-1]
 
             if value_priv in empty_values:
                 value_priv = None
 
             try:
                 value_shared = _annot[(_annot.index('"value.shared"')+len('"value.shared"')):_annot.index('"size.shared"')].strip()
-            except ValueError, errmsg:
+            except ValueError:
                 value_shared = None
 
             try:
                 size_shared = _annot[(_annot.index('"size.shared"')+len('"size.shared"')):].strip().split('"')[1].strip()
                 try:
                     value_shared = value_shared[value_shared.index('{%s}' % (size_shared))+len('{%s}' % (size_shared)):].strip()
-                except Exception, errmsg:
+                except Exception:
                     pass
-            except Exception, errmsg:
+            except Exception:
                 pass
 
             if value_shared in empty_values:
                 value_shared = None
             else:
                 try:
                     value_shared = value_shared[:value_shared.index('"content-type.shared"')].strip()
                 except:
                     pass
 
                 try:
                     value_shared = value_shared[:value_shared.index('"modifiedsince.shared"')].strip()
                 except:
                     pass
 
                 if value_shared.startswith('"'):
                     value_shared = value_shared[1:]
 
                 if value_shared.endswith('"'):
                     value_shared = value_shared[:-1]
 
             if value_shared in empty_values:
                 value_shared = None
 
             if not value_priv == None:
                 ann[folder]['/private' + key] = value_priv
 
             if not value_shared == None:
                 ann[folder]['/shared' + key] = value_shared
 
         return ann
 
     def setannotation(self, mailbox, annotation, value, shared=False):
         """Set Annotation"""
         self.__prepare('SETANNOTATION')
         res, msg = self.__docommand("setannotation", self.decode(mailbox), annotation, value, shared)
         self.__verbose( '[SETANNOTATION %s] %s: %s' % (mailbox, res, msg[0]) )
 
     def __reconstruct(self, mailbox):
         if not mailbox:
             return True
         res, msg = self.__docommand("reconstruct", self.decode(mailbox))
         self.__verbose( '[RECONSTRUCT %s] %s: %s' % (mailbox, res, msg[0]) )
 
     def reconstruct(self, mailbox, recursive=True):
         """Reconstruct"""
         self.__prepare('RECONSTRUCT', mailbox)
         # Cyrus is not recursive for remote reconstruct
         if recursive:
             mbxList = self.lm("%s%s*" % (mailbox, self.SEP))
             mbxList.reverse()
             for mbox in mbxList:
                 self.__reconstruct(mbox)
         self.__reconstruct(mailbox)
 
     def lsub(self, pattern="*"):
         if self.AUSER is None:
             self.__doexception("lsub")
         self.__prepare('LSUB')
         if pattern == '': pattern = "*"
         res, ml = self.__docommand('lsub', '*', pattern)
 
         if not ok(res):
             self.__verbose( '[LIST] %s: %s' % (res, ml) )
             return []
 
         if (len(ml) == 1) and ml[0] is None:
             self.__verbose( '[LIST] No results' )
             return []
 
         mb = []
         for mailbox in ml:
             res = re_mb.match(mailbox)
             if res is None: continue
             mbe = unquote(res.group(2))
             if 'Noselect' in getflags(res.group(1)): continue
             mb.append(self.encode(mbe))
         return mb
 
     def subscribe(self, mailbox):
         """Subscribe"""
         self.__prepare('SUBSCRIBE')
         res, msg = self.__docommand("subscribe", self.decode(mailbox))
         self.__verbose( '[SUBSCRIBE %s] %s: %s' % (mailbox, res, msg[0]) )
 
     def unsubscribe(self, mailbox):
         """Unsubscribe"""
         self.__prepare('UNSUBSCRIBE')
         res, msg = self.__docommand("unsubscribe", self.decode(mailbox))
         self.__verbose( '[UNSUBSCRIBE %s] %s: %s' % (mailbox, res, msg[0]) )
 
diff --git a/ext/python/Tools/freeze/checkextensions_win32.py b/ext/python/Tools/freeze/checkextensions_win32.py
index a198ecf..ba3853c 100644
--- a/ext/python/Tools/freeze/checkextensions_win32.py
+++ b/ext/python/Tools/freeze/checkextensions_win32.py
@@ -1,188 +1,188 @@
 """Extension management for Windows.
 
 Under Windows it is unlikely the .obj files are of use, as special compiler options
 are needed (primarily to toggle the behavior of "public" symbols.
 
 I dont consider it worth parsing the MSVC makefiles for compiler options.  Even if
 we get it just right, a specific freeze application may have specific compiler
 options anyway (eg, to enable or disable specific functionality)
 
 So my basic stragtegy is:
 
 * Have some Windows INI files which "describe" one or more extension modules.
   (Freeze comes with a default one for all known modules - but you can specify
   your own).
 * This description can include:
   - The MSVC .dsp file for the extension.  The .c source file names
     are extraced from there.
   - Specific compiler/linker options
   - Flag to indicate if Unicode compilation is expected.
 
 At the moment the name and location of this INI file is hardcoded,
 but an obvious enhancement would be to provide command line options.
 """
 
 import os, sys
 try:
     import win32api
 except ImportError:
     win32api = None # User has already been warned
 
 class CExtension:
     """An abstraction of an extension implemented in C/C++
     """
     def __init__(self, name, sourceFiles):
         self.name = name
         # A list of strings defining additional compiler options.
         self.sourceFiles = sourceFiles
         # A list of special compiler options to be applied to
         # all source modules in this extension.
         self.compilerOptions = []
         # A list of .lib files the final .EXE will need.
         self.linkerLibs = []
 
     def GetSourceFiles(self):
         return self.sourceFiles
 
     def AddCompilerOption(self, option):
         self.compilerOptions.append(option)
     def GetCompilerOptions(self):
         return self.compilerOptions
 
     def AddLinkerLib(self, lib):
         self.linkerLibs.append(lib)
     def GetLinkerLibs(self):
         return self.linkerLibs
 
 def checkextensions(unknown, extra_inis, prefix):
     # Create a table of frozen extensions
 
     defaultMapName = os.path.join( os.path.split(sys.argv[0])[0], "extensions_win32.ini")
     if not os.path.isfile(defaultMapName):
         sys.stderr.write("WARNING: %s can not be found - standard extensions may not be found\n" % defaultMapName)
     else:
         # must go on end, so other inis can override.
         extra_inis.append(defaultMapName)
 
     ret = []
     for mod in unknown:
         for ini in extra_inis:
 #                       print "Looking for", mod, "in", win32api.GetFullPathName(ini),"...",
             defn = get_extension_defn( mod, ini, prefix )
             if defn is not None:
 #                               print "Yay - found it!"
                 ret.append( defn )
                 break
 #                       print "Nope!"
         else: # For not broken!
             sys.stderr.write("No definition of module %s in any specified map file.\n" % (mod))
 
     return ret
 
 def get_extension_defn(moduleName, mapFileName, prefix):
     if win32api is None: return None
     os.environ['PYTHONPREFIX'] = prefix
     dsp = win32api.GetProfileVal(moduleName, "dsp", "", mapFileName)
     if dsp=="":
         return None
 
     # We allow environment variables in the file name
     dsp = win32api.ExpandEnvironmentStrings(dsp)
     # If the path to the .DSP file is not absolute, assume it is relative
     # to the description file.
     if not os.path.isabs(dsp):
         dsp = os.path.join( os.path.split(mapFileName)[0], dsp)
     # Parse it to extract the source files.
     sourceFiles = parse_dsp(dsp)
     if sourceFiles is None:
         return None
 
     module = CExtension(moduleName, sourceFiles)
     # Put the path to the DSP into the environment so entries can reference it.
     os.environ['dsp_path'] = os.path.split(dsp)[0]
     os.environ['ini_path'] = os.path.split(mapFileName)[0]
 
     cl_options = win32api.GetProfileVal(moduleName, "cl", "", mapFileName)
     if cl_options:
         module.AddCompilerOption(win32api.ExpandEnvironmentStrings(cl_options))
 
     exclude = win32api.GetProfileVal(moduleName, "exclude", "", mapFileName)
     exclude = exclude.split()
 
     if win32api.GetProfileVal(moduleName, "Unicode", 0, mapFileName):
         module.AddCompilerOption('/D UNICODE /D _UNICODE')
 
     libs = win32api.GetProfileVal(moduleName, "libs", "", mapFileName).split()
     for lib in libs:
         module.AddLinkerLib(win32api.ExpandEnvironmentStrings(lib))
 
     for exc in exclude:
         if exc in module.sourceFiles:
             modules.sourceFiles.remove(exc)
 
     return module
 
 # Given an MSVC DSP file, locate C source files it uses
 # returns a list of source files.
 def parse_dsp(dsp):
 #       print "Processing", dsp
     # For now, only support
     ret = []
     dsp_path, dsp_name = os.path.split(dsp)
     try:
         lines = open(dsp, "r").readlines()
-    except IOError, msg:
+    except IOError as msg:
         sys.stderr.write("%s: %s\n" % (dsp, msg))
         return None
     for line in lines:
         fields = line.strip().split("=", 2)
         if fields[0]=="SOURCE":
             if os.path.splitext(fields[1])[1].lower() in ['.cpp', '.c']:
                 ret.append( win32api.GetFullPathName(os.path.join(dsp_path, fields[1] ) ) )
     return ret
 
 def write_extension_table(fname, modules):
     fp = open(fname, "w")
     try:
         fp.write (ext_src_header)
         # Write fn protos
         for module in modules:
             # bit of a hack for .pyd's as part of packages.
             name = module.name.split('.')[-1]
             fp.write('extern void init%s(void);\n' % (name) )
         # Write the table
         fp.write (ext_tab_header)
         for module in modules:
             name = module.name.split('.')[-1]
             fp.write('\t{"%s", init%s},\n' % (name, name) )
 
         fp.write (ext_tab_footer)
         fp.write(ext_src_footer)
     finally:
         fp.close()
 
 
 ext_src_header = """\
 #include "Python.h"
 """
 
 ext_tab_header = """\
 
 static struct _inittab extensions[] = {
 """
 
 ext_tab_footer = """\
         /* Sentinel */
         {0, 0}
 };
 """
 
 ext_src_footer = """\
 extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab);
 
 int PyInitFrozenExtensions()
 {
         return PyImport_ExtendInittab(extensions);
 }
 
 """
diff --git a/ext/python/Tools/freeze/freeze.py b/ext/python/Tools/freeze/freeze.py
index daa345b..a7906b1 100755
--- a/ext/python/Tools/freeze/freeze.py
+++ b/ext/python/Tools/freeze/freeze.py
@@ -1,503 +1,503 @@
 #! /usr/bin/env python
 
 """Freeze a Python script into a binary.
 
 usage: freeze [options...] script [module]...
 
 Options:
 -p prefix:    This is the prefix used when you ran ``make install''
               in the Python build directory.
               (If you never ran this, freeze won't work.)
               The default is whatever sys.prefix evaluates to.
               It can also be the top directory of the Python source
               tree; then -P must point to the build tree.
 
 -P exec_prefix: Like -p but this is the 'exec_prefix', used to
                 install objects etc.  The default is whatever sys.exec_prefix
                 evaluates to, or the -p argument if given.
                 If -p points to the Python source tree, -P must point
                 to the build tree, if different.
 
 -e extension: A directory containing additional .o files that
               may be used to resolve modules.  This directory
               should also have a Setup file describing the .o files.
               On Windows, the name of a .INI file describing one
               or more extensions is passed.
               More than one -e option may be given.
 
 -o dir:       Directory where the output files are created; default '.'.
 
 -m:           Additional arguments are module names instead of filenames.
 
 -a package=dir: Additional directories to be added to the package's
                 __path__.  Used to simulate directories added by the
                 package at runtime (eg, by OpenGL and win32com).
                 More than one -a option may be given for each package.
 
 -l file:      Pass the file to the linker (windows only)
 
 -d:           Debugging mode for the module finder.
 
 -q:           Make the module finder totally quiet.
 
 -h:           Print this help message.
 
 -x module     Exclude the specified module. It will still be imported
               by the frozen binary if it exists on the host system.
 
 -X module     Like -x, except the module can never be imported by
               the frozen binary.
 
 -E:           Freeze will fail if any modules can't be found (that
               were not excluded using -x or -X).
 
 -i filename:  Include a file with additional command line options.  Used
               to prevent command lines growing beyond the capabilities of
               the shell/OS.  All arguments specified in filename
               are read and the -i option replaced with the parsed
               params (note - quoting args in this file is NOT supported)
 
 -s subsystem: Specify the subsystem (For Windows only.);
               'console' (default), 'windows', 'service' or 'com_dll'
 
 -w:           Toggle Windows (NT or 95) behavior.
               (For debugging only -- on a win32 platform, win32 behavior
               is automatic.)
 
 -r prefix=f:  Replace path prefix.
               Replace prefix with f in the source path references
               contained in the resulting binary.
 
 Arguments:
 
 script:       The Python script to be executed by the resulting binary.
 
 module ...:   Additional Python modules (referenced by pathname)
               that will be included in the resulting binary.  These
               may be .py or .pyc files.  If -m is specified, these are
               module names that are search in the path instead.
 
 NOTES:
 
 In order to use freeze successfully, you must have built Python and
 installed it ("make install").
 
 The script should not use modules provided only as shared libraries;
 if it does, the resulting binary is not self-contained.
 """
 
 
 # Import standard modules
 
 import modulefinder
 import getopt
 import os
 import sys
 
 
 # Import the freeze-private modules
 
 import checkextensions
 import makeconfig
 import makefreeze
 import makemakefile
 import parsesetup
 import bkfile
 
 
 # Main program
 
 def main():
     # overridable context
     prefix = None                       # settable with -p option
     exec_prefix = None                  # settable with -P option
     extensions = []
     exclude = []                        # settable with -x option
     addn_link = []      # settable with -l, but only honored under Windows.
     path = sys.path[:]
     modargs = 0
     debug = 1
     odir = ''
     win = sys.platform[:3] == 'win'
     replace_paths = []                  # settable with -r option
     error_if_any_missing = 0
 
     # default the exclude list for each platform
     if win: exclude = exclude + [
         'dos', 'dospath', 'mac', 'macpath', 'macfs', 'MACFS', 'posix',
         'os2', 'ce', 'riscos', 'riscosenviron', 'riscospath',
         ]
 
     fail_import = exclude[:]
 
     # output files
     frozen_c = 'frozen.c'
     config_c = 'config.c'
     target = 'a.out'                    # normally derived from script name
     makefile = 'Makefile'
     subsystem = 'console'
 
     # parse command line by first replacing any "-i" options with the
     # file contents.
     pos = 1
     while pos < len(sys.argv)-1:
         # last option can not be "-i", so this ensures "pos+1" is in range!
         if sys.argv[pos] == '-i':
             try:
                 options = open(sys.argv[pos+1]).read().split()
-            except IOError, why:
+            except IOError as why:
                 usage("File name '%s' specified with the -i option "
                       "can not be read - %s" % (sys.argv[pos+1], why) )
             # Replace the '-i' and the filename with the read params.
             sys.argv[pos:pos+2] = options
             pos = pos + len(options) - 1 # Skip the name and the included args.
         pos = pos + 1
 
     # Now parse the command line with the extras inserted.
     try:
         opts, args = getopt.getopt(sys.argv[1:], 'r:a:dEe:hmo:p:P:b:qs:wX:x:l:')
-    except getopt.error, msg:
+    except getopt.error as msg:
         usage('getopt error: ' + str(msg))
 
     # process option arguments
     for o, a in opts:
         if o == '-h':
             print(__doc__)
             return
         if o == '-b':
             binlib = a
         if o == '-d':
             debug = debug + 1
         if o == '-e':
             extensions.append(a)
         if o == '-m':
             modargs = 1
         if o == '-o':
             odir = a
         if o == '-p':
             prefix = a
         if o == '-P':
             exec_prefix = a
         if o == '-q':
             debug = 0
         if o == '-w':
             win = not win
         if o == '-s':
             if not win:
                 usage("-s subsystem option only on Windows")
             subsystem = a
         if o == '-x':
             exclude.append(a)
         if o == '-X':
             exclude.append(a)
             fail_import.append(a)
         if o == '-E':
             error_if_any_missing = 1
         if o == '-l':
             addn_link.append(a)
         if o == '-a':
             apply(modulefinder.AddPackagePath, tuple(a.split("=", 2)))
         if o == '-r':
             f,r = a.split("=", 2)
             replace_paths.append( (f,r) )
 
     # modules that are imported by the Python runtime
     implicits = []
     for module in ('site', 'warnings',):
         if module not in exclude:
             implicits.append(module)
 
     # default prefix and exec_prefix
     if not exec_prefix:
         if prefix:
             exec_prefix = prefix
         else:
             exec_prefix = sys.exec_prefix
     if not prefix:
         prefix = sys.prefix
 
     # determine whether -p points to the Python source tree
     ishome = os.path.exists(os.path.join(prefix, 'Python', 'ceval.c'))
 
     # locations derived from options
     version = sys.version[:3]
     if win:
         extensions_c = 'frozen_extensions.c'
     if ishome:
         print("(Using Python source directory)")
         binlib = exec_prefix
         incldir = os.path.join(prefix, 'Include')
         config_h_dir = exec_prefix
         config_c_in = os.path.join(prefix, 'Modules', 'config.c.in')
         frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c')
         makefile_in = os.path.join(exec_prefix, 'Makefile')
         if win:
             frozendllmain_c = os.path.join(exec_prefix, 'Pc\\frozen_dllmain.c')
     else:
         if not binlib:
             binlib = os.path.join(exec_prefix,
                               'lib', 'python%s' % version, 'config')
         else:
             binlib = os.path.join(binlib, 'python%s' % version, 'config')
 
         incldir = os.path.join(prefix, 'include', 'python%s' % version)
         config_h_dir = os.path.join(exec_prefix, 'include',
                                     'python%s' % version)
         config_c_in = os.path.join(binlib, 'config.c.in')
         frozenmain_c = os.path.join(binlib, 'frozenmain.c')
         makefile_in = os.path.join(binlib, 'Makefile')
         frozendllmain_c = os.path.join(binlib, 'frozen_dllmain.c')
     supp_sources = []
     defines = []
     includes = ['-I' + incldir, '-I' + config_h_dir]
 
     # sanity check of directories and files
     check_dirs = [prefix, exec_prefix, binlib, incldir]
     if not win:
         # These are not directories on Windows.
         check_dirs = check_dirs + extensions
     for dir in check_dirs:
         if not os.path.exists(dir):
             usage('needed directory %s not found' % dir)
         if not os.path.isdir(dir):
             usage('%s: not a directory' % dir)
     if win:
         files = supp_sources + extensions # extensions are files on Windows.
     else:
         files = [config_c_in, makefile_in] + supp_sources
     for file in supp_sources:
         if not os.path.exists(file):
             usage('needed file %s not found' % file)
         if not os.path.isfile(file):
             usage('%s: not a plain file' % file)
     if not win:
         for dir in extensions:
             setup = os.path.join(dir, 'Setup')
             if not os.path.exists(setup):
                 usage('needed file %s not found' % setup)
             if not os.path.isfile(setup):
                 usage('%s: not a plain file' % setup)
 
     # check that enough arguments are passed
     if not args:
         usage('at least one filename argument required')
 
     # check that file arguments exist
     for arg in args:
         if arg == '-m':
             break
         # if user specified -m on the command line before _any_
         # file names, then nothing should be checked (as the
         # very first file should be a module name)
         if modargs:
             break
         if not os.path.exists(arg):
             usage('argument %s not found' % arg)
         if not os.path.isfile(arg):
             usage('%s: not a plain file' % arg)
 
     # process non-option arguments
     scriptfile = args[0]
     modules = args[1:]
 
     # derive target name from script name
     base = os.path.basename(scriptfile)
     base, ext = os.path.splitext(base)
     if base:
         if base != scriptfile:
             target = base
         else:
             target = base + '.bin'
 
     # handle -o option
     base_frozen_c = frozen_c
     base_config_c = config_c
     base_target = target
     if odir and not os.path.isdir(odir):
         try:
             os.mkdir(odir)
             print("Created output directory", odir)
         except os.error, msg:
             usage('%s: mkdir failed (%s)' % (odir, str(msg)))
     base = ''
     if odir:
         base = os.path.join(odir, '')
         frozen_c = os.path.join(odir, frozen_c)
         config_c = os.path.join(odir, config_c)
         target = os.path.join(odir, target)
         makefile = os.path.join(odir, makefile)
         if win: extensions_c = os.path.join(odir, extensions_c)
 
     # Handle special entry point requirements
     # (on Windows, some frozen programs do not use __main__, but
     # import the module directly.  Eg, DLLs, Services, etc
     custom_entry_point = None  # Currently only used on Windows
     python_entry_is_main = 1   # Is the entry point called __main__?
     # handle -s option on Windows
     if win:
         import winmakemakefile
         try:
             custom_entry_point, python_entry_is_main = \
                 winmakemakefile.get_custom_entry_point(subsystem)
-        except ValueError, why:
+        except ValueError as why:
             usage(why)
 
 
     # Actual work starts here...
 
     # collect all modules of the program
     dir = os.path.dirname(scriptfile)
     path[0] = dir
     mf = modulefinder.ModuleFinder(path, debug, exclude, replace_paths)
 
     if win and subsystem=='service':
         # If a Windows service, then add the "built-in" module.
         mod = mf.add_module("servicemanager")
         mod.__file__="dummy.pyd" # really built-in to the resulting EXE
 
     for mod in implicits:
         mf.import_hook(mod)
     for mod in modules:
         if mod == '-m':
             modargs = 1
             continue
         if modargs:
             if mod[-2:] == '.*':
                 mf.import_hook(mod[:-2], None, ["*"])
             else:
                 mf.import_hook(mod)
         else:
             mf.load_file(mod)
 
     # Add the main script as either __main__, or the actual module name.
     if python_entry_is_main:
         mf.run_script(scriptfile)
     else:
         mf.load_file(scriptfile)
 
     if debug > 0:
         mf.report()
         print()
     dict = mf.modules
 
     if error_if_any_missing:
         missing = mf.any_missing()
         if missing:
             sys.exit("There are some missing modules: %r" % missing)
 
     # generate output for frozen modules
     files = makefreeze.makefreeze(base, dict, debug, custom_entry_point,
                                   fail_import)
 
     # look for unfrozen modules (builtin and of unknown origin)
     builtins = []
     unknown = []
     mods = dict.keys()
     mods.sort()
     for mod in mods:
         if dict[mod].__code__:
             continue
         if not dict[mod].__file__:
             builtins.append(mod)
         else:
             unknown.append(mod)
 
     # search for unknown modules in extensions directories (not on Windows)
     addfiles = []
     frozen_extensions = [] # Windows list of modules.
     if unknown or (not win and builtins):
         if not win:
             addfiles, addmods = \
                       checkextensions.checkextensions(unknown+builtins,
                                                       extensions)
             for mod in addmods:
                 if mod in unknown:
                     unknown.remove(mod)
                     builtins.append(mod)
         else:
             # Do the windows thang...
             import checkextensions_win32
             # Get a list of CExtension instances, each describing a module
             # (including its source files)
             frozen_extensions = checkextensions_win32.checkextensions(
                 unknown, extensions, prefix)
             for mod in frozen_extensions:
                 unknown.remove(mod.name)
 
     # report unknown modules
     if unknown:
         sys.stderr.write('Warning: unknown modules remain: %s\n' %
                          ' '.join(unknown))
 
     # windows gets different treatment
     if win:
         # Taking a shortcut here...
         import winmakemakefile, checkextensions_win32
         checkextensions_win32.write_extension_table(extensions_c,
                                                     frozen_extensions)
         # Create a module definition for the bootstrap C code.
         xtras = [frozenmain_c, os.path.basename(frozen_c),
                  frozendllmain_c, os.path.basename(extensions_c)] + files
         maindefn = checkextensions_win32.CExtension( '__main__', xtras )
         frozen_extensions.append( maindefn )
         outfp = open(makefile, 'w')
         try:
             winmakemakefile.makemakefile(outfp,
                                          locals(),
                                          frozen_extensions,
                                          os.path.basename(target))
         finally:
             outfp.close()
         return
 
     # generate config.c and Makefile
     builtins.sort()
     infp = open(config_c_in)
     outfp = bkfile.open(config_c, 'w')
     try:
         makeconfig.makeconfig(infp, outfp, builtins)
     finally:
         outfp.close()
     infp.close()
 
     cflags = ['$(OPT)']
     cppflags = defines + includes
     libs = [os.path.join(binlib, 'libpython$(VERSION).so')]
 
     somevars = {}
     if os.path.exists(makefile_in):
         makevars = parsesetup.getmakevars(makefile_in)
         for key in makevars.keys():
             somevars[key] = makevars[key]
 
     somevars['CFLAGS'] = ' '.join(cflags) # override
     somevars['CPPFLAGS'] = ' '.join(cppflags) # override
     files = [base_config_c, base_frozen_c] + \
             files + supp_sources +  addfiles + libs + \
             ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
 
     outfp = bkfile.open(makefile, 'w')
     try:
         makemakefile.makemakefile(outfp, somevars, files, base_target)
     finally:
         outfp.close()
 
     # Done!
 
     if odir:
         print('Now run "make" in', odir, end=' ')
         print('to build the target:', base_target)
     else:
         print('Now run "make" to build the target:', base_target)
 
 
 # Print usage message and exit
 
 def usage(msg):
     sys.stdout = sys.stderr
     print("Error:", msg)
     print("Use ``%s -h'' for help" % sys.argv[0])
     sys.exit(2)
 
 
 main()
diff --git a/ext/python/Tools/freeze/winmakemakefile.py b/ext/python/Tools/freeze/winmakemakefile.py
index e473fed..7d198c1 100644
--- a/ext/python/Tools/freeze/winmakemakefile.py
+++ b/ext/python/Tools/freeze/winmakemakefile.py
@@ -1,146 +1,146 @@
 import sys, os
 
 # Template used then the program is a GUI program
 WINMAINTEMPLATE = """
 #include <windows.h>
 
 int WINAPI WinMain(
     HINSTANCE hInstance,      // handle to current instance
     HINSTANCE hPrevInstance,  // handle to previous instance
     LPSTR lpCmdLine,          // pointer to command line
     int nCmdShow              // show state of window
     )
 {
     extern int Py_FrozenMain(int, char **);
     PyImport_FrozenModules = _PyImport_FrozenModules;
     return Py_FrozenMain(__argc, __argv);
 }
 """
 
 SERVICETEMPLATE = """
 extern int PythonService_main(int, char **);
 
 int main( int argc, char **argv)
 {
     PyImport_FrozenModules = _PyImport_FrozenModules;
     return PythonService_main(argc, argv);
 }
 """
 
 subsystem_details = {
     # -s flag        : (C entry point template), (is it __main__?), (is it a DLL?)
     'console'        : (None,                    1,                 0),
     'windows'        : (WINMAINTEMPLATE,         1,                 0),
     'service'        : (SERVICETEMPLATE,         0,                 0),
     'com_dll'        : ("",                      0,                 1),
 }
 
 def get_custom_entry_point(subsystem):
     try:
         return subsystem_details[subsystem][:2]
     except KeyError:
-        raise ValueError, "The subsystem %s is not known" % subsystem
+        raise ValueError("The subsystem %s is not known" % subsystem)
 
 
 def makemakefile(outfp, vars, files, target):
     save = sys.stdout
     try:
         sys.stdout = outfp
         realwork(vars, files, target)
     finally:
         sys.stdout = save
 
 def realwork(vars, moddefns, target):
     version_suffix = "%r%r" % sys.version_info[:2]
     print("# Makefile for Microsoft Visual C++ generated by freeze.py script")
     print()
     print('target = %s' % target)
     print('pythonhome = %s' % vars['prefix'])
     print()
     print('DEBUG=0 # Set to 1 to use the _d versions of Python.')
     print('!IF $(DEBUG)')
     print('debug_suffix=_d')
     print('c_debug=/Zi /Od /DDEBUG /D_DEBUG')
     print('l_debug=/DEBUG')
     print('temp_dir=Build\\Debug')
     print('!ELSE')
     print('debug_suffix=')
     print('c_debug=/Ox')
     print('l_debug=')
     print('temp_dir=Build\\Release')
     print('!ENDIF')
     print()
 
     print('# The following line assumes you have built Python using the standard instructions')
     print('# Otherwise fix the following line to point to the library.')
     print('pythonlib = "$(pythonhome)/pcbuild/python%s$(debug_suffix).lib"' % version_suffix)
     print()
 
     # We only ever write one "entry point" symbol - either
     # "main" or "WinMain".  Therefore, there is no need to
     # pass a subsystem switch to the linker as it works it
     # out all by itself.  However, the subsystem _does_ determine
     # the file extension and additional linker flags.
     target_link_flags = ""
     target_ext = ".exe"
     if subsystem_details[vars['subsystem']][2]:
         target_link_flags = "-dll"
         target_ext = ".dll"
 
 
     print("# As the target uses Python%s.dll, we must use this compiler option!" % version_suffix)
     print("cdl = /MD")
     print()
     print("all: $(target)$(debug_suffix)%s" % (target_ext))
     print()
 
     print('$(temp_dir):')
     print('  if not exist $(temp_dir)\. mkdir $(temp_dir)')
     print()
 
     objects = []
     libs = ["shell32.lib", "comdlg32.lib", "wsock32.lib", "user32.lib", "oleaut32.lib"]
     for moddefn in moddefns:
         print("# Module", moddefn.name)
         for file in moddefn.sourceFiles:
             base = os.path.basename(file)
             base, ext = os.path.splitext(base)
             objects.append(base + ".obj")
             print('$(temp_dir)\%s.obj: "%s"' % (base, file))
             print("\t@$(CC) -c -nologo /Fo$* $(cdl) $(c_debug) /D BUILD_FREEZE", end=' ')
             print('"-I$(pythonhome)/Include"  "-I$(pythonhome)/PC" \\')
             print("\t\t$(cflags) $(cdebug) $(cinclude) \\")
             extra = moddefn.GetCompilerOptions()
             if extra:
                 print("\t\t%s \\" % (' '.join(extra),))
             print('\t\t"%s"' % file)
             print()
 
         # Add .lib files this module needs
         for modlib in moddefn.GetLinkerLibs():
             if modlib not in libs:
                 libs.append(modlib)
 
     print("ADDN_LINK_FILES=", end=' ')
     for addn in vars['addn_link']: print('"%s"' % (addn), end=' ')
     print() ; print()
 
     print("OBJS=", end=' ')
     for obj in objects: print('"$(temp_dir)\%s"' % (obj), end=' ')
     print() ; print()
 
     print("LIBS=", end=' ')
     for lib in libs: print('"%s"' % (lib), end=' ')
     print() ; print()
 
     print("$(target)$(debug_suffix)%s: $(temp_dir) $(OBJS)" % (target_ext))
     print("\tlink -out:$(target)$(debug_suffix)%s %s" % (target_ext, target_link_flags), end=' ')
     print("\t$(OBJS) \\")
     print("\t$(LIBS) \\")
     print("\t$(ADDN_LINK_FILES) \\")
     print("\t$(pythonlib) $(lcustom) $(l_debug)\\")
     print("\t$(resources)")
     print()
     print("clean:")
     print("\t-rm -f *.obj")
     print("\t-rm -f $(target).exe")
diff --git a/kolab-cli.py b/kolab-cli.py
index d97d715..37ea08e 100755
--- a/kolab-cli.py
+++ b/kolab-cli.py
@@ -1,42 +1,42 @@
 #!/usr/bin/python
 #
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import logging
 import os
 import sys
 
 # For development purposes
 sys.path = [ '.' ] + sys.path
 
 from pykolab.translate import _
 from pykolab.cli import Cli
 
 try:
     import pykolab.logger
-except ImportError, e:
+except ImportError as e:
     print(_("Cannot load pykolab/logger.py:"), file=sys.stderr)
     print("%s" % e, file=sys.stderr)
     sys.exit(1)
 
 if __name__ == "__main__":
     kolab = Cli()
     kolab.run()
diff --git a/pykolab/cli/cmd_add_domain.py b/pykolab/cli/cmd_add_domain.py
index a95373e..fecd82a 100644
--- a/pykolab/cli/cmd_add_domain.py
+++ b/pykolab/cli/cmd_add_domain.py
@@ -1,69 +1,69 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab import utils
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('add_domain', execute, description=description())
 
 def cli_options():
     my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
     my_option_group.add_option(
             '--alias',
             dest    = "domains",
             action  = "append",
             default = [],
             help    = _("Add alias domain."),
             metavar = "DOMAIN",
         )
 
 def description():
     return _("Add a new domain.")
 
 def execute(*args, **kw):
     from pykolab import wap_client
 
     # Use uber-administrative privileges
     username = conf.get('ldap', 'bind_dn')
     if username == None:
         log.error(_("Could not find credentials with sufficient permissions" + \
                 "to add a domain name space."))
 
         sys.exit(1)
 
     wap_client.authenticate(username=username)
 
     dna = conf.get('ldap', 'domain_name_attribute')
 
     try:
         domain = conf.cli_args.pop(0)
-    except IndexError, errmsg:
+    except IndexError:
         domain = utils.ask_question(_("Domain name"))
 
     wap_client.domain_add(domain, conf.domains)
diff --git a/pykolab/cli/cmd_add_user_subscription.py b/pykolab/cli/cmd_add_user_subscription.py
index eb0ec40..06ac0e2 100644
--- a/pykolab/cli/cmd_add_user_subscription.py
+++ b/pykolab/cli/cmd_add_user_subscription.py
@@ -1,87 +1,87 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 from pykolab import utils
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register(
         'add_user_subscription',
         execute,
         aliases=['aus', 'subscribe'],
         description=description()
     )
 
 def description():
     return _("Subscribe a user to a folder.")
 
 def execute(*args, **kw):
     folder_pattern = "*"
 
     try:
         user = conf.cli_args.pop(0)
         try:
             folder_pattern = conf.cli_args.pop(0)
-        except IndexError, errmsg:
+        except IndexError:
             folder_pattern = utils.ask_question(_("Folder pattern"))
 
-    except IndexError, errmsg:
+    except IndexError:
         user = utils.ask_question(_("User ID"))
         folder_pattern = utils.ask_question(_("Folder pattern"))
 
     if len(user.split('@')) > 1:
         domain = user.split('@')[1]
     else:
         domain = conf.get('kolab', 'primary_domain')
 
     imap = IMAP()
     imap.connect(domain=domain, login=False)
 
     backend = conf.get(domain, 'imap_backend')
     if backend == None:
         backend = conf.get('kolab', 'imap_backend')
 
     admin_login = conf.get(backend, 'admin_login')
     admin_password = conf.get(backend, 'admin_password')
 
     imap.login_plain(admin_login, admin_password, user)
 
     if not imap.has_folder(folder_pattern):
         print(_("Cannot subscribe user to folder %r:") % (folder_pattern), \
               _("No such folder"), \
               file=sys.stderr)
         sys.exit(1)
 
     _folders = imap.lm(folder_pattern)
 
     for _folder in _folders:
         imap.subscribe(_folder)
 
diff --git a/pykolab/cli/cmd_create_mailbox.py b/pykolab/cli/cmd_create_mailbox.py
index 385cbd1..9e91800 100644
--- a/pykolab/cli/cmd_create_mailbox.py
+++ b/pykolab/cli/cmd_create_mailbox.py
@@ -1,78 +1,78 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('create_mailbox', execute, description=description(), aliases='cm')
 
 def cli_options():
     my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
     my_option_group.add_option(
             '--metadata',
             dest    = "metadata",
             action  = "store",
             default = None,
             help    = _("Set metadata for folder to ANNOTATION=VALUE")
         )
 
     my_option_group.add_option(
             '--partition',
             dest    = "partition",
             action  = "store",
             default = None,
             help    = _("Create folder on PARTITION.")
         )
 
 def description():
     return """Create a mailbox or sub-folder of an existing mailbox."""
 
 def execute(*args, **kw):
     try:
         mailbox = conf.cli_args.pop(0)
-    except IndexError, errmsg:
+    except IndexError:
         log.error(_("Invalid argument"))
         sys.exit(1)
 
     if not conf.metadata == None:
         if len(conf.metadata.split('=')) == 2:
             annotation = conf.metadata.split('=')[0]
             annotation_value = conf.metadata.split('=')[1]
         else:
             log.error(_("Invalid argument for metadata"))
             sys.exit(1)
 
     imap = IMAP()
     imap.connect()
 
     imap.create_folder(mailbox, partition=conf.partition)
 
     if not conf.metadata == None:
         imap.set_metadata(mailbox, conf.metadata.split('=')[0], conf.metadata.split('=')[1])
 
diff --git a/pykolab/cli/cmd_delete_domain.py b/pykolab/cli/cmd_delete_domain.py
index 94418c1..60ca55b 100644
--- a/pykolab/cli/cmd_delete_domain.py
+++ b/pykolab/cli/cmd_delete_domain.py
@@ -1,74 +1,74 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab import utils
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('delete_domain', execute, description=description())
 
 def description():
     return _("Delete a domain.")
 
 def cli_options():
     my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
 
     my_option_group.add_option(
             '--force',
             dest    = "force",
             action  = "store_true",
             default = False,
             help    = _("Force deleting the domain even if it contains user accounts")
         )
 
 def execute(*args, **kw):
     from pykolab import wap_client
 
     # Use uber-administrative privileges
     username = conf.get('ldap', 'bind_dn')
     if username == None:
         log.error(_("Could not find credentials with sufficient permissions" + \
                 "to add a domain name space."))
 
         sys.exit(1)
 
     wap_client.authenticate(username=username)
 
     dna = conf.get('ldap', 'domain_name_attribute')
 
     try:
         domain = conf.cli_args.pop(0)
-    except IndexError, errmsg:
+    except IndexError:
         domain = utils.ask_question(_("Domain name"))
 
     if wap_client.domain_delete(domain, conf.force):
         print("Domain %s has been marked as deleted." % domain)
         print("Please run this command to actually delete the domain: ")
         print("   php /usr/share/kolab-webadmin/bin/domain_delete.php")
     else:
         print("Domain %s has not been deleted." % domain)
diff --git a/pykolab/cli/cmd_delete_mailbox.py b/pykolab/cli/cmd_delete_mailbox.py
index f6ff254..40f8b44 100644
--- a/pykolab/cli/cmd_delete_mailbox.py
+++ b/pykolab/cli/cmd_delete_mailbox.py
@@ -1,72 +1,72 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('delete_mailbox', execute, description=description(), aliases=['dm'])
 
 def description():
     return """Delete a mailbox or sub-folder. Note that the mailbox or folder is removed recursively."""
 
 def execute(*args, **kw):
     """
         Delete mailbox
     """
 
     if len(conf.cli_args) < 1:
         print(_("No mailbox specified"), file=sys.stderr)
         sys.exit(1)
 
     imap = IMAP()
 
     imap.connect()
 
     delete_folders = []
     while len(conf.cli_args) > 0:
         folder = conf.cli_args.pop(0)
         folders = imap.list_folders(folder)
 
         if len(folders) < 1:
             print(_("No such folder(s): %s") % (folder), file=sys.stderr)
 
         delete_folders.extend(folders)
 
     if len(delete_folders) == 0:
         print(_("No folders to delete."), file=sys.stderr)
         sys.exit(1)
 
     for delete_folder in delete_folders:
         try:
             imap.delete_mailfolder(delete_folder)
-        except Exception, errmsg:
+        except Exception:
             log.error(_("Could not delete mailbox '%s'") % (delete_folder))
 
diff --git a/pykolab/cli/cmd_delete_mailbox_acl.py b/pykolab/cli/cmd_delete_mailbox_acl.py
index ebfc2ec..d146aac 100644
--- a/pykolab/cli/cmd_delete_mailbox_acl.py
+++ b/pykolab/cli/cmd_delete_mailbox_acl.py
@@ -1,71 +1,71 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 from pykolab import utils
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('delete_mailbox_acl', execute, description=description(), aliases=['dam'])
 
 def description():
     return """Delete an ACL entry for a folder."""
 
 def execute(*args, **kw):
     try:
         folder = conf.cli_args.pop(0)
         try:
             identifier = conf.cli_args.pop(0)
-        except IndexError, errmsg:
+        except IndexError:
             identifier = utils.ask_question(_("ACI Subject"))
 
-    except IndexError, errmsg:
+    except IndexError:
         folder = utils.ask_question(_("Folder name"))
         quota = utils.ask_question(_("ACI Subject"))
 
     if len(folder.split('@')) > 1:
         domain = folder.split('@')[1]
     else:
         domain = conf.get('kolab', 'primary_domain')
 
     imap = IMAP()
     imap.connect(domain=domain)
 
     if not imap.has_folder(folder):
         print(_("No such folder %r") % (folder), file=sys.stderr)
 
     else:
         folders = imap.list_folders(folder)
         for folder in folders:
             try:
                 imap.set_acl(folder, identifier, '')
             except:
                 # Mailbox no longer exists?
                 pass
diff --git a/pykolab/cli/cmd_find_domain.py b/pykolab/cli/cmd_find_domain.py
index a573634..f65af3f 100644
--- a/pykolab/cli/cmd_find_domain.py
+++ b/pykolab/cli/cmd_find_domain.py
@@ -1,58 +1,58 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab import utils
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('find_domain', execute, description=description())
 
 def description():
     return _("Find a domain.")
 
 def execute(*args, **kw):
     from pykolab import wap_client
 
     # Use uber-administrative privileges
     username = conf.get('ldap', 'bind_dn')
     if username == None:
         log.error(_("Could not find credentials with sufficient permissions" + \
                 "to add a domain name space."))
 
         sys.exit(1)
 
     wap_client.authenticate(username=username)
 
     dna = conf.get('ldap', 'domain_name_attribute')
 
     try:
         domain = conf.cli_args.pop(0)
-    except IndexError, errmsg:
+    except IndexError:
         domain = utils.ask_question(_("Domain name"))
 
     wap_client.domain_find(domain)
diff --git a/pykolab/cli/cmd_list_mailbox_acls.py b/pykolab/cli/cmd_list_mailbox_acls.py
index bf93429..8f14ee6 100644
--- a/pykolab/cli/cmd_list_mailbox_acls.py
+++ b/pykolab/cli/cmd_list_mailbox_acls.py
@@ -1,67 +1,67 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 from pykolab import utils
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('list_mailbox_acls', execute, description=description(), aliases=['lam'])
 
 def description():
     return """Obtain a list of ACL entries on a folder."""
 
 def execute(*args, **kw):
     try:
         folder = conf.cli_args.pop(0)
-    except IndexError, errmsg:
+    except IndexError:
         folder = utils.ask_question(_("Folder name"))
 
     if len(folder.split('@')) > 1:
         domain = folder.split('@')[1]
     else:
         domain = conf.get('kolab', 'primary_domain')
 
     imap = IMAP()
     imap.connect(domain=domain)
 
     if not imap.has_folder(folder):
         print(_("No such folder %r") % (folder), file=sys.stderr)
 
     else:
         acls = []
         folders = imap.list_folders(folder)
         for folder in folders:
             print("Folder", folder)
             acls = imap.list_acls(folder)
 
             for acl in acls.keys():
                 print("  %-13s %s" %(acls[acl], acl))
 
diff --git a/pykolab/cli/cmd_list_mailbox_metadata.py b/pykolab/cli/cmd_list_mailbox_metadata.py
index 718c376..7121723 100644
--- a/pykolab/cli/cmd_list_mailbox_metadata.py
+++ b/pykolab/cli/cmd_list_mailbox_metadata.py
@@ -1,97 +1,97 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 from pykolab import utils
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('list_mailbox_metadata', execute, aliases='lmm', description=description())
 
 def cli_options():
     my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
     my_option_group.add_option(
                 '--user',
                 dest    = "user",
                 action  = "store",
                 default = None,
                 metavar = "USER",
                 help    = _("List annotations as user USER")
             )
 
 def description():
     return """Obtain a list of metadata entries on a folder."""
 
 def execute(*args, **kw):
     try:
         folder = conf.cli_args.pop(0)
-    except IndexError, errmsg:
+    except IndexError:
         folder = utils.ask_question(_("Folder name"))
 
     if len(folder.split('@')) > 1:
         domain = folder.split('@')[1]
     elif not conf.user == None and len(conf.user.split('@')) > 1:
         domain = conf.user.split('@')[1]
     else:
         domain = conf.get('kolab', 'primary_domain')
 
     imap = IMAP()
 
     if not conf.user == None:
         imap.connect(domain=domain, login=False)
 
         backend = conf.get(domain, 'imap_backend')
         if backend == None:
             backend = conf.get('kolab', 'imap_backend')
 
         admin_login = conf.get(backend, 'admin_login')
         admin_password = conf.get(backend, 'admin_password')
 
         imap.login_plain(admin_login, admin_password, conf.user)
     else:
         imap.connect(domain=domain)
 
     if not imap.has_folder(folder):
         print(_("No such folder %r") % (folder), file=sys.stderr)
 
     else:
         metadata = []
         folders = imap.list_folders(folder)
         for folder in folders:
             print("Folder", folder)
 
             metadata = imap.get_metadata(folder)
 
             if metadata.has_key(folder):
                 for annotation in metadata[folder].keys():
                     print("  %-49s %s" % (
                             annotation,
                             metadata[folder][annotation]
                         ))
diff --git a/pykolab/cli/cmd_list_quota.py b/pykolab/cli/cmd_list_quota.py
index 28b0cd5..d956168 100644
--- a/pykolab/cli/cmd_list_quota.py
+++ b/pykolab/cli/cmd_list_quota.py
@@ -1,104 +1,104 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('list_quota', execute, description=description(), aliases=['lq'])
 
 def cli_options():
     my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
     my_option_group.add_option( '--server',
                                 dest    = "connect_server",
                                 action  = "store",
                                 default = None,
                                 metavar = "SERVER",
                                 help    = _("List mailboxes on server SERVER only."))
 
 def description():
     return """List quota for a folder."""
 
 def execute(*args, **kw):
     """
         List quota for a mailbox
     """
 
     try:
         quota_folder = conf.cli_args.pop(0)
-    except IndexError, e:
+    except IndexError:
         quota_folder = '*'
 
     imap = IMAP()
 
     if not conf.connect_server == None:
         imap.connect(server=conf.connect_server)
     else:
         imap.connect()
 
     folders = []
 
     quota_folders = imap.list_folders(quota_folder)
     for quota_folder in quota_folders:
         try:
             (used, quota) = imap.get_quota(quota_folder)
             print("Folder: %s" % (quota_folder))
             if not used == None and not quota == None:
                 if quota == 0:
                     print(_("The quota for folder %s is set to literally allow 0KB of storage.") % (quota_folder), file=sys.stderr)
                     print("%d (Used: %d, Percentage: %s)" % (quota, used, u'\u221E'))
                 else:
                     percentage = round(((float)(used)/(float)(quota)) * 100.0, 1)
                     print("%d (Used: %d, Percentage: %d)" % (quota, used, percentage))
             else:
                 if used == None:
                     print("%d (Used: %d, Percentage: %d)" % (quota, 0, 0))
                 else:
                     print("No quota")
         except:
             try:
                 (quota_root, used, quota) = imap.get_quota_root(quota_folder)
                 print("Folder: %s" % (quota_folder))
                 if not quota_root == None and not used == None and not quota == None:
                     if quota == 0:
                         print(_("The quota for folder %s is set to literally allow 0KB of storage.") % (quota_folder), file=sys.stderr)
                         print("%d (Used: %d, Percentage: %d)" % (quota, used, u'\u221E'))
                     else:
                         percentage = round(((float)(used)/(float)(quota)) * 100.0, 1)
                         print("%d (Root: %s, Used: %d, Percentage: %d)" % (quota, quota_root, used, percentage))
                 else:
                     if used == None and not quota_root == None:
                         print("%d (Root: %s, Used: %d, Percentage: %d)" % (quota, quota_root, 0, 0))
                     else:
                         print("No quota")
             except:
                 print("Folder: %s" % (quota_folder))
                 print("No quota root")
 
diff --git a/pykolab/cli/cmd_list_user_subscriptions.py b/pykolab/cli/cmd_list_user_subscriptions.py
index 2d8e4f3..ad6e549 100644
--- a/pykolab/cli/cmd_list_user_subscriptions.py
+++ b/pykolab/cli/cmd_list_user_subscriptions.py
@@ -1,104 +1,104 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import commands
 
 import pykolab
 
 from pykolab import imap_utf7
 from pykolab.imap import IMAP
 from pykolab.translate import _
 from pykolab import utils
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('list_user_subscriptions', execute, aliases='lus', description=description())
 
 def cli_options(*args, **kw):
     my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
     my_option_group.add_option( '--raw',
                                 dest    = "raw",
                                 action  = "store_true",
                                 default = False,
                                 help    = _("Display raw IMAP UTF-7 folder names"))
 
     my_option_group.add_option( '--unsubscribed',
                                 dest    = "unsubscribed",
                                 action  = "store_true",
                                 default = False,
                                 help    = _("List unsubscribed folders"))
 
 def description():
     return _("List the folders a user is subscribed to.")
 
 def execute(*args, **kw):
     folder_pattern = "*"
 
     try:
         user = conf.cli_args.pop(0)
         try:
             folder_pattern = conf.cli_args.pop(0)
-        except IndexError, errmsg:
+        except IndexError:
             pass
 
-    except IndexError, errmsg:
+    except IndexError:
         user = utils.ask_question(_("User ID"))
 
     if len(user.split('@')) > 1:
         domain = user.split('@')[1]
     else:
         domain = conf.get('kolab', 'primary_domain')
 
     imap = IMAP()
     imap.connect(domain=domain, login=False)
 
     backend = conf.get(domain, 'imap_backend')
     if backend == None:
         backend = conf.get('kolab', 'imap_backend')
 
     admin_login = conf.get(backend, 'admin_login')
     admin_password = conf.get(backend, 'admin_password')
 
     imap.login_plain(admin_login, admin_password, user)
 
     subscribed_folders = imap.lsub(folder_pattern)
 
     if conf.unsubscribed:
         unsubscribed_folders = []
         all_folders = imap.lm(folder_pattern)
 
         for folder in all_folders:
             if not folder in subscribed_folders:
                 unsubscribed_folders.append(folder)
 
         if len(unsubscribed_folders) > 0:
             if not conf.raw:
                 print("\n".join([imap_utf7.decode(x) for x in unsubscribed_folders]))
             else:
                 print("\n".join(unsubscribed_folders))
         else:
             print(_("No unsubscribed folders for user %s") % (user))
 
     else:
         if not conf.raw:
             print("\n".join([imap_utf7.decode(x) for x in subscribed_folders]))
         else:
             print("\n".join(subscribed_folders))
diff --git a/pykolab/cli/cmd_remove_mailaddress.py b/pykolab/cli/cmd_remove_mailaddress.py
index bc94bb1..c3e5669 100644
--- a/pykolab/cli/cmd_remove_mailaddress.py
+++ b/pykolab/cli/cmd_remove_mailaddress.py
@@ -1,95 +1,95 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.auth import Auth
 from pykolab import utils
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('remove_mail', execute, description=description())
 
 def description():
     return """Remove a recipient's mail address."""
 
 def execute(*args, **kw):
     try:
         email_address = conf.cli_args.pop(0)
-    except IndexError, errmsg:
+    except IndexError:
         email_address = utils.ask_question("Email address to remove")
 
     # Get the domain from the email address
     if len(email_address.split('@')) > 1:
         domain = email_address.split('@')[1]
     else:
         log.error(_("Invalid or unqualified email address."))
         sys.exit(1)
 
     auth = Auth()
     auth.connect(domain=domain)
     recipients = auth.find_recipient(email_address)
 
     if len(recipients) == 0:
         log.error(_("No recipient found for email address %r") % (email_address))
         sys.exit(1)
 
     log.debug(_("Found the following recipient(s): %r") % (recipients), level=8)
 
     mail_attributes = conf.get_list(domain, 'mail_attributes')
     if mail_attributes == None or len(mail_attributes) < 1:
         mail_attributes = conf.get_list(conf.get('kolab', 'auth_mechanism'), 'mail_attributes')
 
     log.debug(_("Using the following mail attributes: %r") % (mail_attributes), level=8)
 
     if isinstance(recipients, basestring):
         recipient = recipients
 
         # Only a single recipient found, remove the address
         attributes = auth.get_entry_attributes(domain, recipient, mail_attributes)
 
         # See which attribute holds the value we're trying to remove
         for attribute in attributes.keys():
             if isinstance(attributes[attribute], list):
                 if email_address in attributes[attribute]:
                     attributes[attribute].pop(attributes[attribute].index(email_address))
                     replace_attributes = {
                             attribute: attributes[attribute]
                         }
 
                     auth.set_entry_attributes(domain, recipient, replace_attributes)
             else:
                 if email_address == attributes[attribute]:
                     auth.set_entry_attributes(domain, recipient, {attribute: None})
         pass
 
     else:
         print(_("Found the following recipients:"), file=sys.stderr)
 
         for recipient in recipients:
             print(recipient)
diff --git a/pykolab/cli/cmd_remove_user_subscription.py b/pykolab/cli/cmd_remove_user_subscription.py
index 343ee68..b16249c 100644
--- a/pykolab/cli/cmd_remove_user_subscription.py
+++ b/pykolab/cli/cmd_remove_user_subscription.py
@@ -1,103 +1,103 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 from pykolab import utils
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register(
         'remove_user_subscription',
         execute,
         aliases=['rus', 'unsubscribe'],
         description=description()
     )
 
 def description():
     return _("Unsubscribe a user from a folder.")
 
 def execute(*args, **kw):
     folder_pattern = "*"
 
     try:
         user = conf.cli_args.pop(0)
         try:
             folder_pattern = conf.cli_args.pop(0)
-        except IndexError, errmsg:
+        except IndexError:
             folder_pattern = utils.ask_question(_("Folder pattern"))
 
-    except IndexError, errmsg:
+    except IndexError:
         user = utils.ask_question(_("User ID"))
         folder_pattern = utils.ask_question(_("Folder pattern"))
 
     if len(user.split('@')) > 1:
         domain = user.split('@')[1]
     else:
         domain = conf.get('kolab', 'primary_domain')
 
     imap = IMAP()
     imap.connect(domain=domain, login=False)
 
     backend = conf.get(domain, 'imap_backend')
     if backend == None:
         backend = conf.get('kolab', 'imap_backend')
 
     admin_login = conf.get(backend, 'admin_login')
     admin_password = conf.get(backend, 'admin_password')
 
     imap.login_plain(admin_login, admin_password, user)
 
     if not imap.has_folder(folder_pattern):
         print(_("Cannot subscribe user to folder %r:") % (folder_pattern), \
               _("No such folder"), \
               file=sys.stderr)
         sys.exit(1)
 
     _folders = imap.lm(folder_pattern)
     _subscribed_folders = imap.lsub()
     unsubscribed_folders = []
 
     for _folder in _folders:
         if _folder in _subscribed_folders:
             imap.unsubscribe(_folder)
             unsubscribed_folders.append(_folder)
 
     if len(unsubscribed_folders) > 0:
         print(_("Successfully unsubscribed user %s from the following folders:") % (
                 user
             ))
 
         print("\n".join(unsubscribed_folders))
     else:
         print(_("User %s was not unsubscribed from any folders.") % (
                 user
             ), file=sys.stderr)
 
         sys.exit(1)
diff --git a/pykolab/cli/cmd_rename_mailbox.py b/pykolab/cli/cmd_rename_mailbox.py
index c693211..4373acf 100644
--- a/pykolab/cli/cmd_rename_mailbox.py
+++ b/pykolab/cli/cmd_rename_mailbox.py
@@ -1,76 +1,76 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('rename_mailbox', execute, description=description(), aliases=['rm'])
 
 def description():
     return """Rename a mailbox or sub-folder."""
 
 def execute(*args, **kw):
     """
         Rename mailbox
     """
 
     try:
         source_folder = conf.cli_args.pop(0)
         try:
             target_folder = conf.cli_args.pop(0)
             try:
                 partition = conf.cli_args.pop(0)
-            except IndexError, errmsg:
+            except IndexError:
                 partition = None
-        except IndexError, errmsg:
+        except IndexError:
             print(_("No target mailbox name specified"), file=sys.stderr)
-    except IndexError, errmsg:
+    except IndexError:
         print(_("No source mailbox name specified"), file=sys.stderr)
         sys.exit(1)
 
     if len(source_folder.split('@')) > 1:
         domain = source_folder.split('@')[1]
     else:
         domain = conf.get('kolab', 'primary_domain')
 
     imap = IMAP()
     imap.connect(domain=domain)
 
     if not imap.has_folder(source_folder):
         print(_("Source folder %r does not exist") % (source_folder), file=sys.stderr)
         sys.exit(1)
 
     if imap.has_folder(target_folder) and partition == None:
         print(_("Target folder %r already exists") % (target_folder), file=sys.stderr)
         sys.exit(1)
 
     imap.imap.rename(imap.folder_utf7(source_folder), imap.folder_utf7(target_folder), partition)
 
diff --git a/pykolab/cli/cmd_set_mailbox_acl.py b/pykolab/cli/cmd_set_mailbox_acl.py
index 249c1ee..63b5c2d 100644
--- a/pykolab/cli/cmd_set_mailbox_acl.py
+++ b/pykolab/cli/cmd_set_mailbox_acl.py
@@ -1,74 +1,74 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 from pykolab import utils
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('set_mailbox_acl', execute, description=description(), aliases=['sam'])
 
 def description():
     return """Set an ACL for a identifier on a folder."""
 
 def execute(*args, **kw):
     try:
         folder = conf.cli_args.pop(0)
         try:
             identifier = conf.cli_args.pop(0)
             try:
                 acl = conf.cli_args.pop(0)
-            except IndexError, errmsg:
+            except IndexError:
                 acl = utils.ask_question(_("ACI Permissions"))
 
-        except IndexError, errmsg:
+        except IndexError:
             identifier = utils.ask_question(_("ACI Subject"))
             acl = utils.ask_question(_("ACI Permissions"))
 
-    except IndexError, errmsg:
+    except IndexError:
         folder = utils.ask_question(_("Folder name"))
         identifier = utils.ask_question(_("ACI Subject"))
         acl = utils.ask_question(_("ACI Permissions"))
 
     if len(folder.split('@')) > 1:
         domain = folder.split('@')[1]
     else:
         domain = conf.get('kolab', 'primary_domain')
 
     imap = IMAP()
     imap.connect(domain=domain)
 
     if not imap.has_folder(folder):
         print(_("No such folder %r") % (folder), file=sys.stderr)
 
     else:
         folders = imap.list_folders(folder)
         for folder in folders:
             imap.set_acl(folder, identifier, acl)
diff --git a/pykolab/cli/cmd_set_mailbox_metadata.py b/pykolab/cli/cmd_set_mailbox_metadata.py
index d63e6a6..91aec50 100644
--- a/pykolab/cli/cmd_set_mailbox_metadata.py
+++ b/pykolab/cli/cmd_set_mailbox_metadata.py
@@ -1,101 +1,101 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 from pykolab import imap_utf7
 from pykolab import utils
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('set_mailbox_metadata', execute, description=description())
 
 def cli_options():
     my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
     my_option_group.add_option(
                 '--user',
                 dest    = "user",
                 action  = "store",
                 default = None,
                 metavar = "USER",
                 help    = _("Set annotation as user USER")
             )
 
 def description():
     return """Set an metadata entry on a folder."""
 
 def execute(*args, **kw):
     try:
         folder = conf.cli_args.pop(0)
         try:
             metadata_path = conf.cli_args.pop(0)
             try:
                 metadata_value = conf.cli_args.pop(0)
-            except IndexError, errmsg:
+            except IndexError:
                 metadata_value = utils.ask_question(_("Metadata value"))
 
-        except IndexError, errmsg:
+        except IndexError:
             metadata_path = utils.ask_question(_("Metadata path"))
             metadata_value = utils.ask_question(_("Metadata value"))
 
-    except IndexError, errmsg:
+    except IndexError:
         folder = utils.ask_question(_("Folder name"))
         metadata_path = utils.ask_question(_("Metadata path"))
         metadata_value = utils.ask_question(_("Metadata value"))
 
     if len(folder.split('@')) > 1:
         domain = folder.split('@')[1]
     elif not conf.user == None and len(conf.user.split('@')) > 1:
         domain = conf.user.split('@')[1]
     else:
         domain = conf.get('kolab', 'primary_domain')
 
     imap = IMAP()
 
     if not conf.user == None:
         imap.connect(domain=domain, login=False)
 
         backend = conf.get(domain, 'imap_backend')
         if backend == None:
             backend = conf.get('kolab', 'imap_backend')
 
         admin_login = conf.get(backend, 'admin_login')
         admin_password = conf.get(backend, 'admin_password')
 
         imap.login_plain(admin_login, admin_password, conf.user)
     else:
         imap.connect(domain=domain)
 
     if not imap.has_folder(folder):
         print(_("No such folder %r") % (folder), file=sys.stderr)
 
     else:
         folders = imap.lm(imap_utf7.encode(folder))
         for folder in folders:
             imap.set_metadata(imap_utf7.decode(folder), metadata_path, metadata_value)
diff --git a/pykolab/cli/cmd_set_quota.py b/pykolab/cli/cmd_set_quota.py
index 5b7b249..b6c89c1 100644
--- a/pykolab/cli/cmd_set_quota.py
+++ b/pykolab/cli/cmd_set_quota.py
@@ -1,68 +1,68 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2012 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation; version 3 or, at your option, any later version
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU Library General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 from pykolab import utils
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('set_quota', execute, description=description(), aliases=['sq'])
 
 def description():
     return """Configure quota for a folder."""
 
 def execute(*args, **kw):
     try:
         folder = conf.cli_args.pop(0)
         try:
             quota = conf.cli_args.pop(0)
-        except IndexError, errmsg:
+        except IndexError:
             quota = utils.ask_question(_("New quota"))
 
-    except IndexError, errmsg:
+    except IndexError:
         folder = utils.ask_question(_("Folder name"))
         quota = utils.ask_question(_("New quota"))
 
     if len(folder.split('@')) > 1:
         domain = folder.split('@')[1]
     else:
         domain = conf.get('kolab', 'primary_domain')
 
     imap = IMAP()
     imap.connect(domain=domain)
 
     if not imap.has_folder(folder):
         print(_("No such folder %r") % (folder), file=sys.stderr)
         sys.exit(1)
 
     for _folder in imap.lm(imap.folder_utf7(folder)):
         imap.set_quota(_folder, quota)
         print("Quota for folder '%s' set to %d" % (_folder, int(quota)), file=sys.stdout)
 
diff --git a/pykolab/cli/cmd_sync_mailhost_attrs.py b/pykolab/cli/cmd_sync_mailhost_attrs.py
index 512265b..7b70a77 100644
--- a/pykolab/cli/cmd_sync_mailhost_attrs.py
+++ b/pykolab/cli/cmd_sync_mailhost_attrs.py
@@ -1,196 +1,196 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import commands
 
 import pykolab
 
 from pykolab import imap_utf7
 from pykolab.auth import Auth
 from pykolab.imap import IMAP
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('sync_mailhost_attrs', execute, description=description())
 
 def description():
     return "Synchronize mailHost attribute values with the actual mailserver in a Cyrus IMAP Murder.\n"
 
 def cli_options():
     my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
     my_option_group.add_option( '--delete',
                                 dest    = "delete",
                                 action  = "store_true",
                                 default = False,
                                 help    = _("Delete mailboxes for recipients that do not appear to exist in LDAP."))
 
     my_option_group.add_option( '--dry-run',
                                 dest    = "dry_run",
                                 action  = "store_true",
                                 default = False,
                                 help    = _("Display changes, do not apply them."))
 
     my_option_group.add_option( '--server',
                                 dest    = "connect_server",
                                 action  = "store",
                                 default = None,
                                 metavar = "SERVER",
                                 help    = _("List mailboxes on server SERVER only."))
 
 def execute(*args, **kw):
     """
         Synchronize or display changes
     """
 
     imap = IMAP()
 
     if not conf.connect_server == None:
         imap.connect(server=conf.connect_server)
     else:
         imap.connect()
 
     auth = Auth()
     auth.connect()
 
     result_attribute = conf.get('cyrus-sasl', 'result_attribute')
     if result_attribute is None:
         result_attribute = 'mail'
 
     domains = auth.list_domains()
     folders = imap.lm()
 
     imap_domains_not_domains = []
 
     for folder in folders:
         if len(folder.split('@')) > 1 and not folder.startswith('DELETED'):
             _folder_domain = folder.split('@')[-1]
             if not _folder_domain in list(set(domains.keys() + domains.values())):
                 imap_domains_not_domains.append(folder.split('@')[-1])
 
     imap_domains_not_domains = list(set(imap_domains_not_domains))
 
     log.debug(_("Domains in IMAP not in LDAP: %r") % (imap_domains_not_domains), level=8)
 
     if len(imap_domains_not_domains) > 0:
         for domain in imap_domains_not_domains:
             folders = []
 
             folders.extend(imap.lm('shared/%%@%s' % (domain)))
             folders.extend(imap.lm('user/%%@%s' % (domain)))
 
             for folder in folders:
                 r_folder = folder
                 if not folder.startswith('shared/'):
                     r_folder = '/'.join(folder.split('/')[1:])
 
                 if conf.delete:
                     if conf.dry_run:
                         if not folder.startswith('shared/'):
                             log.warning(_("No recipients for '%s' (would have deleted the mailbox if not for --dry-run)!") % (r_folder))
                         else:
                             continue
                     else:
                         if not folder.startswith('shared/'):
                             log.info(_("Deleting mailbox '%s' because it has no recipients") % (folder))
                             try:
                                 imap.dm(folder)
-                            except Exception, errmsg:
+                            except Exception as errmsg:
                                 log.error(_("An error occurred removing mailbox %r: %r") % (folder, errmsg))
                         else:
                             log.info(_("Not automatically deleting shared folder '%s'") % (folder))
                 else:
                     log.warning(_("No recipients for '%s' (use --delete to delete)!") % (r_folder))
 
     for primary in list(set(domains.values())):
         secondaries = [x for x in domains.keys() if domains[x] == primary]
 
         folders = []
 
         folders.extend(imap.lm('shared/%%@%s' % (primary)))
         folders.extend(imap.lm('user/%%@%s' % (primary)))
 
         for secondary in secondaries:
             folders.extend(imap.lm('shared/%%@%s' % (secondary)))
             folders.extend(imap.lm('user/%%@%s' % (secondary)))
 
         auth = Auth(domain=primary)
         auth.connect()
 
         for folder in folders:
             server = imap.user_mailbox_server(folder)
             r_folder = folder
 
             if folder.startswith('shared/'):
                 recipient = auth.find_folder_resource(folder)
             else:
                 r_folder = '/'.join(folder.split('/')[1:])
                 recipient = auth.find_recipient(r_folder, search_attrs=[result_attribute])
 
             if (isinstance(recipient, list)):
                 if len(recipient) > 1:
                     log.warning(_("Multiple recipients for '%s'!") % (r_folder))
                     continue
                 elif len(recipient) == 0:
                     if conf.delete:
                         if conf.dry_run:
                             if not folder.startswith('shared/'):
                                 log.warning(_("No recipients for '%s' (would have deleted the mailbox if not for --dry-run)!") % (r_folder))
                             else:
                                 continue
                         else:
                             if not folder.startswith('shared/'):
                                 log.info(_("Deleting mailbox '%s' because it has no recipients") % (folder))
                                 try:
                                     imap.dm(folder)
-                                except Exception, errmsg:
+                                except Exception as errmsg:
                                     log.error(_("An error occurred removing mailbox %r: %r") % (folder, errmsg))
                             else:
                                 log.info(_("Not automatically deleting shared folder '%s'") % (folder))
                     else:
                         log.warning(_("No recipients for '%s' (use --delete to delete)!") % (r_folder))
 
                     continue
             else:
                 mailhost = auth.get_entry_attribute(primary, recipient, 'mailhost')
 
             if not server == mailhost:
                 if conf.dry_run:
                     print(folder, server, mailhost)
                 else:
                     auth.set_entry_attribute(primary, recipient, 'mailhost', server)
 
     folders = []
     folders.extend(imap.lm("shared/%%"))
     folders.extend(imap.lm("user/%%"))
 
     auth = Auth()
     auth.connect()
 
     for folder in folders:
         server = imap.user_mailbox_server(folder)
 
         if folder.startswith('shared/'):
             recipient = auth.find_folder_resource(folder)
         else:
             recipient = auth.find_recipient('/'.join(folder.split('/')[1:]), search_attrs=[result_attribute])
 
         print(folder, server, recipient)
diff --git a/pykolab/cli/cmd_user_info.py b/pykolab/cli/cmd_user_info.py
index a7879d1..e8a5766 100644
--- a/pykolab/cli/cmd_user_info.py
+++ b/pykolab/cli/cmd_user_info.py
@@ -1,62 +1,62 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2012 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 import commands
 
 import pykolab
 
 from pykolab import utils
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('user_info', execute, description="Display user information.")
 
 def execute(*args, **kw):
     from pykolab import wap_client
 
     try:
         user = conf.cli_args.pop(0)
-    except IndexError, errmsg:
+    except IndexError:
         user = utils.ask_question(_("Email address"))
 
     result = wap_client.authenticate(username=conf.get("ldap", "bind_dn"), password=conf.get("ldap", "bind_pw"))
 
     if len(user.split('@')) > 1:
         wap_client.system_select_domain(user.split('@')[1])
 
     user_info = wap_client.user_find({'mail':user})
 
     if user_info == None or not user_info:
         print(_("No such user %s") % (user), file=sys.stderr)
         sys.exit(0)
 
     unic_attrs = ['displayname', 'givenname', 'cn', 'sn', 'ou', 'entrydn']
 
     for (k,v) in user_info.iteritems():
         if k in unic_attrs:
             print("%s: %s" % (k,v))
         else:
             print("%s: %r" % (k,v))
diff --git a/pykolab/cli/commands.py b/pykolab/cli/commands.py
index 2f05bb6..196fa93 100644
--- a/pykolab/cli/commands.py
+++ b/pykolab/cli/commands.py
@@ -1,201 +1,201 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import os
 import sys
 
 import pykolab
 
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 commands = {}
 command_groups = {}
 
 def __init__():
     # We only want the base path
     commands_base_path = os.path.dirname(__file__)
 
     for commands_path, dirnames, filenames in os.walk(commands_base_path):
         if not commands_path == commands_base_path:
             continue
 
         for filename in filenames:
             if filename.startswith('cmd_') and filename.endswith('.py'):
                 module_name = filename.replace('.py','')
                 cmd_name = module_name.replace('cmd_', '')
                 #print "exec(\"from %s import __init__ as %s_register\"" % (module_name,cmd_name)
                 try:
                     exec("from %s import __init__ as %s_register" % (module_name,cmd_name))
-                except ImportError, errmsg:
+                except ImportError:
                     pass
 
                 exec("%s_register()" % (cmd_name))
 
         for dirname in dirnames:
             register_group(commands_path, dirname)
 
     register('help', list_commands)
 
     register('delete_user', not_yet_implemented, description="Not yet implemented")
 
     register('list_groups', not_yet_implemented, description="Not yet implemented")
     register('add_group', not_yet_implemented, description="Not yet implemented")
     register('delete_group', not_yet_implemented, description="Not yet implemented")
 
 def list_commands(*args, **kw):
     """
         List commands
     """
 
     __commands = {}
 
     for command in commands.keys():
         if isinstance(command, tuple):
             command_group, command = command
             __commands[command_group] = {
                     command: commands[(command_group,command)]
                 }
         else:
             __commands[command] = commands[command]
 
     _commands = __commands.keys()
     _commands.sort()
 
     for _command in _commands:
         if __commands[_command].has_key('group'):
             continue
 
         if __commands[_command].has_key('function'):
             # This is a top-level command
             if not __commands[_command]['description'] == None:
                 print("%-25s - %s" % (_command.replace('_','-'),__commands[_command]['description']))
             else:
                 print("%-25s" % (_command.replace('_','-')))
 
     for _command in _commands:
         if not __commands[_command].has_key('function'):
             # This is a nested command
             print("\n" + _("Command Group: %s") % (_command) + "\n")
             ___commands = __commands[_command].keys()
             ___commands.sort()
             for __command in ___commands:
                 if not __commands[_command][__command]['description'] == None:
                     print("%-4s%-21s - %s" % ('',__command.replace('_','-'),__commands[_command][__command]['description']))
                 else:
                     print("%-4s%-21s" % ('',__command.replace('_','-')))
 
 def execute(cmd_name, *args, **kw):
     if cmd_name == "":
         execute("help")
         sys.exit(0)
 
     if not commands.has_key(cmd_name):
         log.error(_("No such command."))
         sys.exit(1)
 
     if not commands[cmd_name].has_key('function') and \
         not commands[cmd_name].has_key('group'):
         log.error(_("No such command."))
         sys.exit(1)
 
     if commands[cmd_name].has_key('group'):
         group = commands[cmd_name]['group']
         command_name = commands[cmd_name]['cmd_name']
         try:
             exec("from %s.cmd_%s import cli_options as %s_%s_cli_options" % (group,command_name,group,command_name))
             exec("%s_%s_cli_options()" % (group,command_name))
-        except ImportError, e:
+        except ImportError:
             pass
 
     else:
         command_name = commands[cmd_name]['cmd_name']
         try:
             exec("from cmd_%s import cli_options as %s_cli_options" % (command_name,command_name))
             exec("%s_cli_options()" % (command_name))
-        except ImportError, errmsg:
+        except ImportError:
             pass
 
     conf.finalize_conf()
     commands[cmd_name]['function'](conf.cli_args, kw)
 
 def register_group(dirname, module):
     commands_base_path = os.path.join(os.path.dirname(__file__), module)
 
     commands[module] = {}
 
     for commands_path, dirnames, filenames in os.walk(commands_base_path):
         if not commands_path == commands_base_path:
             continue
 
         for filename in filenames:
             if filename.startswith('cmd_') and filename.endswith('.py'):
                 module_name = filename.replace('.py','')
                 cmd_name = module_name.replace('cmd_', '')
                 #print "exec(\"from %s.%s import __init__ as %s_%s_register\"" % (module,module_name,module,cmd_name)
                 exec("from %s.%s import __init__ as %s_%s_register" % (module,module_name,module,cmd_name))
                 exec("%s_%s_register()" % (module,cmd_name))
 
 def register(cmd_name, func, group=None, description=None, aliases=[]):
     if not group == None:
         command = "%s_%s" % (group,cmd_name)
     else:
         command = cmd_name
 
     if isinstance(aliases, basestring):
         aliases = [aliases]
 
     if commands.has_key(command):
         log.fatal(_("Command '%s' already registered") % (command))
         sys.exit(1)
 
     if callable(func):
         if group == None:
             commands[cmd_name] = {
                     'cmd_name': cmd_name,
                     'function': func,
                     'description': description
                 }
         else:
             commands[group][cmd_name] = {
                     'cmd_name': cmd_name,
                     'function': func,
                     'description': description
                 }
 
             commands[command] = commands[group][cmd_name]
             commands[command]['group'] = group
             commands[command]['cmd_name'] = cmd_name
 
         for alias in aliases:
             commands[alias] = {
                     'cmd_name': cmd_name,
                     'function': func,
                     'description': _("Alias for %s") % (cmd_name.replace('_','-'))
                 }
 
 ##
 ## Commands not yet implemented
 ##
 
 def not_yet_implemented(*args, **kw):
     print(_("Not yet implemented"))
     sys.exit(1)
diff --git a/pykolab/conf/entitlement.py b/pykolab/conf/entitlement.py
index 5dda82c..4ed79ec 100644
--- a/pykolab/conf/entitlement.py
+++ b/pykolab/conf/entitlement.py
@@ -1,266 +1,266 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from ConfigParser import ConfigParser
 import hashlib
 import OpenSSL
 import os
 import StringIO
 import subprocess
 import sys
 
 from pykolab.translate import _
 
 import pykolab
 log = pykolab.getLogger('pykolab.conf')
 
 class Entitlement(object):
     def __init__(self, *args, **kw):
         self.entitlement = {}
 
         self.entitlement_files = []
 
         ca_cert_file = '/etc/pki/tls/certs/mirror.kolabsys.com.ca.cert'
         customer_cert_file = '/etc/pki/tls/private/mirror.kolabsys.com.client.pem'
         customer_key_file = '/etc/pki/tls/private/mirror.kolabsys.com.client.pem'
 
         # Licence lock and key verification.
         self.entitlement_verification = [
                 'f700660f456a60c92ab2f00d0f1968230920d89829d42aa27d30f678',
                 '95783ba5521ea54aa3a32b7949f145aa5015a4c9e92d12b9e4c95c14'
             ]
 
         if os.access(ca_cert_file, os.R_OK):
             # Verify /etc/kolab/mirror_ca.crt
             ca_cert = OpenSSL.crypto.load_certificate(
                     OpenSSL.SSL.FILETYPE_PEM,
                     open(ca_cert_file).read()
                 )
 
             if (bool)(ca_cert.has_expired()):
-                raise Exception, _("Invalid entitlement verification " + \
-                        "certificate at %s" % (ca_cert_file))
+                raise Exception(_("Invalid entitlement verification " + \
+                        "certificate at %s" % (ca_cert_file)))
 
             # TODO: Check validity and warn ~1-2 months in advance.
 
             ca_cert_issuer = ca_cert.get_issuer()
             ca_cert_subject = ca_cert.get_subject()
 
             ca_cert_issuer_hash = subprocess.Popen(
                     [
                             'openssl',
                             'x509',
                             '-in',
                             ca_cert_file,
                             '-noout',
                             '-issuer_hash'
                         ],
                     stdout=subprocess.PIPE
                 ).communicate()[0].strip()
 
             ca_cert_issuer_hash_digest = hashlib.sha224(ca_cert_issuer_hash).hexdigest()
 
             if not ca_cert_issuer_hash_digest in self.entitlement_verification:
-                raise Exception, _("Invalid entitlement verification " + \
-                        "certificate at %s") % (ca_cert_file)
+                raise Exception(_("Invalid entitlement verification " + \
+                        "certificate at %s") % (ca_cert_file))
 
             ca_cert_subject_hash = subprocess.Popen(
                     [
                             'openssl',
                             'x509',
                             '-in',
                             ca_cert_file,
                             '-noout',
                             '-subject_hash'
                         ],
                     stdout=subprocess.PIPE
                 ).communicate()[0].strip()
 
             ca_cert_subject_hash_digest = hashlib.sha224(ca_cert_subject_hash).hexdigest()
 
             if not ca_cert_subject_hash_digest in self.entitlement_verification:
-                raise Exception, _("Invalid entitlement verification " + \
-                        "certificate at %s") % (ca_cert_file)
+                raise Exception(_("Invalid entitlement verification " + \
+                        "certificate at %s") % (ca_cert_file))
 
             customer_cert_issuer_hash = subprocess.Popen(
                     [
                             'openssl',
                             'x509',
                             '-in',
                             customer_cert_file,
                             '-noout',
                             '-issuer_hash'
                         ],
                     stdout=subprocess.PIPE
                 ).communicate()[0].strip()
 
             customer_cert_issuer_hash_digest = hashlib.sha224(customer_cert_issuer_hash).hexdigest()
 
             if not customer_cert_issuer_hash_digest in self.entitlement_verification:
-                raise Exception, _("Invalid entitlement verification " + \
-                        "certificate at %s") % (customer_cert_file)
+                raise Exception(_("Invalid entitlement verification " + \
+                        "certificate at %s") % (customer_cert_file))
 
             if not ca_cert_issuer.countryName == ca_cert_subject.countryName:
-                raise Exception, _("Invalid entitlement certificate")
+                raise Exception(_("Invalid entitlement certificate"))
 
             if not ca_cert_issuer.organizationName == ca_cert_subject.organizationName:
-                raise Exception, _("Invalid entitlement certificate")
+                raise Exception(_("Invalid entitlement certificate"))
 
             if os.path.isdir('/etc/kolab/entitlement.d/') and \
                     os.access('/etc/kolab/entitlement.d/', os.R_OK):
 
                 for root, dirs, files in os.walk('/etc/kolab/entitlement.d/'):
                     if not root == '/etc/kolab/entitlement.d/':
                         continue
                     for entitlement_file in files:
                         log.debug(_("Parsing entitlement file %s") % (entitlement_file), level=8)
 
                         if os.access(os.path.join(root, entitlement_file), os.R_OK):
                             self.entitlement_files.append(
                                     os.path.join(root, entitlement_file)
                                 )
 
                         else:
                             log.error(
                                     _("License file %s not readable!") % (
                                             os.path.join(root, entitlement_file)
                                         )
                                 )
 
             else:
                 log.error(_("No entitlement directory found"))
 
             for entitlement_file in self.entitlement_files:
 
                 decrypt_command = [
                         'openssl',
                         'smime',
                         '-decrypt',
                         '-recip',
                         customer_cert_file,
                         '-in',
                         entitlement_file
                     ]
 
                 decrypt_process = subprocess.Popen(
                         decrypt_command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE
                     )
 
                 verify_command = [
                         'openssl',
                         'smime',
                         '-verify',
                         '-certfile',
                         ca_cert_file,
                         '-CAfile',
                         ca_cert_file,
                         '-inform',
                         'DER'
                     ]
 
                 verify_process = subprocess.Popen(
                         verify_command,
                         stdin=decrypt_process.stdout,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE
                     )
 
                 (stdout, stderr) = verify_process.communicate()
                 license = License(stdout, self.entitlement)
                 license.verify_certificate(customer_cert_file)
                 self.entitlement = license.get()
 
 #        else:
 #            log.error(_("Error reading entitlement certificate authority file"))
 
     def get(self):
         if len(self.entitlement.keys()) == 0:
             return None
         else:
             return self.entitlement
 
 class License(object):
     entitlement = {}
 
     def __init__(self, new_entitlement, existing_entitlement):
         self.parser = ConfigParser()
         fp = StringIO.StringIO(new_entitlement)
         self.parser.readfp(fp)
 
         self.entitlement['users'] = self.parser.get('kolab_entitlements', 'users')
         self.entitlement['margin'] = self.parser.get('kolab_entitlements', 'margin')
 
     def verify_certificate(self, customer_cert_file):
         # Verify the certificate section as well.
         cert_serial = self.parser.get('mirror_ca', 'serial_number')
         cert_issuer_hash = self.parser.get('mirror_ca', 'issuer_hash')
         cert_subject_hash = self.parser.get('mirror_ca', 'subject_hash')
 
         customer_cert_serial = subprocess.Popen(
                 [
                         'openssl',
                         'x509',
                         '-in',
                         customer_cert_file,
                         '-noout',
                         '-serial'
                     ],
                 stdout=subprocess.PIPE
             ).communicate()[0].strip().split('=')[1]
 
         if not customer_cert_serial == cert_serial:
-            raise Exception, _("Invalid entitlement verification " + \
-                    "certificate at %s") % (customer_cert_file)
+            raise Exception(_("Invalid entitlement verification " + \
+                    "certificate at %s") % (customer_cert_file))
 
         customer_cert_issuer_hash = subprocess.Popen(
                 [
                         'openssl',
                         'x509',
                         '-in',
                         customer_cert_file,
                         '-noout',
                         '-issuer_hash'
                     ],
                 stdout=subprocess.PIPE
             ).communicate()[0].strip()
 
         if not customer_cert_issuer_hash == cert_issuer_hash:
-            raise Exception, _("Invalid entitlement verification " + \
-                    "certificate at %s") % (customer_cert_file)
+            raise Exception(_("Invalid entitlement verification " + \
+                    "certificate at %s") % (customer_cert_file))
 
         customer_cert_subject_hash = subprocess.Popen(
                 [
                         'openssl',
                         'x509',
                         '-in',
                         customer_cert_file,
                         '-noout',
                         '-subject_hash'
                     ],
                 stdout=subprocess.PIPE
             ).communicate()[0].strip()
 
         if not customer_cert_subject_hash == cert_subject_hash:
-            raise Exception, _("Invalid entitlement verification " + \
-                    "certificate at %s") % (customer_cert_file)
+            raise Exception(_("Invalid entitlement verification " + \
+                    "certificate at %s") % (customer_cert_file))
 
     def get(self):
         return self.entitlement
diff --git a/pykolab/imap/cyrus.py b/pykolab/imap/cyrus.py
index 3f4c338..26e8f98 100644
--- a/pykolab/imap/cyrus.py
+++ b/pykolab/imap/cyrus.py
@@ -1,631 +1,631 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import cyruslib
 import sys
 import time
 
 from urlparse import urlparse
 
 import pykolab
 
 from pykolab import constants
 from pykolab.imap import IMAP
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.imap')
 conf = pykolab.getConf()
 
 
 class Cyrus(cyruslib.CYRUS):
     """
         Abstraction class for some common actions to do exclusively in Cyrus.
 
         For example, the following functions require the commands to be
         executed against the backend server if a murder is being used.
 
         - Setting quota
         - Renaming the top-level mailbox
         - Setting annotations
 
     """
 
     setquota = cyruslib.CYRUS.sq
 
     def __init__(self, uri):
         """
             Initialize this class, but do not connect yet.
         """
         port = None
 
         result = urlparse(uri)
 
         if hasattr(result, 'hostname'):
             scheme = result.scheme
             hostname = result.hostname
             port = result.port
         else:
             scheme = uri.split(':')[0]
             (hostname, port) = uri.split('/')[2].split(':')
 
         if not port:
             if scheme == 'imap':
                 port = 143
             else:
                 port = 993
 
         self.server = hostname
 
         self.uri = "%s://%s:%s" % (scheme, hostname, port)
 
         while 1:
             try:
                 cyruslib.CYRUS.__init__(self, self.uri)
                 break
             except cyruslib.CYRUSError:
                 log.warning(
                         _("Could not connect to Cyrus IMAP server %r") % (
                                 self.uri
                             )
                     )
 
                 time.sleep(10)
 
         if conf.debuglevel > 8:
             self.VERBOSE = True
             self.m.debug = 5
             sl = pykolab.logger.StderrToLogger(log)
             # imaplib debug outputs everything to stderr. Redirect to Logger
             sys.stderr = sl
             # cyruslib debug outputs everything to LOGFD. Redirect to Logger
             self.LOGFD = sl
 
         # Initialize our variables
         self.separator = self.SEP
 
         # Placeholder for known mailboxes on known servers
         self.mbox = {}
 
     def __del__(self):
         pass
 
     def connect(self, uri):
         """
             Dummy connect function that checks if the server that we want to
             connect to is actually the server we are connected to.
 
             Uses pykolab.imap.IMAP.connect() in the background.
         """
         port = None
 
         result = urlparse(uri)
 
         if hasattr(result, 'hostname'):
             scheme = result.scheme
             hostname = result.hostname
             port = result.port
         else:
             scheme = uri.split(':')[0]
             (hostname, port) = uri.split('/')[2].split(':')
 
         if not port:
             if scheme == 'imap':
                 port = 143
             else:
                 port = 993
 
         if hostname == self.server:
             return
 
         imap = IMAP()
         imap.connect(uri=uri)
 
         if not self.SEP == self.separator:
             self.separator = self.SEP
 
     def login(self, *args, **kw):
         """
             Login to the Cyrus IMAP server through cyruslib.CYRUS, but set our
             hierarchy separator.
         """
         try:
             cyruslib.CYRUS.login(self, *args, **kw)
-        except cyruslib.CYRUSError, errmsg:
+        except cyruslib.CYRUSError as errmsg:
             log.error("Login to Cyrus IMAP server failed: %r", errmsg)
-        except Exception, errmsg:
+        except Exception as errmsg:
             log.exception(errmsg)
 
         self.separator = self.SEP
         try:
             self._id()
-        except Exception, errmsg:
+        except Exception:
             pass
 
         log.debug(
                 _("Continuing with separator: %r") % (self.separator),
                 level=8
             )
 
         self.murder = False
 
         for capability in self.m.capabilities:
             if capability.startswith("MUPDATE="):
                 log.debug(
                         _("Detected we are running in a Murder topology"),
                         level=8
                     )
 
                 self.murder = True
 
         if not self.murder:
             log.debug(
                     _("This system is not part of a murder topology"),
                     level=8
                 )
 
     def find_mailfolder_server(self, mailfolder):
         annotations = {}
 
         _mailfolder = self.parse_mailfolder(mailfolder)
 
         prefix = _mailfolder['path_parts'][0]
         mbox = _mailfolder['path_parts'][1]
         if _mailfolder['domain'] is not None:
             mailfolder = "%s%s%s@%s" % (
                     prefix,
                     self.separator,
                     mbox,
                     _mailfolder['domain']
                 )
 
         # TODO: Workaround for undelete
         if len(self.lm(mailfolder)) < 1 and 'hex_timestamp' in _mailfolder:
             mailfolder = self.folder_utf7("DELETED/%s%s%s@%s" % (
                     self.separator.join(_mailfolder['path_parts']),
                     self.separator,
                     _mailfolder['hex_timestamp'],
                     _mailfolder['domain'])
                )
 
 
         # TODO: Murder capabilities may have been suppressed using Cyrus IMAP
         # configuration.
         if not self.murder:
             return self.server
 
         log.debug(
                 _("Checking actual backend server for folder %s " +
                     "through annotations") % (
                         mailfolder
                     ),
                 level=8
             )
 
         if self.mbox.has_key(mailfolder):
             log.debug(
                     _(
                             "Possibly reproducing the find " +
                             "mailfolder server answer from " +
                             "previously detected and stored " +
                             "annotation value: %r"
                         ) % (
                                 self.mbox[mailfolder]
                             ),
                     level=8
                 )
 
             if not self.mbox[mailfolder] == self.server:
                 return self.mbox[mailfolder]
 
         max_tries = 20
         num_try = 0
 
         ann_path = "/vendor/cmu/cyrus-imapd/server"
         s_ann_path = "/shared%s" % (ann_path)
 
         while 1:
             num_try += 1
             annotations = self._getannotation(
                     '"%s"' % (mailfolder),
                     ann_path
                 )
 
             if annotations.has_key(mailfolder):
                 if annotations[mailfolder].has_key(s_ann_path):
                     break
 
             if max_tries <= num_try:
                 log.error(
                         _("Could not get the annotations after %s tries.") % (
                                 num_try
                             )
                     )
 
                 annotations = {
                         mailfolder: {
                                 s_ann_path: self.server
                             }
                     }
 
                 break
 
             log.warning(
                     _("No annotations for %s: %r") % (
                             mailfolder,
                             annotations
                         )
                 )
 
             time.sleep(1)
 
         server = annotations[mailfolder][s_ann_path]
         self.mbox[mailfolder] = server
 
         log.debug(
                 _("Server for INBOX folder %s is %s") % (
                         mailfolder,
                         server
                     ),
                 level=8
             )
 
         return server
 
     def folder_utf7(self, folder):
         from pykolab import imap_utf7
         return imap_utf7.encode(folder)
 
     def folder_utf8(self, folder):
         from pykolab import imap_utf7
         return imap_utf7.decode(folder)
 
     def _id(self, identity=None):
         if identity is None:
             identity = '("name" "Python/Kolab" "version" "%s")' % (constants.__version__)
 
         typ, dat = self.m._simple_command('ID', identity)
         res, dat = self.m._untagged_response(typ, dat, 'ID')
 
     def _setquota(self, mailfolder, quota):
         """
             Login to the actual backend server.
         """
         server = self.find_mailfolder_server(mailfolder)
         self.connect(self.uri.replace(self.server, server))
 
         log.debug(
                 _("Setting quota for folder %s to %s") % (
                         mailfolder,
                         quota
                     ),
                 level=8
             )
 
         try:
             self.m.setquota(mailfolder, quota)
         except:
             log.error(
                     _("Could not set quota for mailfolder %s") % (
                             mailfolder
                         )
                 )
 
     def _rename(self, from_mailfolder, to_mailfolder, partition=None):
         """
             Login to the actual backend server, then rename.
         """
         server = self.find_mailfolder_server(from_mailfolder)
         self.connect(self.uri.replace(self.server, server))
 
         if partition is not None:
             log.debug(
                     _("Moving INBOX folder %s to %s on partition %s") % (
                             from_mailfolder,
                             to_mailfolder,
                             partition
                         ),
                     level=8
                 )
 
         else:
             log.debug(
                     _("Moving INBOX folder %s to %s") % (
                             from_mailfolder,
                             to_mailfolder
                         ),
                     level=8
                 )
 
         self.m.rename(
                 self.folder_utf7(from_mailfolder),
                 self.folder_utf7(to_mailfolder),
                 partition
             )
 
     def _getannotation(self, *args, **kw):
         return self.getannotation(*args, **kw)
 
     def _setannotation(self, mailfolder, annotation, value, shared=False):
         """
             Login to the actual backend server, then set annotation.
         """
         try:
             server = self.find_mailfolder_server(mailfolder)
         except:
             server = self.server
 
         log.debug(
                 _("Setting annotation %s on folder %s") % (
                         annotation,
                         mailfolder
                     ),
                 level=8
             )
 
         try:
             self.setannotation(mailfolder, annotation, value, shared)
-        except cyruslib.CYRUSError, errmsg:
+        except cyruslib.CYRUSError as errmsg:
             log.error(
                     _("Could not set annotation %r on mail folder %r: %r") % (
                             annotation,
                             mailfolder,
                             errmsg
                         )
                 )
 
     def _xfer(self, mailfolder, current_server, new_server):
         self.connect(self.uri.replace(self.server, current_server))
         log.debug(
                 _("Transferring folder %s from %s to %s") % (
                         mailfolder,
                         current_server,
                         new_server
                     ),
                 level=8
             )
 
         self.xfer(mailfolder, new_server)
 
     def undelete_mailfolder(
                 self,
                 mailfolder,
                 to_mailfolder=None,
                 recursive=True
             ):
         """
             Login to the actual backend server, then "undelete" the mailfolder.
 
             'mailfolder' may be a string representing either of the following
             two options;
 
             - the fully qualified pathof the deleted folder in its current
               location, such as, for a deleted INBOX folder originally known as
               "user/userid[@domain]";
 
                 "DELETED/user/userid/hex[@domain]"
 
             - the original folder name, such as;
 
                 "user/userid[@domain]"
 
             'to_mailfolder' may be the target folder to "undelete" the deleted
             folder to. If not specified, the original folder name is used.
         """
         # Placeholder for folders we have recovered already.
         target_folders = []
 
         mailfolder = self.parse_mailfolder(mailfolder)
 
         undelete_folders = self._find_deleted_folder(mailfolder)
 
         if to_mailfolder is not None:
             target_mbox = self.parse_mailfolder(to_mailfolder)
         else:
             target_mbox = mailfolder
 
         for undelete_folder in undelete_folders:
             undelete_mbox = self.parse_mailfolder(undelete_folder)
 
             prefix = undelete_mbox['path_parts'].pop(0)
             mbox = undelete_mbox['path_parts'].pop(0)
 
             if to_mailfolder is None:
                 target_folder = self.separator.join([prefix, mbox])
             else:
                 target_folder = self.separator.join(target_mbox['path_parts'])
 
             if to_mailfolder is not None:
                 target_folder = "%s%s%s" % (
                         target_folder,
                         self.separator,
                         mbox
                     )
 
             if not len(undelete_mbox['path_parts']) == 0:
                 target_folder = "%s%s%s" % (
                         target_folder,
                         self.separator,
                         self.separator.join(undelete_mbox['path_parts'])
                     )
 
             if target_folder in target_folders:
                 target_folder = "%s%s%s" % (
                         target_folder,
                         self.separator,
                         undelete_mbox['hex_timestamp']
                     )
 
             target_folders.append(target_folder)
 
             if target_mbox['domain'] is not None:
                 target_folder = "%s@%s" % (
                         target_folder,
                         target_mbox['domain']
                     )
 
             log.info(
                     _("Undeleting %s to %s") % (
                             undelete_folder,
                             target_folder
                         )
                 )
 
             target_server = self.find_mailfolder_server(target_folder)
             source_server = self.find_mailfolder_server(undelete_folder)
 
             if hasattr(conf, 'dry_run') and not conf.dry_run:
                 undelete_folder = self.folder_utf7(undelete_folder)
                 target_folder = self.folder_utf7(target_folder)
 
                 if not target_server == source_server:
                     self.xfer(undelete_folder, target_server)
 
                 self.rename(undelete_folder, target_folder)
             else:
                 if not target_server == source_server:
                     print(_("Would have transferred %s from %s to %s") % (
                                 undelete_folder,
                                 source_server,
                                 target_server
                             ), file=sys.stdout)
 
                 print(_("Would have renamed %s to %s") % (
                             undelete_folder,
                             target_folder
                         ), file=sys.stdout)
 
     def parse_mailfolder(self, mailfolder):
         """
             Parse a mailfolder name to it's parts.
 
             Takes a fully qualified mailfolder or mailfolder sub-folder.
         """
         mbox = {
                 'domain': None
             }
 
         if len(mailfolder.split('/')) > 1:
             self.separator = '/'
 
         # Split off the virtual domain identifier, if any
         if len(mailfolder.split('@')) > 1:
             mbox['domain'] = mailfolder.split('@')[1]
             mbox['path_parts'] = mailfolder.split('@')[0].split(self.separator)
         else:
             mbox['path_parts'] = mailfolder.split(self.separator)
 
         # See if the path that has been specified is the current location for
         # the deleted folder, or the original location, we have to find the
         # deleted folder for.
         if not mbox['path_parts'][0] in ['user', 'shared']:
             deleted_prefix = mbox['path_parts'].pop(0)
             # See if the hexadecimal timestamp is actually hexadecimal.
             # This prevents "DELETED/user/userid/Sent", but not
             # "DELETED/user/userid/FFFFFF" from being specified.
             try:
                 hexstamp = mbox['path_parts'][(len(mbox['path_parts'])-1)]
                 epoch = int(hexstamp, 16)
                 try:
                     timestamp = time.asctime(time.gmtime(epoch))
                 except:
                     return None
             except:
                 return None
 
             # Verify that the input for the deleted folder is actually a
             # deleted folder.
             verify_folder_search = "%(dp)s%(sep)s%(mailfolder)s" % {
                     'dp': deleted_prefix,
                     'sep': self.separator,
                     'mailfolder': self.separator.join(mbox['path_parts'])
                 }
 
             if mbox['domain'] is not None:
                 verify_folder_search = "%s@%s" % (
                         verify_folder_search,
                         mbox['domain']
                     )
 
             if ' ' in verify_folder_search:
                 folders = self.lm(
                         '"%s"' % self.folder_utf7(verify_folder_search)
                     )
 
             else:
                 folders = self.lm(self.folder_utf7(verify_folder_search))
 
             # NOTE: Case also covered is valid hexadecimal folders; won't be
             # the actual check as intended, but doesn't give you anyone else's
             # data unless... See the following:
             #
             # TODO: Case not covered is usernames that are hexadecimal.
             #
             # We could probably attempt to convert the int(hex) into a
             # time.gmtime(), but it still would not cover all cases.
             #
 
             # If no folders were found... well... then there you go.
             if len(folders) < 1:
                 return None
 
             # Pop off the hex timestamp, which turned out to be valid
             mbox['hex_timestamp'] = mbox['path_parts'].pop()
 
         return mbox
 
     def _find_deleted_folder(self, mbox):
         """
             Give me the parts that are in an original mailfolder name and I'll
             find the deleted folder name.
 
             TODO: It finds virtdomain folders for non-virtdomain searches.
         """
         deleted_folder_search = "%(deleted_prefix)s%(separator)s%(mailfolder)s%(separator)s*" % {
                 # TODO: The prefix used is configurable
                 'deleted_prefix': "DELETED",
                 'mailfolder': self.separator.join(mbox['path_parts']),
                 'separator': self.separator,
             }
 
         if mbox['domain'] is not None:
             deleted_folder_search = "%s@%s" % (
                     deleted_folder_search,
                     mbox['domain']
                 )
 
         folders = self.lm(self.folder_utf7(deleted_folder_search))
 
         # The folders we have found at this stage include virtdomain folders.
         #
         # For example, having searched for user/userid, it will also find
         # user/userid@example.org
         #
 
         # Here, we explicitly remove any virtdomain folders.
         if mbox['domain'] is None:
             _folders = []
             for folder in folders:
                 if len(folder.split('@')) < 2:
                     _folders.append(folder)
 
             folders = _folders
 
         return [self.folder_utf8(x) for x in folders]
diff --git a/pykolab/imap_utf7.py b/pykolab/imap_utf7.py
index 038623b..0ecb83c 100644
--- a/pykolab/imap_utf7.py
+++ b/pykolab/imap_utf7.py
@@ -1,91 +1,91 @@
 # The contents of this file has been derived code from the Twisted project
 # (http://twistedmatrix.com/). The original author is Jp Calderone.
 
 # Twisted project license follows:
 
 # Permission is hereby granted, free of charge, to any person obtaining
 # a copy of this software and associated documentation files (the
 # "Software"), to deal in the Software without restriction, including
 # without limitation the rights to use, copy, modify, merge, publish,
 # distribute, sublicense, and/or sell copies of the Software, and to
 # permit persons to whom the Software is furnished to do so, subject to
 # the following conditions:
 # 
 # The above copyright notice and this permission notice shall be
 # included in all copies or substantial portions of the Software.
 # 
 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 
 class FolderNameError(ValueError):
     pass
 
 
 def encode(s):
     if isinstance(s, str) and sum(n for n in (ord(c) for c in s) if n > 127):
         try:
             s = unicode(s, "UTF-8")
-        except Exception, errmsg:
+        except Exception:
             raise FolderNameError("%r contains characters not valid in a str folder name. "
                               "Convert to unicode first?" % s)
 
     r = []
     _in = []
     for c in s:
         if ord(c) in (range(0x20, 0x26) + range(0x27, 0x7f)):
             if _in:
                 r.extend(['&', modified_base64(''.join(_in)), '-'])
                 del _in[:]
             r.append(str(c))
         elif c == '&':
             if _in:
                 r.extend(['&', modified_base64(''.join(_in)), '-'])
                 del _in[:]
             r.append('&-')
         else:
             _in.append(c)
     if _in:
         r.extend(['&', modified_base64(''.join(_in)), '-'])
 
     return ''.join(r)
 
 
 def decode(s):
     r = []
     decode = []
     for c in s:
         if c == '&' and not decode:
             decode.append('&')
         elif c == '-' and decode:
             if len(decode) == 1:
                 r.append('&')
             else:
                 r.append(modified_unbase64(''.join(decode[1:])))
             decode = []
         elif decode:
             decode.append(c)
         else:
             r.append(c)
     if decode:
         r.append(modified_unbase64(''.join(decode[1:])))
     out = ''.join(r)
 
     if not isinstance(out, unicode):
         out = unicode(out, 'latin-1')
     return out
 
 
 def modified_base64(s):
     s_utf7 = s.encode('utf-7')
     return s_utf7[1:-1].replace('/', ',')
 
 
 def modified_unbase64(s):
     s_utf7 = '+' + s.replace(',', '/') + '-'
     return s_utf7.decode('utf-7')
diff --git a/pykolab/plugins/__init__.py b/pykolab/plugins/__init__.py
index 82bf525..96e0044 100644
--- a/pykolab/plugins/__init__.py
+++ b/pykolab/plugins/__init__.py
@@ -1,272 +1,272 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import logging
 import os
 import pdb
 import sys
 import traceback
 
 import pykolab
 
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.plugins')
 conf = pykolab.getConf()
 
 
 class KolabPlugins(object):
     """
         Detects, loads and interfaces with plugins for different
         Kolab components.
     """
     def __init__(self):
         """
             Searches the plugin directory for plugins, and loads
             them into a list.
         """
         self.plugins = {}
 
         for plugin_path in [
                 os.path.dirname(__file__),
                 '/usr/share/pykolab/plugins',
                 './pykolab/plugins'
             ]:
 
             if os.path.isdir(plugin_path):
                 for plugin in os.listdir(plugin_path):
                     if os.path.isdir('%s/%s/' % (plugin_path, plugin, )):
                         self.plugins[plugin] = False
 
         self.check_plugins()
 
     def check_plugins(self):
         """
             Checks all plugins in self.plugins and sets the values to
             True (loadable) or False -- not enabled, not installed or
             not loadable.
         """
         for plugin in self.plugins:
             try:
                 exec("from pykolab.plugins import %s" % (plugin))
                 self.plugins[plugin] = True
                 self.load_plugins(plugins=[plugin])
-            except ImportError, e:
+            except ImportError as e:
                 log.error(_("ImportError for plugin %s: %s") % (plugin, e))
                 traceback.print_exc()
                 self.plugins[plugin] = False
-            except RuntimeError, e:
+            except RuntimeError as e:
                 log.error( _("RuntimeError for plugin %s: %s") % (plugin, e))
                 traceback.print_exc()
                 self.plugins[plugin] = False
-            except Exception, e:
+            except Exception as e:
                 log.error(_("Plugin %s failed to load (%s: %s)") % (plugin, e.__class__, e))
                 traceback.print_exc()
             except:
                 traceback.print_exc()
 
     def load_plugins(self, plugins=[]):
         """
             Loads plugins specified by a list of plugins or loads them all
         """
 
         if len(plugins) < 1:
             plugins = self.plugins.keys()
 
         for plugin in plugins:
             if self.plugins[plugin]:
                 try:
                     exec("self.%s = %s.Kolab%s()" % (plugin, plugin, plugin.capitalize()))
                 except:
                     # TODO: A little better verbosity please!
                     traceback.print_exc()
 
     def set_defaults(self, defaults, plugins=[]):
         """
             Test for a function set_defaults() in all available and loaded plugins and execute plugin.set_defaults()
         """
         if len(plugins) < 1:
             plugins = self.plugins.keys()
 
         for plugin in plugins:
             if not self.plugins[plugin]:
                 continue
             if not hasattr(self, plugin):
                 continue
 
             if hasattr(getattr(self, plugin), "set_defaults"):
                 try:
                     getattr(self, plugin).set_defaults(defaults)
-                except TypeError, e:
+                except TypeError as e:
                     log.error(_("Cannot set defaults for plugin %s: %s") % (plugin, e))
-                except RuntimeError, e:
+                except RuntimeError as e:
                     log.error(_("Cannot set defaults for plugin %s: %s") % (plugin, e))
                 except:
                     log.error(_("Cannot set defaults for plugin %s: Unknown Error") % (plugin))
 
             else:
                 log.debug(_("Not setting defaults for plugin %s: No function 'set_defaults()'") % plugin, level=5)
 
     def set_runtime(self, runtime, plugins=[]):
         """
             Set runtime variables from plugins, like 'i_did_all_this'
         """
         if len(plugins) < 1:
             plugins = self.plugins.keys()
 
         for plugin in plugins:
             if not self.plugins[plugin]:
                 continue
             if not hasattr(self, plugin):
                 continue
 
             if hasattr(getattr(self, plugin), "set_runtime"):
                 try:
                     getattr(self, plugin).set_runtime(runtime)
-                except RuntimeError, e:
+                except RuntimeError as e:
                     log.error(_("Cannot set runtime for plugin %s: %s") % (plugin, e))
             else:
                 log.debug(_("Not setting runtime for plugin %s: No function 'set_runtime()'") % (plugin), level=5)
 
     def add_options(self, parser, plugins=[]):
         """
             Add options specified in a plugin to parser. Takes a list of plugin names or does them all
         """
         if len(plugins) < 1:
             plugins = self.plugins.keys()
 
         for plugin in plugins:
             if not self.plugins[plugin]:
                 continue
             if not hasattr(self, plugin):
                 continue
 
             if hasattr(getattr(self, plugin), "add_options"):
                 try:
                     exec("self.%s.add_options(parser)" % plugin)
-                except RuntimeError, e:
+                except RuntimeError as e:
                     log.error(_("Cannot add options for plugin %s: %s") % (plugin, e))
-                except TypeError, e:
+                except TypeError as e:
                     log.error(_("Cannot add options for plugin %s: %s") % (plugin, e))
             else:
                     log.debug(_("Not adding options for plugin %s: No function 'add_options()'") % plugin, level=5)
 
     def check_options(self, plugins=[]):
         """
             Executes plugin.check_plugins() for all enabled plugins or the list of plugin names specified.
         """
 
         if len(plugins) < 1:
             plugins = self.plugins.keys()
 
         for plugin in plugins:
             if not self.plugins[plugin]:
                 continue
             if not hasattr(self, plugin):
                 continue
 
             if hasattr(getattr(self, plugin), "check_options"):
                 try:
                     exec("self.%s.check_options()" % plugin)
-                except AttributeError, e:
+                except AttributeError as e:
                     log.error(_("Cannot check options for plugin %s: %s") % (plugin, e))
             else:
                 log.debug(_("Not checking options for plugin %s: No function 'check_options()'") % (plugin), level=5)
 
     def plugin_check_setting(self, func, option, val, plugins=[]):
         """
             Checks one setting specified by 'option' against the 'val' it is passed by all plugins or by the list of plugins specified
         """
 
         if len(plugins) < 1:
             plugins = self.plugins.keys()
 
         for plugin in plugins:
             if not self.plugins[plugin]:
                 continue
             if not hasattr(self, plugin):
                 continue
 
             if hasattr(getattr(self, plugin), "%s_%s" % (func, option)):
                 exec("retval = getattr(self, plugin).%s_%s(val)" % (func, option))
                 return retval
 
         return False
 
     def exec_hook(self, hook, plugins=[], kw={}, args=()):
         """Execute a hook"""
 
         retval = None
 
         if len(plugins) < 1:
             plugins = self.plugins.keys()
 
         for plugin in plugins:
             if not self.plugins[plugin]:
                 continue
             if not hasattr(self, plugin):
                 continue
 
             if hasattr(getattr(self, plugin), hook):
                 try:
                     log.debug(_("Executing hook %s for plugin %s") % (hook, plugin), level=8)
                     func = getattr(getattr(self, plugin), hook)
                     retval = func(*args, **kw)
                 except TypeError as errmsg:
                     log.error(
                         _("Cannot execute hook %s for plugin %s: %s") % (hook, plugin, errmsg)
                     )
 
                     log.error(traceback.format_exc())
                 except AttributeError as errmsg:
                     log.error(
                         _("Cannot execute hook %s for plugin %s: %s") % (hook, plugin, errmsg)
                     )
 
                     log.error(traceback.format_exc())
 
         return retval
 
     def return_true_boolean_from_plugins(self, bool, plugins=[]):
         """
             Given the name of a boolean, walks all specified plugins, or all available plugins, and
             returns True if a plugin has it set to true
         """
         if len(plugins) < 1:
             plugins = self.plugins.keys()
 
         retval = False
 
         for plugin in plugins:
             if not self.plugins[plugin]:
                 continue
             if not hasattr(self, plugin):
                 continue
 
             if hasattr(getattr(self, plugin), bool):
                 try:
                     exec("boolval = self.%s.%s" % (plugin, bool))
-                except AttributeError, e:
+                except AttributeError:
                     pass
             else:
                 boolval = None
 
             if boolval: retval = True
 
         return retval
diff --git a/pykolab/plugins/defaultfolders/__init__.py b/pykolab/plugins/defaultfolders/__init__.py
index e098ec2..0b6ee10 100644
--- a/pykolab/plugins/defaultfolders/__init__.py
+++ b/pykolab/plugins/defaultfolders/__init__.py
@@ -1,56 +1,56 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import pykolab
 
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.plugins.defaultfolders')
 conf = pykolab.getConf()
 
 class KolabDefaultfolders(object):
     """
         Example plugin to create a set of default folders.
     """
 
     def __init__(self):
         pass
 
     def add_options(self, *args,  **kw):
         pass
 
     def create_user_folders(self, *args, **kw):
         """
             The arguments passed to the 'create_user_folders' hook:
 
             additional_folders - additional folders to create
             user_folder - user folder
         """
 
         if not kw.has_key('additional_folders'):
             log.error(_("Plugin %s called without required keyword %s.") % ("defaultfolders", "additional_folders"))
             return {}
 
         try:
             exec("additional_folders = %s" % (kw['additional_folders']))
-        except Exception, e:
+        except Exception:
             log.error(_("Could not parse additional_folders"))
             return {}
 
         return additional_folders
diff --git a/pykolab/plugins/recipientpolicy/__init__.py b/pykolab/plugins/recipientpolicy/__init__.py
index 23ba863..aab2a5e 100644
--- a/pykolab/plugins/recipientpolicy/__init__.py
+++ b/pykolab/plugins/recipientpolicy/__init__.py
@@ -1,163 +1,163 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import pykolab
 
 from pykolab import utils
 from pykolab.translate import _
 
 conf = pykolab.getConf()
 log = pykolab.getLogger('pykolab.plugins.recipientpolicy')
 
 class KolabRecipientpolicy(object):
     """
         Example plugin making quota adjustments given arbitrary conditions.
     """
 
     def __init__(self):
         pass
 
     def add_options(self, *args,  **kw):
         pass
 
     #def mail_domain_space_policy_check(self, kw={}, args=()):
         #(mail, alternative_mail, domain_name, domain_root_dn) = args
 
         ## Your actions go here. For example:
         #return (mail, alternative_mail)
 
     def set_primary_mail(self, *args, **kw):
         """
             The arguments passed to the 'set_user_attrs_mail' hook:
 
             primary_mail - the policy
             user_attrs - the current user attributes
             primary_domain - the domain to use in the primary mail attribute
             secondary_domains - the secondary domains that are aliases
 
             Return the new primary mail address
         """
 
         user_attrs = utils.normalize(kw['entry'])
 
         if not user_attrs.has_key('domain'):
             user_attrs['domain'] = kw['primary_domain']
         elif not user_attrs['domain'] == kw['primary_domain']:
             user_attrs['domain'] = kw['primary_domain']
 
         if not user_attrs.has_key('preferredlanguage'):
             default_locale = conf.get(user_attrs['domain'], 'default_locale')
             if default_locale == None:
                 default_locale = conf.get('kolab', 'default_locale')
             if default_locale == None:
                 default_locale = 'en_US'
 
             user_attrs['preferredlanguage'] = default_locale
 
         try:
             mail = kw['primary_mail'] % user_attrs
             mail = utils.translate(mail, user_attrs['preferredlanguage'])
             mail = mail.lower()
             return mail
-        except KeyError, e:
+        except KeyError:
             log.warning(_("Attribute substitution for 'mail' failed in Recipient Policy"))
             if user_attrs.has_key('mail'):
                 return user_attrs['mail']
             else:
                 return None
 
     def set_secondary_mail(self, *args, **kw):
         """
             The arguments passed to the 'set_user_attrs_alternative_mail' hook:
 
             primary_mail - the policy
             user_attrs - the current user attributes
             primary_domain - the domain to use in the primary mail attribute
             secondary_domains - the secondary domains that are aliases
 
             Return a list of secondary mail addresses
         """
 
         user_attrs = utils.normalize(kw['entry'])
 
         if not user_attrs.has_key('domain'):
             user_attrs['domain'] = kw['primary_domain']
         elif not user_attrs['domain'] == kw['primary_domain']:
             user_attrs['domain'] = kw['primary_domain']
 
         if not user_attrs.has_key('preferredlanguage'):
             default_locale = conf.get(user_attrs['domain'], 'default_locale')
             if default_locale == None:
                 default_locale = conf.get(user_attrs['domain'], 'default_locale')
             if default_locale == None:
                 default_locale = 'en_US'
 
             user_attrs['preferredlanguage'] = default_locale
 
         try:
             exec("alternative_mail_routines = %s" % kw['secondary_mail'])
-        except Exception, e:
+        except Exception:
             log.error(_("Could not parse the alternative mail routines"))
 
         alternative_mail = []
 
         log.debug(_("Alternative mail routines: %r") % (alternative_mail_routines), level=8)
         _domains = [ kw['primary_domain'] ] + kw['secondary_domains']
 
         for attr in [ 'givenname', 'sn', 'surname' ]:
             try:
                 user_attrs[attr] = utils.translate(user_attrs[attr], user_attrs['preferredlanguage'])
-            except Exception, errmsg:
+            except Exception:
                 log.error(_("An error occurred in composing the secondary mail attribute for entry %r") % (user_attrs['id']))
                 if conf.debuglevel > 8:
                     import traceback
                     traceback.print_exc()
                 return []
 
         for number in alternative_mail_routines.keys():
             for routine in alternative_mail_routines[number].keys():
                 try:
                     exec("retval = '%s'.%s" % (routine,alternative_mail_routines[number][routine] % user_attrs))
 
                     log.debug(_("Appending additional mail address: %s") % (retval), level=8)
                     alternative_mail.append(retval)
 
-                except Exception, errmsg:
+                except Exception as errmsg:
                     log.error(_("Policy for secondary email address failed: %r") % (errmsg))
                     if conf.debuglevel > 8:
                         import traceback
                         traceback.print_exc()
                     return []
 
                 for _domain in kw['secondary_domains']:
                     user_attrs['domain'] = _domain
                     try:
                         exec("retval = '%s'.%s" % (routine,alternative_mail_routines[number][routine] % user_attrs))
 
                         log.debug(_("Appending additional mail address: %s") % (retval), level=8)
                         alternative_mail.append(retval)
 
-                    except KeyError, e:
+                    except KeyError:
                         log.warning(_("Attribute substitution for 'alternative_mail' failed in Recipient Policy"))
 
         alternative_mail = utils.normalize(alternative_mail)
 
         alternative_mail = list(set(alternative_mail))
 
         return alternative_mail
diff --git a/pykolab/setup/components.py b/pykolab/setup/components.py
index 8e9f346..5cc7b58 100644
--- a/pykolab/setup/components.py
+++ b/pykolab/setup/components.py
@@ -1,266 +1,266 @@
 # -*- coding: utf-8 -*-
 #
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import os
 
 import pykolab
 
 from pykolab.constants import *
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.setup')
 conf = pykolab.getConf()
 
 components = {}
 component_groups = {}
 executed_components = []
 
 components_included_in_cli = []
 
 finalize_conf_ok = None
 
 def __init__():
     # We only want the base path
     components_base_path = os.path.dirname(__file__)
 
     for components_path, dirnames, filenames in os.walk(components_base_path):
         if not components_path == components_base_path:
             continue
 
         for filename in filenames:
             if filename.startswith('setup_') and filename.endswith('.py'):
                 module_name = filename.replace('.py','')
                 component_name = module_name.replace('setup_', '')
                 #print "exec(\"from %s import __init__ as %s_register\"" % (module_name,component_name)
                 exec("from %s import __init__ as %s_register" % (module_name,component_name))
                 exec("%s_register()" % (component_name))
 
         for dirname in dirnames:
             register_group(components_path, dirname)
 
     register('help', list_components, description=_("Display this help."))
 
 def list_components(*args, **kw):
     """
         List components
     """
 
     __components = {}
 
     for component in components.keys():
         if isinstance(component, tuple):
             component_group, component = component
             __components[component_group] = {
                     component: components[(component_group,component)]
                 }
         else:
             __components[component] = components[component]
 
     _components = __components.keys()
     _components.sort()
 
     for _component in _components:
         if __components[_component].has_key('function'):
             # This is a top-level component
             if not __components[_component]['description'] == None:
                 print("%-25s - %s" % (_component.replace('_','-'),__components[_component]['description']))
             else:
                 print("%-25s" % (_component.replace('_','-')))
 
     for _component in _components:
         if not __components[_component].has_key('function'):
             # This is a nested component
             print("\n" + _("Command Group: %s") % (_component) + "\n")
             ___components = __components[_component].keys()
             ___components.sort()
             for __component in ___components:
                 if not __components[_component][__component]['description'] == None:
                     print("%-4s%-21s - %s" % ('',__component.replace('_','-'),__components[_component][__component]['description']))
                 else:
                     print("%-4s%-21s" % ('',__component.replace('_','-')))
 
 def _list_components(*args, **kw):
     """
         List components and return API compatible, parseable lists and
         dictionaries.
     """
 
     __components = {}
 
     for component in components.keys():
         if isinstance(component, tuple):
             component_group, component = component
             __components[component_group] = {
                     component: components[(component_group,component)]
                 }
         else:
             __components[component] = components[component]
 
     _components = __components.keys()
     _components.sort()
 
     return _components
 
 def cli_options_from_component(component_name, *args, **kw):
     global components_included_in_cli
 
     if component_name in components_included_in_cli:
         return
 
     if components[component_name].has_key('group'):
         group = components[component_name]['group']
         component_name = components[component_name]['component_name']
         try:
             exec("from %s.setup_%s import cli_options as %s_%s_cli_options" % (group,component_name,group,component_name))
             exec("%s_%s_cli_options()" % (group,component_name))
-        except ImportError, e:
+        except ImportError:
             pass
 
     else:
         try:
             exec("from setup_%s import cli_options as %s_cli_options" % (component_name,component_name))
             exec("%s_cli_options()" % (component_name))
-        except ImportError, e:
+        except ImportError:
             pass
 
     components_included_in_cli.append(component_name)
 
 def execute(component_name, *args, **kw):
     if component_name == '':
 
         log.debug(
                 _("No component selected, continuing for all components"),
                 level=8
             )
 
         while 1:
             for component in _list_components():
                 execute_this = True
 
                 if component in executed_components:
                     execute_this = False
 
                 if component == "help":
                     execute_this = False
 
                 if execute_this:
                     if components[component].has_key('after'):
                         for _component in components[component]['after']:
                             if not _component in executed_components:
                                 execute_this = False
 
                 if execute_this:
                     execute(component)
                     executed_components.append(component)
 
             executed_all = True
             for component in _list_components():
                 if not component in executed_components and not component == "help":
                     executed_all = False
 
             if executed_all:
                 break
 
         return
     else:
         for component in _list_components():
             cli_options_from_component(component)
 
     if not components.has_key(component_name):
         log.error(_("No such component."))
         sys.exit(1)
 
     if not components[component_name].has_key('function') and \
         not components[component_name].has_key('group'):
         log.error(_("No such component."))
         sys.exit(1)
 
     conf.finalize_conf()
 
     if len(conf.cli_args) >= 1:
         _component_name = conf.cli_args.pop(0)
     else:
         _component_name = component_name
 
     components[component_name]['function'](conf.cli_args, kw)
 
 def register_group(dirname, module):
     components_base_path = os.path.join(os.path.dirname(__file__), module)
 
     components[module] = {}
 
     for components_path, dirnames, filenames in os.walk(components_base_path):
         if not components_path == components_base_path:
             continue
 
         for filename in filenames:
             if filename.startswith('setup_') and filename.endswith('.py'):
                 module_name = filename.replace('.py','')
                 component_name = module_name.replace('setup_', '')
                 #print "exec(\"from %s.%s import __init__ as %s_%s_register\"" % (module,module_name,module,component_name)
                 exec("from %s.%s import __init__ as %s_%s_register" % (module,module_name,module,component_name))
                 exec("%s_%s_register()" % (module,component_name))
 
 def register(component_name, func, group=None, description=None, aliases=[], after=[], before=[]):
     if not group == None:
         component = "%s_%s" % (group,component_name)
     else:
         component = component_name
 
     if isinstance(aliases, basestring):
         aliases = [aliases]
 
     if components.has_key(component):
         log.fatal(_("Command '%s' already registered") % (component))
         sys.exit(1)
 
     if callable(func):
         if group == None:
             components[component_name] = {
                     'function': func,
                     'description': description,
                     'after': after,
                     'before': before,
                 }
         else:
             components[group][component_name] = {
                     'function': func,
                     'description': description,
                     'after': after,
                     'before': before,
                 }
 
             components[component] = components[group][component_name]
             components[component]['group'] = group
             components[component]['component_name'] = component_name
 
         for alias in aliases:
             components[alias] = {
                     'function': func,
                     'description': _("Alias for %s") % (component_name)
                 }
 
 ##
 ## Commands not yet implemented
 ##
 
 def not_yet_implemented(*args, **kw):
     print(_("Not yet implemented"))
     sys.exit(1)
diff --git a/pykolab/telemetry.py b/pykolab/telemetry.py
index 28b60f9..adeadd7 100644
--- a/pykolab/telemetry.py
+++ b/pykolab/telemetry.py
@@ -1,654 +1,654 @@
 #!/usr/bin/python
 #
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import os
 import rfc822
 import socket
 import sys
 import time
 
 import sqlalchemy
 
 from sqlalchemy import Boolean
 from sqlalchemy import Column
 from sqlalchemy import Date
 from sqlalchemy import DateTime
 from sqlalchemy import ForeignKey
 from sqlalchemy import Integer
 from sqlalchemy import MetaData
 from sqlalchemy import String
 from sqlalchemy import Table
 from sqlalchemy import Text
 
 from sqlalchemy.interfaces import PoolListener
 
 from sqlalchemy import create_engine
 from sqlalchemy.orm import mapper
 
 try:
     from sqlalchemy.orm import relationship
 except:
     from sqlalchemy.orm import relation as relationship
 
 try:
     from sqlalchemy.orm import sessionmaker
 except:
     from sqlalchemy.orm import create_session
 
 from sqlalchemy.schema import Index
 from sqlalchemy.schema import UniqueConstraint
 
 import pykolab
 
 from pykolab import utils
 from pykolab.translate import _
 
 conf = pykolab.getConf()
 log = pykolab.getLogger('pykolab.telemetry')
 
 metadata = MetaData()
 
 ##
 ## Classes
 ##
 
 class TelemetryCommand(object):
     def __init__(self, command):
         self.command = command
 
 class TelemetryCommandArg(object):
 
     command = relationship(
             'TelemetryCommand',
             order_by='telemetry_command.id',
             backref='command_args'
         )
 
     def __init__(self, command, command_arg=''):
         self.command_id = command.id
         self.command_arg = command_arg
 
 class TelemetryCommandIssue(object):
 
     session = relationship(
             'TelemetrySession',
             order_by='telemetry_session.id',
             backref='command_issues'
         )
 
     response = relationship(
             'TelemetryServerResponse',
             order_by='telemetry_server_response',
             backref='command_issue'
         )
 
     def __init__(self, command_tag, command, command_arg, session):
         self.command_tag = command_tag
         self.command_id = command.id
         self.command_arg_id = command_arg.id
         self.session_id = session.id
 
 class TelemetryFile(object):
 
     #server = relationship(
             #'TelemetryServer',
             #backref='files'
         #)
 
     def __init__(self, filepath, contents):
         self.filepath = filepath
         self.contents = contents
 
 class TelemetryLog(object):
     def __init__(self, log_file):
         self.log_file = log_file
 
         # We start out not being in a session
         self.session = None
         self.server_responding = False
         self.server_response = {}
 
         db = init_db()
 
         # TODO: Makes telemetry logs needs to be processed on the actual node
         server = db.query(
                 TelemetryServer
             ).filter_by(
                     fqdn=socket.gethostname()
                 ).first()
 
         if server == None:
             db.add(TelemetryServer(fqdn=socket.gethostname()))
             db.commit()
             server = db.query(
                     TelemetryServer
                 ).filter_by(
                         fqdn=socket.gethostname()
                     ).first()
 
         self.server = server
 
         # Username is in the directory name
         user_name = os.path.basename(os.path.dirname(log_file))
 
         user = db.query(
                 TelemetryUser
             ).filter_by(
                     sasl_username=user_name
                 ).first()
 
         if user == None:
             db.add(TelemetryUser(sasl_username=user_name))
             db.commit()
             user = db.query(
                     TelemetryUser
                 ).filter_by(
                         sasl_username=user_name
                     ).first()
 
         self.user = user
 
         # Session is at the end of the filename
         self.pid = os.path.basename(log_file)
 
         # Open the log file
         fp = open(self.log_file, 'r')
 
         # Insert log file in database
         db.add(TelemetryFile(filepath=log_file,contents=fp.read()))
         db.commit()
 
         # Go back to the beginning
         fp.seek(0)
 
         line_num = 0
 
         try:
             for line in fp:
                 if line == None:
                     break
 
                 line = line.strip()
 
                 line_num += 1
 
                 log.debug("%s (%d): %s" % (self.log_file,line_num,line), level=8)
 
                 if line.startswith('---------- '):
                     # This is the actual start of a session
                     datetime = ' '.join(line.split(' ')[2:])
 
                     # Translate datetime into epoch
                     timestamp = (int)(time.mktime(rfc822.parsedate(datetime)))
 
                     session = db.query(
                             TelemetrySession
                         ).filter_by(
                                 server_id=self.server.id,
                                 pid=self.pid,
                                 user_id=self.user.id,
                                 start=timestamp
                             ).first()
 
                     if session == None:
                         db.add(
                                 TelemetrySession(
                                         pid=self.pid,
                                         server=self.server,
                                         user=self.user,
                                         start=timestamp
                                     )
                             )
 
                         db.commit()
 
                         session = db.query(
                                 TelemetrySession
                             ).filter_by(
                                     server_id=self.server.id,
                                     pid=self.pid,
                                     user_id=self.user.id,
                                     start=timestamp
                                 ).first()
 
                     self.session = session
 
                     self.server_responding = False
 
                     if hasattr(self,'command_issue'):
                         del self.command_issue
 
                     continue
 
                 if line.startswith('<') and not self.server_responding:
                     # <1310124946<00000003 LIST "" *
                     timestamp = line.split('<')[1]
                     client_command_tag = line.split('<')[2].split(' ')[0]
                     client_command = line.split('<')[2].split(' ')[1]
                     client_command_arg = ' '.join(
                             line.split('<')[2].split(' ')[2:]
                         )
 
                     command = db.query(
                             TelemetryCommand
                         ).filter_by(
                                 command=client_command
                             ).first()
 
                     if command == None:
                         db.add(
                                 TelemetryCommand(
                                         command=client_command
                                     )
                             )
 
                         db.commit()
                         command = db.query(
                                 TelemetryCommand
                             ).filter_by(
                                     command=client_command
                                 ).first()
 
                     command_arg = db.query(
                             TelemetryCommandArg
                         ).filter_by(
                                 command_id=command.id,
                                 command_arg=client_command_arg
                             ).first()
 
                     if command_arg == None:
                         db.add(
                                 TelemetryCommandArg(
                                         command=command,
                                         command_arg=client_command_arg
                                     )
                             )
 
                         db.commit()
                         command_arg = db.query(
                                 TelemetryCommandArg
                             ).filter_by(
                                     command_id=command.id,
                                     command_arg=client_command_arg
                                 ).first()
 
 
                     command_issue = db.query(
                             TelemetryCommandIssue
                         ).filter_by(
                                 command_tag=client_command_tag,
                                 command_id=command.id,
                                 command_arg_id=command_arg.id,
                                 session_id=self.session.id
                             ).first()
 
                     if command_issue == None:
                         db.add(
                                 TelemetryCommandIssue(
                                         command_tag=client_command_tag,
                                         command=command,
                                         command_arg=command_arg,
                                         session=self.session
                                     )
                             )
 
                         db.commit()
                         command_issue = db.query(
                                 TelemetryCommandIssue
                             ).filter_by(
                                     command_tag=client_command_tag,
                                     command_id=command.id,
                                     command_arg_id=command_arg.id,
                                     session_id=self.session.id
                                 ).first()
 
                     self.command_issue = command_issue
 
                     continue
 
                 if line.startswith('>'):
                     self.server_responding = True
 
                     timestamp = line.split('>')[1]
                     server_response_line = ' '.join(line.split('>')[2:])
 
                     if hasattr(self,'command_issue'):
                         self.server_response[self.command_issue] = []
 
                         if hasattr(self.command_issue, 'command_tag'):
                             if server_response_line.startswith(self.command_issue.command_tag):
                                 if self.server_responding:
                                     if hasattr(self,'command_issue'):
                                         self.server_response[self.command_issue].append(
                                                 server_response_line
                                             )
 
                                         response = '\n'.join(
                                                 self.server_response[self.command_issue]
                                             )
 
                                         db.add(
                                                 TelemetryServerResponse(
                                                         command_issue=self.command_issue,
                                                         response=response
                                                     )
                                             )
 
                                         db.commit()
 
                                         self.server_response = {}
 
                                 self.server_responding = False
 
                                 continue
 
                         self.server_response[self.command_issue].append(
                                 server_response_line
                             )
 
                     continue
 
 
                 if line.startswith('*'):
                     if self.server_responding:
                         if hasattr(self,'command_issue'):
                             self.server_response[self.command_issue].append(
                                     line
                                 )
 
                     continue
 
                 if line == "":
                     if self.server_responding:
                         if hasattr(self,'command_issue'):
                             self.server_response[self.command_issue].append(
                                     line
                                 )
 
                     continue
 
                 if hasattr(self, 'command_issue'):
                     if hasattr(self.command_issue, 'command_tag'):
                         if line.startswith(self.command_issue.command_tag):
                             if self.server_responding:
                                 self.server_response[self.command_issue].append(
                                         line
                                     )
 
                                 response = '\n'.join(
                                         self.server_response[self.command_issue]
                                     )
 
                                 db.add(
                                         TelemetryServerResponse(
                                                 command_issue=self.command_issue,
                                                 response=response
                                             )
                                     )
 
                                 db.commit()
 
                                 self.server_response = {}
 
                             self.server_responding = False
 
                             continue
 
         finally:
             fp.close()
 
 class TelemetryServer(object):
 
     sessions = relationship(
             'TelemetrySession',
             order_by='telemetry_session.timestamp',
             backref='server'
         )
 
     #files = relationship(
             #'TelemetryFiles',
             #order_by='telemetry_file.filepath',
             #backref=server
         #)
 
     def __init__(self, fqdn):
         self.fqdn = fqdn
 
 class TelemetryServerResponse(object):
     def __init__(self, command_issue, response):
         self.command_issue_id = command_issue.id
         self.response = response
 
 class TelemetrySession(object):
 
     commands = relationship(
             'TelemetryCommand',
             order_by='telementry_command.id',
             backref='session'
         )
 
     server = relationship(
             'TelemetryServer',
             order_by='telemetry_server.id',
             backref='sessions'
         )
 
     user = relationship('TelemetryUser', uselist=False)
 
     def __init__(self, pid, user, server, start=0):
         self.pid = pid
         self.user_id = user.id
         self.server_id = server.id
         self.start = start
 
     def get_user(self):
         return self.user
 
 class TelemetryUser(object):
 
     commands = relationship(
             'TelemetryCommand',
             order_by="telemetry_command.timestamp",
             backref="user"
         )
 
     sessions = relationship(
             'TelemetrySession',
             uselist=False
         )
 
     def __init__(self, sasl_username=None, created=(int)(time.time())):
         self.sasl_username = sasl_username
         self.created = created
         self.updated = (int)(time.time())
 
 ##
 ## Tables
 ##
 
 telemetry_command_table = Table(
         'telemetry_command', metadata,
         Column('id', Integer, primary_key=True),
         Column('command', String(128), nullable=False),
     )
 
 telemetry_command_arg_table = Table(
         'telemetry_command_arg', metadata,
         Column('id', Integer, primary_key=True),
         Column('command_id', ForeignKey('telemetry_command.id')),
         Column('command_arg', String(256)),
     )
 
 telemetry_command_issue_table = Table(
         'telemetry_command_issue', metadata,
         Column('id', Integer, primary_key=True),
         Column('command_tag', String(16)),
         Column('command_id', ForeignKey('telemetry_command.id')),
         Column('command_arg_id', ForeignKey('telemetry_command_arg.id')),
         Column('session_id', ForeignKey('telemetry_session.id')),
     )
 
 telemetry_file_table = Table(
         'telemetry_file', metadata,
         Column('id', Integer, primary_key=True),
         Column('filepath', String(256)),
         Column('contents', Text),
     )
 
 telemetry_server_table = Table(
         'telemetry_server', metadata,
         Column('id', Integer, primary_key=True),
         Column('fqdn', String(64), nullable=False)
     )
 
 Index(
         'fqdn',
         telemetry_server_table.c.fqdn
     )
 
 telemetry_server_response_table = Table(
         'telemetry_server_response', metadata,
         Column('id', Integer, primary_key=True),
         Column('command_issue_id', ForeignKey('telemetry_command_issue.id')),
         Column('response', Text),
     )
 
 telemetry_session_table = Table(
         'telemetry_session', metadata,
         Column('id', Integer, primary_key=True),
         Column('pid', Integer, nullable=False),
         Column('user_id', ForeignKey('telemetry_user.id')),
         Column('server_id', ForeignKey('telemetry_server.id')),
         Column('start', Integer, nullable=False),
     )
 
 Index(
         'puss',
         telemetry_session_table.c.pid,
         telemetry_session_table.c.user_id,
         telemetry_session_table.c.server_id,
         telemetry_session_table.c.start,
         unique=True
     )
 
 telemetry_user_table = Table(
         'telemetry_user', metadata,
         Column('id', Integer, primary_key=True),
         Column('sasl_username', String(64), nullable=False),
         Column('created', Integer, nullable=False),
         Column('updated', Integer, nullable=False),
     )
 
 Index(
         'sasl_username',
         telemetry_user_table.c.sasl_username,
         unique=True
     )
 
 ##
 ## Table <-> Class Mappers
 ##
 
 mapper(TelemetryCommand, telemetry_command_table)
 mapper(TelemetryCommandArg, telemetry_command_arg_table)
 mapper(TelemetryCommandIssue, telemetry_command_issue_table)
 mapper(TelemetryFile, telemetry_file_table)
 mapper(TelemetryServer, telemetry_server_table)
 mapper(TelemetryServerResponse, telemetry_server_response_table)
 mapper(TelemetrySession, telemetry_session_table)
 mapper(TelemetryUser, telemetry_user_table)
 
 ##
 ## Functions
 ##
 
 def expire_sessions(retention=7):
     """
         Expire sessions older then 'retention' days
     """
     start_max = ((int)(time.time()) - (retention * 24 * 60 * 60))
     #start_max = (int)(time.time())
     log.info(_("Expiring sessions that started before or on %d") % (start_max))
 
     db = init_db()
 
     sessions = db.query(
             TelemetrySession
         ).filter(
                 telemetry_session_table.c.start <= start_max
             ).order_by(
                     telemetry_session_table.c.start
                 )
 
     for session in sessions:
         log.debug(_("Expiring session ID: %d") % (session.id), level=8)
 
         # Expire related information
         command_issue_ids = db.query(
                 TelemetryCommandIssue
             ).filter_by(session_id=session.id)
 
         for command_issue_id in command_issue_ids:
             # Expire server reponses
             server_responses = db.query(
                     TelemetryServerResponse
                 ).filter_by(
                         command_issue_id=command_issue_id.id
                     ).delete()
 
             db.delete(command_issue_id)
             db.commit()
 
         log.debug(
                 _("Session with ID %d expired from database") % (session.id),
                 level=8
             )
 
         db.delete(session)
         db.commit()
 
 def init_db():
     """
         Returns a SQLAlchemy Session() instance.
     """
 
     db = None
     db_uri = None
 
     if conf.has_section('kolab_telemetry'):
         if conf.has_option('kolab_telemetry', 'uri'):
             db_uri = conf.get('kolab_telemetry', 'uri')
 
     if not db_uri == None:
         echo = conf.debuglevel > 8
         engine = create_engine(db_uri, echo=echo)
 
         try:
             metadata.create_all(engine)
-        except sqlalchemy.exc.OperationalError, e:
+        except sqlalchemy.exc.OperationalError as e:
             log.error(_("Operational Error in telemetry database: %s" % (e)))
 
         Session = sessionmaker(bind=engine)
         db = Session()
 
     if db == None:
         log.error(_("No database available"))
 
     return db
diff --git a/pykolab/wap_client/__init__.py b/pykolab/wap_client/__init__.py
index 82f7ebb..956fbf5 100644
--- a/pykolab/wap_client/__init__.py
+++ b/pykolab/wap_client/__init__.py
@@ -1,661 +1,661 @@
 
 import json
 import httplib
 import urllib
 import sys
 from urlparse import urlparse
 
 import pykolab
 
 from pykolab import utils
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.wap_client')
 conf = pykolab.getConf()
 
 if not hasattr(conf, 'defaults'):
     conf.finalize_conf()
 
 API_HOSTNAME = "localhost"
 API_SCHEME = "http"
 API_PORT = 80
 API_SSL = False
 API_BASE = "/kolab-webadmin/api/"
 
 kolab_wap_url = conf.get('kolab_wap', 'api_url')
 
 if not kolab_wap_url == None:
     result = urlparse(kolab_wap_url)
 else:
     result = None
 
 if hasattr(result, 'scheme') and result.scheme == 'https':
     API_SSL = True
     API_PORT = 443
 
 if hasattr(result, 'hostname'):
     API_HOSTNAME = result.hostname
 
 if hasattr(result, 'port'):
     API_PORT = result.port
 
 if hasattr(result, 'path'):
     API_BASE = result.path
 
 session_id = None
 
 conn = None
 
 def authenticate(username=None, password=None, domain=None):
     global session_id
 
     if username == None:
         username = conf.get('ldap', 'bind_dn')
     if password == None:
         password = conf.get('ldap', 'bind_pw')
 
     if domain == None:
         domain = conf.get('kolab', 'primary_domain')
 
     post = json.dumps(
             {
                     'username': username,
                     'password': password,
                     'domain': domain
                 }
         )
 
     response = request('POST', "system.authenticate", post=post)
 
     if not response:
         return False
 
     if response.has_key('session_token'):
         session_id = response['session_token']
         return True
 
 def connect(uri=None):
     global conn, API_SSL, API_PORT, API_HOSTNAME, API_BASE
 
     if not uri == None:
         result = urlparse(uri)
 
         if hasattr(result, 'scheme') and result.scheme == 'https':
             API_SSL = True
             API_PORT = 443
 
         if hasattr(result, 'hostname'):
             API_HOSTNAME = result.hostname
 
         if hasattr(result, 'port'):
             API_PORT = result.port
 
         if hasattr(result, 'path'):
             API_BASE = result.path
 
     if conn == None:
         if API_SSL:
             conn = httplib.HTTPSConnection(API_HOSTNAME, API_PORT)
         else:
             conn = httplib.HTTPConnection(API_HOSTNAME, API_PORT)
 
         conn.connect()
 
     return conn
 
 def disconnect(quit=False):
     global conn, session_id
 
     if quit and session_id:
         request('GET', 'system.quit')
         session_id = None
 
     if conn:
         conn.close()
         conn = None
 
 def domain_add(domain, aliases=[]):
     dna = conf.get('ldap', 'domain_name_attribute')
 
     post = json.dumps({
             dna: [ domain ] + aliases
         })
 
     return request('POST', 'domain.add', post=post)
 
 def domain_delete(domain, force=False):
     domain_id, domain_attrs = domain_find(domain).popitem()
 
     param = {}
     param['id'] = domain_id
 
     if force:
         param['force'] = force
     post = json.dumps(param)
 
     return request('POST', 'domain.delete', post=post)
 
 def domain_find(domain):
     dna = conf.get('ldap', 'domain_name_attribute')
 
     get = { dna: domain }
 
     return request('GET', 'domain.find', get=get)
 
 def domain_info(domain):
     domain_id, domain_attrs = domain_find(domain)
 
     get = { 'id': domain_id }
 
     return request('GET', 'domain.info', get=get)
 
 def domains_capabilities():
     return request('GET', 'domains.capabilities')
 
 def domains_list():
     return request('GET', 'domains.list')
 
 def form_value_generate(params):
     post = json.dumps(params)
 
     return request('POST', 'form_value.generate', post=post)
 
 def form_value_generate_password(*args, **kw):
     return request('GET', 'form_value.generate_password')
 
 def form_value_list_options(object_type, object_type_id, attribute):
     post = json.dumps(
             {
                     'object_type': object_type,
                     'type_id': object_type_id,
                     'attribute': attribute
                 }
         )
 
     return request('POST', 'form_value.list_options', post=post)
 
 def form_value_select_options(object_type, object_type_id, attribute):
     post = json.dumps(
             {
                     'object_type': object_type,
                     'type_id': object_type_id,
                     'attributes': [ attribute ]
                 }
         )
 
     return request('POST', 'form_value.select_options', post=post)
 
 def get_group_input():
     group_types = group_types_list()
 
     if len(group_types.keys()) > 1:
         for key in group_types.keys():
             if not key == "status":
                 print("%s) %s" % (key,group_types[key]['name']))
 
         group_type_id = utils.ask_question("Please select the group type")
 
     elif len(group_types.keys()) > 0:
         print("Automatically selected the only group type available")
         group_type_id = group_types.keys()[0]
 
     else:
         print("No group types available")
         sys.exit(1)
 
     if group_types.has_key(group_type_id):
         group_type_info = group_types[group_type_id]['attributes']
     else:
         print("No such group type")
         sys.exit(1)
 
     params = {
             'group_type_id': group_type_id
         }
 
     for attribute in group_type_info['form_fields'].keys():
         params[attribute] = utils.ask_question(attribute)
 
     for attribute in group_type_info['auto_form_fields'].keys():
         exec("retval = group_form_value_generate_%s(params)" % (attribute))
         params[attribute] = retval[attribute]
 
     return params
 
 def get_user_input():
     user_types = user_types_list()
 
     if user_types['count'] > 1:
         print("")
         for key in user_types['list'].keys():
             if not key == "status":
                 print("%s) %s" % (key,user_types['list'][key]['name']))
 
         print("")
         user_type_id = utils.ask_question("Please select the user type")
 
     elif user_types['count'] > 0:
         print("Automatically selected the only user type available")
         user_type_id = user_types['list'].keys()[0]
 
     else:
         print("No user types available")
         sys.exit(1)
 
     if user_types['list'].has_key(user_type_id):
         user_type_info = user_types['list'][user_type_id]['attributes']
     else:
         print("No such user type")
         sys.exit(1)
 
     params = {
             'object_type': 'user',
             'type_id': user_type_id
         }
 
     must_attrs = []
     may_attrs = []
 
     for attribute in user_type_info['form_fields'].keys():
         if isinstance(user_type_info['form_fields'][attribute], dict):
             if user_type_info['form_fields'][attribute].has_key('optional') and user_type_info['form_fields'][attribute]['optional']:
                 may_attrs.append(attribute)
             else:
                 must_attrs.append(attribute)
         else:
             must_attrs.append(attribute)
 
     for attribute in must_attrs:
         if isinstance(user_type_info['form_fields'][attribute], dict) and \
                 user_type_info['form_fields'][attribute].has_key('type'):
 
             if user_type_info['form_fields'][attribute]['type'] == 'select':
                 if not user_type_info['form_fields'][attribute].has_key('values'):
                     attribute_values = form_value_select_options('user', user_type_id, attribute)
 
                     default = ''
                     if attribute_values[attribute].has_key('default'):
                         default = attribute_values[attribute]['default']
 
                     params[attribute] = utils.ask_menu(
                             "Choose the %s value" % (attribute),
                             attribute_values[attribute]['list'],
                             default=default
                         )
 
                 else:
                     default = ''
                     if user_type_info['form_fields'][attribute].has_key('default'):
                         default = user_type_info['form_fields'][attribute]['default']
 
                     params[attribute] = utils.ask_menu(
                             "Choose the %s value" % (attribute),
                             user_type_info['form_fields'][attribute]['values'],
                             default=default
                         )
 
             else:
                 params[attribute] = utils.ask_question(attribute)
 
         else:
             params[attribute] = utils.ask_question(attribute)
 
     for attribute in user_type_info['fields'].keys():
         params[attribute] = user_type_info['fields'][attribute]
 
     exec("retval = user_form_value_generate(params)")
     print(retval)
 
     return params
 
 def group_add(params=None):
     if params == None:
         params = get_group_input()
 
     post = json.dumps(params)
 
     return request('POST', 'group.add', post=post)
 
 def group_delete(params=None):
     if params == None:
         params = {
                 'id': utils.ask_question("Name of group to delete", "group")
             }
 
     post = json.dumps(params)
 
     return request('POST', 'group.delete', post=post)
 
 def group_form_value_generate_mail(params=None):
     if params == None:
         params = get_user_input()
 
     params = json.dumps(params)
 
     return request('POST', 'group_form_value.generate_mail', params)
 
 def group_find(params=None):
     post = { 'search': { 'params': {} } }
 
     for (k,v) in params.iteritems():
         post['search']['params'][k] = { 'value': v, 'type': 'exact' }
 
     return request('POST', 'group.find', post=json.dumps(post))
 
 def group_info(group=None):
     if group == None:
         group = utils.ask_question("group DN")
     return request('GET', 'group.info', get={ 'id': group })
 
 def group_members_list(group=None):
     if group == None:
         group = utils.ask_question("Group email address")
     group = request('GET', 'group.members_list?group=%s' % (group))
     return group
 
 def group_types_list():
     return request('GET', 'group_types.list')
 
 def groups_list(params={}):
     return request('POST', 'groups.list', post=json.dumps(params))
 
 def ou_add(params={}):
     return request('POST', 'ou.add', post=json.dumps(params))
 
 def ou_delete(params={}):
     return request('POST', 'ou.delete', post=json.dumps(params))
 
 def ou_edit(params={}):
     return request('POST', 'ou.edit', post=json.dumps(params))
 
 def ou_find(params=None):
     post = { 'search': { 'params': {} } }
 
     for (k,v) in params.iteritems():
         post['search']['params'][k] = { 'value': v, 'type': 'exact' }
 
     return request('POST', 'ou.find', post=json.dumps(post))
 
 def ou_info(ou):
     _params = { 'id': ou }
 
     ou = request('GET', 'ou.info', get=_params)
 
     return ou
 
 def ous_list(params={}):
     return request('POST', 'ous.list', post=json.dumps(params))
 
 def request(method, api_uri, get=None, post=None, headers={}):
     response_data = request_raw(method, api_uri, get, post, headers)
 
     if response_data['status'] == "OK":
         del response_data['status']
         return response_data['result']
     else:
         print("%s: %s (code %s)" % (response_data['status'], response_data['reason'], response_data['code']))
         return False
 
 def request_raw(method, api_uri, get=None, post=None, headers={}, isretry=False):
     global session_id
 
     if not session_id == None:
         headers["X-Session-Token"] = session_id
 
     reconnect = False
     conn = connect()
 
     if conf.debuglevel > 8:
         conn.set_debuglevel(9)
 
     if not get == None:
         _get = "?%s" % (urllib.urlencode(get))
     else:
         _get = ""
 
     log.debug(_("Requesting %r with params %r") % ("%s/%s" % (API_BASE,api_uri), (get, post)), level=8)
 
     try:
         conn.request(method.upper(), "%s/%s%s" % (API_BASE, api_uri, _get), post, headers)
 
         response = conn.getresponse()
         data = response.read()
 
         log.debug(_("Got response: %r") % (data), level=8)
 
     except (httplib.BadStatusLine, httplib.CannotSendRequest) as e:
         if isretry:
             raise e
         log.info(_("Connection error: %r; re-connecting..."), e)
         reconnect = True
 
     # retry with a new connection
     if reconnect:
         disconnect()
         return request_raw(method, api_uri, get, post, headers, True)
 
     try:
         response_data = json.loads(data)
-    except ValueError, e:
+    except ValueError:
         # Some data is not JSON
         log.error(_("Response data is not JSON"))
 
     return response_data
 
 def resource_add(params=None):
     if params == None:
         params = get_user_input()
 
     return request('POST', 'resource.add', post=json.dumps(params))
 
 def resource_delete(params=None):
     if params == None:
         params = {
             'id': utils.ask_question("Resource DN to delete", "resource")
         }
 
     return request('POST', 'resource.delete', post=json.dumps(params))
 
 def resource_find(params=None):
     post = { 'search': { 'params': {} } }
 
     for (k,v) in params.iteritems():
         post['search']['params'][k] = { 'value': v, 'type': 'exact' }
 
     return request('POST', 'resource.find', post=json.dumps(post))
 
 def resource_info(resource=None):
     if resource == None:
         resource = utils.ask_question("Resource DN")
     return request('GET', 'resource.info', get={ 'id': resource })
 
 def resource_types_list():
     return request('GET', 'resource_types.list')
 
 def resources_list(params={}):
     return request('POST', 'resources.list', post=json.dumps(params))
 
 def role_add(params=None):
     if params == None:
         role_name = utils.ask_question("Role name")
         params = {
                 'cn': role_name
             }
 
     params = json.dumps(params)
 
     return request('POST', 'role.add', params)
 
 def role_capabilities():
     return request('GET', 'role.capabilities')
 
 def role_delete(params=None):
     if params == None:
         role_name = utils.ask_question("Role name")
         role = role_find_by_attribute({'cn': role_name})
         params = {
                 'role': role.keys()[0]
             }
 
     if not params.has_key('role'):
         role = role_find_by_attribute(params)
         params = {
                 'role': role.keys()[0]
             }
 
     post = json.dumps(params)
 
     return request('POST', 'role.delete', post=post)
 
 def role_find_by_attribute(params=None):
     if params == None:
         role_name = utils.ask_question("Role name")
     else:
         role_name = params['cn']
 
     get = { 'cn': role_name }
     role = request('GET', 'role.find_by_attribute', get=get)
 
     return role
 
 def role_info(role_name):
     role = role_find_by_attribute({'cn': role_name})
 
     get = { 'role': role['id'] }
 
     role = request('GET', 'role.info', get=get)
 
     return role
 
 def roles_list():
     return request('GET', 'roles.list')
 
 def sharedfolder_add(params=None):
     if params == None:
         params = get_user_input()
 
     return request('POST', 'sharedfolder.add', post=json.dumps(params))
 
 def sharedfolder_delete(params=None):
     if params == None:
         params = {
             'id': utils.ask_question("Shared Folder DN to delete", "sharedfolder")
         }
 
     return request('POST', 'sharedfolder.delete', post=json.dumps(params))
 
 def sharedfolders_list(params={}):
     return request('POST', 'sharedfolders.list', post=json.dumps(params))
 
 def system_capabilities(domain=None):
     return request('GET', 'system.capabilities', get={'domain':domain})
 
 def system_get_domain():
     return request('GET', 'system.get_domain')
 
 def system_select_domain(domain=None):
     if domain == None:
         domain = utils.ask_question("Domain name")
 
     get = { 'domain': domain }
 
     return request('GET', 'system.select_domain', get=get)
 
 def user_add(params=None):
     if params == None:
         params = get_user_input()
 
     params = json.dumps(params)
 
     return request('POST', 'user.add', post=params)
 
 def user_delete(params=None):
     if params == None:
         params = {
                 'id': utils.ask_question("Username for user to delete", "user")
             }
 
     post = json.dumps(params)
 
     return request('POST', 'user.delete', post=post)
 
 def user_edit(user = None, attributes={}):
     if user == None:
         get = {
                 'id': utils.ask_question("Username for user to edit", "user")
             }
     else:
         get = {
                 'id': user
             }
 
     user_info = request('GET', 'user.info', get=get)
 
     for attribute in attributes.keys():
         user_info[attribute] = attributes[attribute]
 
     post = json.dumps(user_info)
 
     user_edit = request('POST', 'user.edit', get=get, post=post)
 
     return user_edit
 
 def user_find(attribs=None):
     if attribs == None:
         post = {
                 'search': {
                         'params': {
                                 utils.ask_question("Attribute") : {
                                         'value': utils.ask_question("value"),
                                         'type': 'exact'
                                     }
                             }
                     }
             }
     else:
         post = { 'search': { 'params': {} } }
 
         for (k,v) in attribs.iteritems():
             post['search']['params'][k] = { 'value': v, 'type': 'exact' }
 
     post = json.dumps(post)
 
     user = request('POST', 'user.find', post=post)
 
     return user
 
 def user_form_value_generate(params=None):
     if params == None:
         params = get_user_input()
 
     post = json.dumps(params)
 
     return request('POST', 'form_value.generate', post=post)
 
 def user_form_value_generate_uid(params=None):
     if params == None:
         params = get_user_input()
 
     params = json.dumps(params)
 
     return request('POST', 'form_value.generate_uid', params)
 
 def user_form_value_generate_userpassword(*args, **kw):
     result = form_value_generate_password()
     return { 'userpassword': result['password'] }
 
 def user_info(user=None):
     if user == None:
         user = utils.ask_question("User email address")
 
     _params = { 'id': user }
 
     user = request('GET', 'user.info', get=_params)
 
     return user
 
 def users_list(params={}):
     return request('POST', 'users.list', post=json.dumps(params))
 
 def user_types_list():
     return request('GET', 'user_types.list')
diff --git a/pykolab/xml/attendee.py b/pykolab/xml/attendee.py
index 41447db..099e21a 100644
--- a/pykolab/xml/attendee.py
+++ b/pykolab/xml/attendee.py
@@ -1,280 +1,280 @@
 import kolabformat
 
 from pykolab.translate import _
 from pykolab.translate import N_
 
 from contact_reference import ContactReference
 
 participant_status_labels = {
         "NEEDS-ACTION": N_("Needs Action"),
         "ACCEPTED": N_("Accepted"),
         "DECLINED": N_("Declined"),
         "TENTATIVE": N_("Tentatively Accepted"),
         "DELEGATED": N_("Delegated"),
         "IN-PROCESS": N_("Started"),
         "COMPLETED": N_("Completed"),
         "PENDING": N_("Pending"),
         # support integer values, too
         kolabformat.PartNeedsAction: N_("Needs Action"),
         kolabformat.PartAccepted: N_("Accepted"),
         kolabformat.PartDeclined: N_("Declined"),
         kolabformat.PartTentative: N_("Tentatively Accepted"),
         kolabformat.PartDelegated: N_("Delegated"),
         kolabformat.PartInProcess: N_("Started"),
         kolabformat.PartCompleted: N_("Completed"),
     }
 
 def participant_status_label(status):
     return _(participant_status_labels[status]) if participant_status_labels.has_key(status) else _(status)
 
 
 class Attendee(kolabformat.Attendee):
     cutype_map = {
             "INDIVIDUAL": kolabformat.CutypeIndividual,
             "RESOURCE": kolabformat.CutypeResource,
             "GROUP": kolabformat.CutypeGroup,
             "ROOM": kolabformat.CutypeRoom,
             "UNKNOWN": kolabformat.CutypeUnknown,
         }
 
     participant_status_map = {
             "NEEDS-ACTION": kolabformat.PartNeedsAction,
             "ACCEPTED": kolabformat.PartAccepted,
             "DECLINED": kolabformat.PartDeclined,
             "TENTATIVE": kolabformat.PartTentative,
             "DELEGATED": kolabformat.PartDelegated,
             "IN-PROCESS": kolabformat.PartInProcess,
             "COMPLETED": kolabformat.PartCompleted,
         }
 
     # See RFC 2445, 5445
     role_map = {
             "CHAIR": kolabformat.Chair,
             "REQ-PARTICIPANT": kolabformat.Required,
             "OPT-PARTICIPANT": kolabformat.Optional,
             "NON-PARTICIPANT": kolabformat.NonParticipant,
         }
 
     rsvp_map = {
             "TRUE": True,
             "FALSE": False,
         }
 
     properties_map = {
             'role': 'get_role',
             'rsvp':  'rsvp',
             'partstat':  'get_participant_status',
             'cutype':   'get_cutype',
             'delegated-to': 'get_delegated_to',
             'delegated-from': 'get_delegated_from',
         }
 
     def __init__(
             self,
             email,
             name=None,
             rsvp=False,
             role=None,
             participant_status=None,
             cutype=None,
             ical_params=None
         ):
 
         self.email = email
 
         self.contactreference = ContactReference(email)
 
         if not name == None:
             self.contactreference.set_name(name)
 
         kolabformat.Attendee.__init__(self, self.contactreference)
 
         if isinstance(rsvp, bool):
             self.setRSVP(rsvp)
         else:
             if self.rsvp_map.has_key(rsvp):
                 self.setRSVP(self.rsvp_map[rsvp])
 
         if not role == None:
             self.set_role(role)
 
         if not cutype == None:
             self.set_cutype(cutype)
 
         if ical_params and ical_params.has_key('DELEGATED-FROM'):
             self.delegate_from(Attendee(str(ical_params['DELEGATED-FROM']), role=self.get_role(), cutype=self.get_cutype()))
 
         if ical_params and ical_params.has_key('DELEGATED-TO'):
             self.delegate_to(Attendee(str(ical_params['DELEGATED-TO'])))
 
         if not participant_status == None:
             self.set_participant_status(participant_status)
 
     def copy_from(self, obj):
         if isinstance(obj, kolabformat.Attendee):
             self.contactreference = ContactReference(obj.contact())
             self.email = self.contactreference.get_email()
             self.setContact(self.contactreference)
 
             # manually copy all properities, copy constructor doesn't work :-(
             self.setRSVP(obj.rsvp())
             self.setRole(obj.role())
             self.setCutype(obj.cutype())
             self.setPartStat(obj.partStat())
             self.setDelegatedTo(obj.delegatedTo())
             self.setDelegatedFrom(obj.delegatedFrom())
 
     def delegate_from(self, delegators):
         crefs = []
 
         if not isinstance(delegators, list):
             delegators = [delegators]
 
         for delegator in delegators:
             if not isinstance(delegator, Attendee):
-                raise ValueError, _("Not a valid attendee")
+                raise ValueError(_("Not a valid attendee"))
             else:
                 self.set_role(delegator.get_role())
                 self.set_cutype(delegator.get_cutype())
                 crefs.append(delegator.contactreference)
 
         if len(crefs) == 0:
-            raise ValueError, _("No valid delegator references found")
+            raise ValueError(_("No valid delegator references found"))
         else:
             crefs += self.get_delegated_from()
 
         self.setDelegatedFrom(list(set(crefs)))
 
     def delegate_to(self, delegatees):
         self.set_participant_status("DELEGATED")
 
         crefs = []
         if not isinstance(delegatees, list):
             delegatees = [delegatees]
 
         for delegatee in delegatees:
             if not isinstance(delegatee, Attendee):
-                raise ValueError, _("Not a valid attendee")
+                raise ValueError(_("Not a valid attendee"))
             else:
                 crefs.append(delegatee.contactreference)
 
         if len(crefs) == 0:
-            raise ValueError, _("No valid delegatee references found")
+            raise ValueError(_("No valid delegatee references found"))
         else:
             crefs += self.get_delegated_to()
 
         self.setDelegatedTo(list(set(crefs)))
 
     def get_cutype(self, translated=False):
         cutype = self.cutype()
         if translated:
             return self._translate_value(cutype, self.cutype_map)
         return cutype
 
     def get_delegated_from(self, translated=False):
         delegators = []
         for cr in self.delegatedFrom():
             delegators.append(cr.email() if translated else ContactReference(cr))
         return delegators
 
     def get_delegated_to(self, translated=False):
         delegatees = []
         for cr in self.delegatedTo():
             delegatees.append(cr.email() if translated else ContactReference(cr))
         return delegatees
 
     def get_email(self):
         return self.contactreference.get_email()
 
     def get_name(self):
         return self.contactreference.get_name()
 
     def get_displayname(self):
         name = self.contactreference.get_name()
         email = self.contactreference.get_email()
         return "%s <%s>" % (name, email) if not name == "" else email
 
     def get_participant_status(self, translated=False):
         partstat = self.partStat()
         if translated:
             return self._translate_value(partstat, self.participant_status_map)
         return partstat
 
     def get_role(self, translated=False):
         role = self.role()
         if translated:
             return self._translate_value(role, self.role_map)
         return role
 
     def get_rsvp(self):
         return self.rsvp()
 
     def _translate_value(self, val, map):
         name_map = dict([(v, k) for (k, v) in map.iteritems()])
         return name_map[val] if name_map.has_key(val) else 'UNKNOWN'
 
     def set_cutype(self, cutype):
         if cutype in self.cutype_map.keys():
             self.setCutype(self.cutype_map[cutype])
         elif cutype in self.cutype_map.values():
             self.setCutype(cutype)
         else:
-            raise InvalidAttendeeCutypeError, _("Invalid cutype %r") % (cutype)
+            raise InvalidAttendeeCutypeError(_("Invalid cutype %r") % (cutype))
 
     def set_name(self, name):
         self.contactreference.set_name(name)
         self.setContact(self.contactreference)
 
     def set_participant_status(self, participant_status):
         if participant_status in self.participant_status_map.keys():
             self.setPartStat(self.participant_status_map[participant_status])
         elif participant_status in self.participant_status_map.values():
             self.setPartStat(participant_status)
         else:
-            raise InvalidAttendeeParticipantStatusError, _("Invalid participant status %r") % (participant_status)
+            raise InvalidAttendeeParticipantStatusError(_("Invalid participant status %r") % (participant_status))
 
     def set_role(self, role):
         if role in self.role_map.keys():
             self.setRole(self.role_map[role])
         elif role in self.role_map.values():
             self.setRole(role)
         else:
-            raise InvalidAttendeeRoleError, _("Invalid role %r") % (role)
+            raise InvalidAttendeeRoleError(_("Invalid role %r") % (role))
 
     def set_rsvp(self, rsvp):
         self.setRSVP(rsvp)
 
     def to_dict(self):
         data = self.contactreference.to_dict()
         data.pop('type', None)
 
         for p, getter in self.properties_map.iteritems():
             val = None
             args = {}
             if hasattr(self, getter):
                 if getter.startswith('get_'):
                     args = dict(translated=True)
                 val = getattr(self, getter)(**args)
             if val is not None:
                 data[p] = val
 
         return data
 
     def __str__(self):
         return self.email
 
 class AttendeeIntegrityError(Exception):
     def __init__(self, message):
         Exception.__init__(self, message)
 
 class InvalidAttendeeCutypeError(Exception):
     def __init__(self, message):
         Exception.__init__(self, message)
 
 class InvalidAttendeeParticipantStatusError(Exception):
     def __init__(self, message):
         Exception.__init__(self, message)
 
 class InvalidAttendeeRoleError(Exception):
     def __init__(self, message):
         Exception.__init__(self, message)
diff --git a/pykolab/xml/contact.py b/pykolab/xml/contact.py
index 2b08f9a..f4fd9a5 100644
--- a/pykolab/xml/contact.py
+++ b/pykolab/xml/contact.py
@@ -1,336 +1,336 @@
 import kolabformat
 import datetime
 import pytz
 import base64
 
 from pykolab.xml import utils as xmlutils
 from pykolab.xml.utils import ustr
 
 def contact_from_vcard(string):
     # TODO: implement this
     pass
 
 def contact_from_string(string):
     _xml = kolabformat.readContact(string, False)
     return Contact(_xml)
 
 def contact_from_message(message):
     contact = None
     if message.is_multipart():
         for part in message.walk():
             if part.get_content_type() == "application/vcard+xml":
                 payload = part.get_payload(decode=True)
                 contact = contact_from_string(payload)
 
             # append attachment parts to Contact object
             elif contact and part.has_key('Content-ID'):
                 contact._attachment_parts.append(part)
 
     return contact
 
 
 class Contact(kolabformat.Contact):
     type = 'contact'
 
     related_map = {
         'manager': kolabformat.Related.Manager,
         'assistant': kolabformat.Related.Assistant,
         'spouse': kolabformat.Related.Spouse,
         'children': kolabformat.Related.Child,
         None: kolabformat.Related.NoRelation,
     }
 
     addresstype_map = {
         'home': kolabformat.Address.Home,
         'work': kolabformat.Address.Work,
     }
 
     phonetype_map = {
         'home':    kolabformat.Telephone.Home,
         'work':    kolabformat.Telephone.Work,
         'text':    kolabformat.Telephone.Text,
         'main':    kolabformat.Telephone.Voice,
         'homefax': kolabformat.Telephone.Fax,
         'workfax': kolabformat.Telephone.Fax,
         'mobile':  kolabformat.Telephone.Cell,
         'video':   kolabformat.Telephone.Video,
         'pager':   kolabformat.Telephone.Pager,
         'car':     kolabformat.Telephone.Car,
         'other':   kolabformat.Telephone.Textphone,
     }
 
     emailtype_map = {
         'home': kolabformat.Email.Home,
         'work': kolabformat.Email.Work,
         'other': kolabformat.Email.Work,
     }
 
     urltype_map = {
         'homepage': kolabformat.Url.NoType,
         'blog': kolabformat.Url.Blog,
     }
 
     keytype_map = {
         'pgp': kolabformat.Key.PGP,
         'pkcs7': kolabformat.Key.PKCS7_MIME,
         None: kolabformat.Key.Invalid,
     }
 
     gender_map = {
         'female': kolabformat.Contact.Female,
         'male': kolabformat.Contact.Male,
         None: kolabformat.Contact.NotSet,
     }
 
     properties_map = {
         'uid':               'get_uid',
         'lastmodified-date': 'get_lastmodified',
         'fn':                'name',
         'nickname':          'nickNames',
         'title':             'titles',
         'email':             'emailAddresses',
         'tel':               'telephones',
         'url':               'urls',
         'im':                'imAddresses',
         'address':           'addresses',
         'note':              'note',
         'freebusyurl':       'freeBusyUrl',
         'birthday':          'bDay',
         'anniversary':       'anniversary',
         'categories':        'categories',
         'lang':              'languages',
         'gender':            'get_gender',
         'gpspos':            'gpsPos',
         'key':               'keys',
     }
 
     def __init__(self, *args, **kw):
         self._attachment_parts = []
         kolabformat.Contact.__init__(self, *args, **kw)
 
     def get_uid(self):
         uid = self.uid()
         if not uid == '':
             return uid
         else:
             self.__str__()
             return kolabformat.getSerializedUID()
 
     def get_lastmodified(self):
         try:
             _datetime = self.lastModified()
             if _datetime == None or not _datetime.isValid():
                 self.__str__()
         except:
             return datetime.datetime.now(pytz.utc)
 
         return xmlutils.from_cdatetime(self.lastModified(), True)
 
     def get_email(self, preferred=True):
         if preferred:
             return self.emailAddresses()[self.emailAddressPreferredIndex()]
         else:
             return [x for x in self.emailAddresses()]
 
     def set_email(self, email, preferred_index=0):
         if isinstance(email, basestring):
             self.setEmailAddresses([email], preferred_index)
         else:
             self.setEmailAddresses(email, preferred_index)
 
     def add_email(self, email):
         if isinstance(email, basestring):
             self.add_emails([email])
         elif isinstance(email, list):
             self.add_emails(email)
 
     def add_emails(self, emails):
         preferred_email = self.get_email()
         emails = [x for x in set(self.get_email(preferred=False) + emails)]
         preferred_email_index = emails.index(preferred_email)
         self.setEmailAddresses(emails, preferred_email_index)
 
     def set_name(self, name):
         self.setName(ustr(name))
 
     def get_gender(self, translated=True):
         _gender = self.gender()
         if translated:
             return self._translate_value(_gender, self.gender_map)
         return _gender
 
     def _translate_value(self, val, map):
         name_map = dict([(v, k) for (k, v) in map.iteritems()])
         return name_map[val] if name_map.has_key(val) else 'UNKNOWN'
 
     def to_dict(self):
         if not self.isValid():
             return None
 
         data = self._names2dict(self.nameComponents())
 
         for p, getter in self.properties_map.iteritems():
             val = None
             if hasattr(self, getter):
                 val = getattr(self, getter)()
             if isinstance(val, kolabformat.cDateTime):
                 val = xmlutils.from_cdatetime(val, True)
             elif isinstance(val, kolabformat.vectori):
                 val = [int(x) for x in val]
             elif isinstance(val, kolabformat.vectors):
                 val = [str(x) for x in val]
             elif isinstance(val, kolabformat.vectortelephone):
                 val = [self._struct2dict(x, 'number', self.phonetype_map) for x in val]
             elif isinstance(val, kolabformat.vectoremail):
                 val = [self._struct2dict(x, 'address', self.emailtype_map) for x in val]
             elif isinstance(val, kolabformat.vectorurl):
                 val = [self._struct2dict(x, 'url', self.urltype_map) for x in val]
             elif isinstance(val, kolabformat.vectorkey):
                 val = [self._struct2dict(x, 'key', self.keytype_map) for x in val]
             elif isinstance(val, kolabformat.vectoraddress):
                 val = [self._address2dict(x) for x in val]
             elif isinstance(val, kolabformat.vectorgeo):
                 val = [[x.latitude, x.longitude] for x in val]
 
             if val is not None:
                 data[p] = val
 
         affiliations = self.affiliations()
         if len(affiliations) > 0:
             _affiliation = self._affiliation2dict(affiliations[0])
             if _affiliation.has_key('address'):
                 data['address'].extend(_affiliation['address'])
                 _affiliation.pop('address', None)
             data.update(_affiliation)
 
         data.update(self._relateds2dict(self.relateds()))
 
         if self.photoMimetype():
             data['photo'] = dict(mimetype=self.photoMimetype(), base64=base64.b64encode(self.photo()))
         elif self.photo():
             data['photo'] = dict(uri=self.photo())
 
         return data
 
     def _names2dict(self, namecomp):
         names_map = {
             'surname':    'surnames',
             'given':      'given',
             'additional': 'additional',
             'prefix':     'prefixes',
             'suffix':     'suffixes',
         }
 
         data = dict()
 
         for p, getter in names_map.iteritems():
             val = None
             if hasattr(namecomp, getter):
                 val = getattr(namecomp, getter)()
             if isinstance(val, kolabformat.vectors):
                 val = [str(x) for x in val][0] if len(val) > 0 else None
             if val is not None:
                 data[p] = val
 
         return data
 
     def _affiliation2dict(self, affiliation):
         props_map = {
             'organization': 'organisation',
             'department':   'organisationalUnits',
             'role':         'roles',
         }
 
         data = dict()
 
         for p, getter in props_map.iteritems():
             val = None
             if hasattr(affiliation, getter):
                 val = getattr(affiliation, getter)()
             if isinstance(val, kolabformat.vectors):
                 val = [str(x) for x in val][0] if len(val) > 0 else None
             if val is not None:
                 data[p] = val
 
         data.update(self._relateds2dict(affiliation.relateds(), True))
 
         addresses = affiliation.addresses()
         if len(addresses):
             data['address'] = [self._address2dict(adr, 'office') for adr in addresses]
 
         return data
 
     def _address2dict(self, adr, adrtype=None):
         props_map = {
             'label':    'label',
             'street':   'street',
             'locality': 'locality',
             'region':   'region',
             'code':     'code',
             'country':  'country',
         }
         addresstype_map = dict([(v, k) for (k, v) in self.addresstype_map.iteritems()])
 
         data = dict()
 
         if adrtype is None:
             adrtype = addresstype_map.get(adr.types(), None)
 
         if adrtype is not None:
             data['type'] = adrtype
 
         for p, getter in props_map.iteritems():
             val = None
             if hasattr(adr, getter):
                 val = getattr(adr, getter)()
             if isinstance(val, kolabformat.vectors):
                 val = [str(x) for x in val][0] if len(val) > 0 else None
             if val is not None:
                 data[p] = val
 
         return data
 
     def _relateds2dict(self, relateds, aslist=True):
         data = dict()
 
         related_map = dict([(v, k) for (k, v) in self.related_map.iteritems()])
         for rel in relateds:
             reltype = related_map.get(rel.relationTypes(), None)
             val = rel.uri() if rel.type() == kolabformat.Related.Uid else rel.text()
             if reltype and val is not None:
                 if aslist:
                     if not data.has_key(reltype):
                         data[reltype] = []
                     data[reltype].append(val)
                 else:
                     data[reltype] = val
 
         return data
 
     def _struct2dict(self, struct, propname, map):
         type_map = dict([(v, k) for (k, v) in map.iteritems()])
         result = dict()
 
         if hasattr(struct, 'types'):
             result['type'] = type_map.get(struct.types(), None)
         elif hasattr(struct, 'type'):
             result['type'] = type_map.get(struct.type(), None)
 
         if hasattr(struct, propname):
             result[propname] = getattr(struct, propname)()
 
         return result
 
     def __str__(self):
         xml = kolabformat.writeContact(self)
         error = kolabformat.error()
 
         if error == None or not error:
             return xml
         else:
-            raise ContactIntegrityError, kolabformat.errorMessage()
+            raise ContactIntegrityError(kolabformat.errorMessage())
 
 
 class ContactIntegrityError(Exception):
     def __init__(self, message):
         Exception.__init__(self, message)
diff --git a/pykolab/xml/event.py b/pykolab/xml/event.py
index a668f77..3fa5cdc 100644
--- a/pykolab/xml/event.py
+++ b/pykolab/xml/event.py
@@ -1,1511 +1,1511 @@
 import datetime
 import icalendar
 import kolabformat
 import pytz
 import time
 import uuid
 import base64
 import re
 
 import pykolab
 from pykolab import constants
 from pykolab import utils
 from pykolab.xml import utils as xmlutils
 from pykolab.xml import participant_status_label
 from pykolab.xml.utils import ustr
 from pykolab.translate import _
 
 from os import path
 from attendee import Attendee
 from contact_reference import ContactReference
 from recurrence_rule import RecurrenceRule
 from collections import OrderedDict
 
 log = pykolab.getLogger('pykolab.xml_event')
 
 
 def event_from_ical(ical, string=None):
     return Event(from_ical=ical, from_string=string)
 
 def event_from_string(string):
     return Event(from_string=string)
 
 def event_from_message(message):
     event = None
     if message.is_multipart():
         for part in message.walk():
             if part.get_content_type() == "application/calendar+xml":
                 payload = part.get_payload(decode=True)
                 event = event_from_string(payload)
 
             # append attachment parts to Event object
             elif event and part.has_key('Content-ID'):
                 event._attachment_parts.append(part)
 
     return event
 
 
 class Event(object):
     type = 'event'
     thisandfuture = False
 
     status_map = {
             None: kolabformat.StatusUndefined,
             "TENTATIVE": kolabformat.StatusTentative,
             "CONFIRMED": kolabformat.StatusConfirmed,
             "CANCELLED": kolabformat.StatusCancelled,
             "COMPLETED":  kolabformat.StatusCompleted,
             "IN-PROCESS": kolabformat.StatusInProcess,
             "NEEDS-ACTION": kolabformat.StatusNeedsAction,
         }
 
     classification_map = {
             "PUBLIC": kolabformat.ClassPublic,
             "PRIVATE": kolabformat.ClassPrivate,
             "CONFIDENTIAL": kolabformat.ClassConfidential,
         }
 
     alarm_type_map = {
             'EMAIL': kolabformat.Alarm.EMailAlarm,
             'DISPLAY': kolabformat.Alarm.DisplayAlarm,
             'AUDIO': kolabformat.Alarm.AudioAlarm
         }
 
     related_map = {
             'START': kolabformat.Start,
             'END': kolabformat.End
         }
 
     properties_map = {
         # property: getter
         "uid": "get_uid",
         "created": "get_created",
         "lastmodified-date": "get_lastmodified",
         "sequence": "sequence",
         "classification": "get_classification",
         "categories": "categories",
         "start": "get_start",
         "end": "get_end",
         "duration": "get_duration",
         "transparency": "transparency",
         "rrule": "recurrenceRule",
         "rdate": "recurrenceDates",
         "exdate": "exceptionDates",
         "recurrence-id": "recurrenceID",
         "summary": "summary",
         "description": "description",
         "priority": "priority",
         "status": "get_ical_status",
         "location": "location",
         "organizer": "organizer",
         "attendee": "get_attendees",
         "attach": "attachments",
         "url": "url",
         "alarm": "alarms",
         "x-custom": "customProperties",
         # TODO: add to_dict() support for these
         # "exception": "exceptions",
     }
 
     def __init__(self, from_ical="", from_string=""):
         self._attendees = []
         self._categories = []
         self._exceptions = []
         self._attachment_parts = []
 
         if isinstance(from_ical, str) and from_ical == "":
             if from_string == "":
                 self.event = kolabformat.Event()
             else:
                 self.event = kolabformat.readEvent(from_string, False)
                 self._load_attendees()
                 self._load_exceptions()
         else:
             self.from_ical(from_ical, from_string)
 
         self.set_created(self.get_created())
         self.uid = self.get_uid()
 
     def _load_attendees(self):
         for a in self.event.attendees():
             att = Attendee(a.contact().email())
             att.copy_from(a)
             self._attendees.append(att)
 
     def _load_exceptions(self):
         for ex in self.event.exceptions():
             exception = Event()
             exception.uid = ex.uid()
             exception.event = ex
             exception._load_attendees()
             self._exceptions.append(exception)
 
     def add_attendee(self, email_or_attendee, name=None, rsvp=False, role=None, participant_status=None, cutype="INDIVIDUAL", params=None):
         if isinstance(email_or_attendee, Attendee):
             attendee = email_or_attendee
         else:
             attendee = Attendee(email_or_attendee, name, rsvp, role, participant_status, cutype, params)
 
         # apply update to self and all exceptions
         self.update_attendees([attendee], True)
 
     def add_category(self, category):
         self._categories.append(ustr(category))
         self.event.setCategories(self._categories)
 
     def add_recurrence_date(self, _datetime):
         valid_datetime = False
         if isinstance(_datetime, datetime.date):
             valid_datetime = True
 
         if isinstance(_datetime, datetime.datetime):
             # If no timezone information is passed on, make it UTC
             if _datetime.tzinfo is None:
                 _datetime = _datetime.replace(tzinfo=pytz.utc)
 
             valid_datetime = True
 
         if not valid_datetime:
-            raise InvalidEventDateError, _("Rdate needs datetime.date or datetime.datetime instance, got %r") % (type(_datetime))
+            raise InvalidEventDateError(_("Rdate needs datetime.date or datetime.datetime instance, got %r") % (type(_datetime)))
 
         self.event.addRecurrenceDate(xmlutils.to_cdatetime(_datetime, True))
 
     def add_exception_date(self, _datetime):
         valid_datetime = False
         if isinstance(_datetime, datetime.date):
             valid_datetime = True
 
         if isinstance(_datetime, datetime.datetime):
             # If no timezone information is passed on, make it UTC
             if _datetime.tzinfo == None:
                 _datetime = _datetime.replace(tzinfo=pytz.utc)
 
             valid_datetime = True
 
         if not valid_datetime:
-            raise InvalidEventDateError, _("Exdate needs datetime.date or datetime.datetime instance, got %r") % (type(_datetime))
+            raise InvalidEventDateError(_("Exdate needs datetime.date or datetime.datetime instance, got %r") % (type(_datetime)))
 
         self.event.addExceptionDate(xmlutils.to_cdatetime(_datetime, True))
 
     def add_exception(self, exception):
         recurrence_id = exception.get_recurrence_id()
         if recurrence_id is None:
-            raise EventIntegrityError, "Recurrence exceptions require a Recurrence-ID property"
+            raise EventIntegrityError("Recurrence exceptions require a Recurrence-ID property")
 
         # check if an exception with the given recurrence-id already exists
         append = True
         vexceptions = self.event.exceptions()
         for i, ex in enumerate(self._exceptions):
             if ex.get_recurrence_id() == recurrence_id and ex.thisandfuture == exception.thisandfuture:
                 # update the existing exception
                 vexceptions[i] = exception.event
                 self._exceptions[i] = exception
                 append = False
 
         # check if main event matches the given recurrence-id
         if append and self.get_recurrence_id() == recurrence_id:
             self.event = exception.event
             self._load_attendees()
             self._load_exceptions()
             append = False
 
         if append:
             vexceptions.append(exception.event)
             self._exceptions.append(exception)
 
         self.event.setExceptions(vexceptions)
 
     def del_exception(self, exception):
         recurrence_id = exception.get_recurrence_id()
         if recurrence_id is None:
-            raise EventIntegrityError, "Recurrence exceptions require a Recurrence-ID property"
+            raise EventIntegrityError("Recurrence exceptions require a Recurrence-ID property")
 
         updated = False
         vexceptions = self.event.exceptions()
         for i, ex in enumerate(self._exceptions):
             if ex.get_recurrence_id() == recurrence_id and ex.thisandfuture == exception.thisandfuture:
                 del vexceptions[i]
                 del self._exceptions[i]
                 updated = True
 
         if updated:
             self.event.setExceptions(vexceptions)
 
 
     def as_string_itip(self, method="REQUEST"):
         cal = icalendar.Calendar()
         cal.add(
                 'prodid',
                 '-//pykolab-%s-%s//kolab.org//' % (
                         constants.__version__,
                         constants.__release__
                     )
             )
 
         cal.add('version', '2.0')
         # TODO: Really?
         cal.add('calscale', 'GREGORIAN')
         # TODO: Not always a request...
         cal.add('method', method)
 
         # TODO: Add timezone information using icalendar.?()
         #       Not sure if there is a class for it.
 
         cal.add_component(self.to_ical())
 
         # add recurrence exceptions
         if len(self._exceptions) > 0 and not method == 'REPLY':
             for exception in self._exceptions:
                 cal.add_component(exception.to_ical())
 
         if hasattr(cal, 'to_ical'):
             return cal.to_ical()
         elif hasattr(cal, 'as_string'):
             return cal.as_string()
 
     def to_ical(self):
         event = icalendar.Event()
 
         # Required
         event['uid'] = self.get_uid()
 
         # NOTE: Make sure to list(set()) or duplicates may arise
         for attr in list(set(event.singletons)):
             _attr = attr.lower().replace('-', '')
             ical_getter = 'get_ical_%s' % (_attr)
             default_getter = 'get_%s' % (_attr)
             retval = None
             if hasattr(self, ical_getter):
                 retval = getattr(self, ical_getter)()
                 if not retval == None and not retval == "":
                     event.add(attr.lower(), retval)
             elif hasattr(self, default_getter):
                 retval = getattr(self, default_getter)()
                 if not retval == None and not retval == "":
                     event.add(attr.lower(), retval, encode=0)
 
         # NOTE: Make sure to list(set()) or duplicates may arise
         for attr in list(set(event.multiple)):
             _attr = attr.lower().replace('-', '')
             ical_getter = 'get_ical_%s' % (_attr)
             default_getter = 'get_%s' % (_attr)
             retval = None
             if hasattr(self, ical_getter):
                 retval = getattr(self, ical_getter)()
             elif hasattr(self, default_getter):
                 retval = getattr(self, default_getter)()
 
             if isinstance(retval, list) and not len(retval) == 0:
                 for _retval in retval:
                     event.add(attr.lower(), _retval, encode=0)
 
         # copy custom properties to iCal
         for cs in self.event.customProperties():
             event.add(cs.identifier, cs.value)
 
         return event
 
     def delegate(self, delegators, delegatees, names=None):
         if not isinstance(delegators, list):
             delegators = [delegators]
 
         if not isinstance(delegatees, list):
             delegatees = [delegatees]
 
         if not isinstance(names, list):
             names = [names]
 
         _delegators = []
         for delegator in delegators:
             _delegators.append(self.get_attendee(delegator))
 
         _delegatees = []
 
         for i,delegatee in enumerate(delegatees):
             try:
                 _delegatees.append(self.get_attendee(delegatee))
             except:
                 # TODO: An iTip needs to be sent out to the new attendee
                 self.add_attendee(delegatee, names[i] if i < len(names) else None)
                 _delegatees.append(self.get_attendee(delegatee))
 
         for delegator in _delegators:
             delegator.delegate_to(_delegatees)
 
         for delegatee in _delegatees:
             delegatee.delegate_from(_delegators)
 
         self.event.setAttendees(self._attendees)
 
     def from_ical(self, ical, raw=None):
         if isinstance(ical, icalendar.Event) or isinstance(ical, icalendar.Calendar):
             ical_event = ical
         elif hasattr(icalendar.Event, 'from_ical'):
             ical_event = icalendar.Event.from_ical(ical)
         elif hasattr(icalendar.Event, 'from_string'):
             ical_event = icalendar.Event.from_string(ical)
 
         # VCALENDAR block was given, find the first VEVENT item
         if isinstance(ical_event, icalendar.Calendar):
             for c in ical_event.walk():
                 if c.name == 'VEVENT':
                     ical_event = c
                     break
 
         # use the libkolab calendaring bindings to load the full iCal data
         if ical_event.has_key('RRULE') or ical_event.has_key('ATTACH') \
              or [part for part in ical_event.walk() if part.name == 'VALARM']:
             if raw is None or raw == "":
                 raw = ical if isinstance(ical, str) else ical.to_ical()
             self._xml_from_ical(raw)
         else:
             self.event = kolabformat.Event()
 
         # TODO: Clause the timestamps for zulu suffix causing datetime.datetime
         # to fail substitution.
         for attr in list(set(ical_event.required)):
             if ical_event.has_key(attr):
                 self.set_from_ical(attr.lower(), ical_event[attr])
 
         # NOTE: Make sure to list(set()) or duplicates may arise
         # NOTE: Keep the original order e.g. to read DTSTART before RECURRENCE-ID
         for attr in list(OrderedDict.fromkeys(ical_event.singletons)):
             if ical_event.has_key(attr):
                 if isinstance(ical_event[attr], list):
                     ical_event[attr] = ical_event[attr][0];
                 self.set_from_ical(attr.lower(), ical_event[attr])
 
         # NOTE: Make sure to list(set()) or duplicates may arise
         for attr in list(set(ical_event.multiple)):
             if ical_event.has_key(attr):
                 self.set_from_ical(attr.lower(), ical_event[attr])
 
     def _xml_from_ical(self, ical):
         if not "BEGIN:VCALENDAR" in ical:
             ical = "BEGIN:VCALENDAR\nVERSION:2.0\n" + ical + "\nEND:VCALENDAR"
         from kolab.calendaring import EventCal
         self.event = EventCal()
         success = self.event.fromICal(ical)
         if success:
             self._load_exceptions()
         return success
 
     def get_attendee_participant_status(self, attendee):
         return attendee.get_participant_status()
 
     def get_attendee(self, attendee):
         if isinstance(attendee, basestring):
             if attendee in [x.get_email() for x in self.get_attendees()]:
                 attendee = self.get_attendee_by_email(attendee)
 
             elif attendee in [x.get_name() for x in self.get_attendees()]:
                 attendee = self.get_attendee_by_name(attendee)
 
             else:
-                raise ValueError, _("No attendee with email or name %r") %(attendee)
+                raise ValueError(_("No attendee with email or name %r") %(attendee))
 
             return attendee
 
         elif isinstance(attendee, Attendee):
             return attendee
 
         else:
-            raise ValueError, _("Invalid argument value attendee %r, must be basestring or Attendee") % (attendee)
+            raise ValueError(_("Invalid argument value attendee %r, must be basestring or Attendee") % (attendee))
 
     def find_attendee(self, attendee):
         try:
             return self.get_attendee(attendee)
         except:
             return None
 
     def get_attendee_by_email(self, email):
         if email in [x.get_email() for x in self.get_attendees()]:
             return [x for x in self.get_attendees() if x.get_email() == email][0]
 
-        raise ValueError, _("No attendee with email %r") %(email)
+        raise ValueError(_("No attendee with email %r") %(email))
 
     def get_attendee_by_name(self, name):
         if name in [x.get_name() for x in self.get_attendees()]:
             return [x for x in self.get_attendees() if x.get_name() == name][0]
 
-        raise ValueError, _("No attendee with name %r") %(name)
+        raise ValueError(_("No attendee with name %r") %(name))
 
     def get_attendees(self):
         return self._attendees
 
     def get_categories(self):
         return [str(c) for c in self.event.categories()]
 
     def get_classification(self):
         return self.event.classification()
 
     def get_created(self):
         try:
             return xmlutils.from_cdatetime(self.event.created(), True)
         except ValueError:
             return datetime.datetime.now()
 
     def get_description(self):
         return self.event.description()
 
     def get_comment(self):
         if hasattr(self.event, 'comment'):
             return self.event.comment()
         else:
             return None
 
     def get_duration(self):
         duration = self.event.duration()
         if duration and duration.isValid():
             dtd = datetime.timedelta(
                 days=duration.days(),
                 seconds=duration.seconds(),
                 minutes=duration.minutes(),
                 hours=duration.hours(),
                 weeks=duration.weeks()
             )
             return dtd
 
         return None
 
     def get_end(self):
         dt = xmlutils.from_cdatetime(self.event.end(), True)
         if not dt:
             duration = self.get_duration()
             if duration is not None:
                 dt = self.get_start() + duration
         return dt
 
     def get_date_text(self, date_format=None, time_format=None):
         if date_format is None:
             date_format = _("%Y-%m-%d")
         if time_format is None:
             time_format = _("%H:%M (%Z)")
 
         start = self.get_start()
         end = self.get_end()
         all_day = not hasattr(start, 'date')
         start_date = start.date() if not all_day else start
         end_date = end.date() if not all_day else end
 
         if start_date == end_date:
             end_format = time_format
         else:
             end_format = date_format + " " + time_format
 
         if all_day:
             time_format = ''
             if start_date == end_date:
                 return start.strftime(date_format)
 
         return "%s - %s" % (start.strftime(date_format + " " + time_format), end.strftime(end_format))
 
     def get_recurrence_dates(self):
         return map(lambda _: xmlutils.from_cdatetime(_, True), self.event.recurrenceDates())
 
     def get_exception_dates(self):
         return map(lambda _: xmlutils.from_cdatetime(_, True), self.event.exceptionDates())
 
     def get_exceptions(self):
         return self._exceptions
 
     def has_exceptions(self):
         return len(self._exceptions) > 0
 
     def get_attachments(self):
         return self.event.attachments()
 
     def get_attachment_data(self, i):
         vattach = self.event.attachments()
         if i < len(vattach):
             attachment = vattach[i]
             uri = attachment.uri()
             if uri and uri[0:4] == 'cid:':
                 # get data from MIME part with matching content-id
                 cid = '<' + uri[4:] + '>'
                 for p in self._attachment_parts:
                     if p['Content-ID'] == cid:
                         return p.get_payload(decode=True)
             else:
                 return attachment.data()
 
         return None
 
     def get_alarms(self):
         return self.event.alarms()
 
     def get_ical_attendee(self):
         # TODO: Formatting, aye? See also the example snippet:
         #
         # ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP:
         # MAILTO:employee-A@host.com
 
         attendees = []
         for attendee in self.get_attendees():
             email = attendee.get_email()
             name = attendee.get_name()
             rsvp = attendee.get_rsvp()
             role = attendee.get_role()
             partstat = attendee.get_participant_status()
             cutype = attendee.get_cutype()
             delegators = attendee.get_delegated_from()
             delegatees = attendee.get_delegated_to()
 
             if rsvp in attendee.rsvp_map.keys():
                 _rsvp = rsvp
             elif rsvp in attendee.rsvp_map.values():
                 _rsvp = [k for k, v in attendee.rsvp_map.iteritems() if v == rsvp][0]
             else:
                 _rsvp = None
 
             if role in attendee.role_map.keys():
                 _role = role
             elif role in attendee.role_map.values():
                 _role = [k for k, v in attendee.role_map.iteritems() if v == role][0]
             else:
                 _role = None
 
             if partstat in attendee.participant_status_map.keys():
                 _partstat = partstat
             elif partstat in attendee.participant_status_map.values():
                 _partstat = [k for k, v in attendee.participant_status_map.iteritems() if v == partstat][0]
             else:
                 _partstat = None
 
             if cutype in attendee.cutype_map.keys():
                 _cutype = cutype
             elif cutype in attendee.cutype_map.values():
                 _cutype = [k for k, v in attendee.cutype_map.iteritems() if v == cutype][0]
             else:
                 _cutype = None
 
             _attendee = icalendar.vCalAddress("MAILTO:%s" % email)
             if not name == None and not name == "":
                 _attendee.params['CN'] = icalendar.vText(name)
 
             if not _rsvp == None:
                 _attendee.params['RSVP'] = icalendar.vText(_rsvp)
 
             if not _role == None:
                 _attendee.params['ROLE'] = icalendar.vText(_role)
 
             if not _partstat == None:
                 _attendee.params['PARTSTAT'] = icalendar.vText(_partstat)
 
             if not _cutype == None:
                 _attendee.params['CUTYPE'] = icalendar.vText(_cutype)
 
             if not delegators == None and len(delegators) > 0:
                 _attendee.params['DELEGATED-FROM'] = icalendar.vText(delegators[0].email())
 
             if not delegatees == None and len(delegatees) > 0:
                 _attendee.params['DELEGATED-TO'] = icalendar.vText(delegatees[0].email())
 
             attendees.append(_attendee)
 
         return attendees
 
     def get_ical_attendee_participant_status(self, attendee):
         attendee = self.get_attendee(attendee)
 
         if attendee.get_participant_status() in attendee.participant_status_map.keys():
             return attendee.get_participant_status()
         elif attendee.get_participant_status() in attendee.participant_status_map.values():
             return [k for k, v in attendee.participant_status_map.iteritems() if v == attendee.get_participant_status()][0]
         else:
-            raise ValueError, _("Invalid participant status")
+            raise ValueError(_("Invalid participant status"))
 
     def get_ical_created(self):
         return self.get_created()
 
     def get_ical_dtend(self):
         dtend = self.get_end()
         # shift end by one day on all-day events
         if not hasattr(dtend, 'hour'):
             dtend = dtend + datetime.timedelta(days=1)
         return dtend
 
     def get_ical_dtstamp(self):
         try:
             retval = self.get_lastmodified()
             if retval == None or retval == "":
                 return datetime.datetime.now()
         except:
             return datetime.datetime.now()
 
     def get_ical_lastmodified(self):
         return self.get_ical_dtstamp()
 
     def get_ical_dtstart(self):
         return self.get_start()
 
     def get_ical_organizer(self):
         contact = self.get_organizer()
         organizer = icalendar.vCalAddress("MAILTO:%s" % contact.email())
         name = contact.name()
 
         if not name == None and not name == "":
             organizer.params["CN"] = icalendar.vText(name)
 
         return organizer
 
     def get_ical_status(self):
         status = self.event.status()
 
         if status in self.status_map.keys():
             return status
 
         return self._translate_value(status, self.status_map) if status else None
 
     def get_ical_class(self):
         class_ = self.event.classification()
         return self._translate_value(class_, self.classification_map) if class_ else None
 
     def get_ical_sequence(self):
         return str(self.event.sequence()) if self.event.sequence() else None
 
     def get_ical_comment(self):
         comment = self.get_comment()
         if comment is not None:
             return [ comment ]
         return None
 
     def get_ical_recurrenceid(self):
         rid = self.get_recurrence_id()
         if isinstance(rid, datetime.datetime) or isinstance(rid, datetime.date):
             prop = icalendar.vDatetime(rid) if isinstance(rid, datetime.datetime) else icalendar.vDate(rid)
             if self.thisandfuture:
                 prop.params.update({'RANGE':'THISANDFUTURE'})
             return prop
 
         return None
 
     def get_ical_rrule(self):
         result = []
         rrule = self.get_recurrence()
         if rrule.isValid():
             result.append(rrule.to_ical())
         return result
 
     def get_ical_rdate(self):
         rdates = self.get_recurrence_dates()
         for i in range(len(rdates)):
             rdates[i] = icalendar.prop.vDDDLists(rdates[i])
 
         return rdates
 
     def get_location(self):
         return self.event.location()
 
     def get_lastmodified(self):
         try:
             _datetime = self.event.lastModified()
             if _datetime == None or not _datetime.isValid():
                 self.__str__()
         except:
             self.__str__()
 
         return xmlutils.from_cdatetime(self.event.lastModified(), True)
 
     def get_organizer(self):
         organizer = self.event.organizer()
         return organizer
 
     def get_priority(self):
         return str(self.event.priority())
 
     def get_start(self):
         return xmlutils.from_cdatetime(self.event.start(), True)
 
     def get_status(self, translated=False):
         status = self.event.status()
         if translated:
             return self._translate_value(status, self.status_map) if status else None
 
         return status
 
     def get_summary(self):
         return self.event.summary()
 
     def get_uid(self):
         uid = self.event.uid()
         if not uid == '':
             return uid
         else:
             self.set_uid(uuid.uuid4())
             return self.get_uid()
 
     def get_recurrence_id(self):
         self.thisandfuture = self.event.thisAndFuture();
         recurrence_id = xmlutils.from_cdatetime(self.event.recurrenceID(), True)
 
         # fix recurrence-id type if stored as date instead of datetime
         if recurrence_id is not None and isinstance(recurrence_id, datetime.date):
             dtstart = self.get_start()
             if not type(recurrence_id) == type(dtstart):
                 recurrence_id = datetime.datetime.combine(recurrence_id, dtstart.time()).replace(tzinfo=dtstart.tzinfo)
 
         return recurrence_id
 
     def get_thisandfuture(self):
         self.thisandfuture = self.event.thisAndFuture();
         return self.thisandfuture
 
     def get_sequence(self):
         return self.event.sequence()
 
     def get_url(self):
         return self.event.url()
 
     def get_transparency(self):
         return self.event.transparency()
 
     def get_recurrence(self):
         return RecurrenceRule(self.event.recurrenceRule())
 
     def set_attendees(self, _attendees, recursive=False):
         if recursive:
             self._attendees = []
             self.update_attendees(_attendees, True)
         else:
             self._attendees = _attendees
             self.event.setAttendees(self._attendees)
 
     def set_attendee_participant_status(self, attendee, status, rsvp=None):
         """
             Set the participant status of an attendee to status.
 
             As the attendee arg, pass an email address or name, for this
             function to obtain the attendee object by searching the list of
             attendees for this event.
         """
         attendee = self.get_attendee(attendee)
         attendee.set_participant_status(status)
 
         if rsvp is not None:
             attendee.set_rsvp(rsvp)
 
         # apply update to self and all exceptions
         self.update_attendees([attendee], False)
 
     def update_attendees(self, _attendees, append=True):
         self.merge_attendee_data(_attendees, append)
 
         if len(self._exceptions):
             vexceptions = self.event.exceptions()
             for i, exception in enumerate(self._exceptions):
                 exception.merge_attendee_data(_attendees, append)
                 vexceptions[i] = exception.event
             self.event.setExceptions(vexceptions)
 
     def merge_attendee_data(self, _attendees, append=True):
         for attendee in _attendees:
             found = False
 
             for candidate in self._attendees:
                 if candidate.get_email() == attendee.get_email():
                     candidate.copy_from(attendee)
                     found = True
                     break
 
             if not found and append:
                 self._attendees.append(attendee)
 
         self.event.setAttendees(self._attendees)
 
     def set_classification(self, classification):
         if classification in self.classification_map.keys():
             self.event.setClassification(self.classification_map[classification])
         elif classification in self.classification_map.values():
             self.event.setClassification(classification)
         else:
-            raise ValueError, _("Invalid classification %r") % (classification)
+            raise ValueError(_("Invalid classification %r") % (classification))
 
     def set_created(self, _datetime=None):
         if _datetime is None or isinstance(_datetime, datetime.time):
             _datetime = datetime.datetime.utcnow()
 
         self.event.setCreated(xmlutils.to_cdatetime(_datetime, False, True))
 
     def set_description(self, description):
         self.event.setDescription(ustr(description))
 
     def set_comment(self, comment):
         if hasattr(self.event, 'setComment'):
             self.event.setComment(ustr(comment))
 
     def set_dtstamp(self, _datetime):
         self.event.setLastModified(xmlutils.to_cdatetime(_datetime, False, True))
 
     def set_end(self, _datetime):
         valid_datetime = False
         if isinstance(_datetime, datetime.date):
             valid_datetime = True
 
         if isinstance(_datetime, datetime.datetime):
             # If no timezone information is passed on, make it UTC
             if _datetime.tzinfo == None:
                 _datetime = _datetime.replace(tzinfo=pytz.utc)
 
             valid_datetime = True
 
         if not valid_datetime:
-            raise InvalidEventDateError, _("Event end needs datetime.date or datetime.datetime instance, got %r") % (type(_datetime))
+            raise InvalidEventDateError(_("Event end needs datetime.date or datetime.datetime instance, got %r") % (type(_datetime)))
 
         self.event.setEnd(xmlutils.to_cdatetime(_datetime, True))
 
     def set_exception_dates(self, _datetimes):
         for _datetime in _datetimes:
             self.add_exception_date(_datetime)
 
     def set_recurrence_dates(self, _datetimes):
         for _datetime in _datetimes:
             self.add_recurrence_date(_datetime)
 
     def add_custom_property(self, name, value):
         if not name.upper().startswith('X-'):
-            raise ValueError, _("Invalid custom property name %r") % (name)
+            raise ValueError(_("Invalid custom property name %r") % (name))
 
         props = self.event.customProperties()
         props.append(kolabformat.CustomProperty(name.upper(), value))
         self.event.setCustomProperties(props)
 
     def set_from_ical(self, attr, value):
         attr = attr.replace('-', '')
         ical_setter = 'set_ical_' + attr
         default_setter = 'set_' + attr
         params = value.params if hasattr(value, 'params') else {}
 
         if isinstance(value, icalendar.vDDDTypes) and hasattr(value, 'dt'):
             value = value.dt
 
         if attr == "categories":
             self.add_category(value)
         elif attr == "class":
             if (value and value[:2] not in ['X-', 'x-']):
                 self.set_classification(value)
         elif attr == "recurrenceid":
             self.set_ical_recurrenceid(value, params)
         elif hasattr(self, ical_setter):
             getattr(self, ical_setter)(value)
         elif hasattr(self, default_setter):
             getattr(self, default_setter)(value)
 
     def set_ical_attendee(self, _attendee):
         if isinstance(_attendee, basestring):
             _attendee = [_attendee]
 
         if isinstance(_attendee, list):
             for attendee in _attendee:
                 address = str(attendee).split(':')[-1]
 
                 if hasattr(attendee, 'params'):
                     params = attendee.params
                 else:
                     params = {}
 
                 if params.has_key('CN'):
                     name = ustr(params['CN'])
                 else:
                     name = None
 
                 if params.has_key('ROLE'):
                     role = params['ROLE']
                 else:
                     role = None
 
                 if params.has_key('PARTSTAT'):
                     partstat = params['PARTSTAT']
                 else:
                     partstat = None
 
                 if params.has_key('RSVP'):
                     rsvp = params['RSVP']
                 else:
                     rsvp = None
 
                 if params.has_key('CUTYPE'):
                     cutype = params['CUTYPE']
                 else:
                     cutype = kolabformat.CutypeIndividual
 
                 att = self.add_attendee(address, name=name, rsvp=rsvp, role=role, participant_status=partstat, cutype=cutype, params=params)
 
     def set_ical_dtend(self, dtend):
         # shift end by one day on all-day events
         if not hasattr(dtend, 'hour'):
             dtend = dtend - datetime.timedelta(days=1)
         self.set_end(dtend)
 
     def set_ical_dtstamp(self, dtstamp):
         self.set_dtstamp(dtstamp)
 
     def set_ical_dtstart(self, dtstart):
         self.set_start(dtstart)
 
     def set_ical_lastmodified(self, lastmod):
         self.set_lastmodified(lastmod)
 
     def set_ical_duration(self, value):
         if hasattr(value, 'dt'):
             value = value.dt
 
         duration = kolabformat.Duration(value.days, 0, 0, value.seconds, False)
         self.event.setDuration(duration)
 
     def set_ical_organizer(self, organizer):
         address = str(organizer).split(':')[-1]
 
         cn = None
 
         if hasattr(organizer, 'params'):
             params = organizer.params
         else:
             params = {}
 
         if params.has_key('CN'):
             cn = ustr(params['CN'])
 
         self.set_organizer(str(address), name=cn)
 
     def set_ical_priority(self, priority):
         self.set_priority(priority)
 
     def set_ical_sequence(self, sequence):
         self.set_sequence(sequence)
 
     def set_ical_summary(self, summary):
         self.set_summary(ustr(summary))
 
     def set_ical_uid(self, uid):
         self.set_uid(str(uid))
 
     def set_ical_rdate(self, rdate):
         rdates = []
         # rdate here can be vDDDLists or a list of vDDDLists, why?!
         if isinstance(rdate, icalendar.prop.vDDDLists):
             rdate = [rdate]
 
         for _rdate in rdate:
             if isinstance(_rdate, icalendar.prop.vDDDLists):
                 tzid = None
                 if hasattr(_rdate, 'params') and 'TZID' in _rdate.params:
                     tzid = _rdate.params['TZID']
                 dts = icalendar.prop.vDDDLists.from_ical(_rdate.to_ical(), tzid)
                 for datetime in dts:
                     rdates.append(datetime)
 
         self.set_recurrence_dates(rdates)
 
     def set_ical_recurrenceid(self, value, params):
         try:
             self.thisandfuture = params.get('RANGE', '') == 'THISANDFUTURE'
             self.set_recurrence_id(value, self.thisandfuture)
-        except InvalidEventDateError, e:
+        except InvalidEventDateError:
             pass
 
     def set_lastmodified(self, _datetime=None):
         valid_datetime = False
         if isinstance(_datetime, datetime.date):
             valid_datetime = True
 
         if isinstance(_datetime, datetime.datetime):
             valid_datetime = True
 
         if _datetime is None or isinstance(_datetime, datetime.time):
             valid_datetime = True
             _datetime = datetime.datetime.utcnow()
 
         if not valid_datetime:
-            raise InvalidEventDateError, _("Event last-modified needs datetime.date or datetime.datetime instance, got %r") % (type(_datetime))
+            raise InvalidEventDateError(_("Event last-modified needs datetime.date or datetime.datetime instance, got %r") % (type(_datetime)))
 
         self.event.setLastModified(xmlutils.to_cdatetime(_datetime, False, True))
 
     def set_location(self, location):
         self.event.setLocation(ustr(location))
 
     def set_organizer(self, email, name=None):
         contactreference = ContactReference(email)
         if not name == None:
             contactreference.setName(name)
 
         self.event.setOrganizer(contactreference)
 
     def set_priority(self, priority):
         self.event.setPriority(priority)
 
     def set_sequence(self, sequence):
         self.event.setSequence(int(sequence))
 
     def set_url(self, url):
         self.event.setUrl(ustr(url))
 
     def set_recurrence(self, recurrence):
         self.event.setRecurrenceRule(recurrence)
 
         # reset eventcal instance
         if hasattr(self, 'eventcal'):
             del self.eventcal
 
     def set_start(self, _datetime):
         valid_datetime = False
         if isinstance(_datetime, datetime.date):
             valid_datetime = True
 
         if isinstance(_datetime, datetime.datetime):
             # If no timezone information is passed on, make it UTC
             if _datetime.tzinfo == None:
                 _datetime = _datetime.replace(tzinfo=pytz.utc)
 
             valid_datetime = True
 
         if not valid_datetime:
-            raise InvalidEventDateError, _("Event start needs datetime.date or datetime.datetime instance, got %r") % (type(_datetime))
+            raise InvalidEventDateError(_("Event start needs datetime.date or datetime.datetime instance, got %r") % (type(_datetime)))
 
         self.event.setStart(xmlutils.to_cdatetime(_datetime, True))
 
     def set_status(self, status):
         if status in self.status_map.keys():
             self.event.setStatus(self.status_map[status])
         elif status in self.status_map.values():
             self.event.setStatus(status)
         elif not status == kolabformat.StatusUndefined:
-            raise InvalidEventStatusError, _("Invalid status set: %r") % (status)
+            raise InvalidEventStatusError(_("Invalid status set: %r") % (status))
 
     def set_summary(self, summary):
         self.event.setSummary(summary)
 
     def set_uid(self, uid):
         self.uid = uid
         self.event.setUid(str(uid))
 
     def set_recurrence_id(self, _datetime, _thisandfuture=None):
         valid_datetime = False
         if isinstance(_datetime, datetime.date):
             valid_datetime = True
 
         if isinstance(_datetime, datetime.datetime):
             # If no timezone information is passed on, use the one from event start
             if _datetime.tzinfo == None:
                 _start = self.get_start()
                 _datetime = _datetime.replace(tzinfo=_start.tzinfo)
 
             valid_datetime = True
 
         if not valid_datetime:
-            raise InvalidEventDateError, _("Event recurrence-id needs datetime.datetime instance")
+            raise InvalidEventDateError(_("Event recurrence-id needs datetime.datetime instance"))
 
         if _thisandfuture is None:
             _thisandfuture = self.thisandfuture
 
         self.event.setRecurrenceID(xmlutils.to_cdatetime(_datetime), _thisandfuture)
 
     def set_transparency(self, transp):
         return self.event.setTransparency(transp)
 
     def __str__(self):
         event_xml = kolabformat.writeEvent(self.event)
 
         error = kolabformat.error()
 
         if error == None or not error:
             return event_xml
         else:
-            raise EventIntegrityError, kolabformat.errorMessage()
+            raise EventIntegrityError(kolabformat.errorMessage())
 
     def to_dict(self):
         data = dict()
 
         for p, getter in self.properties_map.iteritems():
             val = None
             if hasattr(self, getter):
                 val = getattr(self, getter)()
             elif hasattr(self.event, getter):
                 val = getattr(self.event, getter)()
 
             if isinstance(val, kolabformat.cDateTime):
                 val = xmlutils.from_cdatetime(val, True)
             elif isinstance(val, kolabformat.vectordatetime):
                 val = [xmlutils.from_cdatetime(x, True) for x in val]
             elif isinstance(val, kolabformat.vectors):
                 val = [str(x) for x in val]
             elif isinstance(val, kolabformat.vectorcs):
                 for x in val:
                     data[x.identifier] = x.value
                 val = None
             elif isinstance(val, kolabformat.ContactReference):
                 val = ContactReference(val).to_dict()
             elif isinstance(val, kolabformat.RecurrenceRule):
                 val = RecurrenceRule(val).to_dict()
             elif isinstance(val, kolabformat.vectorattachment):
                 val = [dict(fmttype=x.mimetype(), label=x.label(), uri=x.uri()) for x in val]
             elif isinstance(val, kolabformat.vectoralarm):
                 val = [self._alarm_to_dict(x) for x in val]
             elif isinstance(val, list):
                 val = [x.to_dict() for x in val if hasattr(x, 'to_dict')]
 
             if val is not None:
                 data[p] = val
 
         return data
 
     def _alarm_to_dict(self, alarm):
         ret = dict(
             action=self._translate_value(alarm.type(), self.alarm_type_map),
             summary=alarm.summary(),
             description=alarm.description(),
             trigger=None
         )
 
         start = alarm.start()
         if start and start.isValid():
             ret['trigger'] = xmlutils.from_cdatetime(start, True)
         else:
             ret['trigger'] = dict(related=self._translate_value(alarm.relativeTo(), self.related_map))
             duration = alarm.relativeStart()
             if duration and duration.isValid():
                 prefix = '-' if duration.isNegative() else '+'
                 value = prefix + "P%dW%dDT%dH%dM%dS" % (
                     duration.weeks(), duration.days(), duration.hours(), duration.minutes(), duration.seconds()
                 )
                 ret['trigger']['value'] = re.sub(r"T$", '', re.sub(r"0[WDHMS]", '', value))
 
         if alarm.type() == kolabformat.Alarm.EMailAlarm:
             ret['attendee'] = [ContactReference(a).to_dict() for a in alarm.attendees()]
 
         return ret
 
     def _translate_value(self, val, map):
         name_map = dict([(v, k) for (k, v) in map.iteritems()])
         return name_map[val] if name_map.has_key(val) else 'UNKNOWN'
 
     def to_message(self, creator=None):
         from email.MIMEMultipart import MIMEMultipart
         from email.MIMEBase import MIMEBase
         from email.MIMEText import MIMEText
         from email.Utils import COMMASPACE, formatdate
 
         msg = MIMEMultipart()
         organizer = self.get_organizer()
         email = organizer.email()
         name = organizer.name()
 
         if creator:
             msg['From'] = creator
         elif not name:
             msg['From'] = email
         else:
             msg['From'] = '"%s" <%s>' % (name, email)
 
         msg['To'] = ', '.join([x.__str__() for x in self.get_attendees()])
         msg['Date'] = formatdate(localtime=True)
 
         msg.add_header('X-Kolab-MIME-Version', '3.0')
         msg.add_header('X-Kolab-Type', 'application/x-vnd.kolab.' + self.type)
 
         text = utils.multiline_message("""
                     This is a Kolab Groupware object. To view this object you
                     will need an email client that understands the Kolab
                     Groupware format. For a list of such email clients please
                     visit http://www.kolab.org/
             """)
 
         msg.attach( MIMEText(text) )
 
         part = MIMEBase('application', "calendar+xml")
         part.set_charset('UTF-8')
 
         msg["Subject"] = self.get_uid()
 
         # extract attachment data into separate MIME parts
         vattach = self.event.attachments()
         i = 0
         for attach in vattach:
             if attach.uri():
                 continue
 
             mimetype = attach.mimetype()
             (primary, seconday) = mimetype.split('/')
             name = attach.label()
             if not name:
                 name = 'unknown.x'
 
             (basename, suffix) = path.splitext(name)
             t = datetime.datetime.now()
             cid = "%s.%s.%s%s" % (basename, time.mktime(t.timetuple()), t.microsecond + len(self._attachment_parts), suffix)
 
             p = MIMEBase(primary, seconday)
             p.add_header('Content-Disposition', 'attachment', filename=name)
             p.add_header('Content-Transfer-Encoding', 'base64')
             p.add_header('Content-ID', '<' + cid + '>')
             p.set_payload(base64.b64encode(attach.data()))
 
             self._attachment_parts.append(p)
 
             # modify attachment object
             attach.setData('', mimetype)
             attach.setUri('cid:' + cid, mimetype)
             vattach[i] = attach
             i += 1
 
         self.event.setAttachments(vattach)
 
         part.set_payload(str(self))
 
         part.add_header('Content-Disposition', 'attachment; filename="kolab.xml"')
         part.replace_header('Content-Transfer-Encoding', '8bit')
 
         msg.attach(part)
 
         # append attachment parts
         for p in self._attachment_parts:
             msg.attach(p)
 
         return msg
 
     def to_message_itip(self, from_address, method="REQUEST", participant_status="ACCEPTED", subject=None, message_text=None):
         from email.MIMEMultipart import MIMEMultipart
         from email.MIMEBase import MIMEBase
         from email.MIMEText import MIMEText
         from email.Utils import COMMASPACE, formatdate
 
         # encode unicode strings with quoted-printable
         from email import charset
         charset.add_charset('utf-8', charset.SHORTEST, charset.QP)
 
         msg = MIMEMultipart("alternative")
 
         msg_from = None
         attendees = None
 
         if method == "REPLY":
             # TODO: Make user friendly name <email>
             msg['To'] = self.get_organizer().email()
 
             attendees = self.get_attendees()
             reply_attendees = []
 
             # There's an exception here for delegation (partstat DELEGATED)
             for attendee in attendees:
                 if attendee.get_email() == from_address:
                     # Only the attendee is supposed to be listed in a reply
                     attendee.set_participant_status(participant_status)
                     attendee.set_rsvp(False)
 
                     reply_attendees.append(attendee)
 
                     name = attendee.get_name()
                     email = attendee.get_email()
 
                     if not name:
                         msg_from = email
                     else:
                         msg_from = '"%s" <%s>' % (name, email)
 
                 elif from_address in attendee.get_delegated_from(True):
                     reply_attendees.append(attendee)
 
             # keep only replying (and delegated) attendee(s)
             self._attendees = reply_attendees
             self.event.setAttendees(self._attendees)
 
             if msg_from == None:
                 organizer = self.get_organizer()
                 email = organizer.email()
                 name = organizer.name()
                 if email == from_address:
                     if not name:
                         msg_from = email
                     else:
                         msg_from = '"%s" <%s>' % (name, email)
 
         elif method == "REQUEST":
             organizer = self.get_organizer()
             email = organizer.email()
             name = organizer.name()
             if not name:
                 msg_from = email
             else:
                 msg_from = '"%s" <%s>' % (name, email)
 
         if msg_from == None:
             if from_address == None:
                 log.error(_("No sender specified"))
             else:
                 msg_from = from_address
 
         msg['From'] = utils.str2unicode(msg_from)
 
         msg['Date'] = formatdate(localtime=True)
 
         if subject is None:
             subject = _("Invitation for %s was %s") % (self.get_summary(), participant_status_label(participant_status))
 
         msg['Subject'] = utils.str2unicode(subject)
 
         if message_text is None:
             message_text = _("""This is an automated response to one of your event requests.""")
 
         msg.attach(MIMEText(utils.stripped_message(message_text), _charset='utf-8'))
 
         part = MIMEBase('text', 'calendar', charset='UTF-8', method=method)
         del part['MIME-Version']  # mime parts don't need this
 
         part.set_payload(self.as_string_itip(method=method))
 
         part.add_header('Content-Transfer-Encoding', '8bit')
 
         msg.attach(part)
 
         # restore the original list of attendees
         # attendees being reduced to the replying attendee above
         if attendees is not None:
             self._attendees = attendees
             self.event.setAttendees(self._attendees)
 
         return msg
 
     def is_recurring(self):
         return self.event.recurrenceRule().isValid() or len(self.get_recurrence_dates()) > 0
 
     def to_event_cal(self):
         from kolab.calendaring import EventCal
         return EventCal(self.event)
 
     def get_next_occurence(self, _datetime):
         if not hasattr(self, 'eventcal'):
             self.eventcal = self.to_event_cal()
 
         next_cdatetime = self.eventcal.getNextOccurence(xmlutils.to_cdatetime(_datetime, True))
         next_datetime  = xmlutils.from_cdatetime(next_cdatetime, True) if next_cdatetime is not None else None
 
         # cut infinite recurrence at a reasonable point
         if next_datetime and not self.get_last_occurrence() and next_datetime > xmlutils.to_dt(self._recurrence_end()):
             next_datetime = None
 
         # next_datetime is always a cdatetime, convert to date if necessary
         if next_datetime and not isinstance(self.get_start(), datetime.datetime):
             next_datetime = datetime.date(next_datetime.year, next_datetime.month, next_datetime.day)
 
         return next_datetime
 
     def get_occurence_end_date(self, datetime):
         if not datetime:
             return None
 
         if not hasattr(self, 'eventcal'):
             return None
 
         end_cdatetime = self.eventcal.getOccurenceEndDate(xmlutils.to_cdatetime(datetime, True))
         return xmlutils.from_cdatetime(end_cdatetime, True) if end_cdatetime is not None else None
 
     def get_last_occurrence(self, force=False):
         if not hasattr(self, 'eventcal'):
             self.eventcal = self.to_event_cal()
 
         last = self.eventcal.getLastOccurrence()
         last_datetime = xmlutils.from_cdatetime(last, True) if last is not None else None
 
         # we're forced to return some date
         if last_datetime is None and force:
             last_datetime = self._recurrence_end()
 
         return last_datetime
 
     def get_next_instance(self, datetime):
         next_start = self.get_next_occurence(datetime)
         if next_start:
             instance = Event(from_string=str(self))
             instance.set_start(next_start)
             instance.event.setRecurrenceID(xmlutils.to_cdatetime(next_start), False)
             next_end = self.get_occurence_end_date(next_start)
             if next_end:
                 instance.set_end(next_end)
 
             # unset recurrence rule and exceptions
             instance.set_recurrence(kolabformat.RecurrenceRule())
             instance.event.setExceptions(kolabformat.vectorevent())
             instance.event.setExceptionDates(kolabformat.vectordatetime())
             instance._exceptions = []
             instance._isexception = False
 
             # unset attachments list (only stored in main event)
             instance.event.setAttachments(kolabformat.vectorattachment())
 
             # copy data from matching exception
             # (give precedence to single occurrence exceptions over thisandfuture)
             for exception in self._exceptions:
                 recurrence_id = exception.get_recurrence_id()
                 if recurrence_id == next_start and (not exception.thisandfuture or not instance._isexception):
                     instance = exception
                     instance._isexception = True
                     if not exception.thisandfuture:
                         break
                 elif exception.thisandfuture and next_start > recurrence_id:
                     # TODO: merge exception properties over this instance + adjust start/end with the according offset
                     pass
 
             return instance
 
         return None
 
     def get_instance(self, _datetime):
         # If no timezone information is given, use the one from event start
         if isinstance(_datetime, datetime.datetime) and _datetime.tzinfo == None:
             _start = self.get_start()
             if hasattr(_start, 'tzinfo'):
                 _datetime = _datetime.replace(tzinfo=_start.tzinfo)
 
         if self.is_recurring():
             instance = self.get_next_instance(_datetime - datetime.timedelta(days=1))
             while instance:
                 recurrence_id = instance.get_recurrence_id()
                 if type(recurrence_id) == type(_datetime) and recurrence_id <= _datetime:
                     if xmlutils.dates_equal(recurrence_id, _datetime):
                         return instance
                     instance = self.get_next_instance(instance.get_start())
                 else:
                     break
 
         elif self.has_exceptions():
             for exception in self._exceptions:
                 recurrence_id = exception.get_recurrence_id()
                 if type(recurrence_id) == type(_datetime) and xmlutils.dates_equal(recurrence_id, _datetime):
                     return exception
 
         if self.get_recurrence_id():
             recurrence_id = self.get_recurrence_id()
             if type(recurrence_id) == type(_datetime) and xmlutils.dates_equal(recurrence_id, _datetime):
                 return self
 
         return None
 
     def _recurrence_end(self):
         """
             Determine a reasonable end date for infinitely recurring events
         """
         rrule = self.event.recurrenceRule()
         if rrule.isValid() and rrule.count() < 0 and not rrule.end().isValid():
             now = datetime.datetime.now()
             switch = {
                 kolabformat.RecurrenceRule.Yearly: 100,
                 kolabformat.RecurrenceRule.Monthly: 20
             }
             intvl = switch[rrule.frequency()] if rrule.frequency() in switch else 10
             return self.get_start().replace(year=now.year + intvl)
 
         return xmlutils.from_cdatetime(rrule.end(), True)
 
 
 class EventIntegrityError(Exception):
     def __init__(self, message):
         Exception.__init__(self, message)
 
 class InvalidEventDateError(Exception):
     def __init__(self, message):
         Exception.__init__(self, message)
 
 class InvalidEventStatusError(Exception):
     def __init__(self, message):
         Exception.__init__(self, message)
 
diff --git a/pykolab/xml/note.py b/pykolab/xml/note.py
index 0fc7c24..5784ebb 100644
--- a/pykolab/xml/note.py
+++ b/pykolab/xml/note.py
@@ -1,138 +1,138 @@
 import pytz
 import datetime
 import kolabformat
 
 from pykolab.translate import _
 from pykolab.xml import utils as xmlutils
 from pykolab.xml.utils import ustr
 
 def note_from_string(string):
     _xml = kolabformat.readNote(string, False)
     return Note(_xml)
 
 def note_from_message(message):
     note = None
     if message.is_multipart():
         for part in message.walk():
             if part.get_content_type() == "application/vnd.kolab+xml":
                 payload = part.get_payload(decode=True)
                 note = note_from_string(payload)
 
             # append attachment parts to Note object
             elif note and part.has_key('Content-ID'):
                 note._attachment_parts.append(part)
 
     return note
 
 
 class Note(kolabformat.Note):
     type = 'note'
 
     classification_map = {
         'PUBLIC': kolabformat.ClassPublic,
         'PRIVATE': kolabformat.ClassPrivate,
         'CONFIDENTIAL': kolabformat.ClassConfidential,
     }
 
     properties_map = {
         'uid':               'get_uid',
         'summary':           'summary',
         'description':       'description',
         'created':           'get_created',
         'lastmodified-date': 'get_lastmodified',
         'classification':    'get_classification',
         'categories':        'categories',
         'color':             'color',
     }
 
     def __init__(self, *args, **kw):
         self._attachment_parts = []
         kolabformat.Note.__init__(self, *args, **kw)
 
     def get_uid(self):
         uid = self.uid()
         if not uid == '':
             return uid
         else:
             self.__str__()
             return kolabformat.getSerializedUID()
 
     def get_created(self):
         try:
             return xmlutils.from_cdatetime(self.created(), True)
         except ValueError:
             return datetime.datetime.now()
 
     def get_lastmodified(self):
         try:
             _datetime = self.lastModified()
             if _datetime == None or not _datetime.isValid():
                 self.__str__()
         except:
             return datetime.datetime.now(pytz.utc)
 
         return xmlutils.from_cdatetime(self.lastModified(), True)
 
     def set_summary(self, summary):
         self.setSummary(ustr(summary))
 
     def set_description(self, description):
         self.setDescription(ustr(description))
 
     def get_classification(self, translated=True):
         _class = self.classification()
         if translated:
             return self._translate_value(_class, self.classification_map)
         return _class
 
     def set_classification(self, classification):
         if classification in self.classification_map.keys():
             self.setClassification(self.classification_map[classification])
         elif classification in self.classification_map.values():
             self.setClassification(status)
         else:
-            raise ValueError, _("Invalid classification %r") % (classification)
+            raise ValueError(_("Invalid classification %r") % (classification))
 
     def add_category(self, category):
         _categories = self.categories()
         _categories.append(ustr(category))
         self.setCategories(_categories)
 
     def _translate_value(self, val, map):
         name_map = dict([(v, k) for (k, v) in map.iteritems()])
         return name_map[val] if name_map.has_key(val) else 'UNKNOWN'
 
     def to_dict(self):
         if not self.isValid():
             return None
 
         data = dict()
 
         for p, getter in self.properties_map.iteritems():
             val = None
             if hasattr(self, getter):
                 val = getattr(self, getter)()
             if isinstance(val, kolabformat.cDateTime):
                 val = xmlutils.from_cdatetime(val, True)
             elif isinstance(val, kolabformat.vectori):
                 val = [int(x) for x in val]
             elif isinstance(val, kolabformat.vectors):
                 val = [str(x) for x in val]
 
             if val is not None:
                 data[p] = val
 
         return data
 
     def __str__(self):
         xml = kolabformat.writeNote(self)
         error = kolabformat.error()
 
         if error == None or not error:
             return xml
         else:
-            raise NoteIntegrityError, kolabformat.errorMessage()
+            raise NoteIntegrityError(kolabformat.errorMessage())
 
 class NoteIntegrityError(Exception):
     def __init__(self, message):
         Exception.__init__(self, message)
diff --git a/pykolab/xml/todo.py b/pykolab/xml/todo.py
index ccd798f..1c711b5 100644
--- a/pykolab/xml/todo.py
+++ b/pykolab/xml/todo.py
@@ -1,267 +1,267 @@
 import datetime
 import kolabformat
 import icalendar
 import pytz
 import base64
 
 import pykolab
 from pykolab import constants
 from pykolab.xml import Event
 from pykolab.xml import RecurrenceRule
 from pykolab.xml import utils as xmlutils
 from pykolab.xml.event import InvalidEventDateError
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.xml_todo')
 
 def todo_from_ical(ical, string=None):
     return Todo(from_ical=ical, from_string=string)
 
 def todo_from_string(string):
     return Todo(from_string=string)
 
 def todo_from_message(message):
     todo = None
     if message.is_multipart():
         for part in message.walk():
             if part.get_content_type() == "application/calendar+xml":
                 payload = part.get_payload(decode=True)
                 todo = todo_from_string(payload)
 
             # append attachment parts to Todo object
             elif todo and part.has_key('Content-ID'):
                 todo._attachment_parts.append(part)
 
     return todo
 
 # FIXME: extend a generic pykolab.xml.Xcal class instead of Event
 class Todo(Event):
     type = 'task'
 
     # This have to be a copy (see T1221)
     properties_map = Event.properties_map.copy()
 
     def __init__(self, from_ical="", from_string=""):
         self._attendees = []
         self._categories = []
         self._exceptions = []
         self._attachment_parts = []
 
         self.properties_map.update({
             "due": "get_due",
             "percent-complete": "get_percentcomplete",
             "related-to": "get_related_to",
             "duration": "void",
             "end": "void"
         })
 
         if isinstance(from_ical, str) and from_ical == "":
             if from_string == "":
                 self.event = kolabformat.Todo()
             else:
                 self.event = kolabformat.readTodo(from_string, False)
                 self._load_attendees()
         else:
             self.from_ical(from_ical, from_string)
 
         self.set_created(self.get_created())
         self.uid = self.get_uid()
 
     def from_ical(self, ical, raw):
         if isinstance(ical, icalendar.Todo):
             ical_todo = ical
         elif hasattr(icalendar.Todo, 'from_ical'):
             ical_todo = icalendar.Todo.from_ical(ical)
         elif hasattr(icalendar.Todo, 'from_string'):
             ical_todo = icalendar.Todo.from_string(ical)
 
         # VCALENDAR block was given, find the first VTODO item
         if isinstance(ical_todo, icalendar.Calendar):
             for c in ical_todo.walk():
                 if c.name == 'VTODO':
                     ical_todo = c
                     break
 
         log.debug("Todo.from_ical(); %r, %r, %r" % (type(ical_todo), ical_todo.has_key('ATTACH'), ical_todo.has_key('ATTENDEE')), level=8)
 
         # DISABLED: use the libkolab calendaring bindings to load the full iCal data
         # TODO: this requires support for iCal parsing in the kolab.calendaring bindings
         if False and ical_todo.has_key('ATTACH') or [part for part in ical_todo.walk() if part.name == 'VALARM']:
             if raw is None or raw == "":
                 raw = ical if isinstance(ical, str) else ical.to_ical()
             self._xml_from_ical(raw)
         else:
             self.event = kolabformat.Todo()
 
         for attr in list(set(ical_todo.required)):
             if ical_todo.has_key(attr):
                 self.set_from_ical(attr.lower(), ical_todo[attr])
 
         for attr in list(set(ical_todo.singletons)):
             if ical_todo.has_key(attr):
                 if isinstance(ical_todo[attr], list):
                     ical_todo[attr] = ical_todo[attr][0];
                 self.set_from_ical(attr.lower(), ical_todo[attr])
 
         for attr in list(set(ical_todo.multiple)):
             if ical_todo.has_key(attr):
                 self.set_from_ical(attr.lower(), ical_todo[attr])
 
         # although specified by RFC 2445/5545, icalendar doesn't have this property listed
         if ical_todo.has_key('PERCENT-COMPLETE'):
             self.set_from_ical('percentcomplete', ical_todo['PERCENT-COMPLETE'])
 
     def _xml_from_ical(self, ical):
         # FIXME: kolabformat or kolab.calendaring modules do not provide bindings to import Todo from iCal
         self.event = Todo()
 
     def set_ical_attach(self, attachment):
         if hasattr(attachment, 'params'):
             params = attachment.params
         else:
             params = {}
 
         _attachment = kolabformat.Attachment()
         if params.has_key('FMTTYPE'):
             mimetype = str(params['FMTTYPE'])
         else:
             mimetype = 'application/octet-stream'
 
         if params.has_key('X-LABEL'):
             _attachment.setLabel(str(params['X-LABEL']))
 
         decode = False
         if params.has_key('ENCODING'):
             if params['ENCODING'] == "BASE64" or params['ENCODING'] == "B":
                 decode = True
 
         _attachment.setData(base64.b64decode(str(attachment)) if decode else str(attachment), mimetype)
         vattach = self.event.attachments()
         vattach.append(_attachment)
         self.event.setAttachments(vattach)
 
     def set_ical_rrule(self, rrule):
         _rrule = RecurrenceRule()
         _rrule.from_ical(rrule)
         if _rrule.isValid():
             self.event.setRecurrenceRule(_rrule)
 
     def set_ical_due(self, due):
         self.set_due(due)
 
     def set_due(self, _datetime):
         valid_datetime = False
         if isinstance(_datetime, datetime.date):
             valid_datetime = True
 
         if isinstance(_datetime, datetime.datetime):
             # If no timezone information is passed on, make it UTC
             if _datetime.tzinfo == None:
                 _datetime = _datetime.replace(tzinfo=pytz.utc)
 
             valid_datetime = True
 
         if not valid_datetime:
-            raise InvalidEventDateError, _("Todo due needs datetime.date or datetime.datetime instance")
+            raise InvalidEventDateError(_("Todo due needs datetime.date or datetime.datetime instance"))
 
         self.event.setDue(xmlutils.to_cdatetime(_datetime, True))
 
     def set_ical_percent(self, percent):
         self.set_percentcomplete(percent)
 
     def set_percentcomplete(self, percent):
         self.event.setPercentComplete(int(percent))
 
     def set_transparency(self, transp):
         # empty stub
         pass
 
     def get_due(self):
         return xmlutils.from_cdatetime(self.event.due(), True)
 
     def get_ical_due(self):
         dt = self.get_due()
         if dt:
             return icalendar.vDatetime(dt)
         return None
 
     def get_percentcomplete(self):
         return self.event.percentComplete()
 
     def get_duration(self):
         return None
 
     def get_related_to(self):
         for x in self.event.relatedTo():
             return x
         return None
 
     def as_string_itip(self, method="REQUEST"):
         cal = icalendar.Calendar()
         cal.add(
             'prodid',
             '-//pykolab-%s-%s//kolab.org//' % (
                 constants.__version__,
                 constants.__release__
             )
         )
 
         cal.add('version', '2.0')
         cal.add('calscale', 'GREGORIAN')
         cal.add('method', method)
 
         ical_todo = icalendar.Todo()
 
         singletons = list(set(ical_todo.singletons))
         singletons.extend(['PERCENT-COMPLETE'])
         for attr in singletons:
             ical_getter = 'get_ical_%s' % (attr.lower())
             default_getter = 'get_%s' % (attr.lower())
             retval = None
             if hasattr(self, ical_getter):
                 retval = getattr(self, ical_getter)()
                 if not retval == None and not retval == "":
                     ical_todo.add(attr.lower(), retval)
             elif hasattr(self, default_getter):
                 retval = getattr(self, default_getter)()
                 if not retval == None and not retval == "":
                     ical_todo.add(attr.lower(), retval, encode=0)
 
         for attr in list(set(ical_todo.multiple)):
             ical_getter = 'get_ical_%s' % (attr.lower())
             default_getter = 'get_%s' % (attr.lower())
             retval = None
             if hasattr(self, ical_getter):
                 retval = getattr(self, ical_getter)()
             elif hasattr(self, default_getter):
                 retval = getattr(self, default_getter)()
 
             if isinstance(retval, list) and not len(retval) == 0:
                 for _retval in retval:
                     ical_todo.add(attr.lower(), _retval, encode=0)
 
         # copy custom properties to iCal
         for cs in self.event.customProperties():
             ical_todo.add(cs.identifier, cs.value)
 
         cal.add_component(ical_todo)
 
         if hasattr(cal, 'to_ical'):
             return cal.to_ical()
         elif hasattr(cal, 'as_string'):
             return cal.as_string()
 
     def __str__(self):
         xml = kolabformat.writeTodo(self.event)
 
         error = kolabformat.error()
 
         if error == None or not error:
             return xml
         else:
-            raise TodoIntegrityError, kolabformat.errorMessage()
+            raise TodoIntegrityError(kolabformat.errorMessage())
 
 
 class TodoIntegrityError(Exception):
     def __init__(self, message):
         Exception.__init__(self, message)
diff --git a/saslauthd/__init__.py b/saslauthd/__init__.py
index 6d89929..d2459ad 100644
--- a/saslauthd/__init__.py
+++ b/saslauthd/__init__.py
@@ -1,381 +1,381 @@
 # Copyright 2010-2016 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 """
     SASL authentication daemon for multi-domain Kolab deployments.
 
     The SASL authentication daemon can use the domain name space or realm
     in the login credentials to determine the backend authentication
     database, and authenticate the credentials supplied against that
     backend.
 """
 
 from __future__ import print_function
 from optparse import OptionParser
 from ConfigParser import SafeConfigParser
 
 import grp
 import os
 import pwd
 import shutil
 import sys
 import time
 import traceback
 
 import pykolab
 
 from pykolab import utils
 from pykolab.auth import Auth
 from pykolab.constants import *
 from pykolab.translate import _
 
 log = pykolab.getLogger('saslauthd')
 conf = pykolab.getConf()
 
 
 class SASLAuthDaemon(object):
     def __init__(self):
         daemon_group = conf.add_cli_parser_option_group(_("Daemon Options"))
 
         daemon_group.add_option(
                 "--fork",
                 dest    = "fork_mode",
                 action  = "store_true",
                 default = False,
                 help    = _("Fork to the background.")
             )
 
         daemon_group.add_option(
                 "-p",
                 "--pid-file",
                 dest    = "pidfile",
                 action  = "store",
                 default = "/var/run/kolab-saslauthd/kolab-saslauthd.pid",
                 help    = _("Path to the PID file to use.")
             )
 
         daemon_group.add_option(
                 "-s",
                 "--socket",
                 dest    = "socketfile",
                 action  = "store",
                 default = "/var/run/saslauthd/mux",
                 help    = _("Socket file to bind to.")
             )
 
         daemon_group.add_option(
                 "-u",
                 "--user",
                 dest    = "process_username",
                 action  = "store",
                 default = "kolab",
                 help    = _("Run as user USERNAME"),
                 metavar = "USERNAME"
             )
 
         daemon_group.add_option(
                 "-g",
                 "--group",
                 dest    = "process_groupname",
                 action  = "store",
                 default = "kolab",
                 help    = _("Run as group GROUPNAME"),
                 metavar = "GROUPNAME"
             )
 
         conf.finalize_conf()
 
         try:
             utils.ensure_directory(
                     os.path.dirname(conf.pidfile),
                     conf.process_username,
                     conf.process_groupname
                 )
-        except Exception, errmsg:
+        except Exception as errmsg:
             log.error(_("Could not create %r: %r") % (os.path.dirname(conf.pidfile), errmsg))
             sys.exit(1)
 
         self.thread_count = 0
 
     def run(self):
         """
             Run the SASL authentication daemon.
         """
 
         exitcode = 0
 
         self._ensure_socket_dir()
 
         self._drop_privileges()
 
         try:
             pid = os.getpid()
 
             if conf.fork_mode:
                 pid = os.fork()
 
             if pid > 0 and not conf.fork_mode:
                 self.do_saslauthd()
 
             elif pid > 0:
                 sys.exit(0)
 
             else:
                 # Give up the session, all control,
                 # all open file descriptors, see #5151
                 os.chdir("/")
                 old_umask = os.umask(0)
                 os.setsid()
 
                 pid = os.fork()
 
                 if pid > 0:
                     sys.exit(0)
 
                 sys.stderr.flush()
                 sys.stdout.flush()
 
                 os.close(0)
                 os.close(1)
                 os.close(2)
 
                 os.umask(old_umask)
 
                 self.thread_count += 1
                 log.remove_stdout_handler()
                 self.set_signal_handlers()
                 self.write_pid()
                 self.do_saslauthd()
 
-        except SystemExit, e:
+        except SystemExit as e:
             exitcode = e
         except KeyboardInterrupt:
             exitcode = 1
             log.info(_("Interrupted by user"))
-        except AttributeError, e:
+        except AttributeError:
             exitcode = 1
             traceback.print_exc()
             print(_("Traceback occurred, please report a " +
                                    "bug at https://issues.kolab.org"),
                   file=sys.stderr)
-        except TypeError, e:
+        except TypeError as e:
             exitcode = 1
             traceback.print_exc()
             log.error(_("Type Error: %s") % e)
         except:
             exitcode = 2
             traceback.print_exc()
             print(_("Traceback occurred, please report a " +
                                    "bug at https://issues.kolab.org"),
                   file=sys.stderr)
 
         sys.exit(exitcode)
 
     def do_saslauthd(self):
         """
             Create the actual listener socket, and handle the authentication.
 
             The actual authentication handling is passed on to the appropriate
             backend authentication classes through the more generic Auth().
         """
         import binascii
         import socket
         import struct
 
         s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
 
         # TODO: The saslauthd socket path could be a setting.
         try:
             os.remove(conf.socketfile)
         except:
             # TODO: Do the "could not remove, could not start" dance
             pass
 
         s.bind(conf.socketfile)
         os.chmod(conf.socketfile, 0o777)
 
         s.listen(5)
 
         while 1:
             max_tries = 20
             cur_tries = 0
             bound = False
             while not bound:
                 cur_tries += 1
                 try:
                     (clientsocket, address) = s.accept()
                     bound = True
-                except Exception, errmsg:
+                except Exception as errmsg:
                     log.error(
                             _("kolab-saslauthd could not accept " +
                               "connections on socket: %r") % (errmsg)
                         )
 
                     if cur_tries >= max_tries:
                         log.fatal(_("Maximum tries exceeded, exiting"))
                         sys.exit(1)
 
                     time.sleep(1)
 
             received = clientsocket.recv(4096)
 
             login = []
 
             start = 0
             end = 2
 
             while end < len(received):
                 (length,) = struct.unpack("!H", received[start:end])
                 start += 2
                 end += length
                 (value,) = struct.unpack("!%ds" % (length), received[start:end])
                 start += length
                 end = start + 2
                 login.append(value)
 
             if len(login) == 4:
                 realm = login[3]
             elif len(login[0].split('@')) > 1:
                 realm = login[0].split('@')[1]
             else:
                 realm = conf.get('kolab', 'primary_domain')
 
             auth = Auth(domain=realm)
             auth.connect()
 
             success = False
 
             try:
                 success = auth.authenticate(login)
             except:
                 success = False
 
             if success:
                 # #1170: Catch broken pipe error (incomplete authentication request)
                 try:
                     clientsocket.send(struct.pack("!H2s", 2, "OK"))
                 except:
                     pass
             else:
                 # #1170: Catch broken pipe error (incomplete authentication request)
                 try:
                     clientsocket.send(struct.pack("!H2s", 2, "NO"))
                 except:
                     pass
 
             clientsocket.close()
             auth.disconnect()
 
     def reload_config(self, *args, **kw):
         pass
 
     def remove_pid(self, *args, **kw):
         if os.access(conf.pidfile, os.R_OK):
             os.remove(conf.pidfile)
         raise SystemExit
 
     def set_signal_handlers(self):
         import signal
         signal.signal(signal.SIGHUP, self.reload_config)
         signal.signal(signal.SIGTERM, self.remove_pid)
 
     def write_pid(self):
         pid = os.getpid()
         fp = open(conf.pidfile, 'w')
         fp.write("%d\n" % (pid))
         fp.close()
 
     def _ensure_socket_dir(self):
         utils.ensure_directory(
                 os.path.dirname(conf.socketfile),
                 conf.process_username,
                 conf.process_groupname
             )
 
     def _drop_privileges(self):
         try:
             try:
                 (ruid, euid, suid) = os.getresuid()
                 (rgid, egid, sgid) = os.getresgid()
-            except AttributeError, errmsg:
+            except AttributeError:
                 ruid = os.getuid()
                 rgid = os.getgid()
 
             if ruid == 0:
                 # Means we can setreuid() / setregid() / setgroups()
                 if rgid == 0:
                     # Get group entry details
                     try:
                         (
                                 group_name,
                                 group_password,
                                 group_gid,
                                 group_members
                             ) = grp.getgrnam(conf.process_groupname)
 
                     except KeyError:
                         print(_("Group %s does not exist") % (
                                 conf.process_groupname
                             ), file=sys.stderr)
 
                         sys.exit(1)
 
                     # Set real and effective group if not the same as current.
                     if not group_gid == rgid:
                         log.debug(
                                 _("Switching real and effective group id to %d") % (
                                         group_gid
                                     ),
                                 level=8
                             )
 
                         os.setregid(group_gid, group_gid)
 
                 if ruid == 0:
                     # Means we haven't switched yet.
                     try:
                         (
                                 user_name,
                                 user_password,
                                 user_uid,
                                 user_gid,
                                 user_gecos,
                                 user_homedir,
                                 user_shell
                             ) = pwd.getpwnam(conf.process_username)
 
                     except KeyError:
                         print(_("User %s does not exist") % (
                                 conf.process_username
                             ), file=sys.stderr)
 
                         sys.exit(1)
 
                     # Set real and effective user if not the same as current.
                     if not user_uid == ruid:
                         log.debug(
                                 _("Switching real and effective user id to %d") % (
                                         user_uid
                                     ),
                                 level=8
                             )
 
                         os.setreuid(user_uid, user_uid)
 
         except:
             log.error(_("Could not change real and effective uid and/or gid"))
diff --git a/setup-kolab.py b/setup-kolab.py
index ef6fcb7..12b5d1c 100755
--- a/setup-kolab.py
+++ b/setup-kolab.py
@@ -1,45 +1,45 @@
 #!/usr/bin/python -u
 # -*- coding: utf-8 -*-
 #
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import logging
 import os
 import sys
 
 # For development purposes
 sys.path = ['.'] + sys.path
 
 import pykolab
 
 from pykolab.setup import Setup
 from pykolab.translate import _
 
 try:
     from pykolab.constants import *
-except ImportError, e:
+except ImportError as e:
     print(_("Cannot load pykolab/constants.py:"), file=sys.stderr)
     print("%s" % e, file=sys.stderr)
     sys.exit(1)
 
 if __name__ == "__main__":
     setup = Setup()
     setup.run()
diff --git a/wallace.py b/wallace.py
index 6fbcadd..ebfddda 100755
--- a/wallace.py
+++ b/wallace.py
@@ -1,41 +1,41 @@
 #!/usr/bin/python
 #
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 from __future__ import print_function
 
 import sys
 
 # For development purposes
 sys.path.extend(['.', '..'])
 
 from pykolab.translate import _
 
 try:
     from pykolab.constants import *
-except ImportError, e:
+except ImportError as e:
     print(_("Cannot load pykolab/constants.py:"), file=sys.stderr)
     print("%s" % e, file=sys.stderr)
     sys.exit(1)
 
 import wallace
 
 if __name__ == "__main__":
     wallace = wallace.WallaceDaemon()
     wallace.run()
diff --git a/wallace/module_gpgencrypt.py b/wallace/module_gpgencrypt.py
index e623fe1..100d1d1 100644
--- a/wallace/module_gpgencrypt.py
+++ b/wallace/module_gpgencrypt.py
@@ -1,293 +1,293 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation; version 3 or, at your option, any later version
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU Library General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 #
 
 import os
 import tempfile
 import time
 
 from email import message_from_string
 from email.MIMEBase import MIMEBase
 from email.MIMEText import MIMEText
 from email.parser import Parser
 from email.utils import formataddr
 from email.utils import getaddresses
 
 import email.mime.application
 import email.mime.multipart
 import email.mime.text
 import email.encoders
 import gnupg
 
 import modules
 
 import pykolab
 
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.wallace/gpgencrypt')
 extra_log_params = {'qid': '-'}
 log = pykolab.logger.LoggerAdapter(log, extra_log_params)
 conf = pykolab.getConf()
 
 mybasepath = '/var/spool/pykolab/wallace/gpgencrypt/'
 
 def __init__():
     modules.register('gpgencrypt', execute, description=description())
 
 def description():
     return """Encrypt messages to the recipient(s)."""
 
 def pgp_mime(msg, recepients):
     gpg = gnupg.GPG(gnupghome='/var/lib/kolab/.gnupg', verbose=conf.debuglevel > 8)
     gpg.encoding = 'utf-8'
 
     msg = msg
 
     msg_boundary = str(msg.get_boundary())
     msg_content_type = str(msg.get_content_type())
     payload = msg.get_payload()
 
     content = "Content-Type: " + msg_content_type + ";" + "\n boundary=\"" + msg_boundary + "\"\n\n" + payload
     encrypted_content = gpg.encrypt(content, recepients, always_trust=True)
     msg.set_type("multipart/encrypted")
     msg.set_param("protocol","application/pgp-encrypted")
 
     msg_boundary_gpg = "--boundary-gpg-encryption-42"
 
     msg_preamble = "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\n\
 " + msg_boundary_gpg + "\n\
 Content-Type: application/pgp-encrypted\n\
 Content-Description: PGP/MIME version identification\n\
 \n\
 Version: 1\n\
 \n\
 " + msg_boundary_gpg + "\n\
 Content-Type: application/octet-stream; name=\"encrypted.asc\"\n\
 Content-Description: OpenPGP encrypted message\n\
 Content-Disposition: inline; filename=\"encrypted.asc\"\n\n"
 
     msg.set_boundary(msg_boundary_gpg)
     msg.set_payload(msg_preamble + str(encrypted_content) + "\n" + msg_boundary_gpg)
 
     return msg
 
 def execute(*args, **kw):
     global extra_log_params
 
     # TODO: Test for correct call.
     filepath = args[0]
 
     extra_log_params['qid'] = os.path.basename(filepath)
 
     if not os.path.isdir(mybasepath):
         os.makedirs(mybasepath)
 
     for stage in ['incoming', 'ACCEPT' ]:
         if not os.path.isdir(os.path.join(mybasepath, stage)):
             os.makedirs(os.path.join(mybasepath, stage))
 
     if kw.has_key('stage'):
         log.debug(_("Issuing callback after processing to stage %s") % (kw['stage']), level=8)
         log.debug(_("Testing cb_action_%s()") % (kw['stage']), level=8)
         if hasattr(modules, 'cb_action_%s' % (kw['stage'])):
             log.debug(_("Attempting to execute cb_action_%s()") % (kw['stage']), level=8)
             exec('modules.cb_action_%s(%r, %r)' % (kw['stage'],'gpgencrypt',filepath))
 
     log.debug(_("Executing module gpgencrypt for %r, %r") % (args, kw), level=8)
 
     new_filepath = os.path.join('/var/spool/pykolab/wallace/gpgencrypt/incoming', os.path.basename(filepath))
 
     if not filepath == new_filepath:
         log.debug("Renaming %r to %r" % (filepath, new_filepath))
         os.rename(filepath, new_filepath)
         filepath = new_filepath
 
     # parse message headers
     # @TODO: make sure we can use True as the 2nd argument here
     message = Parser().parse(open(filepath, 'r'), True)
 
     # Possible gpgencrypt answers are limited to ACCEPT only
     answers = [ 'ACCEPT' ]
 
 # from Mail::GnuPG.is_encrypted
 # 
 #sub is_encrypted {
 #  my ($self,$entity) = @_;
 #  return 1
 #    if (($entity->effective_type =~ m!multipart/encrypted!)
 #    ||
 #    ($entity->as_string =~ m!^-----BEGIN PGP MESSAGE-----!m));
 #  return 0;
 #}
 
     message_already_encrypted = False
 
     for part in message.walk():
         if part.get_content_type() in [ "application/pgp-encrypted" ]:
             message_already_encrypted = True
             log.debug(_("Message is already encrypted (app/pgp-enc content-type)"), level=8)
 
     if message.get_content_type() in [ "multipart/encrypted" ]:
         message_already_encrypted = True
         log.debug(_("Message already encrypted by main content-type header"), level=8)
 
     if message_already_encrypted:
         return filepath
 
     try:
         # What are recipient addresses to encrypt to (bitmask)?
         # 1 - organization key
         # 2 - envelope to
         # 4 - to
         # 8 - cc
         # 16 - resent-to
         # 32 - resent-cc
         encrypt_to_rcpts = conf.get('wallace', 'gpgencrypt_to_rcpts')
         if encrypt_to_rcpts == None:
             encrypt_to_rcpts = 14
         else:
             encrypt_to_rcpts = (int)(encrypt_to_rcpts)
 
         # Only encrypt to keys that are trusted
         strict_crypt = conf.get('wallace', 'gpgencrypt_strict')
         if strict_crypt == None:
             strict_crypt = False
 
         # Organization key ID
         if encrypt_to_rcpts & 1:
             encrypt_to_org = conf.get('wallace', 'gpgencrypt_to_org_key')
             if encrypt_to_org == None and encrypt_to_rcpts & 1:
                 if strict_crypt:
                     log.error(_("Configured to encrypt to a key not configured, and strict policy enabled. Bailing out."))
                     modules.cb_action_REJECT('gpgencrypt',filepath)
                 else:
                     log.error(_("Configured to encrypt to a key not configured, but continuing anyway (see 'gpgencrypt_strict')."))
         else:
             encrypt_to_org = []
 
         # Bounce the message if encryption fails?
         force_crypt = conf.get('wallace', 'gpgencrypt_force')
         if force_crypt == None:
             force_crypt = False
 
         # Retrieve keys from remote server(s) automatically?
         retrieve_keys = conf.get('wallace', 'gpgencrypt_retrieve_keys')
         if retrieve_keys == None:
             retrieve_keys = False
 
         if retrieve_keys:
             gpgserver = conf.get('wallace', 'gpgencrypt_server')
             if gpgserver == None:
                 gpgserver = 'pgp.mit.edu'
 
         encrypt_to = []
         if encrypt_to_rcpts & 2:
             encrypt_to.extend(message.get_all('X-Kolab-To', []))
 
         if encrypt_to_rcpts & 4:
             encrypt_to.extend(message.get_all('to', []))
 
         if encrypt_to_rcpts & 8:
             encrypt_to.extend(message.get_all('cc', []))
 
         if encrypt_to_rcpts & 16:
             encrypt_to.extend(message.get_all('resent-to', []))
 
         if encrypt_to_rcpts & 32:
             encrypt_to.extend(message.get_all('resent-cc', []))
 
         recipients = [address for displayname,address in getaddresses(encrypt_to)]
 
         log.debug(_("Recipients: %r") % (recipients))
 
         # Split between recipients we can encrypt for/to, and ones we can not
         encrypt_rcpts = []
         nocrypt_rcpts = []
 
 
         gpg = gnupg.GPG(gnupghome='/var/lib/kolab/.gnupg', verbose=conf.debuglevel > 8)
         gpg.encoding = 'utf-8'
 
         local_keys = gpg.list_keys()
         log.debug(_("Current keys: %r") % (local_keys), level=8)
 
         for recipient in recipients:
             key_local = False
 
             log.debug(_("Retrieving key for recipient: %r") % (recipient))
 
             for key in local_keys:
                 for address in [x for x in [address for displayname,address in getaddresses(key['uids'])] if x == recipient]:
                     log.debug(_("Found matching address %r") % (address))
                     key_local = key['keyid']
 
             if key_local == False:
                 if retrieve_keys:
                     remote_keys = gpg.search_keys(recipient, gpgserver)
                     if len(remote_keys) == 1:
                         for address in [x for x in [address for displayname,address in getaddresses(remote_keys[0]['uids'])] if x == recipient]:
                             log.debug(_("Found matching address %r in remote keys") % (address))
                             gpg.recv_keys(gpgserver, remote_keys[0]['keyid'])
                             local_keys = gpg.list_keys()
                     else:
                         nocrypt_rcpts.append(recipient)
 
             for key in local_keys:
                 for address in [x for x in [address for displayname,address in getaddresses(key['uids'])] if x == recipient]:
                     log.debug(_("Found matching address %r") % (address))
                     key_local = key['keyid']
             if not key_local == False:
                 encrypt_rcpts.append(key_local)
 
         payload = message.get_payload()
         #print "payload:", payload
         if len(encrypt_rcpts) < 1:
             return filepath
 
         if "multipart" in message.get_content_type():
 
             log.debug(_("Mime Message - we need to build multipart/encrypted structure"), level=8)
 
             msg = message
             enc_mime_message = pgp_mime(msg, encrypt_rcpts)
 
             message = enc_mime_message
 
         else:
 
             log.debug(_("No Mime Message - encypt plain"), level=8)
 
             encrypted_data = gpg.encrypt(payload, encrypt_rcpts, always_trust=True)
             encrypted_string = str(encrypted_data)
 
             message.set_payload(encrypted_string)
 
             message.add_header('X-wallace-gpg-encrypted', 'true')
 
         (fp, new_filepath) = tempfile.mkstemp(dir="/var/spool/pykolab/wallace/gpgencrypt/ACCEPT")
         os.write(fp, message.as_string())
         os.close(fp)
         os.unlink(filepath)
 
         exec('modules.cb_action_%s(%r, %r)' % ('ACCEPT','gpgencrypt', new_filepath))
-    except Exception, errmsg:
+    except Exception as errmsg:
         log.error(_("An error occurred: %r") % (errmsg))
         if conf.debuglevel > 8:
             import traceback
             traceback.print_exc()
diff --git a/wallace/module_invitationpolicy.py b/wallace/module_invitationpolicy.py
index 46e506d..72295a7 100644
--- a/wallace/module_invitationpolicy.py
+++ b/wallace/module_invitationpolicy.py
@@ -1,1480 +1,1480 @@
 # -*- coding: utf-8 -*-
 # Copyright 2014 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Thomas Bruederli (Kolab Systems) <bruederli@kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import datetime
 import os
 import random
 import signal
 import tempfile
 import time
 from urlparse import urlparse
 import urllib
 import hashlib
 import traceback
 import re
 
 from email import message_from_string
 from email.parser import Parser
 from email.utils import formataddr
 from email.utils import getaddresses
 
 import modules
 
 import pykolab
 import kolabformat
 
 from pykolab import utils
 from pykolab.auth import Auth
 from pykolab.conf import Conf
 from pykolab.imap import IMAP
 from pykolab.xml import to_dt
 from pykolab.xml import utils as xmlutils
 from pykolab.xml import todo_from_message
 from pykolab.xml import event_from_message
 from pykolab.xml import participant_status_label
 from pykolab.itip import objects_from_message
 from pykolab.itip import check_event_conflict
 from pykolab.itip import send_reply
 from pykolab.translate import _
 
 # define some contstants used in the code below
 ACT_MANUAL         = 1
 ACT_ACCEPT         = 2
 ACT_DELEGATE       = 4
 ACT_REJECT         = 8
 ACT_UPDATE         = 16
 ACT_CANCEL_DELETE  = 32
 ACT_SAVE_TO_FOLDER = 64
 
 COND_IF_AVAILABLE  = 128
 COND_IF_CONFLICT   = 256
 COND_TENTATIVE     = 512
 COND_NOTIFY        = 1024
 COND_FORWARD       = 2048
 COND_TYPE_EVENT    = 4096
 COND_TYPE_TASK     = 8192
 COND_TYPE_ALL      = COND_TYPE_EVENT + COND_TYPE_TASK
 
 ACT_TENTATIVE         = ACT_ACCEPT + COND_TENTATIVE
 ACT_UPDATE_AND_NOTIFY = ACT_UPDATE + COND_NOTIFY
 ACT_SAVE_AND_FORWARD  = ACT_SAVE_TO_FOLDER + COND_FORWARD
 ACT_CANCEL_DELETE_AND_NOTIFY = ACT_CANCEL_DELETE + COND_NOTIFY
 
 FOLDER_TYPE_ANNOTATION = '/vendor/kolab/folder-type'
 
 MESSAGE_PROCESSED = 1
 MESSAGE_FORWARD   = 2
 
 policy_name_map = {
     # policy values applying to all object types
     'ALL_MANUAL':                     ACT_MANUAL + COND_TYPE_ALL,
     'ALL_ACCEPT':                     ACT_ACCEPT + COND_TYPE_ALL,
     'ALL_REJECT':                     ACT_REJECT + COND_TYPE_ALL,
     'ALL_DELEGATE':                   ACT_DELEGATE + COND_TYPE_ALL,  # not implemented
     'ALL_UPDATE':                     ACT_UPDATE + COND_TYPE_ALL,
     'ALL_UPDATE_AND_NOTIFY':          ACT_UPDATE_AND_NOTIFY + COND_TYPE_ALL,
     'ALL_SAVE_TO_FOLDER':             ACT_SAVE_TO_FOLDER + COND_TYPE_ALL,
     'ALL_SAVE_AND_FORWARD':           ACT_SAVE_AND_FORWARD + COND_TYPE_ALL,
     'ALL_CANCEL_DELETE':              ACT_CANCEL_DELETE + COND_TYPE_ALL,
     'ALL_CANCEL_DELETE_AND_NOTIFY':   ACT_CANCEL_DELETE_AND_NOTIFY + COND_TYPE_ALL,
     # event related policy values
     'EVENT_MANUAL':                   ACT_MANUAL + COND_TYPE_EVENT,
     'EVENT_ACCEPT':                   ACT_ACCEPT + COND_TYPE_EVENT,
     'EVENT_TENTATIVE':                ACT_TENTATIVE + COND_TYPE_EVENT,
     'EVENT_REJECT':                   ACT_REJECT + COND_TYPE_EVENT,
     'EVENT_DELEGATE':                 ACT_DELEGATE + COND_TYPE_EVENT,  # not implemented
     'EVENT_UPDATE':                   ACT_UPDATE + COND_TYPE_EVENT,
     'EVENT_UPDATE_AND_NOTIFY':        ACT_UPDATE_AND_NOTIFY + COND_TYPE_EVENT,
     'EVENT_ACCEPT_IF_NO_CONFLICT':    ACT_ACCEPT + COND_IF_AVAILABLE + COND_TYPE_EVENT,
     'EVENT_TENTATIVE_IF_NO_CONFLICT': ACT_ACCEPT + COND_TENTATIVE + COND_IF_AVAILABLE + COND_TYPE_EVENT,
     'EVENT_DELEGATE_IF_CONFLICT':     ACT_DELEGATE + COND_IF_CONFLICT + COND_TYPE_EVENT,
     'EVENT_REJECT_IF_CONFLICT':       ACT_REJECT + COND_IF_CONFLICT + COND_TYPE_EVENT,
     'EVENT_SAVE_TO_FOLDER':           ACT_SAVE_TO_FOLDER + COND_TYPE_EVENT,
     'EVENT_SAVE_AND_FORWARD':         ACT_SAVE_AND_FORWARD + COND_TYPE_EVENT,
     'EVENT_CANCEL_DELETE':            ACT_CANCEL_DELETE + COND_TYPE_EVENT,
     'EVENT_CANCEL_DELETE_AND_NOTIFY': ACT_CANCEL_DELETE_AND_NOTIFY + COND_TYPE_EVENT,
     # task related policy values
     'TASK_MANUAL':                    ACT_MANUAL + COND_TYPE_TASK,
     'TASK_ACCEPT':                    ACT_ACCEPT + COND_TYPE_TASK,
     'TASK_REJECT':                    ACT_REJECT + COND_TYPE_TASK,
     'TASK_DELEGATE':                  ACT_DELEGATE + COND_TYPE_TASK,  # not implemented
     'TASK_UPDATE':                    ACT_UPDATE + COND_TYPE_TASK,
     'TASK_UPDATE_AND_NOTIFY':         ACT_UPDATE_AND_NOTIFY + COND_TYPE_TASK,
     'TASK_SAVE_TO_FOLDER':            ACT_SAVE_TO_FOLDER + COND_TYPE_TASK,
     'TASK_SAVE_AND_FORWARD':          ACT_SAVE_AND_FORWARD + COND_TYPE_TASK,
     'TASK_CANCEL_DELETE':             ACT_CANCEL_DELETE + COND_TYPE_TASK,
     'TASK_CANCEL_DELETE_AND_NOTIFY':  ACT_CANCEL_DELETE_AND_NOTIFY + COND_TYPE_TASK,
     # legacy values
     'ACT_MANUAL':                     ACT_MANUAL + COND_TYPE_ALL,
     'ACT_ACCEPT':                     ACT_ACCEPT + COND_TYPE_ALL,
     'ACT_ACCEPT_IF_NO_CONFLICT':      ACT_ACCEPT + COND_IF_AVAILABLE + COND_TYPE_EVENT,
     'ACT_TENTATIVE':                  ACT_TENTATIVE + COND_TYPE_EVENT,
     'ACT_TENTATIVE_IF_NO_CONFLICT':   ACT_ACCEPT + COND_TENTATIVE + COND_IF_AVAILABLE + COND_TYPE_EVENT,
     'ACT_DELEGATE':                   ACT_DELEGATE + COND_TYPE_ALL,
     'ACT_DELEGATE_IF_CONFLICT':       ACT_DELEGATE + COND_IF_CONFLICT + COND_TYPE_EVENT,
     'ACT_REJECT':                     ACT_REJECT + COND_TYPE_ALL,
     'ACT_REJECT_IF_CONFLICT':         ACT_REJECT + COND_IF_CONFLICT + COND_TYPE_EVENT,
     'ACT_UPDATE':                     ACT_UPDATE + COND_TYPE_ALL,
     'ACT_UPDATE_AND_NOTIFY':          ACT_UPDATE_AND_NOTIFY + COND_TYPE_ALL,
     'ACT_CANCEL_DELETE':              ACT_CANCEL_DELETE + COND_TYPE_ALL,
     'ACT_CANCEL_DELETE_AND_NOTIFY':   ACT_CANCEL_DELETE_AND_NOTIFY + COND_TYPE_ALL,
     'ACT_SAVE_TO_CALENDAR':           ACT_SAVE_TO_FOLDER + COND_TYPE_EVENT,
     'ACT_SAVE_AND_FORWARD':           ACT_SAVE_AND_FORWARD + COND_TYPE_EVENT,
 }
 
 policy_value_map = dict([(v &~ COND_TYPE_ALL, k) for (k, v) in policy_name_map.iteritems()])
 
 object_type_conditons = {
     'event': COND_TYPE_EVENT,
     'task':  COND_TYPE_TASK
 }
 
 log = pykolab.getLogger('pykolab.wallace/invitationpolicy')
 extra_log_params = {'qid': '-'}
 log = pykolab.logger.LoggerAdapter(log, extra_log_params)
 
 conf = pykolab.getConf()
 
 mybasepath = '/var/spool/pykolab/wallace/invitationpolicy/'
 
 auth = None
 imap = None
 write_locks = []
 
 def __init__():
     modules.register('invitationpolicy', execute, description=description())
 
 def accept(filepath):
     new_filepath = os.path.join(
         mybasepath,
         'ACCEPT',
         os.path.basename(filepath)
     )
 
     cleanup()
     os.rename(filepath, new_filepath)
     filepath = new_filepath
     exec('modules.cb_action_ACCEPT(%r, %r)' % ('invitationpolicy',filepath))
 
 def reject(filepath):
     new_filepath = os.path.join(
         mybasepath,
         'REJECT',
         os.path.basename(filepath)
     )
 
     os.rename(filepath, new_filepath)
     filepath = new_filepath
     exec('modules.cb_action_REJECT(%r, %r)' % ('invitationpolicy',filepath))
 
 def description():
     return """Invitation policy execution module."""
 
 def cleanup():
     global auth, imap, write_locks, extra_log_params
 
     log.debug("cleanup(): %r, %r" % (auth, imap), level=8)
 
     extra_log_params['qid'] = '-'
 
     auth.disconnect()
     del auth
 
     # Disconnect IMAP or we lock the mailbox almost constantly
     imap.disconnect()
     del imap
 
     # remove remaining write locks
     for key in write_locks:
         remove_write_lock(key, False)
 
 def execute(*args, **kw):
     global auth, imap, extra_log_params
 
     filepath = args[0]
 
     extra_log_params['qid'] = os.path.basename(filepath)
 
     # (re)set language to default
     pykolab.translate.setUserLanguage(conf.get('kolab','default_locale'))
 
     if not os.path.isdir(mybasepath):
         os.makedirs(mybasepath)
 
     for stage in ['incoming', 'ACCEPT', 'REJECT', 'HOLD', 'DEFER', 'locks']:
         if not os.path.isdir(os.path.join(mybasepath, stage)):
             os.makedirs(os.path.join(mybasepath, stage))
 
     log.debug(_("Invitation policy called for %r, %r") % (args, kw), level=8)
 
     auth = Auth()
     imap = IMAP()
 
     # ignore calls on lock files
     if '/locks/' in filepath or kw.has_key('stage') and kw['stage'] == 'locks':
         return False
 
     log.debug("Invitation policy executing for %r, %r" % (filepath, '/locks/' in filepath), level=8)
 
     if kw.has_key('stage'):
         log.debug(_("Issuing callback after processing to stage %s") % (kw['stage']), level=8)
 
         log.debug(_("Testing cb_action_%s()") % (kw['stage']), level=8)
         if hasattr(modules, 'cb_action_%s' % (kw['stage'])):
             log.debug(_("Attempting to execute cb_action_%s()") % (kw['stage']), level=8)
 
             exec(
                 'modules.cb_action_%s(%r, %r)' % (
                     kw['stage'],
                     'invitationpolicy',
                     filepath
                 )
             )
 
             return filepath
     else:
         # Move to incoming
         new_filepath = os.path.join(
             mybasepath,
             'incoming',
             os.path.basename(filepath)
         )
 
         if not filepath == new_filepath:
             log.debug("Renaming %r to %r" % (filepath, new_filepath))
             os.rename(filepath, new_filepath)
             filepath = new_filepath
 
     # parse full message
     message = Parser().parse(open(filepath, 'r'))
 
     # invalid message, skip
     if not message.get('X-Kolab-To'):
         return filepath
 
     recipients = [address for displayname,address in getaddresses(message.get_all('X-Kolab-To'))]
     sender_email = [address for displayname,address in getaddresses(message.get_all('X-Kolab-From'))][0]
 
     any_itips = False
     recipient_email = None
     recipient_emails = []
     recipient_user_dn = None
 
     # An iTip message may contain multiple events. Later on, test if the message
     # is an iTip message by checking the length of this list.
     try:
         itip_events = objects_from_message(message, ['VEVENT','VTODO'], ['REQUEST', 'REPLY', 'CANCEL'])
-    except Exception, errmsg:
+    except Exception as errmsg:
         log.error(_("Failed to parse iTip objects from message: %r" % (errmsg)))
         itip_events = []
 
     if not len(itip_events) > 0:
         log.info(_("Message is not an iTip message or does not contain any (valid) iTip objects."))
 
     else:
         any_itips = True
         log.debug(_("iTip objects attached to this message contain the following information: %r") % (itip_events), level=8)
 
     # See if any iTip actually allocates a user.
     if any_itips and len([x['uid'] for x in itip_events if x.has_key('attendees') or x.has_key('organizer')]) > 0:
         auth.connect()
 
         # we're looking at the first itip object
         itip_event = itip_events[0]
 
         for recipient in recipients:
             recipient_user_dn = user_dn_from_email_address(recipient)
             if recipient_user_dn:
                 receiving_user = auth.get_entry_attributes(None, recipient_user_dn, ['*'])
                 recipient_emails = auth.extract_recipient_addresses(receiving_user)
                 recipient_email = recipient
 
                 # extend with addresses from delegators
                 # (only do this lookup for REPLY messages)
                 receiving_user['_delegated_mailboxes'] = []
 
                 if itip_event['method'] == 'REPLY':
                     for _delegator in auth.list_delegators(recipient_user_dn):
                         if not _delegator['_mailbox_basename'] == None:
                             receiving_user['_delegated_mailboxes'].append(
                                     _delegator['_mailbox_basename'].split('@')[0]
                                 )
 
                 log.debug(_("Recipient emails for %s: %r") % (recipient_user_dn, recipient_emails), level=8)
                 break
 
     if not any_itips:
         log.debug(_("No itips, no users, pass along %r") % (filepath), level=5)
         return filepath
     elif recipient_email is None:
         log.debug(_("iTips, but no users, pass along %r") % (filepath), level=5)
         return filepath
 
     # for replies, the organizer is the recipient
     if itip_event['method'] == 'REPLY':
         # Outlook can send iTip replies without an organizer property
         if itip_event.has_key('organizer'):
             organizer_mailto = str(itip_event['organizer']).split(':')[-1]
             user_attendees = [organizer_mailto] if organizer_mailto in recipient_emails else []
         else:
             user_attendees = [recipient_email]
 
     else:
         # Limit the attendees to the one that is actually invited with the current message.
         attendees = [str(a).split(':')[-1] for a in (itip_event['attendees'] if itip_event.has_key('attendees') else [])]
         user_attendees = [a for a in attendees if a in recipient_emails]
 
         if itip_event.has_key('organizer'):
             sender_email = itip_event['xml'].get_organizer().email()
 
     # abort if no attendee matches the envelope recipient
     if len(user_attendees) == 0:
         log.info(_("No user attendee matching envelope recipient %s, skip message") % (recipient_email))
         return filepath
 
     log.debug(_("Receiving user: %r") % (receiving_user), level=8)
 
     # set recipient_email to the matching attendee mailto: address
     recipient_email = user_attendees[0]
 
     # change gettext language to the preferredlanguage setting of the receiving user
     if receiving_user.has_key('preferredlanguage'):
         pykolab.translate.setUserLanguage(receiving_user['preferredlanguage'])
 
     # find user's kolabInvitationPolicy settings and the matching policy values
     type_condition = object_type_conditons.get(itip_event['type'], COND_TYPE_ALL)
     policies = get_matching_invitation_policies(receiving_user, sender_email, type_condition)
 
     # select a processing function according to the iTip request method
     method_processing_map = {
         'REQUEST': process_itip_request,
         'REPLY':   process_itip_reply,
         'CANCEL':  process_itip_cancel
     }
 
     done = None
     if method_processing_map.has_key(itip_event['method']):
         processor_func = method_processing_map[itip_event['method']]
 
         # connect as cyrus-admin
         imap.connect()
 
         for policy in policies:
             log.debug(_("Apply invitation policy %r for sender %r") % (policy_value_map[policy], sender_email), level=8)
             done = processor_func(itip_event, policy, recipient_email, sender_email, receiving_user)
 
             # matching policy found
             if done is not None:
                 break
 
             # remove possible write lock from this iteration
             remove_write_lock(get_lock_key(receiving_user, itip_event['uid']))
 
     else:
         log.debug(_("Ignoring '%s' iTip method") % (itip_event['method']), level=8)
 
     # message has been processed by the module, remove it
     if done == MESSAGE_PROCESSED:
         log.debug(_("iTip message %r consumed by the invitationpolicy module") % (message.get('Message-ID')), level=5)
         os.unlink(filepath)
         cleanup()
         return None
 
     # accept message into the destination inbox
     accept(filepath)
 
 
 def process_itip_request(itip_event, policy, recipient_email, sender_email, receiving_user):
     """
         Process an iTip REQUEST message according to the given policy
     """
 
     # if invitation policy is set to MANUAL, pass message along
     if policy & ACT_MANUAL:
         log.info(_("Pass invitation for manual processing"))
         return MESSAGE_FORWARD
 
     try:
         receiving_attendee = itip_event['xml'].get_attendee_by_email(recipient_email)
         log.debug(_("Receiving attendee: %r") % (receiving_attendee.to_dict()), level=8)
-    except Exception, errmsg:
+    except Exception as errmsg:
         log.error("Could not find envelope attendee: %r" % (errmsg))
         return MESSAGE_FORWARD
 
     # process request to participating attendees with RSVP=TRUE or PARTSTAT=NEEDS-ACTION
     is_task = itip_event['type'] == 'task'
     nonpart = receiving_attendee.get_role() == kolabformat.NonParticipant
     partstat = receiving_attendee.get_participant_status()
     save_object = not nonpart or not partstat == kolabformat.PartNeedsAction
     rsvp = receiving_attendee.get_rsvp()
     scheduling_required = rsvp or partstat == kolabformat.PartNeedsAction
     respond_with = receiving_attendee.get_participant_status(True)
     condition_fulfilled = True
 
     # find existing event in user's calendar
     (existing, master) = find_existing_object(itip_event['uid'], itip_event['type'], itip_event['recurrence-id'], receiving_user, True)
 
     # compare sequence number to determine a (re-)scheduling request
     if existing is not None:
         scheduling_required = itip_event['sequence'] > 0 and itip_event['sequence'] > existing.get_sequence()
         log.debug(_("Scheduling required: %r, for existing %s: %s") % (scheduling_required, existing.type, existing.get_uid()), level=8)
         save_object = True
 
     # if scheduling: check availability (skip that for tasks)
     if scheduling_required:
         if not is_task and policy & (COND_IF_AVAILABLE | COND_IF_CONFLICT):
             condition_fulfilled = check_availability(itip_event, receiving_user)
         if not is_task and policy & COND_IF_CONFLICT:
             condition_fulfilled = not condition_fulfilled
 
         log.debug(_("Precondition for object %r fulfilled: %r") % (itip_event['uid'], condition_fulfilled), level=5)
 
         if existing:
             respond_with = None
 
         if policy & ACT_ACCEPT and condition_fulfilled:
             respond_with = 'TENTATIVE' if policy & COND_TENTATIVE else 'ACCEPTED'
 
         elif policy & ACT_REJECT and condition_fulfilled:
             respond_with = 'DECLINED'
             # TODO: only save declined invitation when a certain config option is set?
 
         elif policy & ACT_DELEGATE and condition_fulfilled:
             # TODO: delegate (but to whom?)
             return None
 
     # auto-update changes if enabled for this user
     elif policy & ACT_UPDATE and existing:
         # compare sequence number to avoid outdated updates
         if not itip_event['sequence'] == existing.get_sequence():
             log.info(_("The iTip request sequence (%r) doesn't match the referred object version (%r). Ignoring.") % (
                 itip_event['sequence'], existing.get_sequence()
             ))
             return None
 
         log.debug(_("Auto-updating %s %r on iTip REQUEST (no re-scheduling)") % (existing.type, existing.uid), level=8)
         save_object = True
         rsvp = False
 
         # retain task status and percent-complete properties from my old copy
         if is_task:
             itip_event['xml'].set_status(existing.get_status())
             itip_event['xml'].set_percentcomplete(existing.get_percentcomplete())
 
         if policy & COND_NOTIFY:
             sender = itip_event['xml'].get_organizer()
             comment = itip_event['xml'].get_comment()
             send_update_notification(itip_event['xml'], receiving_user, existing, False,
                                      sender, comment)
 
     # if RSVP, send an iTip REPLY
     if rsvp or scheduling_required:
         # set attendee's CN from LDAP record if yet missing
         if not receiving_attendee.get_name() and receiving_user.has_key('cn'):
             receiving_attendee.set_name(receiving_user['cn'])
 
         # send iTip reply
         if respond_with is not None and not respond_with == 'NEEDS-ACTION':
             receiving_attendee.set_participant_status(respond_with)
             send_reply(recipient_email, itip_event, invitation_response_text(itip_event['type']),
                 subject=_('"%(summary)s" has been %(status)s'))
 
         elif policy & ACT_SAVE_TO_FOLDER:
             # copy the invitation into the user's default folder with PARTSTAT=NEEDS-ACTION
             itip_event['xml'].set_attendee_participant_status(receiving_attendee, respond_with or 'NEEDS-ACTION')
             save_object = True
 
         else:
             # policy doesn't match, pass on to next one
             return None
 
     if save_object:
         targetfolder = None
 
         # delete old version from IMAP
         if existing:
             targetfolder = existing._imap_folder
             delete_object(existing)
         elif master and hasattr(master, '_imap_folder'):
             targetfolder = master._imap_folder
             delete_object(master)
 
         if not nonpart or existing:
             # save new copy from iTip
             if store_object(itip_event['xml'], receiving_user, targetfolder, master):
                 if policy & COND_FORWARD:
                     log.debug(_("Forward invitation for notification"), level=5)
                     return MESSAGE_FORWARD
                 else:
                     return MESSAGE_PROCESSED
 
     return None
 
 
 def process_itip_reply(itip_event, policy, recipient_email, sender_email, receiving_user):
     """
         Process an iTip REPLY message according to the given policy
     """
 
     # if invitation policy is set to MANUAL, pass message along
     if policy & ACT_MANUAL:
         log.info(_("Pass reply for manual processing"))
         return MESSAGE_FORWARD
 
     # auto-update is enabled for this user
     if policy & ACT_UPDATE:
         try:
             sender_attendee = itip_event['xml'].get_attendee_by_email(sender_email)
             log.debug(_("Sender Attendee: %r") % (sender_attendee), level=8)
-        except Exception, errmsg:
+        except Exception as errmsg:
             log.error("Could not find envelope sender attendee: %r" % (errmsg))
             return MESSAGE_FORWARD
 
         # find existing event in user's calendar
         # sets/checks lock to avoid concurrent wallace processes trying to update the same event simultaneously
         (existing, master) = find_existing_object(itip_event['uid'], itip_event['type'], itip_event['recurrence-id'], receiving_user, True)
 
         if existing:
             # compare sequence number to avoid outdated replies?
             if not itip_event['sequence'] == existing.get_sequence():
                 log.info(_("The iTip reply sequence (%r) doesn't match the referred object version (%r). Forwarding to Inbox.") % (
                     itip_event['sequence'], existing.get_sequence()
                 ))
                 remove_write_lock(existing._lock_key)
                 return MESSAGE_FORWARD
 
             log.debug(_("Auto-updating %s %r on iTip REPLY") % (existing.type, existing.uid), level=8)
             updated_attendees = []
             try:
                 existing.set_attendee_participant_status(sender_email, sender_attendee.get_participant_status(), rsvp=False)
                 existing_attendee = existing.get_attendee(sender_email)
                 updated_attendees.append(existing_attendee)
-            except Exception, errmsg:
+            except Exception as errmsg:
                 log.error("Could not find corresponding attende in organizer's copy: %r" % (errmsg))
 
                 # append delegated-from attendee ?
                 if len(sender_attendee.get_delegated_from()) > 0:
                     existing.add_attendee(sender_attendee)
                     updated_attendees.append(sender_attendee)
                 else:
                     # TODO: accept new participant if ACT_ACCEPT ?
                     remove_write_lock(existing._lock_key)
                     return MESSAGE_FORWARD
 
             # append delegated-to attendee
             if len(sender_attendee.get_delegated_to()) > 0:
                 try:
                     delegatee_email = sender_attendee.get_delegated_to(True)[0]
                     sender_delegatee = itip_event['xml'].get_attendee_by_email(delegatee_email)
                     existing_delegatee = existing.find_attendee(delegatee_email)
 
                     if not existing_delegatee:
                         existing.add_attendee(sender_delegatee)
                         log.debug(_("Add delegatee: %r") % (sender_delegatee.to_dict()), level=8)
                     else:
                         existing_delegatee.copy_from(sender_delegatee)
                         log.debug(_("Update existing delegatee: %r") % (existing_delegatee.to_dict()), level=8)
 
                     updated_attendees.append(sender_delegatee)
 
                     # copy all parameters from replying attendee (e.g. delegated-to, role, etc.)
                     existing_attendee.copy_from(sender_attendee)
                     existing.update_attendees([existing_attendee])
                     log.debug(_("Update delegator: %r") % (existing_attendee.to_dict()), level=8)
 
-                except Exception, errmsg:
+                except Exception as errmsg:
                     log.error("Could not find delegated-to attendee: %r" % (errmsg))
 
             # update the organizer's copy of the object
             if update_object(existing, receiving_user, master):
                 if policy & COND_NOTIFY:
                     send_update_notification(existing, receiving_user, existing, True,
                                              sender_attendee, itip_event['xml'].get_comment())
 
                 # update all other attendee's copies
                 if conf.get('wallace','invitationpolicy_autoupdate_other_attendees_on_reply'):
                     propagate_changes_to_attendees_accounts(existing, updated_attendees)
 
                 return MESSAGE_PROCESSED
 
         else:
             log.error(_("The object referred by this reply was not found in the user's folders. Forwarding to Inbox."))
             return MESSAGE_FORWARD
 
     return None
 
 
 def process_itip_cancel(itip_event, policy, recipient_email, sender_email, receiving_user):
     """
         Process an iTip CANCEL message according to the given policy
     """
 
     # if invitation policy is set to MANUAL, pass message along
     if policy & ACT_MANUAL:
         log.info(_("Pass cancellation for manual processing"))
         return MESSAGE_FORWARD
 
     # auto-update the local copy
     if policy & ACT_UPDATE or policy & ACT_CANCEL_DELETE:
         # find existing object in user's folders
         (existing, master) = find_existing_object(itip_event['uid'], itip_event['type'], itip_event['recurrence-id'], receiving_user, True)
         remove_object = policy & ACT_CANCEL_DELETE
 
         if existing:
             # on this-and-future cancel requests, set the recurrence until date on the master event
             if itip_event['recurrence-id'] and master and itip_event['xml'].get_thisandfuture():
                 rrule = master.get_recurrence()
                 rrule.set_count(0)
                 rrule.set_until(existing.get_start() + datetime.timedelta(days=-1))
                 master.set_recurrence(rrule)
                 existing.set_recurrence_id(existing.get_recurrence_id(), True)
                 remove_object = False
 
             # delete the local copy
             if remove_object:
                 # remove exception and register an exdate to the main event
                 if master:
                     log.debug(_("Remove cancelled %s instance %s from %r") % (existing.type, itip_event['recurrence-id'], existing.uid), level=8)
                     master.add_exception_date(existing.get_start())
                     master.del_exception(existing)
                     success = update_object(master, receiving_user)
 
                 # delete main event
                 else:
                     success = delete_object(existing)
 
             # update the local copy with STATUS=CANCELLED
             else:
                 log.debug(_("Update cancelled %s %r with STATUS=CANCELLED") % (existing.type, existing.uid), level=8)
                 existing.set_status('CANCELLED')
                 existing.set_transparency(True)
                 success = update_object(existing, receiving_user, master)
 
             if success:
                 # send cancellation notification
                 if policy & COND_NOTIFY:
                     sender = itip_event['xml'].get_organizer()
                     comment = itip_event['xml'].get_comment()
                     send_cancel_notification(existing, receiving_user, remove_object, sender, comment)
 
                 return MESSAGE_PROCESSED
 
         else:
             log.error(_("The object referred by this cancel request was not found in the user's folders. Forwarding to Inbox."))
             return MESSAGE_FORWARD
 
     return None
 
 
 def user_dn_from_email_address(email_address):
     """
         Resolves the given email address to a Kolab user entity
     """
     global auth
 
     if not auth:
         auth = Auth()
         auth.connect()
 
     # return cached value
     if user_dn_from_email_address.cache.has_key(email_address):
         return user_dn_from_email_address.cache[email_address]
 
     local_domains = auth.list_domains()
 
     if local_domains is not None:
         local_domains = list(set(local_domains.keys()))
 
     if not email_address.split('@')[1] in local_domains:
         user_dn_from_email_address.cache[email_address] = None
         return None
 
     log.debug(_("Checking if email address %r belongs to a local user") % (email_address), level=8)
 
     user_dn = auth.find_user_dn(email_address, True)
 
     if isinstance(user_dn, basestring):
         log.debug(_("User DN: %r") % (user_dn), level=8)
     else:
         log.debug(_("No user record(s) found for %r") % (email_address), level=8)
 
     # remember this lookup
     user_dn_from_email_address.cache[email_address] = user_dn
 
     return user_dn
 
 user_dn_from_email_address.cache = {}
 
 
 def get_matching_invitation_policies(receiving_user, sender_email, type_condition=COND_TYPE_ALL):
     # get user's kolabInvitationPolicy settings
     policies = receiving_user['kolabinvitationpolicy'] if receiving_user.has_key('kolabinvitationpolicy') else []
     if policies and not isinstance(policies, list):
         policies = [policies]
 
     if len(policies) == 0:
         policies = conf.get_list('wallace', 'kolab_invitation_policy')
 
     # match policies agains the given sender_email
     matches = []
     for p in policies:
         if ':' in p:
             (value, domain) = p.split(':', 1)
         else:
             value = p
             domain = ''
 
         if domain == '' or domain == '*' or str(sender_email).endswith(domain):
             value = value.upper()
             if policy_name_map.has_key(value):
                 val = policy_name_map[value]
                 # append if type condition matches
                 if val & type_condition:
                     matches.append(val &~ COND_TYPE_ALL)
 
     # add manual as default action
     if len(matches) == 0:
         matches.append(ACT_MANUAL)
 
     return matches
 
 
 def imap_proxy_auth(user_rec):
     """
         Perform IMAP login using proxy authentication with admin credentials
     """
     global imap
 
     mail_attribute = conf.get('cyrus-sasl', 'result_attribute')
     if mail_attribute is None:
         mail_attribute = 'mail'
 
     mail_attribute = mail_attribute.lower()
 
     if not user_rec.has_key(mail_attribute):
         log.error(_("User record doesn't have the mailbox attribute %r set" % (mail_attribute)))
         return False
 
     # do IMAP prox auth with the given user
     backend = conf.get('kolab', 'imap_backend')
     admin_login = conf.get(backend, 'admin_login')
     admin_password = conf.get(backend, 'admin_password')
 
     try:
         imap.disconnect()
         imap.connect(login=False)
         imap.login_plain(admin_login, admin_password, user_rec[mail_attribute])
-    except Exception, errmsg:
+    except Exception as errmsg:
         log.error(_("IMAP proxy authentication failed: %r") % (errmsg))
         return False
 
     return True
 
 
 def list_user_folders(user_rec, _type):
     """
         Get a list of the given user's private calendar/tasks folders
     """
     global imap
 
     # return cached list
     if '_imap_folders' in user_rec:
         return user_rec['_imap_folders']
 
     result = []
 
     if not imap_proxy_auth(user_rec):
         return result
 
     folders = imap.get_metadata('*')
 
     log.debug(
         _("List %r folders for user %r: %r") % (
             _type,
             user_rec['mail'],
             folders
         ),
         level=8
     )
 
     (ns_personal, ns_other, ns_shared) = imap.namespaces()
 
     _folders = {}
 
     # Filter the folders by type relevance
     for folder, metadata in folders.items():
         key = '/shared' + FOLDER_TYPE_ANNOTATION
         if key in metadata:
             if metadata[key].startswith(_type):
                 _folders[folder] = metadata
 
         key = '/private' + FOLDER_TYPE_ANNOTATION
         if key in metadata:
             if metadata[key].startswith(_type):
                 _folders[folder] = metadata
 
     for folder, metadata in _folders.items():
         folder_delegated = False
 
         # Exclude shared and other user's namespace
         #
         # First, test if this is another users folder
         if ns_other is not None and folder.startswith(ns_other):
             # If we have no delegated mailboxes, we can skip this entirely
             if '_delegated_mailboxes' not in user_rec:
                 continue
 
             for _m in user_rec['_delegated_mailboxes']:
                 if folder.startswith(ns_other + _m + '/'):
                     folder_delegated = True
 
             if not folder_delegated:
                 continue
 
         # TODO: list shared folders the user has write privileges ?
         if ns_shared is not None:
             if len([_ns for _ns in ns_shared if folder.startswith(_ns)]) > 0:
                 continue
 
         key = '/shared' + FOLDER_TYPE_ANNOTATION
         if key in metadata:
             if metadata[key].startswith(_type):
                 result.append(folder)
 
         key = '/private' + FOLDER_TYPE_ANNOTATION
         if key in metadata:
             if metadata[key].startswith(_type):
                 result.append(folder)
 
             # store default folder in user record
             if metadata[key].endswith('.default'):
                 user_rec['_default_folder'] = folder
                 continue
 
             # store private and confidential folders in user record
             if metadata[key].endswith('.confidential'):
                 if '_confidential_folder' not in user_rec:
                     user_rec['_confidential_folder'] = folder
 
                 continue
 
             if metadata[key].endswith('.private'):
                 if '_private_folder' not in user_rec:
                     user_rec['_private_folder'] = folder
 
                 continue
 
     # cache with user record
     user_rec['_imap_folders'] = result
 
     return result
 
 
 def find_existing_object(uid, type, recurrence_id, user_rec, lock=False):
     """
         Search user's private folders for the given object (by UID+type)
     """
     global imap
 
     lock_key = None
 
     if lock:
         lock_key = get_lock_key(user_rec, uid)
         set_write_lock(lock_key)
 
     event = None
     master = None
     for folder in list_user_folders(user_rec, type):
         log.debug(_("Searching folder %r for %s %r") % (folder, type, uid), level=8)
         imap.imap.m.select(imap.folder_utf7(folder))
 
         res, data = imap.imap.m.search(None, '(UNDELETED HEADER SUBJECT "%s")' % (uid))
         for num in reversed(data[0].split()):
             res, data = imap.imap.m.fetch(num, '(UID RFC822)')
 
             try:
                 msguid = re.search(r"\WUID (\d+)", data[0][0]).group(1)
-            except Exception, errmsg:
+            except Exception:
                 log.error(_("No UID found in IMAP response: %r") % (data[0][0]))
                 continue
 
             try:
                 if type == 'task':
                     event = todo_from_message(message_from_string(data[0][1]))
                 else:
                     event = event_from_message(message_from_string(data[0][1]))
 
                 # find instance in a recurring series
                 if recurrence_id and (event.is_recurring() or event.has_exceptions() or event.get_recurrence_id()):
                     master = event
                     event = master.get_instance(recurrence_id)
                     setattr(master, '_imap_folder', folder)
                     setattr(master, '_msguid', msguid)
 
                     # return master, even if instance is not found
                     if not event and master.uid == uid:
                         return (event, master)
 
                 if event is not None:
                     setattr(event, '_imap_folder', folder)
                     setattr(event, '_lock_key', lock_key)
                     setattr(event, '_msguid', msguid)
 
-            except Exception, errmsg:
+            except Exception:
                 log.error(_("Failed to parse %s from message %s/%s: %s") % (type, folder, num, traceback.format_exc()))
                 event = None
                 master = None
                 continue
 
             if event and event.uid == uid:
                 return (event, master)
 
     if lock_key is not None:
         remove_write_lock(lock_key)
 
     return (event, master)
 
 
 def check_availability(itip_event, receiving_user):
     """
         For the receiving user, determine if the event in question is in conflict.
     """
 
     start = time.time()
     num_messages = 0
     conflict = False
 
     # return previously detected conflict
     if itip_event.has_key('_conflicts'):
         return not itip_event['_conflicts']
 
     for folder in list_user_folders(receiving_user, 'event'):
         log.debug(_("Listing events from folder %r") % (folder), level=8)
         imap.imap.m.select(imap.folder_utf7(folder))
 
         res, data = imap.imap.m.search(None, '(UNDELETED HEADER X-Kolab-Type "application/x-vnd.kolab.event")')
         num_messages += len(data[0].split())
 
         for num in reversed(data[0].split()):
             event = None
             res, data = imap.imap.m.fetch(num, '(RFC822)')
 
             try:
                 event = event_from_message(message_from_string(data[0][1]))
-            except Exception, errmsg:
+            except Exception as errmsg:
                 log.error(_("Failed to parse event from message %s/%s: %r") % (folder, num, errmsg))
                 continue
 
             if event and event.uid:
                 conflict = check_event_conflict(event, itip_event)
                 if conflict:
                     log.info(_("Existing event %r conflicts with invitation %r") % (event.uid, itip_event['uid']))
                     break
 
         if conflict:
             break
 
     end = time.time()
     log.debug(_("start: %r, end: %r, total: %r, messages: %d") % (start, end, (end-start), num_messages), level=8)
 
     # remember the result of this check for further iterations
     itip_event['_conflicts'] = conflict
 
     return not conflict
 
 
 def set_write_lock(key, wait=True):
     """
         Set a write-lock for the given key and wait if such a lock already exists
     """
     if not os.path.isdir(mybasepath):
         os.makedirs(mybasepath)
     if not os.path.isdir(os.path.join(mybasepath, 'locks')):
         os.makedirs(os.path.join(mybasepath, 'locks'))
 
     filename = os.path.join(mybasepath, 'locks', key + '.lock')
     locktime = 0
 
     if os.path.isfile(filename):
         locktime = os.path.getmtime(filename)
 
     # wait if file lock is in place
     while time.time() < locktime + 300:
         if not wait:
             return False
 
         log.debug(_("%r is locked, waiting...") % (key), level=8)
         time.sleep(0.5)
         locktime = os.path.getmtime(filename) if os.path.isfile(filename) else 0
 
     # touch the file
     if os.path.isfile(filename):
         os.utime(filename, None)
     else:
         open(filename, 'w').close()
 
     # register active lock
     write_locks.append(key)
 
     return True
 
 
 def remove_write_lock(key, update=True):
     """
         Remove the lock file for the given key
     """
     global write_locks
 
     if key is not None:
         file = os.path.join(mybasepath, 'locks', key + '.lock')
         if os.path.isfile(file):
             os.remove(file)
             if update:
                 write_locks = [k for k in write_locks if not k == key]
 
 
 def get_lock_key(user, uid):
     return hashlib.md5("%s/%s" % (user['mail'], uid)).hexdigest()
 
 
 def update_object(object, user_rec, master=None):
     """
         Update the given object in IMAP (i.e. delete + append)
     """
     success = False
     saveobj = object
 
     # updating a single instance only: use master event
     if object.get_recurrence_id() and master:
         saveobj = master
 
     if hasattr(saveobj, '_imap_folder'):
         if delete_object(saveobj):
             saveobj.set_lastmodified()  # update last-modified timestamp
             success = store_object(object, user_rec, saveobj._imap_folder, master)
 
         # remove write lock for this event
         if hasattr(saveobj, '_lock_key') and saveobj._lock_key is not None:
             remove_write_lock(saveobj._lock_key)
 
     return success
 
 
 def store_object(object, user_rec, targetfolder=None, master=None):
     """
         Append the given object to the user's default calendar/tasklist
     """
 
     # find calendar folder to save object to if not specified
     if targetfolder is None:
         targetfolders = list_user_folders(user_rec, object.type)
         oc = object.get_classification()
 
         # use *.confidential/private folder for confidential/private invitations
         if oc == kolabformat.ClassConfidential and user_rec.has_key('_confidential_folder'):
             targetfolder = user_rec['_confidential_folder']
         elif oc == kolabformat.ClassPrivate and user_rec.has_key('_private_folder'):
             targetfolder = user_rec['_private_folder']
         # use *.default folder if exists
         elif user_rec.has_key('_default_folder'):
             targetfolder = user_rec['_default_folder']
         # fallback to any existing folder of specified type
         elif targetfolders is not None and len(targetfolders) > 0:
             targetfolder = targetfolders[0]
 
     if targetfolder is None:
         log.error(_("Failed to save %s: no target folder found for user %r") % (object.type, user_rec['mail']))
         return False
 
     saveobj = object
 
     # updating a single instance only: add exception to master event
     if object.get_recurrence_id() and master:
         object.set_lastmodified()  # update last-modified timestamp
         master.add_exception(object)
         saveobj = master
 
     log.debug(_("Save %s %r to user folder %r") % (saveobj.type, saveobj.uid, targetfolder), level=8)
 
     try:
         imap.imap.m.select(imap.folder_utf7(targetfolder))
         result = imap.imap.m.append(
             imap.folder_utf7(targetfolder),
             None,
             None,
             saveobj.to_message(creator="Kolab Server <wallace@localhost>").as_string()
         )
         return result
 
-    except Exception, errmsg:
+    except Exception as errmsg:
         log.error(_("Failed to save %s to user folder at %r: %r") % (
             saveobj.type, targetfolder, errmsg
         ))
 
     return False
 
 
 def delete_object(existing):
     """
         Removes the IMAP object with the given UID from a user's folder
     """
     targetfolder = existing._imap_folder
     msguid = existing._msguid if hasattr(existing, '_msguid') else None
 
     try:
         imap.imap.m.select(imap.folder_utf7(targetfolder))
 
         # delete by IMAP UID
         if msguid is not None:
             log.debug(_("Delete %s %r in %r by UID: %r") % (
                 existing.type, existing.uid, targetfolder, msguid
             ), level=8)
 
             imap.imap.m.uid('store', msguid, '+FLAGS', '(\\Deleted)')
         else:
             res, data = imap.imap.m.search(None, '(HEADER SUBJECT "%s")' % existing.uid)
 
             log.debug(_("Delete %s %r in %r: %r") % (
                 existing.type, existing.uid, targetfolder, data
             ), level=8)
 
             for num in data[0].split():
                 imap.imap.m.store(num, '+FLAGS', '(\\Deleted)')
 
         imap.imap.m.expunge()
         return True
 
-    except Exception, errmsg:
+    except Exception as errmsg:
         log.error(_("Failed to delete %s from folder %r: %r") % (
             existing.type, targetfolder, errmsg
         ))
 
     return False
 
 
 def send_update_notification(object, receiving_user, old=None, reply=True, sender=None, comment=None):
     """
         Send a (consolidated) notification about the current participant status to organizer
     """
     global auth
 
     from email.MIMEText import MIMEText
     from email.Utils import formatdate
     from email.header import Header
     from email import charset
 
     # encode unicode strings with quoted-printable
     charset.add_charset('utf-8', charset.SHORTEST, charset.QP)
 
     organizer = object.get_organizer()
     orgemail = organizer.email()
     orgname = organizer.name()
 
     itip_comment = None
     if comment is not None:
         comment = comment.strip()
 
     if sender is not None and not comment == '':
         itip_comment = _("%s commented: %s") % (_attendee_name(sender), comment)
 
     if reply:
         log.debug(_("Compose participation status summary for %s %r to user %r") % (
             object.type, object.uid, receiving_user['mail']
         ), level=8)
 
         auto_replies_expected = 0
         auto_replies_received = 0
         is_manual_reply = True
         partstats = {'ACCEPTED': [], 'TENTATIVE': [], 'DECLINED': [], 'DELEGATED': [], 'IN-PROCESS': [], 'COMPLETED': [], 'PENDING': []}
 
         for attendee in object.get_attendees():
             parstat = attendee.get_participant_status(True)
             if partstats.has_key(parstat):
                 partstats[parstat].append(attendee.get_displayname())
             else:
                 partstats['PENDING'].append(attendee.get_displayname())
 
             # look-up kolabinvitationpolicy for this attendee
             if attendee.get_cutype() == kolabformat.CutypeResource:
                 resource_dns = auth.find_resource(attendee.get_email())
                 if isinstance(resource_dns, list):
                     attendee_dn = resource_dns[0] if len(resource_dns) > 0 else None
                 else:
                     attendee_dn = resource_dns
             else:
                 attendee_dn = user_dn_from_email_address(attendee.get_email())
 
             if attendee_dn:
                 attendee_rec = auth.get_entry_attributes(None, attendee_dn, ['kolabinvitationpolicy'])
                 if is_auto_reply(attendee_rec, orgemail, object.type):
                     auto_replies_expected += 1
                     if not parstat == 'NEEDS-ACTION':
                         auto_replies_received += 1
 
                     if sender is not None and sender.get_email() == attendee.get_email():
                         is_manual_reply = False
 
         # skip notification until we got replies from all automatically responding attendees
         if not is_manual_reply and auto_replies_received < auto_replies_expected:
             log.debug(_("Waiting for more automated replies (got %d of %d); skipping notification") % (
                 auto_replies_received, auto_replies_expected
             ), level=8)
             return
 
         # build notification message body
         roundup = ''
 
         if itip_comment is not None:
             roundup += "\n" + itip_comment
 
         for status,attendees in partstats.iteritems():
             if len(attendees) > 0:
                 roundup += "\n" + participant_status_label(status) + ":\n\t" + "\n\t".join(attendees) + "\n"
     else:
         # build notification message body
         roundup = ''
 
         if itip_comment is not None:
             roundup += "\n" + itip_comment
 
         roundup += "\n" + _("Changes submitted by %s have been automatically applied.") % (orgname if orgname else orgemail)
 
         # list properties changed from previous version
         if old:
             diff = xmlutils.compute_diff(old.to_dict(), object.to_dict())
             if len(diff) > 1:
                 roundup += "\n"
                 for change in diff:
                     if not change['property'] in ['created','lastmodified-date','sequence']:
                         new_value = xmlutils.property_to_string(change['property'], change['new']) if change['new'] else _("(removed)")
                         if new_value:
                             roundup += "\n- %s: %s" % (xmlutils.property_label(change['property']), new_value)
 
     # compose different notification texts for events/tasks
     if object.type == 'task':
         message_text = _("""
             The assignment for '%(summary)s' has been updated in your tasklist.
             %(roundup)s
         """) % {
             'summary': object.get_summary(),
             'roundup': roundup
         }
     else:
         message_text = _("""
             The event '%(summary)s' at %(start)s has been updated in your calendar.
             %(roundup)s
         """) % {
             'summary': object.get_summary(),
             'start': xmlutils.property_to_string('start', object.get_start()),
             'roundup': roundup
         }
 
     if object.get_recurrence_id():
         message_text += _("NOTE: This update only refers to this single occurrence!") + "\n"
 
     message_text += "\n" + _("*** This is an automated message. Please do not reply. ***")
 
     # compose mime message
     msg = MIMEText(utils.stripped_message(message_text), _charset='utf-8')
 
     msg['To'] = receiving_user['mail']
     msg['Date'] = formatdate(localtime=True)
     msg['Subject'] = utils.str2unicode(_('"%s" has been updated') % (object.get_summary()))
     msg['From'] = Header(utils.str2unicode('%s' % orgname) if orgname else '')
     msg['From'].append("<%s>" % orgemail)
 
     seed = random.randint(0, 6)
     alarm_after = (seed * 10) + 60
     log.debug(_("Set alarm to %s seconds") % (alarm_after), level=8)
     signal.alarm(alarm_after)
 
     result = modules._sendmail(orgemail, receiving_user['mail'], msg.as_string())
     log.debug(_("Sent update notification to %r: %r") % (receiving_user['mail'], result), level=8)
     signal.alarm(0)
 
 def send_cancel_notification(object, receiving_user, deleted=False, sender=None, comment=None):
     """
         Send a notification about event/task cancellation
     """
     from email.MIMEText import MIMEText
     from email.Utils import formatdate
     from email.header import Header
     from email import charset
 
     # encode unicode strings with quoted-printable
     charset.add_charset('utf-8', charset.SHORTEST, charset.QP)
 
     log.debug(_("Send cancellation notification for %s %r to user %r") % (
         object.type, object.uid, receiving_user['mail']
     ), level=8)
 
     organizer = object.get_organizer()
     orgemail = organizer.email()
     orgname = organizer.name()
 
     # compose different notification texts for events/tasks
     if object.type == 'task':
         message_text = _("The assignment for '%(summary)s' has been cancelled by %(organizer)s.") % {
             'summary': object.get_summary(),
             'organizer': orgname if orgname else orgemail
         }
         if deleted:
             message_text += " " + _("The copy in your tasklist has been removed accordingly.")
         else:
             message_text += " " + _("The copy in your tasklist has been marked as cancelled accordingly.")
     else:
         message_text = _("The event '%(summary)s' at %(start)s has been cancelled by %(organizer)s.") % {
             'summary': object.get_summary(),
             'start': xmlutils.property_to_string('start', object.get_start()),
             'organizer': orgname if orgname else orgemail
         }
         if deleted:
             message_text += " " + _("The copy in your calendar has been removed accordingly.")
         else:
             message_text += " " + _("The copy in your calendar has been marked as cancelled accordingly.")
 
     if comment is not None:
         comment = comment.strip()
 
     if sender is not None and not comment == '':
         message_text += "\n" + _("%s commented: %s") % (_attendee_name(sender), comment)
 
     if object.get_recurrence_id():
         message_text += "\n" + _("NOTE: This cancellation only refers to this single occurrence!")
 
     message_text += "\n\n" + _("*** This is an automated message. Please do not reply. ***")
 
     # compose mime message
     msg = MIMEText(utils.stripped_message(message_text), _charset='utf-8')
 
     msg['To'] = receiving_user['mail']
     msg['Date'] = formatdate(localtime=True)
     msg['Subject'] = utils.str2unicode(_('"%s" has been cancelled') % (object.get_summary()))
     msg['From'] = Header(utils.str2unicode('%s' % orgname) if orgname else '')
     msg['From'].append("<%s>" % orgemail)
 
     seed = random.randint(0, 6)
     alarm_after = (seed * 10) + 60
     log.debug(_("Set alarm to %s seconds") % (alarm_after), level=8)
     signal.alarm(alarm_after)
 
     result = modules._sendmail(orgemail, receiving_user['mail'], msg.as_string())
     log.debug(_("Sent cancel notification to %r: %r") % (receiving_user['mail'], result), level=8)
     signal.alarm(0)
 
 def is_auto_reply(user, sender_email, type):
     accept_available = False
     accept_conflicts = False
     for policy in get_matching_invitation_policies(user, sender_email, object_type_conditons.get(type, COND_TYPE_EVENT)):
         if policy & (ACT_ACCEPT | ACT_REJECT | ACT_DELEGATE):
             if check_policy_condition(policy, True):
                 accept_available = True
             if check_policy_condition(policy, False):
                 accept_conflicts = True
 
         # we have both cases covered by a policy
         if accept_available and accept_conflicts:
             return True
 
         # manual action reached
         if policy & (ACT_MANUAL | ACT_SAVE_TO_FOLDER):
             return False
 
     return False
 
 
 def check_policy_condition(policy, available):
     condition_fulfilled = True
     if policy & (COND_IF_AVAILABLE | COND_IF_CONFLICT):
         condition_fulfilled = available
     if policy & COND_IF_CONFLICT:
         condition_fulfilled = not condition_fulfilled
     return condition_fulfilled
 
 
 def propagate_changes_to_attendees_accounts(object, updated_attendees=None):
     """
         Find and update copies of this object in all attendee's personal folders
     """
     recurrence_id = object.get_recurrence_id()
 
     for attendee in object.get_attendees():
         attendee_user_dn = user_dn_from_email_address(attendee.get_email())
         if attendee_user_dn:
             attendee_user = auth.get_entry_attributes(None, attendee_user_dn, ['*'])
             (attendee_object, master_object) = find_existing_object(object.uid, object.type, recurrence_id, attendee_user, True)  # does IMAP authenticate
             if attendee_object:
                 # find attendee's entry by one of its email addresses
                 attendee_emails = auth.extract_recipient_addresses(attendee_user)
                 for attendee_email in attendee_emails:
                     try:
                         attendee_entry = attendee_object.get_attendee_by_email(attendee_email)
                     except:
                         attendee_entry = None
                     if attendee_entry:
                         break
 
                 # copy all attendees from master object (covers additions and removals)
                 new_attendees = []
                 for a in object.get_attendees():
                     # keep my own entry intact
                     if attendee_entry is not None and attendee_entry.get_email() == a.get_email():
                         new_attendees.append(attendee_entry)
                     else:
                         new_attendees.append(a)
 
                 attendee_object.set_attendees(new_attendees)
 
                 if updated_attendees and not recurrence_id:
                     log.debug("Update Attendees %r for %s" % ([a.get_email()+':'+a.get_participant_status(True) for a in updated_attendees], attendee_user['mail']), level=8)
                     attendee_object.update_attendees(updated_attendees, False)
 
                 success = update_object(attendee_object, attendee_user, master_object)
                 log.debug(_("Updated %s's copy of %r: %r") % (attendee_user['mail'], object.uid, success), level=8)
 
             else:
                 log.debug(_("Attendee %s's copy of %r not found") % (attendee_user['mail'], object.uid), level=8)
 
         else:
             log.debug(_("Attendee %r not found in LDAP") % (attendee.get_email()), level=8)
 
 
 def invitation_response_text(type):
     footer = "\n\n" + _("*** This is an automated message. Please do not reply. ***")
 
     if type == 'task':
         return _("%(name)s has %(status)s your assignment for %(summary)s.") + footer
     else:
         return _("%(name)s has %(status)s your invitation for %(summary)s.") + footer
 
 
 def _attendee_name(attendee):
     # attendee here can be Attendee or ContactReference
     try:
         name = attendee.get_name()
     except Exception:
         name = attendee.name()
 
     if name == '':
         try:
             name = attendee.get_email()
         except Exception:
             name = attendee.email()
 
     return name
diff --git a/wallace/module_optout.py b/wallace/module_optout.py
index 2b9d4b2..4fd199f 100644
--- a/wallace/module_optout.py
+++ b/wallace/module_optout.py
@@ -1,192 +1,192 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
 #
 # Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
 
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 import json
 import os
 import random
 import tempfile
 import time
 from urlparse import urlparse
 import urllib
 
 from email import message_from_file
 from email.utils import formataddr
 from email.utils import getaddresses
 
 import modules
 
 import pykolab
 
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.wallace/optout')
 conf = pykolab.getConf()
 
 mybasepath = '/var/spool/pykolab/wallace/optout/'
 
 def __init__():
     modules.register('optout', execute, description=description())
 
 def description():
     return """Consult the opt-out service."""
 
 def execute(*args, **kw):
     if not os.path.isdir(mybasepath):
         os.makedirs(mybasepath)
 
     for stage in ['incoming', 'ACCEPT', 'REJECT', 'HOLD', 'DEFER' ]:
         if not os.path.isdir(os.path.join(mybasepath, stage)):
             os.makedirs(os.path.join(mybasepath, stage))
 
     # TODO: Test for correct call.
     filepath = args[0]
 
     if kw.has_key('stage'):
         log.debug(_("Issuing callback after processing to stage %s") % (kw['stage']), level=8)
         log.debug(_("Testing cb_action_%s()") % (kw['stage']), level=8)
         if hasattr(modules, 'cb_action_%s' % (kw['stage'])):
             log.debug(_("Attempting to execute cb_action_%s()") % (kw['stage']), level=8)
             exec('modules.cb_action_%s(%r, %r)' % (kw['stage'],'optout',filepath))
             return
 
         #modules.next_module('optout')
 
     log.debug(_("Consulting opt-out service for %r, %r") % (args, kw), level=8)
 
     message = message_from_file(open(filepath, 'r'))
     envelope_sender = getaddresses(message.get_all('From', []))
 
     recipients = {
             "To": getaddresses(message.get_all('To', [])),
             "Cc": getaddresses(message.get_all('Cc', []))
             # TODO: Are those all recipient addresses?
         }
 
     # optout answers are ACCEPT, REJECT, HOLD or DEFER
     answers = [ 'ACCEPT', 'REJECT', 'HOLD', 'DEFER' ]
 
     # Initialize our results placeholders.
     _recipients = {}
 
     for answer in answers:
         _recipients[answer] = {
                 "To": [],
                 "Cc": []
             }
 
     for recipient_type in recipients.keys():
         for recipient in recipients[recipient_type]:
             log.debug(
                     _("Running opt-out consult from envelope sender '%s " + \
                         "<%s>' to recipient %s <%s>") % (
                             envelope_sender[0][0],
                             envelope_sender[0][1],
                             recipient[0],
                             recipient[1]
                         ),
                     level=8
                 )
 
             optout_answer = request(
                     {
                             'unique-message-id': 'bogus',
                             'envelope_sender': envelope_sender[0][1],
                             'recipient': recipient[1]
                         }
                 )
 
             _recipients[optout_answer][recipient_type].append(recipient)
 
     #print _recipients
 
     ##
     ## TODO
     ##
     ## If one of them all is DEFER, DEFER the entire message and discard the
     ## other results.
     ##
 
     for answer in answers:
         # Create the directory for the answer
         if not os.path.isdir(os.path.join(mybasepath, answer)):
             os.makedirs(os.path.join(mybasepath, answer))
 
         # Consider using a new mktemp()-like call
         new_filepath = os.path.join(mybasepath, answer, os.path.basename(filepath))
 
         # Write out a message file representing the new contents for the message
         # use formataddr(recipient)
         _message = message_from_file(open(filepath, 'r'))
 
         use_this = False
 
         for recipient_type in _recipients[answer].keys():
             _message.__delitem__(recipient_type)
             if not len(_recipients[answer][recipient_type]) == 0:
                 _message.__setitem__(
                         recipient_type,
                         ',\n  '.join(
                                 [formataddr(x) for x in _recipients[answer][recipient_type]]
                             )
                     )
 
                 use_this = True
 
         if use_this:
             # TODO: Do not set items with an empty list.
 
             (fp, filename) = tempfile.mkstemp(dir="/var/spool/pykolab/wallace/optout/%s" % (answer))
             os.write(fp, _message.__str__())
             os.close(fp)
 
             # Callback with new filename
             if hasattr(modules, 'cb_action_%s' % (answer)):
                 log.debug(_("Attempting to execute cb_action_%s(%r, %r)") % (answer, 'optout', filename), level=8)
                 exec('modules.cb_action_%s(%r, %r)' % (answer,'optout', filename))
 
     os.unlink(filepath)
 
     #print "Moving filepath %s to new_filepath %s" % (filepath, new_filepath)
     #os.rename(filepath, new_filepath)
 
     #if hasattr(modules, 'cb_action_%s' % (optout_answer)):
         #log.debug(_("Attempting to execute cb_action_%s()") % (optout_answer), level=8)
         #exec('modules.cb_action_%s(%r, %r)' % (optout_answer,'optout', new_filepath))
         #return
 
 def request(params=None):
     params = json.dumps(params)
 
     optout_url = conf.get('wallace_optout', 'optout_url')
 
     try:
         f = urllib.urlopen(optout_url, params)
-    except Exception, e:
+    except Exception:
         log.error(_("Could not send request to optout_url %s") % (optout_url))
         return "DEFER"
 
     response = f.read()
 
     try:
         response_data = json.loads(response)
-    except ValueError, e:
+    except ValueError:
         # Some data is not JSON
         print("Response data is not JSON")
 
     return response_data['result']