diff --git a/conf.py b/conf.py
index 8e5ef43..9a111b6 100755
--- a/conf.py
+++ b/conf.py
@@ -1,45 +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:
-    print >> sys.stderr, _("Cannot load pykolab/logger.py:")
-    print >> sys.stderr, "%s" % 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 9e42f39..0edbfdb 100644
--- a/cyruslib.py
+++ b/cyruslib.py
@@ -1,831 +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.3
+# 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
+    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 >> self.LOGFD, msg
+            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:
             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:
             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:
             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:
                 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:
             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:
                 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:
                     pass
             except Exception, errmsg:
                 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:
                 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:
                     pass
             except Exception, errmsg:
                 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/freeze.py b/ext/python/Tools/freeze/freeze.py
index 46bfd90..daa345b 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:
                 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:
         usage('getopt error: ' + str(msg))
 
     # process option arguments
     for o, a in opts:
         if o == '-h':
-            print __doc__
+            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)"
+        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
+            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:
             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
+        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,
-        print 'to build the target:', base_target
+        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('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]
+    print("Error:", msg)
+    print("Use ``%s -h'' for help" % sys.argv[0])
     sys.exit(2)
 
 
 main()
diff --git a/ext/python/Tools/freeze/hello.py b/ext/python/Tools/freeze/hello.py
index f978acc..d7a3729 100644
--- a/ext/python/Tools/freeze/hello.py
+++ b/ext/python/Tools/freeze/hello.py
@@ -1 +1 @@
-print 'Hello world...'
+print('Hello world...')
diff --git a/ext/python/Tools/freeze/makeconfig.py b/ext/python/Tools/freeze/makeconfig.py
index b9bfd08..8aba55e 100644
--- a/ext/python/Tools/freeze/makeconfig.py
+++ b/ext/python/Tools/freeze/makeconfig.py
@@ -1,61 +1,61 @@
 import re
 
 
 # Write the config.c file
 
 never = ['marshal', '__main__', '__builtin__', 'sys', 'exceptions', '_warnings']
 
 def makeconfig(infp, outfp, modules, with_ifdef=0):
     m1 = re.compile('-- ADDMODULE MARKER 1 --')
     m2 = re.compile('-- ADDMODULE MARKER 2 --')
     while 1:
         line = infp.readline()
         if not line: break
         outfp.write(line)
         if m1 and m1.search(line):
             m1 = None
             for mod in modules:
                 if mod in never:
                     continue
                 if with_ifdef:
                     outfp.write("#ifndef init%s\n"%mod)
                 outfp.write('extern void init%s(void);\n' % mod)
                 if with_ifdef:
                     outfp.write("#endif\n")
         elif m2 and m2.search(line):
             m2 = None
             for mod in modules:
                 if mod in never:
                     continue
                 outfp.write('\t{"%s", init%s},\n' %
                             (mod, mod))
     if m1:
         sys.stderr.write('MARKER 1 never found\n')
     elif m2:
         sys.stderr.write('MARKER 2 never found\n')
 
 
 # Test program.
 
 def test():
     import sys
     if not sys.argv[3:]:
-        print 'usage: python makeconfig.py config.c.in outputfile',
-        print 'modulename ...'
+        print('usage: python makeconfig.py config.c.in outputfile', end=' ')
+        print('modulename ...')
         sys.exit(2)
     if sys.argv[1] == '-':
         infp = sys.stdin
     else:
         infp = open(sys.argv[1])
     if sys.argv[2] == '-':
         outfp = sys.stdout
     else:
         outfp = open(sys.argv[2], 'w')
     makeconfig(infp, outfp, sys.argv[3:])
     if outfp != sys.stdout:
         outfp.close()
     if infp != sys.stdin:
         infp.close()
 
 if __name__ == '__main__':
     test()
diff --git a/ext/python/Tools/freeze/makefreeze.py b/ext/python/Tools/freeze/makefreeze.py
index 1208b67..e359db9 100644
--- a/ext/python/Tools/freeze/makefreeze.py
+++ b/ext/python/Tools/freeze/makefreeze.py
@@ -1,90 +1,90 @@
 import marshal
 import bkfile
 
 
 # Write a file containing frozen code for the modules in the dictionary.
 
 header = """
 #include "Python.h"
 
 static struct _frozen _PyImport_FrozenModules[] = {
 """
 trailer = """\
     {0, 0, 0} /* sentinel */
 };
 """
 
 # if __debug__ == 0 (i.e. -O option given), set Py_OptimizeFlag in frozen app.
 default_entry_point = """
 int
 main(int argc, char **argv)
 {
         extern int Py_FrozenMain(int, char **);
 """ + ((not __debug__ and """
         Py_OptimizeFlag++;
 """) or "")  + """
         PyImport_FrozenModules = _PyImport_FrozenModules;
         return Py_FrozenMain(argc, argv);
 }
 
 """
 
 def makefreeze(base, dict, debug=0, entry_point=None, fail_import=()):
     if entry_point is None: entry_point = default_entry_point
     done = []
     files = []
     mods = dict.keys()
     mods.sort()
     for mod in mods:
         m = dict[mod]
         mangled = "__".join(mod.split("."))
         if m.__code__:
             file = 'M_' + mangled + '.c'
             outfp = bkfile.open(base + file, 'w')
             files.append(file)
             if debug:
-                print "freezing", mod, "..."
+                print("freezing", mod, "...")
             str = marshal.dumps(m.__code__)
             size = len(str)
             if m.__path__:
                 # Indicate package by negative size
                 size = -size
             done.append((mod, mangled, size))
             writecode(outfp, mangled, str)
             outfp.close()
     if debug:
-        print "generating table of frozen modules"
+        print("generating table of frozen modules")
     outfp = bkfile.open(base + 'frozen.c', 'w')
     for mod, mangled, size in done:
         outfp.write('extern unsigned char M_%s[];\n' % mangled)
     outfp.write(header)
     for mod, mangled, size in done:
         outfp.write('\t{"%s", M_%s, %d},\n' % (mod, mangled, size))
     outfp.write('\n')
     # The following modules have a NULL code pointer, indicating
     # that the prozen program should not search for them on the host
     # system. Importing them will *always* raise an ImportError.
     # The zero value size is never used.
     for mod in fail_import:
         outfp.write('\t{"%s", NULL, 0},\n' % (mod,))
     outfp.write(trailer)
     outfp.write(entry_point)
     outfp.close()
     return files
 
 
 
 # Write a C initializer for a module containing the frozen python code.
 # The array is called M_<mod>.
 
 def writecode(outfp, mod, str):
     outfp.write('unsigned char M_%s[] = {' % mod)
     for i in range(0, len(str), 16):
         outfp.write('\n\t')
         for c in str[i:i+16]:
             outfp.write('%d,' % ord(c))
     outfp.write('\n};\n')
 
 ## def writecode(outfp, mod, str):
 ##     outfp.write('unsigned char M_%s[%d] = "%s";\n' % (mod, len(str),
 ##     '\\"'.join(map(lambda s: repr(s)[1:-1], str.split('"')))))
diff --git a/ext/python/Tools/freeze/parsesetup.py b/ext/python/Tools/freeze/parsesetup.py
index 856234d..ae0bc43 100644
--- a/ext/python/Tools/freeze/parsesetup.py
+++ b/ext/python/Tools/freeze/parsesetup.py
@@ -1,112 +1,112 @@
 # Parse Makefiles and Python Setup(.in) files.
 
 import re
 
 
 # Extract variable definitions from a Makefile.
 # Return a dictionary mapping names to values.
 # May raise IOError.
 
 makevardef = re.compile('^([a-zA-Z0-9_]+)[ \t]*=(.*)')
 
 def getmakevars(filename):
     variables = {}
     fp = open(filename)
     pendingline = ""
     try:
         while 1:
             line = fp.readline()
             if pendingline:
                 line = pendingline + line
                 pendingline = ""
             if not line:
                 break
             if line.endswith('\\\n'):
                 pendingline = line[:-2]
             matchobj = makevardef.match(line)
             if not matchobj:
                 continue
             (name, value) = matchobj.group(1, 2)
             # Strip trailing comment
             i = value.find('#')
             if i >= 0:
                 value = value[:i]
             value = value.strip()
             variables[name] = value
     finally:
         fp.close()
     return variables
 
 
 # Parse a Python Setup(.in) file.
 # Return two dictionaries, the first mapping modules to their
 # definitions, the second mapping variable names to their values.
 # May raise IOError.
 
 setupvardef = re.compile('^([a-zA-Z0-9_]+)=(.*)')
 
 def getsetupinfo(filename):
     modules = {}
     variables = {}
     fp = open(filename)
     pendingline = ""
     try:
         while 1:
             line = fp.readline()
             if pendingline:
                 line = pendingline + line
                 pendingline = ""
             if not line:
                 break
             # Strip comments
             i = line.find('#')
             if i >= 0:
                 line = line[:i]
             if line.endswith('\\\n'):
                 pendingline = line[:-2]
                 continue
             matchobj = setupvardef.match(line)
             if matchobj:
                 (name, value) = matchobj.group(1, 2)
                 variables[name] = value.strip()
             else:
                 words = line.split()
                 if words:
                     modules[words[0]] = words[1:]
     finally:
         fp.close()
     return modules, variables
 
 
 # Test the above functions.
 
 def test():
     import sys
     import os
     if not sys.argv[1:]:
-        print 'usage: python parsesetup.py Makefile*|Setup* ...'
+        print('usage: python parsesetup.py Makefile*|Setup* ...')
         sys.exit(2)
     for arg in sys.argv[1:]:
         base = os.path.basename(arg)
         if base[:8] == 'Makefile':
-            print 'Make style parsing:', arg
+            print('Make style parsing:', arg)
             v = getmakevars(arg)
             prdict(v)
         elif base[:5] == 'Setup':
-            print 'Setup style parsing:', arg
+            print('Setup style parsing:', arg)
             m, v = getsetupinfo(arg)
             prdict(m)
             prdict(v)
         else:
-            print arg, 'is neither a Makefile nor a Setup file'
-            print '(name must begin with "Makefile" or "Setup")'
+            print(arg, 'is neither a Makefile nor a Setup file')
+            print('(name must begin with "Makefile" or "Setup")')
 
 def prdict(d):
     keys = d.keys()
     keys.sort()
     for key in keys:
         value = d[key]
-        print "%-15s" % key, str(value)
+        print("%-15s" % key, str(value))
 
 if __name__ == '__main__':
     test()
diff --git a/ext/python/Tools/freeze/winmakemakefile.py b/ext/python/Tools/freeze/winmakemakefile.py
index 8570f3d..e473fed 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
 
 
 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
+    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("# 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
+    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
+        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",
-            print '"-I$(pythonhome)/Include"  "-I$(pythonhome)/PC" \\'
-            print "\t\t$(cflags) $(cdebug) $(cinclude) \\"
+            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
+                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=",
-    for addn in vars['addn_link']: print '"%s"' % (addn),
-    print ; print
-
-    print "OBJS=",
-    for obj in objects: print '"$(temp_dir)\%s"' % (obj),
-    print ; print
-
-    print "LIBS=",
-    for lib in libs: print '"%s"' % (lib),
-    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),
-    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"
+    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 fbcfac5..d97d715 100755
--- a/kolab-cli.py
+++ b/kolab-cli.py
@@ -1,40 +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:
-    print >> sys.stderr, _("Cannot load pykolab/logger.py:")
-    print >> sys.stderr, "%s" % 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_alias.py b/pykolab/cli/cmd_add_alias.py
index 9f7a2c6..e905f79 100644
--- a/pykolab/cli/cmd_add_alias.py
+++ b/pykolab/cli/cmd_add_alias.py
@@ -1,129 +1,131 @@
 # -*- 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.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('add_alias', execute, description="Add alias.")
 
 def execute(*args, **kw):
 
     try:
         primary_rcpt_address = conf.cli_args.pop(0)
         try:
             secondary_rcpt_address = conf.cli_args.pop(0)
         except:
-            print >> sys.stderr, _("Specify the (new) alias address")
+            print(_("Specify the (new) alias address"), file=sys.stderr)
             sys.exit(1)
     except:
-        print >> sys.stderr, _("Specify the existing recipient address")
+        print(_("Specify the existing recipient address"), file=sys.stderr)
         sys.exit(1)
 
     if len(primary_rcpt_address.split('@')) > 1:
         primary_rcpt_domain = primary_rcpt_address.split('@')[-1]
     else:
         primary_rcpt_domain = conf.get('kolab', 'primary_domain')
 
     auth = Auth(domain=primary_rcpt_domain)
 
     domains = auth.list_domains()
 
     #print domains
 
     if len(secondary_rcpt_address.split('@')) > 1:
         secondary_rcpt_domain = secondary_rcpt_address.split('@')[-1]
     else:
         secondary_rcpt_domain = conf.get('kolab', 'primary_domain')
 
     # Check if either is in fact a domain
     if not primary_rcpt_domain.lower() in domains.keys():
-        print >> sys.stderr, _("Domain %r is not a local domain") % (primary_rcpt_domain)
+        print(_("Domain %r is not a local domain") % (primary_rcpt_domain), file=sys.stderr)
         sys.exit(1)
 
     if not secondary_rcpt_domain.lower() in domains.keys():
-        print >> sys.stderr, _("Domain %r is not a local domain") % (secondary_rcpt_domain)
+        print(_("Domain %r is not a local domain") % (secondary_rcpt_domain), file=sys.stderr)
         sys.exit(1)
 
     if not primary_rcpt_domain == secondary_rcpt_domain:
         if not domains[primary_rcpt_domain] == domains[secondary_rcpt_domain]:
-            print >> sys.stderr, _("Primary and secondary domain do not have the same parent domain")
+            print(_("Primary and secondary domain do not have the same parent domain"), file=sys.stderr)
             sys.exit(1)
 
     primary_recipient_dn = auth.find_recipient(primary_rcpt_address)
 
     if primary_recipient_dn == [] or len(primary_recipient_dn) == 0:
-        print >> sys.stderr, _("No such recipient %r") % (primary_rcpt_address)
+        print(_("No such recipient %r") % (primary_rcpt_address), file=sys.stderr)
         sys.exit(1)
 
     secondary_recipient_dn = auth.find_recipient(secondary_rcpt_address)
 
     if not secondary_recipient_dn == [] and not len(secondary_recipient_dn) == 0:
-        print >> sys.stderr, _("Recipient for alias %r already exists") % (secondary_rcpt_address)
+        print(_("Recipient for alias %r already exists") % (secondary_rcpt_address), file=sys.stderr)
         sys.exit(1)
 
     rcpt_attrs = conf.get_list('ldap', 'mail_attributes')
 
     primary_rcpt_attr = rcpt_attrs[0]
 
     if len(rcpt_attrs) >= 2:
         secondary_rcpt_attr = rcpt_attrs[1]
     else:
-        print >> sys.stderr, _("Environment is not configured for " + \
-            "users to hold secondary mail attributes")
+        print(_("Environment is not configured for " + \
+            "users to hold secondary mail attributes"), file=sys.stderr)
 
         sys.exit(1)
 
     primary_recipient = auth.get_entry_attributes(primary_rcpt_domain, primary_recipient_dn, rcpt_attrs)
 
     if not primary_recipient.has_key(primary_rcpt_attr):
-        print >> sys.stderr, _("Recipient %r is not the primary recipient for address %r") % (primary_recipient, primary_rcpt_address)
+        print(_("Recipient %r is not the primary recipient for address %r") % (primary_recipient, primary_rcpt_address), file=sys.stderr)
         sys.exit(1)
 
     if not primary_recipient.has_key(secondary_rcpt_attr):
         auth.set_entry_attributes(primary_rcpt_domain, primary_recipient_dn, {secondary_rcpt_attr: [ secondary_rcpt_address ] })
     else:
         if isinstance(primary_recipient[secondary_rcpt_attr], basestring):
             new_secondary_rcpt_attrs = [
                     primary_recipient[secondary_rcpt_attr],
                     secondary_rcpt_address
                 ]
 
         else:
             new_secondary_rcpt_attrs = \
                     primary_recipient[secondary_rcpt_attr] + \
                     [ secondary_rcpt_address ]
 
         auth.set_entry_attributes(
                 primary_rcpt_domain,
                 primary_recipient_dn,
                 {
                         secondary_rcpt_attr: new_secondary_rcpt_attrs
                     }
             )
 
diff --git a/pykolab/cli/cmd_add_user_subscription.py b/pykolab/cli/cmd_add_user_subscription.py
index d8a8901..eb0ec40 100644
--- a/pykolab/cli/cmd_add_user_subscription.py
+++ b/pykolab/cli/cmd_add_user_subscription.py
@@ -1,85 +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:
             folder_pattern = utils.ask_question(_("Folder pattern"))
 
     except IndexError, errmsg:
         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 >> sys.stderr, \
-                _("Cannot subscribe user to folder %r:") % (folder_pattern), \
-                _("No such folder")
+        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_check_quota.py b/pykolab/cli/cmd_check_quota.py
index ecbd66d..71af296 100644
--- a/pykolab/cli/cmd_check_quota.py
+++ b/pykolab/cli/cmd_check_quota.py
@@ -1,108 +1,110 @@
 # -*- 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.imap import IMAP
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('check_quota', execute, description=description())
 
 def cli_options():
     my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
 
     my_option_group.add_option(
             '--dry-run',
             dest    = "dryrun",
             action  = "store",
             default = False,
             help    = _("Do not apply any changes.")
         )
 
     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 _("Compare existing IMAP quota with LDAP quota.")
 
 def execute(*args, **kw):
     """
         List mailboxes
     """
 
     imap = IMAP()
     imap.connect(server=conf.connect_server)
 
     auth = Auth()
     auth.connect()
 
     domains = auth.list_domains()
 
     folders = []
     for domain in domains.keys():
         folders = imap.lm("user/%%@%s" % (domain))
 
         domain_auth = Auth(domain=domain)
         domain_auth.connect(domain=domain)
 
         for folder in folders:
             login = folder.split('/')[1]
             user_dn = domain_auth.find_recipient(login)
 
             if user_dn == None:
-                print >> sys.stderr, _("No such user %s") % (login)
+                print(_("No such user %s") % (login), file=sys.stderr)
                 continue
 
             if len(login.split('@')) > 1:
                 domain = login.split('@')[1]
             else:
                 domain = conf.get('kolab', 'primary_domain')
 
             try:
                 user_quota = auth.get_entry_attribute(domain, user_dn, 'mailquota')
             except:
                 user_quota = None
 
             if user_quota == None:
-                print >> sys.stderr, _("No quota for user %s") % (login)
+                print(_("No quota for user %s") % (login), file=sys.stderr)
                 continue
 
             try:
                 (used, quota) = imap.get_quota(folder)
 
                 if not (int)(quota) == (int)(user_quota):
-                    print >> sys.stderr, _("user quota does not match for %s (IMAP: %d, LDAP: %d)") % (login, (int)(quota), (int)(user_quota))
+                    print(_("user quota does not match for %s (IMAP: %d, LDAP: %d)") % (login, (int)(quota), (int)(user_quota)), file=sys.stderr)
                 
             except:
                 pass
diff --git a/pykolab/cli/cmd_count_domain_mailboxes.py b/pykolab/cli/cmd_count_domain_mailboxes.py
index 958bccd..2a3ea95 100644
--- a/pykolab/cli/cmd_count_domain_mailboxes.py
+++ b/pykolab/cli/cmd_count_domain_mailboxes.py
@@ -1,66 +1,66 @@
 # -*- 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 datetime
 
 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('count_domain_mailboxes', execute)
 
 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 execute(*args, **kw):
     """
         List deleted mailboxes
     """
     imap = IMAP()
     imap.connect()
 
     auth = Auth()
     auth.connect()
 
     domains = auth.list_domains()
 
     folders = []
     for domain in domains.keys():
-        print "%s: %d" % (domain,len(imap.lm("user/%%@%s" % (domain))))
+        print("%s: %d" % (domain,len(imap.lm("user/%%@%s" % (domain)))))
 
     null_realm = len(imap.lm("user/%%"))
 
     if null_realm > 0:
-        print "null: %d" % (null_realm)
+        print("null: %d" % (null_realm))
 
diff --git a/pykolab/cli/cmd_delete_mailbox.py b/pykolab/cli/cmd_delete_mailbox.py
index 9a7e1b1..f6ff254 100644
--- a/pykolab/cli/cmd_delete_mailbox.py
+++ b/pykolab/cli/cmd_delete_mailbox.py
@@ -1,70 +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 >> sys.stderr, _("No mailbox specified")
+        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 >> sys.stderr, _("No such folder(s): %s") % (folder)
+            print(_("No such folder(s): %s") % (folder), file=sys.stderr)
 
         delete_folders.extend(folders)
 
     if len(delete_folders) == 0:
-        print >> sys.stderr, _("No folders to delete.")
+        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:
             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 b7a7ede..ebfc2ec 100644
--- a/pykolab/cli/cmd_delete_mailbox_acl.py
+++ b/pykolab/cli/cmd_delete_mailbox_acl.py
@@ -1,69 +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:
             identifier = utils.ask_question(_("ACI Subject"))
 
     except IndexError, errmsg:
         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 >> sys.stderr, _("No such folder %r") % (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_export_mailbox.py b/pykolab/cli/cmd_export_mailbox.py
index 37a862c..78d55f1 100644
--- a/pykolab/cli/cmd_export_mailbox.py
+++ b/pykolab/cli/cmd_export_mailbox.py
@@ -1,122 +1,124 @@
 # -*- 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 commands
 
 import pykolab
 
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('export_mailbox', execute)
 
 def cli_options():
     my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
     my_option_group.add_option( '--all',
                                 dest    = "all",
                                 action  = "store_true",
                                 default = False,
                                 help    = _("All folders this user has access to"))
 
 def execute(*args, **kw):
     import os
     import subprocess
 
     user = conf.cli_args.pop(0)
 
     # TODO: /etc/imapd.conf is not the definitive location for the
     # imapd.conf configuration file.
     partition_proc = subprocess.Popen(
             ['grep', '^partition', '/etc/imapd.conf'],
             stdout=subprocess.PIPE
         )
 
     partitions = [
             x.split(':')[1].strip()
             for x in partition_proc.communicate()[0].split('\n')
             if len(x.split(':')) > 1
         ]
 
     # TODO: ctl_mboxlist is not necessarily in this location.
     ctl_mboxlist_args = [ '/usr/lib/cyrus-imapd/ctl_mboxlist', '-d' ]
     ctl_mboxlist = subprocess.Popen(
             ctl_mboxlist_args,
             stdout=subprocess.PIPE,
             stderr=subprocess.PIPE
         )
 
     mboxlist_proc = subprocess.Popen(
             ['grep', '-E', '\s*%s\s*.*i.*p.*' % (user)],
             stdin=ctl_mboxlist.stdout,
             stdout=subprocess.PIPE
         )
 
     ctl_mboxlist.stdout.close()
 
     # TODO: Handle errors from ctl_mboxlist process (stderr)
     mboxlist_output = mboxlist_proc.communicate()[0]
 
     zipper_args = [ 'zip', '-r', '%s.zip' % (user) ]
     directories = []
 
     for mbox_internal in mboxlist_output.split('\n'):
         if len(mbox_internal.split('\t')[0].split('!')) > 1:
             domain = mbox_internal.split('\t')[0].split('!')[0]
             mailbox = '/'.join(
                     mbox_internal.split(
                             '\t'
                         )[0].split(
                                 '!'
                             )[1].split(
                                     '.'
                                 )[1:]
                 )
 
             for partition in partitions:
                 mbox_dir = '%s/domain/%s/%s/%s/user/%s/' % (
                         partition,
                         domain[0],
                         domain,
                         user[0],
                         mailbox
                     )
 
                 if os.path.isdir(mbox_dir):
                     directories.append(mbox_dir)
 
                 else:
                     log.debug(
                             _('%s is not a directory') % (mbox_dir),
                             level=5
                         )
 
     if not len(directories) == 0:
         zipper_output = subprocess.Popen(
                 zipper_args + directories,
                 stdout=subprocess.PIPE
             ).communicate()[0]
 
-        print >> sys.stderr, _("ZIP file at %s.zip") % (user)
+        print(_("ZIP file at %s.zip") % (user), file=sys.stderr)
     else:
-        print >> sys.stderr, _("No directories found for user %s") % (user)
+        print(_("No directories found for user %s") % (user), file=sys.stderr)
         sys.exit(1)
 
diff --git a/pykolab/cli/cmd_list_deleted_mailboxes.py b/pykolab/cli/cmd_list_deleted_mailboxes.py
index 6d07816..55b6b9f 100644
--- a/pykolab/cli/cmd_list_deleted_mailboxes.py
+++ b/pykolab/cli/cmd_list_deleted_mailboxes.py
@@ -1,114 +1,114 @@
 # -*- 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 datetime
 
 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('list_deleted_mailboxes', execute)
 
 
 def cli_options():
     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(
         '--server',
         dest="connect_server",
         action="store",
         default=None,
         metavar="SERVER",
         help=_("List mailboxes on server SERVER only.")
     )
 
 
 def execute(*args, **kw):
     """
         List deleted mailboxes
     """
 
     folders = []
     searches = []
 
     imap = IMAP()
 
     if conf.connect_server is not None:
         imap.connect(server=conf.connect_server)
     else:
         imap.connect()
 
     # See if conf.cli_args components make sense.
     for arg in conf.cli_args:
         if arg == '*':
             searches.append(arg)
         if arg.startswith('user'):
             searches.append(arg)
         if arg.startswith('shared'):
             searches.append(arg)
         if arg.startswith('DELETED'):
             searches.append(arg)
         if arg.startswith('news'):
             searches.append(arg)
 
     if len(searches) == 0:
         auth = Auth()
         auth.connect()
 
         domains = auth.list_domains()
 
         folders = []
         for domain in list(set(domains.keys())):
             folders.extend(imap.lm("DELETED/*@%s" % (domain)))
 
         folders.extend(imap.lm("DELETED/*"))
     else:
         for search in searches:
             log.debug(_("Appending folder search for %r") % (search), level=8)
             folders.extend(imap.lm(imap_utf7.encode(search)))
 
-    print "Deleted folders:"
+    print("Deleted folders:")
 
     for folder in folders:
         utf8_folder = imap_utf7.decode(folder).encode('utf-8')
         mbox_parts = imap.parse_mailfolder(utf8_folder)
         ts = datetime.datetime.fromtimestamp(int(mbox_parts['hex_timestamp'], 16))
 
         if not conf.raw:
-            print "%s (Deleted at %s)" % (utf8_folder, ts)
+            print("%s (Deleted at %s)" % (utf8_folder, ts))
         else:
-            print "%s (Deleted at %s)" % (folder, ts)
+            print("%s (Deleted at %s)" % (folder, ts))
diff --git a/pykolab/cli/cmd_list_domain_mailboxes.py b/pykolab/cli/cmd_list_domain_mailboxes.py
index d4c8bfe..fd9a7fd 100644
--- a/pykolab/cli/cmd_list_domain_mailboxes.py
+++ b/pykolab/cli/cmd_list_domain_mailboxes.py
@@ -1,84 +1,84 @@
 # -*- 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 utils
 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('list_domain_mailboxes', execute)
 
 def cli_options():
     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( '--server',
                                 dest    = "connect_server",
                                 action  = "store",
                                 default = None,
                                 metavar = "SERVER",
                                 help    = _("List mailboxes on server SERVER only."))
 
 def execute(*args, **kw):
     """
         List deleted mailboxes
     """
 
     try:
         domain = conf.cli_args.pop(0)
     except:
         domain = utils.ask_question(_("Domain"))
 
     imap = IMAP()
     imap.connect()
 
     auth = Auth()
     auth.connect()
 
     domains = auth.list_domains()
 
     folders = []
     for primary,secondaries in domains:
         if not domain == primary and not domain in secondaries:
             continue
 
         folders.extend(imap.lm("user/%%@%s" % (primary)))
         for secondary in secondaries:
             folders.extend(imap.lm("user/%%@%s" % (secondary)))
 
-    print "Deleted folders:"
+    print("Deleted folders:")
 
     for folder in folders:
         if not conf.raw:
-            print imap_utf7.decode(folder)
+            print(imap_utf7.decode(folder))
         else:
-            print folder
+            print(folder)
diff --git a/pykolab/cli/cmd_list_domains.py b/pykolab/cli/cmd_list_domains.py
index 3ee4d20..62ee29f 100644
--- a/pykolab/cli/cmd_list_domains.py
+++ b/pykolab/cli/cmd_list_domains.py
@@ -1,52 +1,52 @@
 # -*- 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.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('list_domains', execute, description="List Kolab domains.")
 
 def execute(*args, **kw):
     from pykolab import wap_client
     # Create the authentication object.
     # TODO: Binds with superuser credentials!
     wap_client.authenticate()
     domains = wap_client.domains_list()
 
     dna = conf.get('ldap', 'domain_name_attribute')
 
-    print "%-39s %-40s" % ("Primary Domain Name Space","Secondary Domain Name Space(s)")
+    print("%-39s %-40s" % ("Primary Domain Name Space","Secondary Domain Name Space(s)"))
 
     # TODO: Take a hint in --quiet, and otherwise print out a nice table
     # with headers and such.
     if isinstance(domains['list'], dict):
         for domain_dn in domains['list'].keys():
             if isinstance(domains['list'][domain_dn][dna], list):
-                print domains['list'][domain_dn][dna][0]
+                print(domains['list'][domain_dn][dna][0])
                 for domain_alias in domains['list'][domain_dn][dna][1:]:
-                    print "%-39s %-40s" % ('', domain_alias)
+                    print("%-39s %-40s" % ('', domain_alias))
             else:
-                print domains['list'][domain_dn][dna]
+                print(domains['list'][domain_dn][dna])
diff --git a/pykolab/cli/cmd_list_mailbox_acls.py b/pykolab/cli/cmd_list_mailbox_acls.py
index c9e47ac..bf93429 100644
--- a/pykolab/cli/cmd_list_mailbox_acls.py
+++ b/pykolab/cli/cmd_list_mailbox_acls.py
@@ -1,65 +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:
         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 >> sys.stderr, _("No such folder %r") % (folder)
+        print(_("No such folder %r") % (folder), file=sys.stderr)
 
     else:
         acls = []
         folders = imap.list_folders(folder)
         for folder in folders:
-            print "Folder", folder
+            print("Folder", folder)
             acls = imap.list_acls(folder)
 
             for acl in acls.keys():
-                print "  %-13s %s" %(acls[acl], acl)
+                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 223482b..718c376 100644
--- a/pykolab/cli/cmd_list_mailbox_metadata.py
+++ b/pykolab/cli/cmd_list_mailbox_metadata.py
@@ -1,95 +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:
         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 >> sys.stderr, _("No such folder %r") % (folder)
+        print(_("No such folder %r") % (folder), file=sys.stderr)
 
     else:
         metadata = []
         folders = imap.list_folders(folder)
         for folder in folders:
-            print "Folder", folder
+            print("Folder", folder)
 
             metadata = imap.get_metadata(folder)
 
             if metadata.has_key(folder):
                 for annotation in metadata[folder].keys():
-                    print "  %-49s %s" % (
+                    print("  %-49s %s" % (
                             annotation,
                             metadata[folder][annotation]
-                        )
+                        ))
diff --git a/pykolab/cli/cmd_list_mailboxes.py b/pykolab/cli/cmd_list_mailboxes.py
index 3b3bbd8..738cd86 100644
--- a/pykolab/cli/cmd_list_mailboxes.py
+++ b/pykolab/cli/cmd_list_mailboxes.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/>.
 #
 
 import commands
 
 import pykolab
 
 from pykolab import imap_utf7
 from pykolab.imap import IMAP
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 
 def __init__():
     commands.register('list_mailboxes', execute, description=description(), aliases='lm')
 
 
 def description():
     return "List mailboxes.\n" + \
         "%-28s" % ('') + \
         "Use wildcards '*' and '%' for more control.\n"
 
 
 def cli_options():
     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(
         '--server',
         dest="connect_server",
         action="store",
         default=None,
         metavar="SERVER",
         help=_("List mailboxes on server SERVER only.")
     )
 
 
 def execute(*args, **kw):
     """
         List mailboxes
     """
 
     searches = []
 
     # See if conf.cli_args components make sense.
     for arg in conf.cli_args:
         if arg == '*':
             searches.append(arg)
         if arg.startswith('user'):
             searches.append(arg)
         if arg.startswith('shared'):
             searches.append(arg)
         if arg.startswith('DELETED'):
             searches.append(arg)
         if arg.startswith('news'):
             searches.append(arg)
 
     if len(searches) == 0:
         searches = ['']
 
     imap = IMAP()
 
     if not conf.connect_server == None:
         imap.connect(server=conf.connect_server)
     else:
         imap.connect()
 
     folders = []
 
     for search in searches:
         log.debug(_("Appending folder search for %r") % (search), level=8)
         folders.extend(imap.lm(imap_utf7.encode(search)))
 
     for folder in folders:
         if not conf.raw:
-            print imap_utf7.decode(folder)
+            print(imap_utf7.decode(folder))
         else:
-            print folder
+            print(folder)
diff --git a/pykolab/cli/cmd_list_messages.py b/pykolab/cli/cmd_list_messages.py
index db59a48..786a5f0 100644
--- a/pykolab/cli/cmd_list_messages.py
+++ b/pykolab/cli/cmd_list_messages.py
@@ -1,134 +1,134 @@
 # -*- 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 imap_utf7
 from pykolab.imap import IMAP
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('list_messages', execute, description=description())
 
 def cli_options():
     my_option_group = conf.add_cli_parser_option_group(_("CLI Options"))
     my_option_group.add_option(
             '--deleted',
             dest    = "list_deleted",
             action  = "store_true",
             default = False,
             help    = _("Include messages flagged as \Deleted")
         )
 
     my_option_group.add_option(
             '--server',
             dest    = "connect_server",
             action  = "store",
             default = None,
             metavar = "SERVER",
             help    = _("List mailboxes on server SERVER only.")
         )
 
     my_option_group.add_option(
             '--user',
             dest    = "user",
             action  = "store",
             default = None,
             metavar = "USER",
             help    = _("List messages as user USER")
         )
 
 def description():
     return _("List messages in a folder")
 
 def execute(*args, **kw):
     """
         List messages in a folder
     """
 
     try:
         folder = conf.cli_args.pop(0)
 
     except:
         log.error(_("Specify a folder"))
         sys.exit(1)
 
     domain = None
 
     imap = IMAP()
 
     if not conf.user == None:
         imap.connect(domain=domain, login=False, server=conf.connect_server)
 
         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, server=conf.connect_server)
 
     _folder = imap.lm(imap_utf7.encode(folder))
 
     if _folder == None or _folder == []:
         log.error(_("No such folder"))
         sys.exit(1)
 
     if conf.user == None:
         imap.set_acl(folder, 'cyrus-admin', 'lrs')
 
     imap.select(imap_utf7.encode(folder))
 
     if conf.list_deleted:
         typ, data = imap.search(None, 'ALL')
     else:
         typ, data = imap.search(None, '(ALL UNDELETED)')
 
     num_messages = len(data[0].split())
 
     for num in data[0].split():
         typ, flags = imap.fetch(num, 'FLAGS')
         flags = flags[0].split()
         if len(flags) >= 3:
             # Any flags are set
             if flags[2] == '(\\Deleted))':
-                print num, '\Deleted'
+                print(num, '\Deleted')
             elif flags[2] == '(\\Deleted':
-                print num, '\Deleted'
+                print(num, '\Deleted')
             elif '\\Deleted' in flags[3:]:
-                print num, '\Deleted'
+                print(num, '\Deleted')
             elif '\\Deleted))' in flags[3:]:
-                print num, '\Deleted'
+                print(num, '\Deleted')
             else:
-                print num
+                print(num)
         else:
-            print num
+            print(num)
 
     if conf.user == None:
         imap.set_acl(folder, 'cyrus-admin', '')
diff --git a/pykolab/cli/cmd_list_ous.py b/pykolab/cli/cmd_list_ous.py
index 670b609..61a4cb9 100644
--- a/pykolab/cli/cmd_list_ous.py
+++ b/pykolab/cli/cmd_list_ous.py
@@ -1,39 +1,39 @@
 # -*- 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/>.
 #
 
 import commands
 
 import pykolab
 
 from pykolab import utils
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('list_ous', execute, description="List organizational units.")
 
 def execute(*args, **kw):
     from pykolab import wap_client
 
     wap_client.authenticate(username=conf.get("ldap", "bind_dn"), password=conf.get("ldap", "bind_pw"))
 
     ous = wap_client.ous_list()
-    print '\n'.join(ous['list'].keys())
+    print('\n'.join(ous['list'].keys()))
diff --git a/pykolab/cli/cmd_list_quota.py b/pykolab/cli/cmd_list_quota.py
index f66f580..28b0cd5 100644
--- a/pykolab/cli/cmd_list_quota.py
+++ b/pykolab/cli/cmd_list_quota.py
@@ -1,102 +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:
         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)
+            print("Folder: %s" % (quota_folder))
             if not used == None and not quota == None:
                 if quota == 0:
-                    print >> sys.stderr, _("The quota for folder %s is set to literally allow 0KB of storage.") % (quota_folder)
-                    print "%d (Used: %d, Percentage: %s)" % (quota, used, u'\u221E')
+                    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)
+                    print("%d (Used: %d, Percentage: %d)" % (quota, used, percentage))
             else:
                 if used == None:
-                    print "%d (Used: %d, Percentage: %d)" % (quota, 0, 0)
+                    print("%d (Used: %d, Percentage: %d)" % (quota, 0, 0))
                 else:
-                    print "No quota"
+                    print("No quota")
         except:
             try:
                 (quota_root, used, quota) = imap.get_quota_root(quota_folder)
-                print "Folder: %s" % (quota_folder)
+                print("Folder: %s" % (quota_folder))
                 if not quota_root == None and not used == None and not quota == None:
                     if quota == 0:
-                        print >> sys.stderr, _("The quota for folder %s is set to literally allow 0KB of storage.") % (quota_folder)
-                        print "%d (Used: %d, Percentage: %d)" % (quota, used, u'\u221E')
+                        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)
+                        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)
+                        print("%d (Root: %s, Used: %d, Percentage: %d)" % (quota, quota_root, 0, 0))
                     else:
-                        print "No quota"
+                        print("No quota")
             except:
-                print "Folder: %s" % (quota_folder)
-                print "No quota root"
+                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 9095604..2d8e4f3 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:
             pass
 
     except IndexError, errmsg:
         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])
+                print("\n".join([imap_utf7.decode(x) for x in unsubscribed_folders]))
             else:
-                print "\n".join(unsubscribed_folders)
+                print("\n".join(unsubscribed_folders))
         else:
-            print _("No unsubscribed folders for user %s") % (user)
+            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])
+            print("\n".join([imap_utf7.decode(x) for x in subscribed_folders]))
         else:
-            print "\n".join(subscribed_folders)
+            print("\n".join(subscribed_folders))
diff --git a/pykolab/cli/cmd_list_users.py b/pykolab/cli/cmd_list_users.py
index ff1ddef..a17eeba 100644
--- a/pykolab/cli/cmd_list_users.py
+++ b/pykolab/cli/cmd_list_users.py
@@ -1,39 +1,39 @@
 # -*- 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/>.
 #
 
 import commands
 
 import pykolab
 
 from pykolab import utils
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('list_users', execute, description="List organizational units.")
 
 def execute(*args, **kw):
     from pykolab import wap_client
 
     wap_client.authenticate(username=conf.get("ldap", "bind_dn"), password=conf.get("ldap", "bind_pw"))
 
     users = wap_client.users_list()
-    print '\n'.join(users['list'].keys())
+    print('\n'.join(users['list'].keys()))
diff --git a/pykolab/cli/cmd_remove_mailaddress.py b/pykolab/cli/cmd_remove_mailaddress.py
index af64609..bc94bb1 100644
--- a/pykolab/cli/cmd_remove_mailaddress.py
+++ b/pykolab/cli/cmd_remove_mailaddress.py
@@ -1,93 +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:
         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 >> sys.stderr, _("Found the following recipients:")
+        print(_("Found the following recipients:"), file=sys.stderr)
 
         for recipient in recipients:
-            print recipient
+            print(recipient)
diff --git a/pykolab/cli/cmd_remove_user_subscription.py b/pykolab/cli/cmd_remove_user_subscription.py
index 59975c8..343ee68 100644
--- a/pykolab/cli/cmd_remove_user_subscription.py
+++ b/pykolab/cli/cmd_remove_user_subscription.py
@@ -1,101 +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:
             folder_pattern = utils.ask_question(_("Folder pattern"))
 
     except IndexError, errmsg:
         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 >> sys.stderr, \
-                _("Cannot subscribe user to folder %r:") % (folder_pattern), \
-                _("No such folder")
+        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:") % (
+        print(_("Successfully unsubscribed user %s from the following folders:") % (
                 user
-            )
+            ))
 
-        print "\n".join(unsubscribed_folders)
+        print("\n".join(unsubscribed_folders))
     else:
-        print >> sys.stderr, _("User %s was not unsubscribed from any folders.") % (
+        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 7432e08..c693211 100644
--- a/pykolab/cli/cmd_rename_mailbox.py
+++ b/pykolab/cli/cmd_rename_mailbox.py
@@ -1,74 +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:
                 partition = None
         except IndexError, errmsg:
-            print >> sys.stderr, _("No target mailbox name specified")
+            print(_("No target mailbox name specified"), file=sys.stderr)
     except IndexError, errmsg:
-        print >> sys.stderr, _("No source mailbox name specified")
+        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 >> sys.stderr, _("Source folder %r does not exist") % (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 >> sys.stderr, _("Target folder %r already exists") % (target_folder)
+        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_server_info.py b/pykolab/cli/cmd_server_info.py
index e512fcc..0ae0c0e 100644
--- a/pykolab/cli/cmd_server_info.py
+++ b/pykolab/cli/cmd_server_info.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 commands
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('server_info', execute, description=description())
 
 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 "Display server info.\n"
 
 def execute(*args, **kw):
     """
         List mailboxes
     """
 
     imap = IMAP()
 
     if not conf.connect_server == None:
         imap.connect(server=conf.connect_server)
     else:
         imap.connect()
 
-    print imap.get_metadata("")
+    print(imap.get_metadata(""))
diff --git a/pykolab/cli/cmd_set_mailbox_acl.py b/pykolab/cli/cmd_set_mailbox_acl.py
index 379a8c3..249c1ee 100644
--- a/pykolab/cli/cmd_set_mailbox_acl.py
+++ b/pykolab/cli/cmd_set_mailbox_acl.py
@@ -1,72 +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:
                 acl = utils.ask_question(_("ACI Permissions"))
 
         except IndexError, errmsg:
             identifier = utils.ask_question(_("ACI Subject"))
             acl = utils.ask_question(_("ACI Permissions"))
 
     except IndexError, errmsg:
         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 >> sys.stderr, _("No such folder %r") % (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 0b7b1fe..d63e6a6 100644
--- a/pykolab/cli/cmd_set_mailbox_metadata.py
+++ b/pykolab/cli/cmd_set_mailbox_metadata.py
@@ -1,99 +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:
                 metadata_value = utils.ask_question(_("Metadata value"))
 
         except IndexError, errmsg:
             metadata_path = utils.ask_question(_("Metadata path"))
             metadata_value = utils.ask_question(_("Metadata value"))
 
     except IndexError, errmsg:
         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 >> sys.stderr, _("No such folder %r") % (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 2fa9ba3..5b7b249 100644
--- a/pykolab/cli/cmd_set_quota.py
+++ b/pykolab/cli/cmd_set_quota.py
@@ -1,66 +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:
             quota = utils.ask_question(_("New quota"))
 
     except IndexError, errmsg:
         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 >> sys.stderr, _("No such folder %r") % (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 >> sys.stdout, "Quota for folder '%s' set to %d" % (_folder, int(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 3f5a731..512265b 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:
                                 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:
                                     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
+                    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
+        print(folder, server, recipient)
diff --git a/pykolab/cli/cmd_user_info.py b/pykolab/cli/cmd_user_info.py
index d7b83a6..a7879d1 100644
--- a/pykolab/cli/cmd_user_info.py
+++ b/pykolab/cli/cmd_user_info.py
@@ -1,60 +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:
         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 >> sys.stderr, _("No such user %s") % (user)
+        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)
+            print("%s: %s" % (k,v))
         else:
-            print "%s: %r" % (k,v)
+            print("%s: %r" % (k,v))
diff --git a/pykolab/cli/commands.py b/pykolab/cli/commands.py
index 515d40c..2f05bb6 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:
                     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'])
+                print("%-25s - %s" % (_command.replace('_','-'),__commands[_command]['description']))
             else:
-                print "%-25s" % (_command.replace('_','-'))
+                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"
+            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'])
+                    print("%-4s%-21s - %s" % ('',__command.replace('_','-'),__commands[_command][__command]['description']))
                 else:
-                    print "%-4s%-21s" % ('',__command.replace('_','-'))
+                    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:
             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:
             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")
+    print(_("Not yet implemented"))
     sys.exit(1)
diff --git a/pykolab/cli/sieve/cmd_list.py b/pykolab/cli/sieve/cmd_list.py
index d10517c..20c967a 100644
--- a/pykolab/cli/sieve/cmd_list.py
+++ b/pykolab/cli/sieve/cmd_list.py
@@ -1,121 +1,121 @@
 # -*- 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 time
 from urlparse import urlparse
 
 import pykolab
 
 from pykolab import utils
 from pykolab.auth import Auth
 from pykolab.cli import commands
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register(
             'list',
             execute,
             group='sieve',
             description=description()
         )
 
 def description():
     return """List a user's sieve scripts."""
 
 def execute(*args, **kw):
     try:
         address = conf.cli_args.pop(0)
     except:
         address = utils.ask_question(_("Email Address"))
 
     auth = Auth()
     auth.connect()
 
     user = auth.find_recipient(address)
 
     # Get the main, default backend
     backend = conf.get('kolab', 'imap_backend')
 
     if len(address.split('@')) > 1:
         domain = address.split('@')[1]
     else:
         domain = conf.get('kolab', 'primary_domain')
 
     if conf.has_section(domain) and conf.has_option(domain, 'imap_backend'):
         backend = conf.get(domain, 'imap_backend')
 
     if conf.has_section(domain) and conf.has_option(domain, 'imap_uri'):
         uri = conf.get(domain, 'imap_uri')
     else:
         uri = conf.get(backend, 'uri')
 
     hostname = None
     port = None
 
     result = urlparse(uri)
 
     if hasattr(result, 'hostname'):
         hostname = result.hostname
     else:
         scheme = uri.split(':')[0]
         (hostname, port) = uri.split('/')[2].split(':')
 
     port = 4190
 
     # Get the credentials
     admin_login = conf.get(backend, 'admin_login')
     admin_password = conf.get(backend, 'admin_password')
 
     import sievelib.managesieve
 
     sieveclient = sievelib.managesieve.Client(
             hostname,
             port,
             conf.debuglevel > 8
         )
 
     sieveclient.connect(None, None, True)
 
     result = sieveclient._plain_authentication(
             admin_login,
             admin_password,
             address
         )
 
     if not result:
-        print "LOGIN FAILED??"
+        print("LOGIN FAILED??")
 
     sieveclient.authenticated = True
 
     result = sieveclient.listscripts()
 
     if result == None:
-        print "No scripts"
+        print("No scripts")
         sys.exit(0)
 
     (active, scripts) = result
 
-    print "%s (active)" % (active)
+    print("%s (active)" % (active))
     for script in scripts:
-        print script
+        print(script)
 
diff --git a/pykolab/cli/sieve/cmd_test.py b/pykolab/cli/sieve/cmd_test.py
index 0c9421e..7906570 100644
--- a/pykolab/cli/sieve/cmd_test.py
+++ b/pykolab/cli/sieve/cmd_test.py
@@ -1,129 +1,129 @@
 # -*- 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.auth import Auth
 from pykolab.cli import commands
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 import time
 from urlparse import urlparse
 
 
 def __init__():
     commands.register('test', execute, group='sieve', description=description())
 
 def description():
     return """Syntactically check a user's sieve scripts."""
 
 def execute(*args, **kw):
     try:
         address = conf.cli_args.pop(0)
     except:
         address = utils.ask_question(_("Email Address"))
 
     auth = Auth()
     auth.connect()
 
     user = auth.find_recipient(address)
 
     # Get the main, default backend
     backend = conf.get('kolab', 'imap_backend')
 
     if len(address.split('@')) > 1:
         domain = address.split('@')[1]
     else:
         domain = conf.get('kolab', 'primary_domain')
 
     if conf.has_section(domain) and conf.has_option(domain, 'imap_backend'):
         backend = conf.get(domain, 'imap_backend')
 
     if conf.has_section(domain) and conf.has_option(domain, 'imap_uri'):
         uri = conf.get(domain, 'imap_uri')
     else:
         uri = conf.get(backend, 'uri')
 
     hostname = None
     port = None
 
     result = urlparse(uri)
 
     if hasattr(result, 'hostname'):
         hostname = result.hostname
     else:
         scheme = uri.split(':')[0]
         (hostname, port) = uri.split('/')[2].split(':')
 
     port = 4190
 
     # Get the credentials
     admin_login = conf.get(backend, 'admin_login')
     admin_password = conf.get(backend, 'admin_password')
 
     import sievelib.managesieve
  
     sieveclient = sievelib.managesieve.Client(hostname, port, True)
     sieveclient.connect(None, None, True)
     sieveclient._plain_authentication(admin_login, admin_password, address)
     sieveclient.authenticated = True
 
     active, scripts = sieveclient.listscripts()
 
-    print "%s (active)" % (active)
+    print("%s (active)" % (active))
     
     _all_scripts = [ active ] + scripts
     _used_scripts = [ active ]
     _included_scripts = []
 
     _a_script = sieveclient.getscript(active)
 
-    print _a_script
+    print(_a_script)
 
     import sievelib.parser
 
     _a_parser = sievelib.parser.Parser(debug=True)
     _a_parsed = _a_parser.parse(_a_script)
 
     #print "%r" % (_a_parsed)
 
     if not _a_parsed:
-        print _a_parser.error
+        print(_a_parser.error)
 
-    print "%r" % (_a_parser.result)
+    print("%r" % (_a_parser.result))
 
     for _a_command in _a_parser.result:
-        print _a_command.name, _a_command.arguments
+        print(_a_command.name, _a_command.arguments)
         if len(_a_command.children) > 0:
             for _a_child in _a_command.children:
-                print "  ", _a_child.name, _a_child.arguments
+                print("  ", _a_child.name, _a_child.arguments)
 
         if _a_command.name == "include":
             if _a_command.arguments["script"].strip('"') in scripts:
-                print "OK"
+                print("OK")
                 _used_scripts.append(_a_command.arguments["script"].strip('"'))
             else:
-                print "Not OK"
+                print("Not OK")
 
     for script in scripts:
-        print script
+        print(script)
 
diff --git a/pykolab/cli/telemetry/cmd_examine_command_issue.py b/pykolab/cli/telemetry/cmd_examine_command_issue.py
index 9563dbc..15d8673 100644
--- a/pykolab/cli/telemetry/cmd_examine_command_issue.py
+++ b/pykolab/cli/telemetry/cmd_examine_command_issue.py
@@ -1,123 +1,123 @@
 
 # -*- 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.cli')
 conf = pykolab.getConf()
 
 from pykolab import telemetry
 from pykolab.cli import commands
 
 def __init__():
     commands.register('examine_command_issue', execute, group='telemetry', description="Examine a particular telemetry command issue.")
 
 def execute(*args, **kw):
     db = telemetry.init_db()
 
     try:
         wanted = conf.cli_args.pop(0)
     except:
         log.error(_("Unspecified command issue identifier"))
         sys.exit(1)
 
     command_issue = db.query(
             telemetry.TelemetryCommandIssue
         ).filter_by(
                 id=wanted
             ).first()
 
     if command_issue == None:
         log.error(_("Invalid command issue identifier"))
         sys.exit(1)
 
     session = db.query(
             telemetry.TelemetrySession
         ).filter_by(
                 id=command_issue.session_id
             ).first()
 
     if session == None:
         log.error(_("Invalid session identifier"))
         sys.exit(1)
 
     user = db.query(
             telemetry.TelemetryUser
         ).filter_by(
                 id=session.user_id
             ).first()
 
     server = db.query(
             telemetry.TelemetryServer
         ).filter_by(
                 id=session.server_id
             ).first()
 
-    print _("Session by %s on server %s") % (user.sasl_username,server.fqdn)
+    print(_("Session by %s on server %s") % (user.sasl_username,server.fqdn))
 
     command_issues = db.query(
             telemetry.TelemetryCommandIssue
         ).filter_by(
                 session_id=session.id
             )
 
     for _command_issue in command_issues:
         command = db.query(
                 telemetry.TelemetryCommand
             ).filter_by(
                     id=_command_issue.command_id
                 ).first()
 
         command_arg = db.query(
                 telemetry.TelemetryCommandArg
             ).filter_by(
                     id=_command_issue.command_arg_id
                 ).first()
 
         if command_issue.id == _command_issue.id:
-            print "========="
+            print("=========")
 
-        print "Client(%d): %s %s %s" % (
+        print("Client(%d): %s %s %s" % (
                 _command_issue.id,
                 _command_issue.command_tag,
                 command.command,
                 command_arg.command_arg
-            )
+            ))
 
         server_responses = db.query(
                 telemetry.TelemetryServerResponse
             ).filter_by(
                     command_issue_id=_command_issue.id
                 )
 
         for server_response in server_responses:
             server_response_lines = server_response.response.split('\n');
 
             for server_response_line in server_response_lines:
-                print "Server(%d): %s" % (
+                print("Server(%d): %s" % (
                         server_response.id,
                         server_response_line
-                    )
+                    ))
 
         if command_issue.id == _command_issue.id:
-            print "========="
+            print("=========")
 
diff --git a/pykolab/cli/telemetry/cmd_examine_session.py b/pykolab/cli/telemetry/cmd_examine_session.py
index cf09b74..d6851d5 100644
--- a/pykolab/cli/telemetry/cmd_examine_session.py
+++ b/pykolab/cli/telemetry/cmd_examine_session.py
@@ -1,141 +1,141 @@
 
 # -*- 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.cli')
 conf = pykolab.getConf()
 
 from pykolab import telemetry
 from pykolab.cli import commands
 
 def __init__():
     commands.register('examine_session', execute, group='telemetry', description="Examine a Telemetry session.")
 
 def execute(*args, **kw):
     db = telemetry.init_db()
 
     wanted = False
 
     if session_id == None:
         try:
             wanted = conf.cli_args.pop(0)
         except:
             log.error(_("Unspecified session identifier"))
             sys.exit(1)
 
     if not wanted:
         wanted = session_id
 
     session_wanted = None
 
     try:
         _wanted = (int)(wanted)
         session_wanted = _wanted
     except:
         user_wanted = wanted
 
     if not session_wanted == None:
         session = db.query(
                 telemetry.TelemetrySession
             ).filter_by(
                     id=session_wanted
                 ).first()
 
         if session == None:
             log.error(_("Invalid session identifier"))
             sys.exit(1)
 
         user = db.query(
                 telemetry.TelemetryUser
             ).filter_by(
                     id=session.user_id
                 ).first()
 
         server = db.query(
                 telemetry.TelemetryServer
             ).filter_by(
                     id=session.server_id
                 ).first()
 
     else:
         user = db.query(
                 telemetry.TelemetryUser
             ).filter_by(
                 sasl_username=user_wanted
                 ).first()
 
         sessions = db.query(
                 telemetry.TelemetrySession
             ).filter_by(
                     user_id=user.id
                 ).order_by(
                         telemetry.telemetry_session_table.c.start
                     )
 
         for session in sessions:
             self.action_telemetry_examine_session(session_id=session.id)
 
         return
 
-    print _("Session by %s on server %s") % (user.sasl_username,server.fqdn)
+    print(_("Session by %s on server %s") % (user.sasl_username,server.fqdn))
 
     command_issues = db.query(
             telemetry.TelemetryCommandIssue
         ).filter_by(
                 session_id=session.id
             )
 
     for command_issue in command_issues:
         command = db.query(
                 telemetry.TelemetryCommand
             ).filter_by(
                     id=command_issue.command_id
                 ).first()
 
         command_arg = db.query(
                 telemetry.TelemetryCommandArg
             ).filter_by(
                     id=command_issue.command_arg_id
                 ).first()
 
-        print "Client(%d): %s %s %s" % (
+        print("Client(%d): %s %s %s" % (
                 command_issue.id,
                 command_issue.command_tag,
                 command.command,
                 command_arg.command_arg
-            )
+            ))
 
         server_responses = db.query(
                 telemetry.TelemetryServerResponse
             ).filter_by(
                     command_issue_id=command_issue.id
                 )
 
         for server_response in server_responses:
             server_response_lines = server_response.response.split('\n');
             for server_response_line in server_response_lines:
-                print "Server(%d): %s" % (
+                print("Server(%d): %s" % (
                         server_response.id,
                         server_response_line
-                    )
+                    ))
 
diff --git a/pykolab/cli/telemetry/cmd_list_sessions.py b/pykolab/cli/telemetry/cmd_list_sessions.py
index 6ed0ab7..773856f 100644
--- a/pykolab/cli/telemetry/cmd_list_sessions.py
+++ b/pykolab/cli/telemetry/cmd_list_sessions.py
@@ -1,63 +1,63 @@
 
 # -*- 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.cli')
 conf = pykolab.getConf()
 
 from pykolab import telemetry
 from pykolab.cli import commands
 
 def __init__():
     commands.register('list_sessions', execute, group='telemetry', description="List IMAP sessions using Telemetry.")
 
 def cli_options():
     my_option_group = conf.add_cli_parser_option_group(_("List Options"))
     my_option_group.add_option( '--since',
                                 dest    = "since",
                                 action  = "store",
                                 default = 0,
                                 help    = _("Display sessions since ..."))
 
 def execute(*args, **kw):
     db = telemetry.init_db()
 
     sessions = db.query(
             telemetry.TelemetrySession
         ).order_by(
                 telemetry.telemetry_session_table.c.start
             )
 
     for session in sessions:
         user = db.query(
                 telemetry.TelemetryUser
             ).filter_by(
                     id=session.user_id
                 ).first()
 
-        print _("Session for user %s started at %s with ID %s") % (
+        print(_("Session for user %s started at %s with ID %s") % (
                 user.sasl_username,
                 session.start,
                 session.id
-            )
+            ))
 
diff --git a/pykolab/cli/wap/cmd_system_capabilities.py b/pykolab/cli/wap/cmd_system_capabilities.py
index 3cabdd7..6e8a06e 100644
--- a/pykolab/cli/wap/cmd_system_capabilities.py
+++ b/pykolab/cli/wap/cmd_system_capabilities.py
@@ -1,50 +1,50 @@
 # -*- 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 pykolab
 from pykolab.cli import commands
 
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('system_capabilities', execute, group='wap', description="Display the system capabilities.")
 
 def execute(*args, **kw):
     from pykolab import wap_client
     # Create the authentication object.
     # TODO: Binds with superuser credentials!
     wap_client.authenticate()
     system_capabilities = wap_client.system_capabilities()
 
     if system_capabilities['count'] < 1:
-        print "No system capabilities"
+        print("No system capabilities")
         sys.exit(1)
 
     for domain in system_capabilities['list'].keys():
-        print "Domain capabilities for %s" % (domain)
+        print("Domain capabilities for %s" % (domain))
 
         domain_capabilities = system_capabilities['list'][domain]
 
         for service in domain_capabilities['actions'].keys():
-            print "  %-15s - %r" % (service, domain_capabilities['actions'][service]['type'])
+            print("  %-15s - %r" % (service, domain_capabilities['actions'][service]['type']))
diff --git a/pykolab/cli/wap/cmd_user_types_list.py b/pykolab/cli/wap/cmd_user_types_list.py
index ee1e7f9..0136781 100644
--- a/pykolab/cli/wap/cmd_user_types_list.py
+++ b/pykolab/cli/wap/cmd_user_types_list.py
@@ -1,40 +1,40 @@
 # -*- 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.cli import commands
 
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
 conf = pykolab.getConf()
 
 def __init__():
     commands.register('list_user_types', execute, group='wap', description="List WAP user types.")
 
 def execute(*args, **kw):
     from pykolab import wap_client
     # Create the authentication object.
     # TODO: Binds with superuser credentials!
     wap_client.authenticate()
     user_types = wap_client.user_types_list()
 
     for user_type in user_types['list']:
         type = user_types['list'][user_type]
-        print "%-15s - %s" % (type['key'], type['description'])
+        print("%-15s - %s" % (type['key'], type['description']))
diff --git a/pykolab/constants.py.in b/pykolab/constants.py.in
index 9b06666..0cd1f6d 100644
--- a/pykolab/constants.py.in
+++ b/pykolab/constants.py.in
@@ -1,126 +1,126 @@
 # -*- coding: utf-8 -*-
 # Copyright 2010 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 math
 import socket
 import sys
 
 from pykolab.translate import _
 
 __license__   = "GPLv2+"
 __copyright__ = "Kolab Systems AG"
 __version__   = '@VERSION@'
 __release__   = '@RELEASE@'
 
 if @RELEASE@ < 1:
     __status__    = 'snapshot'
 elif math.floor((float)(@RELEASE@)) < (float)(@RELEASE@):
     __status__    = 'prerelease'
 else:
     __status__    = 'stable'
 
 domain = 'pykolab'
 
 epilog = _("PyKolab is a Kolab Systems product. For more information about Kolab or PyKolab, visit http://www.kolabsys.com")
 
 COMPONENTS = [
         'imap',
         'ldap',
         'mta'
     ]
 
 hostname = socket.gethostname()
 fqdn = socket.getfqdn()
 try:
     domain_parts = fqdn.split('.')
     if len(domain_parts) < 3:
-        print >> sys.stderr, _("WARNING") + ": " + _("The Fully Qualified Domain Name or FQDN for this system is incorrect. Falling back to 'localdomain'.")
+        print(_("WARNING") + ": " + _("The Fully Qualified Domain Name or FQDN for this system is incorrect. Falling back to 'localdomain'."), file=sys.stderr)
         domainname = "localdomain"
     else:
         domainname = '.'.join(domain_parts[1:])
 except IndexError:
     domainname = "localdomain"
 
 # The system RC directory
 RC_DIR = "/etc/rc.d/init.d/"
 KOLAB_LIB_PATH = '/var/lib/kolab/'
 
 # Service map;
 #
 # Convert names of registered system services to their type. For example,
 # on Red Hat, OpenLDAP is 'slapd', whereas on Debian, OpenLDAP is 'ldap'.
 #
 SERVICE_MAP = {
         'dirsrv': {
                 'type':         '389ds',
                 'description':  _('389 Directory Server or Red Hat Directory Server')
             },
         'ldap': {
                 'type':         'openldap',
                 'description':  _('OpenLDAP or compatible')
             },
         'slapd': {
                 'type':         'openldap',
                 'description':  _('OpenLDAP or compatible')
             },
     }
 
 import ldap
 LDAP_SCOPE = {
         'base': ldap.SCOPE_BASE,
         'sub': ldap.SCOPE_SUBTREE,
         'one': ldap.SCOPE_ONELEVEL
     }
 
 SUPPORTED_LDAP_CONTROLS = {
         0: {
                 'desc': 'Persistent Search Control',
                 'oid': '2.16.840.1.113730.3.4.3',
                 'func': '_persistent_search'
             },
         1: {
                 'desc': 'OpenLDAP Syncrepl (RFC4533)',
                 'oid': '1.3.6.1.4.1.4203.1.9.1.1',
                 'func': '_sync_repl'
             },
         2: {
                 'desc': 'Simple Paged Results Control',
                 'oid': '1.2.840.113556.1.4.319',
                 'func': '_paged_search'
             },
         3: {
                 'desc': 'Virtual List View Control',
                 'oid': '2.16.840.1.113730.3.4.9',
                 'func': '_vlv_search'
             },
     }
 
 #SUPPORTED_LDAP_CONTROLS = {
 #        0: {
 #                'desc': 'OpenLDAP Syncrepl (RFC4533)',
 #                'oid': '1.3.6.1.4.1.4203.1.9.1.1',
 #                'func': '_sync_repl'
 #            }
 #    }
 
 # Binay attributes which should not be stripped
 BINARY_ATTRS = (
         'objectguid',
         'objectsid'
     )
diff --git a/pykolab/imap/cyrus.py b/pykolab/imap/cyrus.py
index 15a7d00..3f4c338 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:
             log.error("Login to Cyrus IMAP server failed: %r", errmsg)
         except Exception, errmsg:
             log.exception(errmsg)
 
         self.separator = self.SEP
         try:
             self._id()
         except Exception, errmsg:
             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:
             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 >> sys.stdout, \
-                        _("Would have transferred %s from %s to %s") % (
+                    print(_("Would have transferred %s from %s to %s") % (
                                 undelete_folder,
                                 source_server,
                                 target_server
-                            )
+                            ), file=sys.stdout)
 
-                print >> sys.stdout, \
-                    _("Would have renamed %s to %s") % (
+                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/dovecot.py b/pykolab/imap/dovecot.py
index 8d5c0a5..eb854ab 100644
--- a/pykolab/imap/dovecot.py
+++ b/pykolab/imap/dovecot.py
@@ -1,634 +1,636 @@
 # -*- coding: utf-8 -*-
 # Copyright 2015 Instituto Tecnológico de Informática (http://www.iti.es)
 #
 # Sergio Talens-Oliag (ITI) <sto at iti.es>
 #
 # 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/>.
 #
 
 # -----
 # Note:
 #
 # This file is based on the original cyrus.py driver from Kolab,
 # replacing annotation related functions with metadata functions; to use it
 # on a debian installation it can be copied to the path:
 #
 #   /usr/share/pyshared/pykolab/imap/dovecot.py
 #
 # The file needs some review, as some functions have been modified to behave
 # as we want, but the real changes should be done on other places.
 #
 # As an example, with annotations you can get all existing annotations with
 # one call, but if we use metadatata we have to ask for specific variables,
 # there is no function to get all of them at once (at least on the RFC); in
 # our case when a pattern like '*' is received we look for fields of the form
 # 'vendor/kolab/folder-type', as we know they are the fields the functions we
 # are using need.
 # -----
 
+from __future__ import print_function
+
 import cyruslib
 import imaplib
 import sys
 import time
 
 from urlparse import urlparse
 
 import pykolab
 
 from pykolab.imap import IMAP
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.imap')
 conf = pykolab.getConf()
 
 # BEG: Add GETMETADATA and SETMETADATA support to the cyruslib IMAP objects
 
 Commands = {
     'GETMETADATA':   ('AUTH',),
     'SETMETADATA':   ('AUTH',),
 }
 
 imaplib.Commands.update(Commands)
 
 def imap_getmetadata(self, mailbox, pattern='*', shared=None):
     # If pattern is '*' clean pattern and search all entries under /shared
     # and/or /private (depens on the shared parameter value) to emulate the
     # ANNOTATEMORE behaviour
     if pattern == '*':
         pattern = ''
         options = '(DEPTH infinity)'
     else:
         options = '(DEPTH 0)'
     if shared == None:
         entries = '( /shared%s /private%s )' % (pattern, pattern)
     elif shared:
         entries = "/shared%s" % pattern
     else:
         entries = " /private%s" % pattern
 
     typ, dat = self._simple_command('GETMETADATA', options, mailbox, entries)
 
     return self._untagged_response(typ, dat, 'METADATA')
 
 def imap_setmetadata(self, mailbox, desc, value, shared=False):
     if value:
         value = value.join(['"', '"'])
     else:
         value = "NIL"
 
     if shared:
         typ, dat = self._simple_command('SETMETADATA', mailbox, 
                                         "(/shared%s %s)" % (desc,value))
     else:
         typ, dat = self._simple_command('SETMETADATA', mailbox,
                                         "(/private%s %s)" % (desc,value))
 
     return self._untagged_response(typ, dat, 'METADATA')
 
 # Bind the new methods to the cyruslib IMAP4 and IMAP4_SSL objects
 from types import MethodType
 cyruslib.IMAP4.getmetadata = MethodType(imap_getmetadata, None, cyruslib.IMAP4)
 cyruslib.IMAP4.setmetadata = MethodType(imap_setmetadata, None, cyruslib.IMAP4)
 cyruslib.IMAP4_SSL.getmetadata = MethodType(imap_getmetadata, None, cyruslib.IMAP4_SSL)
 cyruslib.IMAP4_SSL.setmetadata = MethodType(imap_setmetadata, None, cyruslib.IMAP4_SSL)
 
 # END: Add GETMETADATA and SETMETADATA support to the cyruslib IMAP objects
 
 # Auxiliary functions
 def _get_line_entries(lines):
     """Function to get metadata entries """
     entries   = {}
     name  = None
     value = ""
     vlen = 0
     for line in lines:
         line_len = len(line)
         i = 0
         while i < line_len:
             if name == None:
                 if line[i] == '/':
                     j = i
                     while j < line_len:
                         if line[j] == ' ':
                             break
                         j += 1
                     name = line[i:j]
                     i = j
             elif vlen != 0:
                 j = i + vlen
                 if j > line_len:
                     value += line[i:line_len]
                     vlen -= line_len - i
                 else:
                     value += line[i:i+vlen]
                     if value in ('', 'NIL'):
                         entries[name] = ""
                     else:
                         entries[name] = value
                     name  = None
                     value = ""
                     vlen  = 0
             elif line[i] == '{':
                 j = i
                 while j < line_len:
                     if line[j] == '}':
                         vlen = int(line[i+1:j])
                         break
                     j += 1
                 i = j
             elif line[i] != ' ':
                 j = i
                 if line[i] == '"':
                     while j < line_len:
                         # Skip quoted text
                         if line[j] == '\\':
                             j += 2
                             continue
                         elif line[j] == '"':
                             break
                         j += 1
                 else:
                     while j < line_len:
                         if line[j] == ' ' or line[j] == ')':
                             break
                         j += 1
                 value = line[i:j]
                 if value in ('', 'NIL'):
                     entries[name] = ""
                 else:
                     entries[name] = value
                 name = None
                 value = ""
                 i = j
             i += 1
     return entries
 
 class Dovecot(cyruslib.CYRUS):
     """
         Abstraction class for some common actions to do exclusively in
         Dovecot.
 
         Initially based on the Cyrus driver, will remove dependencies on
         cyruslib later; right now this module has only been tested to use the
         dovecot metadata support (no quota or folder operations tests have
         been performed).
 
     """
 
     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 Dovecot IMAP server %r") % (self.uri))
                 time.sleep(10)
 
         if conf.debuglevel > 8:
             self.VERBOSE = True
             self.m.debug = 5
 
         # Initialize our variables
         self.separator = self.SEP
 
         # Placeholder for known mailboxes on known servers
         self.mbox = {}
 
         # By default don't assume that we have metadata support
         self.metadata = False
 
     def __del__(self):
         pass
 
     def __verbose(self, msg):
         if self.VERBOSE:
-            print >> self.LOGFD, msg
+            print(msg, file=self.LOGFD)
 
     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 Dovecot IMAP server through cyruslib.CYRUS, but set our
             hierarchy separator.
         """
         cyruslib.CYRUS.login(self, *args, **kw)
         self.separator = self.SEP
 
         log.debug(_("Continuing with separator: %r") % (self.separator), level=8)
 
         # Check if we have metadata support or not
         self.metadata = False
         typ, dat = self.m.capability()
         for capability in tuple(dat[-1].upper().split()):
             if capability.startswith("METADATA"):
                 log.debug(_("Detected METADATA support"), level=8)
                 self.metadata = True
         if not self.metadata:
             log.debug(_("This system does not support METADATA: '%s'" % ','.join(self.m.capabilities)), level=8)
 
     def find_mailfolder_server(self, mailfolder):
         # Nothing to do in dovecot, returns the current server
         return self.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 _setquota(self, mailfolder, quota):
         # Removed server reconnection for dovecot, we only have one 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):
         # Removed server reconnection for dovecot, we only have one server
         if not partition == 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), '"%s"' % (partition))
 
 # BEG: METADATA support functions ... quite similar to annotations, really
 
     def _getmetadata(self, mailbox, pattern='*', shared=None):
         """Get Metadata"""
         # This test needs to be reviewed
         #if not self.metadata:
         #    return {}
 
         # Annotations vs. Metadata fix ... we set a pattern that we know is
         # good enough for our purposes for now, but the fact is that the
         # calling programs should be fixed instead. 
 
         res, data = self.m.getmetadata(self.decode(mailbox), pattern, shared)
 
         if (len(data) == 1) and data[0] is None:
             self.__verbose( '[GETMETADATA %s] No results' % (mailbox) )
             return {}
 
         # Get the first response line (it can be a string or a tuple)
         if isinstance(data[0], tuple):
             fline = data[0][0]
         else:
             fline = data[0]
 
         # Find the folder name 
         fbeg = 0
         fend = -1
         if fline[0] == '"':
             # Quoted name
             fbeg = 1
             i    = 1
             while i < len(fline):
                 if fline[i] == '"':
                     # folder name ended unless the previous char is \ (we
                     # should test more, this test would fail if we had a \
                     # at the end of the folder name, but we leave it at that
                     # right now
                     if fline[i-1] != '\\':
                         fend = i
                         break
                 i += 1
         else:
             # For unquoted names the first word is the folder name
             fend = fline.find(' ')
 
         # No mailbox found
         if fend < 0:
             self.__verbose( '[GETMETADATA %s] Mailbox not found in results' % (mailbox) )
             return {}
 
         # Folder name
         folder = fline[fbeg:fend]
 
         # Check mailbox name against the folder name
         if folder != mailbox:
             quoted_mailbox = "\"%s\"" % (mailbox)
             if folder != quoted_mailbox:
                 self.__verbose(
                    '[GETMETADATA %s] Mailbox \'%s\' is not the same as \'%s\'' \
                    % (mailbox, quoted_mailbox, folder)
                 )
                 return {}
 
         # Process the rest of the first line, the first value will be
         # available after the first '(' found
         i=fend
         ebeg = -1
         while i < len(fline):
             if fline[i] == '(':
                 ebeg = i+1
                 break
             i += 1
 
         if ebeg < 0:
             self.__verbose(
                 '[GETMETADATA %s] Mailbox has no values, skipping' % (mailbox)
             )
             return {}
 
         # This variable will start with an entry name and will continue with
         # the value lenght or the value
         nfline = fline[ebeg:]
         if isinstance(data[0], tuple):
             entries = _get_line_entries((nfline,) + data[0][1:])
         else:
             entries = _get_line_entries((nfline,))
 
         for line in data[1:]:
             if isinstance(line, tuple):
                 lentries = _get_line_entries(line)
             else:
                 lentries = _get_line_entries([line,])
 
             if lentries != None and lentries != {}:
                 entries.update(lentries)
 
         mdat = { mailbox: entries };
         return mdat
 
     def _setmetadata(self, mailbox, desc, value, shared=False):
         """Set METADADATA"""
         res, msg = self.m.setmetadata(self.decode(mailbox), desc, value, shared)
         self.__verbose( '[SETMETADATA %s] %s: %s' % (mailbox, res, msg[0]) )
 
     # Use metadata instead of annotations
     def _getannotation(self, *args, **kw):
         return self._getmetadata(*args, **kw)
 
     def getannotation(self, *args, **kw):
         return self._getmetadata(*args, **kw)
 
     # Use metadata instead of annotations
     def _setannotation(self, *args, **kw):
         return self._setmetadata(*args, **kw)
 
     def setannotation(self, *args, **kw):
         return self._setmetadata(*args, **kw)
 
 # END: METADATA / Annotations
 
     # The functions that follow are the same ones used with Cyrus, probably a
     # review is needed
 
     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 not to_mailfolder == 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 == None:
                 target_folder = self.separator.join([prefix,mbox])
             else:
                 target_folder = self.separator.join(target_mbox['path_parts'])
 
             if not to_mailfolder == 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 not target_mbox['domain'] == 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)
 
             if hasattr(conf,'dry_run') and not conf.dry_run:
                 if not target_server == self.server:
                     self.xfer(undelete_folder,target_server)
 
                 self.rename(undelete_folder,target_folder)
             else:
                 if not target_server == self.server:
-                    print >> sys.stdout, _("Would have transfered %s from %s to %s") % (undelete_folder, self.server, target_server)
+                    print(_("Would have transfered %s from %s to %s") % (undelete_folder, self.server, target_server), file=sys.stdout)
 
-                print >> sys.stdout, _("Would have renamed %s to %s") % (undelete_folder, target_folder)
+                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:
                 epoch = int(mbox['path_parts'][(len(mbox['path_parts'])-1)], 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 not mbox['domain'] == 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 not mbox['domain'] == None:
             deleted_folder_search = "%s@%s" % (deleted_folder_search,mbox['domain'])
 
         folders = self.lm(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 explicitely remove any virtdomain folders.
         if mbox['domain'] == None:
             _folders = []
             for folder in folders:
                 if len(folder.split('@')) < 2:
                     _folders.append(folder)
 
             folders = _folders
 
         return folders
diff --git a/pykolab/setup/components.py b/pykolab/setup/components.py
index a9adbfa..8e9f346 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'])
+                print("%-25s - %s" % (_component.replace('_','-'),__components[_component]['description']))
             else:
-                print "%-25s" % (_component.replace('_','-'))
+                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"
+            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'])
+                    print("%-4s%-21s - %s" % ('',__component.replace('_','-'),__components[_component][__component]['description']))
                 else:
-                    print "%-4s%-21s" % ('',__component.replace('_','-'))
+                    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:
             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:
             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")
+    print(_("Not yet implemented"))
     sys.exit(1)
diff --git a/pykolab/setup/setup_ldap.py b/pykolab/setup/setup_ldap.py
index 8dc6327..fb865e9 100644
--- a/pykolab/setup/setup_ldap.py
+++ b/pykolab/setup/setup_ldap.py
@@ -1,688 +1,690 @@
 # -*- 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 ldap
 import ldap.modlist
 import os
 import pwd
 import shutil
 import subprocess
 import tempfile
 import time
 
 import components
 
 import pykolab
 
 from pykolab import utils
 from pykolab.auth import Auth
 from pykolab.constants import *
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.setup')
 conf = pykolab.getConf()
 
 def __init__():
     components.register('ldap', execute, description=description())
 
 def cli_options():
     ldap_group = conf.add_cli_parser_option_group(_("LDAP Options"))
 
     ldap_group.add_option(
             "--fqdn",
             dest    = "fqdn",
             action  = "store",
             default = fqdn,
             help    = _("Specify FQDN (overriding defaults).")
         )
 
     ldap_group.add_option(
             "--allow-anonymous",
             dest    = "anonymous",
             action  = "store_true",
             default = False,
             help    = _("Allow anonymous binds (default: no).")
         )
 
     ldap_group.add_option(
             "--without-ldap",
             dest    = "without_ldap",
             action  = "store_true",
             default = False,
             help    = _("Skip setting up the LDAP server.")
         )
 
     ldap_group.add_option(
             "--with-openldap",
             dest    = "with_openldap",
             action  = "store_true",
             default = False,
             help    = _("Setup configuration for OpenLDAP compatibility.")
         )
 
     ldap_group.add_option(
             "--with-ad",
             dest    = "with_ad",
             action  = "store_true",
             default = False,
             help    = _("Setup configuration for Active Directory compatibility.")
         )
 
     ldap_group.add_option(
             "--directory-manager-pwd",
             dest    = "directory_manager_pwd",
             action  = "store",
             default = None,
             help    = _("Specify password for the Domain Manager.")
         )
 
 def description():
     return _("Setup LDAP.")
 
 def execute(*args, **kw):
     ask_questions = True
 
     if not conf.config_file == conf.defaults.config_file:
         ask_questions = False
 
     if conf.without_ldap:
-        print >> sys.stderr, _("Skipping setup of LDAP, as specified")
+        print(_("Skipping setup of LDAP, as specified"), file=sys.stderr)
         return
 
     _input = {}
 
     if conf.with_openldap and not conf.with_ad:
 
         conf.command_set('ldap', 'unique_attribute', 'entryuuid')
 
         fp = open(conf.defaults.config_file, "w+")
         conf.cfg_parser.write(fp)
         fp.close()
 
         return
 
     elif conf.with_ad and not conf.with_openldap:
         conf.command_set('ldap', 'auth_attributes', 'samaccountname')
         conf.command_set('ldap', 'modifytimestamp_format', '%%Y%%m%%d%%H%%M%%S.0Z')
         conf.command_set('ldap', 'unique_attribute', 'userprincipalname')
 
         # TODO: These attributes need to be checked
         conf.command_set('ldap', 'mail_attributes', 'mail')
         conf.command_set('ldap', 'mailserver_attributes', 'mailhost')
         conf.command_set('ldap', 'quota_attribute', 'mailquota')
 
         return
 
     elif conf.with_ad and conf.with_openldap:
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
                 _("""
                         You can not configure Kolab to run against OpenLDAP
                         and Active Directory simultaneously.
                     """)
-            )
+            ), file=sys.stderr)
 
         sys.exit(1)
 
     # Pre-execution checks
     for path, directories, files in os.walk('/etc/dirsrv/'):
         for direct in directories:
             if direct.startswith('slapd-'):
-                print >> sys.stderr, utils.multiline_message(
+                print(utils.multiline_message(
                         _("""
                                 It seems 389 Directory Server has an existing
                                 instance configured. This setup script does not
                                 intend to destroy or overwrite your data. Please
                                 make sure /etc/dirsrv/ and /var/lib/dirsrv/ are
                                 clean so that this setup does not have to worry.
                             """)
-                    )
+                    ), file=sys.stderr)
 
                 sys.exit(1)
 
     _input = {}
 
     if ask_questions:
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
                 _("""
                         Please supply a password for the LDAP administrator user
                         'admin', used to login to the graphical console of 389
                         Directory server.
                     """)
-            )
+            ), file=sys.stderr)
 
         _input['admin_pass'] = utils.ask_question(
                 _("Administrator password"),
                 default=utils.generate_password(),
                 password=True,
                 confirm=True
             )
 
         if conf.directory_manager_pwd is not None:
             _input['dirmgr_pass'] = conf.directory_manager_pwd
         else:
-            print >> sys.stderr, utils.multiline_message(
+            print(utils.multiline_message(
                 _("""
                         Please supply a password for the LDAP Directory Manager
                         user, which is the administrator user you will be using
                         to at least initially log in to the Web Admin, and that
                         Kolab uses to perform administrative tasks.
                     """)
-            )
+            ), file=sys.stderr)
 
             _input['dirmgr_pass'] = utils.ask_question(
                 _("Directory Manager password"),
                 default=utils.generate_password(),
                 password=True,
                 confirm=True
             )
 
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
                 _("""
                         Please choose the system user and group the service
                         should use to run under. These should be existing,
                         unprivileged, local system POSIX accounts with no shell.
                     """)
-            )
+            ), file=sys.stderr)
 
         try:
             pw = pwd.getpwnam("dirsrv")
         except:
             _input['userid'] = utils.ask_question(_("User"), default="nobody")
             _input['group'] = utils.ask_question(_("Group"), default="nobody")
         else:
             _input['userid'] = utils.ask_question(_("User"), default="dirsrv")
             _input['group'] = utils.ask_question(_("Group"), default="dirsrv")
 
     else:
         _input['admin_pass'] = conf.get('ldap', 'bind_pw')
         _input['dirmgr_pass'] = conf.get('ldap', 'bind_pw')
         try:
             pw = pwd.getpwnam("dirsrv")
         except:
             _input['userid'] = "nobody"
             _input['group'] = "nobody"
         else:
             _input['userid'] = "dirsrv"
             _input['group'] = "dirsrv"
 
     # TODO: Verify the user and group exist.
 
     # TODO: This takes the system fqdn, domainname and hostname, rather then
     # the desired fqdn, domainname and hostname.
     #
     # TODO^2: This should be confirmed.
 
     if conf.fqdn:
         _input['fqdn'] = conf.fqdn
         _input['hostname'] = conf.fqdn.split('.')[0]
         _input['domain'] = '.'.join(conf.fqdn.split('.')[1:])
     else:
         _input['fqdn'] = fqdn
         _input['hostname'] = hostname.split('.')[0]
         _input['domain'] = domainname
     _input['nodotdomain'] = _input['domain'].replace('.','_')
 
     _input['rootdn'] = utils.standard_root_dn(_input['domain'])
 
     if ask_questions:
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
                 _("""
                         This setup procedure plans to set up Kolab Groupware for
                         the following domain name space. This domain name is
                         obtained from the reverse DNS entry on your network
                         interface. Please confirm this is the appropriate domain
                         name space.
                     """)
-            )
+            ), file=sys.stderr)
 
         answer = utils.ask_confirmation("%s" % (_input['domain']))
 
         if not answer:
             positive_answer = False
             while not positive_answer:
                 _input['domain'] = utils.ask_question(_("Domain name to use"))
                 if not _input['domain'] == None and not _input['domain'] == "":
                     positive_answer = True
                 else:
-                    print >> sys.stderr, utils.multiline_message(
+                    print(utils.multiline_message(
                             _("""
                                     Invalid input. Please try again.
                                 """)
-                        )
+                        ), file=sys.stderr)
 
         _input['nodotdomain'] = _input['domain'].replace('.','_')
         _input['rootdn'] = utils.standard_root_dn(_input['domain'])
 
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
                 _("""
                         The standard root dn we composed for you follows. Please
                         confirm this is the root dn you wish to use.
                     """)
-            )
+            ), file=sys.stderr)
 
         answer = utils.ask_confirmation("%s" % (_input['rootdn']))
 
         if not answer:
             positive_answer = False
             while not positive_answer:
                 _input['rootdn'] = utils.ask_question(_("Root DN to use"))
                 if not _input['rootdn'] == None and not _input['rootdn'] == "":
                     positive_answer = True
                 else:
-                    print >> sys.stderr, utils.multiline_message(
+                    print(utils.multiline_message(
                             _("""
                                     Invalid input. Please try again.
                                 """)
-                        )
+                        ), file=sys.stderr)
 
     # TODO: Loudly complain if the fqdn does not resolve back to this system.
 
     data = """
 [General]
 FullMachineName = %(fqdn)s
 SuiteSpotUserID = %(userid)s
 SuiteSpotGroup = %(group)s
 AdminDomain = %(domain)s
 ConfigDirectoryLdapURL = ldap://%(fqdn)s:389/o=NetscapeRoot
 ConfigDirectoryAdminID = admin
 ConfigDirectoryAdminPwd = %(admin_pass)s
 
 [slapd]
 SlapdConfigForMC = Yes
 UseExistingMC = 0
 ServerPort = 389
 ServerIdentifier = %(hostname)s
 Suffix = %(rootdn)s
 RootDN = cn=Directory Manager
 RootDNPwd = %(dirmgr_pass)s
 ds_bename = %(nodotdomain)s
 AddSampleEntries = No
 
 [admin]
 Port = 9830
 ServerAdminID = admin
 ServerAdminPwd = %(admin_pass)s
 """ % (_input)
 
     (fp, filename) = tempfile.mkstemp(dir="/tmp/")
     os.write(fp, data)
     os.close(fp)
 
     if os.path.isfile("/usr/sbin/setup-ds-admin.pl"):
         setup_ds_admin = "/usr/sbin/setup-ds-admin.pl"
     elif os.path.isfile("/usr/sbin/setup-ds-admin"):
         setup_ds_admin = "/usr/sbin/setup-ds-admin"
     elif os.path.isfile("/usr/sbin/setup-ds.pl"):
         setup_ds_admin = "/usr/sbin/setup-ds.pl"
     elif os.path.isfile("/usr/sbin/setup-ds"):
         setup_ds_admin = "/usr/sbin/setup-ds"
     else:
         log.error(_("No directory server setup tool available."))
         sys.exit(1)
 
     command = [
             setup_ds_admin,
             '--debug',
             '--silent',
             '--force',
             '--file=%s' % (filename)
         ]
 
-    print >> sys.stderr, utils.multiline_message(
+    print(utils.multiline_message(
             _("""
                     Setup is now going to set up the 389 Directory Server. This
                     may take a little while (during which period there is no
                     output and no progress indication).
                 """)
-        )
+        ), file=sys.stderr)
 
     log.info(_("Setting up 389 Directory Server"))
 
     setup_389 = subprocess.Popen(
             command,
             stdout=subprocess.PIPE,
             stderr=subprocess.PIPE
         )
 
     (stdoutdata, stderrdata) = setup_389.communicate()
 
     if not setup_389.returncode == 0:
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
                 _("""
                         An error was detected in the setup procedure for 389
                         Directory Server. This setup will write out stderr and
                         stdout to /var/log/kolab/setup.error.log and
                         /var/log/kolab/setup.out.log respectively, before it
                         exits.
                     """)
-            )
+            ), file=sys.stderr)
 
         fp = open('/var/log/kolab/setup.error.log', 'w')
         fp.write(stderrdata)
         fp.close()
 
         fp = open('/var/log/kolab/setup.out.log', 'w')
         fp.write(stdoutdata)
         fp.close()
 
     log.debug(_("Setup DS stdout:"), level=8)
     log.debug(stdoutdata, level=8)
 
     log.debug(_("Setup DS stderr:"), level=8)
     log.debug(stderrdata, level=8)
 
     if not setup_389.returncode == 0:
         sys.exit(1)
 
     # Find the kolab schema. It's installed as %doc in the kolab-schema package.
     # TODO: Chown nobody, nobody, chmod 440
     schema_file = None
     for root, directories, filenames in os.walk('/usr/share/doc/'):
         for filename in filenames:
             if filename.startswith('kolab') and filename.endswith('.ldif') and schema_file == None:
                 schema_file = os.path.join(root,filename)
 
     if not schema_file == None:
         try:
             shutil.copy(
                     schema_file,
                     '/etc/dirsrv/slapd-%s/schema/99%s' % (
                             _input['hostname'],
                             os.path.basename(schema_file)
                         )
                 )
 
             schema_error = False
         except:
             log.error(_("Could not copy the LDAP extensions for Kolab"))
             schema_error = True
     else:
         log.error(_("Could not find the ldap Kolab schema file"))
         schema_error = True
 
     if os.path.isfile('/bin/systemctl'):
         subprocess.call(['/bin/systemctl', 'restart', 'dirsrv.target'])
         subprocess.call(['/bin/systemctl', 'restart', 'dirsrv@' + _input['hostname']])
         time.sleep(20)
     elif os.path.isfile('/sbin/service'):
         subprocess.call(['/sbin/service', 'dirsrv', 'restart'])
     elif os.path.isfile('/usr/sbin/service'):
         subprocess.call(['/usr/sbin/service','dirsrv','stop'])
         time.sleep(20)
         subprocess.call(['/usr/sbin/service','dirsrv','start'])
     else:
         log.error(_("Could not start the directory server service."))
 
     if os.path.isfile('/bin/systemctl'):
         subprocess.call(['/bin/systemctl', 'enable', 'dirsrv.target'])
         subprocess.call(['/bin/systemctl', 'enable', 'dirsrv@' + _input['hostname']])
     elif os.path.isfile('/sbin/chkconfig'):
         subprocess.call(['/sbin/chkconfig', 'dirsrv', 'on'])
     elif os.path.isfile('/usr/sbin/update-rc.d'):
         subprocess.call(['/usr/sbin/update-rc.d', 'dirsrv', 'defaults'])
     else:
         log.error(_("Could not configure to start on boot, the " + \
                 "directory server service."))
 
     if ask_questions:
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
                 _("""
                         Please supply a Cyrus Administrator password. This
                         password is used by Kolab to execute administrative
                         tasks in Cyrus IMAP. You may also need the password
                         yourself to troubleshoot Cyrus IMAP and/or perform
                         other administrative tasks against Cyrus IMAP directly.
                     """)
-            )
+            ), file=sys.stderr)
 
         _input['cyrus_admin_pass'] = utils.ask_question(
                 _("Cyrus Administrator password"),
                 default=utils.generate_password(),
                 password=True,
                 confirm=True
             )
 
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
                 _("""
                         Please supply a Kolab Service account password. This
                         account is used by various services such as Postfix,
                         and Roundcube, as anonymous binds to the LDAP server
                         will not be allowed.
                     """)
-            )
+            ), file=sys.stderr)
 
         _input['kolab_service_pass'] = utils.ask_question(
                 _("Kolab Service password"),
                 default=utils.generate_password(),
                 password=True,
                 confirm=True
             )
 
     else:
         _input['cyrus_admin_pass'] = conf.get('cyrus-imap', 'admin_password')
         _input['kolab_service_pass'] = conf.get('ldap', 'service_bind_pw')
 
     log.info(_("Writing out configuration to kolab.conf"))
 
     # Write out kolab configuration
     conf.command_set('kolab', 'primary_domain', _input['domain'])
     conf.command_set('ldap', 'base_dn', _input['rootdn'])
     conf.command_set('ldap', 'bind_dn', 'cn=Directory Manager')
     conf.command_set('ldap', 'bind_pw', _input['dirmgr_pass'])
     conf.command_set('ldap', 'service_bind_dn', 'uid=kolab-service,ou=Special Users,%s' % (_input['rootdn']))
     conf.command_set('ldap', 'service_bind_pw', _input['kolab_service_pass'])
 
     fp = open(conf.defaults.config_file, "w+")
     conf.cfg_parser.write(fp)
     fp.close()
 
     log.info(_("Inserting service users into LDAP."))
 
     # Insert service users
     auth = Auth(_input['domain'])
     auth.connect()
     auth._auth.connect()
     auth._auth._bind(bind_dn='cn=Directory Manager', bind_pw=_input['dirmgr_pass'])
 
     dn = 'uid=%s,ou=Special Users,%s' % (conf.get('cyrus-imap', 'admin_login'), _input['rootdn'])
 
     # A dict to help build the "body" of the object
     attrs = {}
     attrs['objectclass'] = ['top','person','inetorgperson','organizationalperson']
     attrs['uid'] = conf.get('cyrus-imap', 'admin_login')
     attrs['givenname'] = "Cyrus"
     attrs['surname'] = "Administrator"
     attrs['cn'] = "Cyrus Administrator"
     attrs['userPassword'] = _input['cyrus_admin_pass']
 
     # Convert our dict to nice syntax for the add-function using modlist-module
     ldif = ldap.modlist.addModlist(attrs)
 
     # Do the actual synchronous add-operation to the ldapserver
     auth._auth.ldap.add_s(dn, ldif)
 
     conf.command_set('cyrus-imap', 'admin_password', _input['cyrus_admin_pass'])
 
     dn = 'uid=kolab-service,ou=Special Users,%s' % (_input['rootdn'])
 
     # A dict to help build the "body" of the object
     attrs = {}
     attrs['objectclass'] = ['top','person','inetorgperson','organizationalperson']
     attrs['uid'] = "kolab-service"
     attrs['givenname'] = "Kolab"
     attrs['surname'] = "Service"
     attrs['cn'] = "Kolab Service"
     attrs['userPassword'] = _input['kolab_service_pass']
     attrs['nslookthroughlimit'] = '-1'
     attrs['nssizelimit'] = '-1'
     attrs['nstimelimit'] = '-1'
     attrs['nsidletimeout'] = '-1'
 
     # Convert our dict to nice syntax for the add-function using modlist-module
     ldif = ldap.modlist.addModlist(attrs)
 
     # Do the actual synchronous add-operation to the ldapserver
     auth._auth.ldap.add_s(dn, ldif)
 
     dn = 'ou=Resources,%s' % (_input['rootdn'])
 
     # A dict to help build the "body" of the object
     attrs = {}
     attrs['objectclass'] = ['top','organizationalunit']
     attrs['ou'] = "Resources"
 
     # Convert our dict to nice syntax for the add-function using modlist-module
     ldif = ldap.modlist.addModlist(attrs)
 
     # Do the actual synchronous add-operation to the ldapserver
     auth._auth.ldap.add_s(dn, ldif)
 
     dn = 'ou=Shared Folders,%s' % (_input['rootdn'])
 
     # A dict to help build the "body" of the object
     attrs = {}
     attrs['objectclass'] = ['top','organizationalunit']
     attrs['ou'] = "Shared Folders"
 
     # Convert our dict to nice syntax for the add-function using modlist-module
     ldif = ldap.modlist.addModlist(attrs)
 
     # Do the actual synchronous add-operation to the ldapserver
     auth._auth.ldap.add_s(dn, ldif)
 
     log.info(_("Writing out cn=kolab,cn=config"))
 
     dn = 'cn=kolab,cn=config'
 
     # A dict to help build the "body" of the object
     attrs = {}
     attrs['objectclass'] = ['top','extensibleobject']
     attrs['cn'] = "kolab"
     attrs['aci'] = '(targetattr = "*") (version 3.0;acl "Kolab Services";allow (read,compare,search)(userdn = "ldap:///uid=kolab-service,ou=Special Users,%s");)' % (_input['rootdn'])
 
     # Convert our dict to nice syntax for the add-function using modlist-module
     ldif = ldap.modlist.addModlist(attrs)
 
     # Do the actual synchronous add-operation to the ldapserver
     auth._auth.ldap.add_s(dn, ldif)
 
     log.info(_("Adding domain %s to list of domains for this deployment") % (_input['domain']))
     dn = "associateddomain=%s,cn=kolab,cn=config" % (_input['domain'])
     attrs = {}
     attrs['objectclass'] = ['top','domainrelatedobject']
     attrs['associateddomain'] = [
             '%s' % (_input['domain']),
             '%s' % (_input['fqdn']),
             'localhost.localdomain',
             'localhost'
         ]
 
     # De-duplicate attribute values before attempting to insert the object (#2205)
     attrs['associateddomain'] = list(set(attrs['associateddomain']))
     attrs['associateddomain'].pop(attrs['associateddomain'].index(_input['domain']))
     attrs['associateddomain'] = [ _input['domain'] ] + attrs['associateddomain']
 
     attrs['aci'] = '(targetattr = "*") (version 3.0;acl "Read Access for %(domain)s Users";allow (read,compare,search)(userdn = "ldap:///%(rootdn)s??sub?(objectclass=*)");)' % (_input)
 
     # Add inetdomainbasedn in case the configured root dn is not the same as the
     # standard root dn for the domain name configured
     if not _input['rootdn'] == utils.standard_root_dn(_input['domain']):
         attrs['objectclass'].append('inetdomain')
         attrs['inetdomainbasedn'] = _input['rootdn']
 
     ldif = ldap.modlist.addModlist(attrs)
     auth._auth.ldap.add_s(dn, ldif)
 
     if not conf.anonymous:
         log.info(_("Disabling anonymous binds"))
         dn = "cn=config"
         modlist = []
         modlist.append((ldap.MOD_REPLACE, "nsslapd-allow-anonymous-access", "off"))
         auth._auth.ldap.modify_s(dn, modlist)
 
     # TODO: Ensure the uid attribute is unique
     # TODO^2: Consider renaming the general "attribute uniqueness to "uid attribute uniqueness"
     log.info(_("Enabling attribute uniqueness plugin"))
     dn = "cn=attribute uniqueness,cn=plugins,cn=config"
     modlist = []
     modlist.append((ldap.MOD_REPLACE, "nsslapd-pluginEnabled", "on"))
     auth._auth.ldap.modify_s(dn, modlist)
 
     log.info(_("Enabling referential integrity plugin"))
     dn = "cn=referential integrity postoperation,cn=plugins,cn=config"
     modlist = []
     modlist.append((ldap.MOD_REPLACE, "nsslapd-pluginEnabled", "on"))
     auth._auth.ldap.modify_s(dn, modlist)
 
     log.info(_("Enabling and configuring account policy plugin"))
     dn = "cn=Account Policy Plugin,cn=plugins,cn=config"
     modlist = []
     modlist.append((ldap.MOD_REPLACE, "nsslapd-pluginEnabled", "on"))
     modlist.append((ldap.MOD_ADD, "nsslapd-pluginarg0", "cn=config,cn=Account Policy Plugin,cn=plugins,cn=config"))
     auth._auth.ldap.modify_s(dn, modlist)
 
     dn = "cn=config,cn=Account Policy Plugin,cn=plugins,cn=config"
     modlist = []
     modlist.append((ldap.MOD_REPLACE, "alwaysrecordlogin", "yes"))
     modlist.append((ldap.MOD_ADD, "stateattrname", "lastLoginTime"))
     modlist.append((ldap.MOD_ADD, "altstateattrname", "createTimestamp"))
     auth._auth.ldap.modify_s(dn, modlist)
 
     # Add kolab-admin role
     log.info(_("Adding the kolab-admin role"))
     dn = "cn=kolab-admin,%s" % (_input['rootdn'])
     attrs = {}
     attrs['description'] = "Kolab Administrator"
     attrs['objectClass'] = ['top','ldapsubentry','nsroledefinition','nssimpleroledefinition','nsmanagedroledefinition']
     attrs['cn'] = "kolab-admin"
     ldif = ldap.modlist.addModlist(attrs)
 
     auth._auth.ldap.add_s(dn, ldif)
 
     # User writeable attributes on root_dn
     log.info(_("Setting access control to %s") % (_input['rootdn']))
     dn = _input['rootdn']
     aci = []
 
     if schema_error:
         aci.append('(targetattr = "carLicense || description || displayName || facsimileTelephoneNumber || homePhone || homePostalAddress || initials || jpegPhoto || l || labeledURI || mobile || o || pager || photo || postOfficeBox || postalAddress || postalCode || preferredDeliveryMethod || preferredLanguage || registeredAddress || roomNumber || secretary || seeAlso || st || street || telephoneNumber || telexNumber || title || userCertificate || userPassword || userSMIMECertificate || x500UniqueIdentifier") (version 3.0; acl "Enable self write for common attributes"; allow (read,compare,search,write)(userdn = "ldap:///self");)')
     else:
         aci.append('(targetattr = "carLicense || description || displayName || facsimileTelephoneNumber || homePhone || homePostalAddress || initials || jpegPhoto || l || labeledURI || mobile || o || pager || photo || postOfficeBox || postalAddress || postalCode || preferredDeliveryMethod || preferredLanguage || registeredAddress || roomNumber || secretary || seeAlso || st || street || telephoneNumber || telexNumber || title || userCertificate || userPassword || userSMIMECertificate || x500UniqueIdentifier || kolabDelegate || kolabInvitationPolicy || kolabAllowSMTPSender") (version 3.0; acl "Enable self write for common attributes"; allow (read,compare,search,write)(userdn = "ldap:///self");)')
 
     aci.append('(targetattr = "*") (version 3.0;acl "Directory Administrators Group";allow (all)(groupdn = "ldap:///cn=Directory Administrators,%(rootdn)s" or roledn = "ldap:///cn=kolab-admin,%(rootdn)s");)' % (_input))
     aci.append('(targetattr="*")(version 3.0; acl "Configuration Administrators Group"; allow (all) groupdn="ldap:///cn=Configuration Administrators,ou=Groups,ou=TopologyManagement,o=NetscapeRoot";)')
     aci.append('(targetattr="*")(version 3.0; acl "Configuration Administrator"; allow (all) userdn="ldap:///uid=admin,ou=Administrators,ou=TopologyManagement,o=NetscapeRoot";)')
     aci.append('(targetattr = "*")(version 3.0; acl "SIE Group"; allow (all) groupdn = "ldap:///cn=slapd-%(hostname)s,cn=389 Directory Server,cn=Server Group,cn=%(fqdn)s,ou=%(domain)s,o=NetscapeRoot";)' % (_input))
     aci.append('(targetattr != "userPassword") (version 3.0;acl "Search Access";allow (read,compare,search)(userdn = "ldap:///all");)')
     modlist = []
     modlist.append((ldap.MOD_REPLACE, "aci", aci))
     auth._auth.ldap.modify_s(dn, modlist)
 
     if os.path.isfile('/bin/systemctl'):
         if not os.path.isfile('/usr/lib/systemd/system/dirsrv-admin.service'):
             log.info(_("directory server admin service not available"))
         else:
             subprocess.call(['/bin/systemctl', 'enable', 'dirsrv-admin.service'])
     elif os.path.isfile('/sbin/chkconfig'):
         subprocess.call(['/sbin/chkconfig', 'dirsrv-admin', 'on'])
     elif os.path.isfile('/usr/sbin/update-rc.d'):
         subprocess.call(['/usr/sbin/update-rc.d', 'dirsrv-admin', 'defaults'])
     else:
         log.error(_("Could not start and configure to start on boot, the " + \
                 "directory server admin service."))
diff --git a/pykolab/setup/setup_mysql.py b/pykolab/setup/setup_mysql.py
index 73c2245..335c353 100644
--- a/pykolab/setup/setup_mysql.py
+++ b/pykolab/setup/setup_mysql.py
@@ -1,341 +1,343 @@
 # -*- 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 os
 import subprocess
 import tempfile
 import time
 
 import components
 
 import pykolab
 
 from pykolab import utils
 from pykolab.constants import *
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.setup')
 conf = pykolab.getConf()
 
 
 def __init__():
     components.register('mysql', execute, description=description())
 
 
 def cli_options():
     mysql_group = conf.add_cli_parser_option_group(_("MySQL Options"))
 
     mysql_group.add_option(
         "--mysqlserver",
         dest="mysqlserver",
         action="store",
         help=_("Specify whether to use an (existing), (unix_socket) or (new) MySQL server.")
     )
 
     mysql_group.add_option(
         "--mysqlhost",
         dest="mysqlhost",
         action="store",
         default='127.0.0.1',
         help=_("The MySQL host address.")
     )
 
     mysql_group.add_option(
         "--mysqlrootpw",
         dest="mysqlrootpw",
         action="store",
         help=_("The MySQL root user password.")
     )
 
 
 def description():
     return _("Setup MySQL.")
 
 
 def execute(*args, **kw):  # noqa: C901
 
     socket_paths = [
         "/var/lib/mysql/mysql.sock",
         "/var/run/mysqld/mysqld.sock",
         "/var/run/mysql/mysql.sock",
         "/var/run/mysqld/mysqld.pid"
     ]
 
     # on CentOS7, there is MariaDB instead of MySQL
     if conf.mysqlserver != 'existing':
         mysqlservice = 'mysqld.service'
         if os.path.isfile('/usr/lib/systemd/system/mariadb.service'):
             mysqlservice = 'mariadb.service'
         elif os.path.isfile('/usr/lib/systemd/system/mysql.service'):
             mysqlservice = 'mysql.service'
         if not os.path.isfile('/usr/lib/systemd/system/' + mysqlservice):
             # on Debian Jessie, systemctl restart mysql
             mysqlservice = 'mysql'
 
         if os.path.isfile('/bin/systemctl'):
             subprocess.call(['/bin/systemctl', 'restart', mysqlservice])
         elif os.path.isfile('/sbin/service'):
             subprocess.call(['/sbin/service', 'mysqld', 'restart'])
         elif os.path.isfile('/usr/sbin/service'):
             subprocess.call(['/usr/sbin/service', 'mysql', 'restart'])
         else:
             log.error(_("Could not start the MySQL database service."))
 
         if os.path.isfile('/bin/systemctl'):
             subprocess.call(['/bin/systemctl', 'enable', mysqlservice])
         elif os.path.isfile('/sbin/chkconfig'):
             subprocess.call(['/sbin/chkconfig', 'mysqld', 'on'])
         elif os.path.isfile('/usr/sbin/update-rc.d'):
             subprocess.call(['/usr/sbin/update-rc.d', 'mysql', 'defaults'])
         else:
             log.error(
                 _("Could not configure to start on boot, the MySQL database service.")
             )
 
         log.info(_("Waiting for at most 30 seconds for MySQL/MariaDB to settle..."))
         max_wait = 30
         while max_wait > 0:
             for socket_path in socket_paths:
                 if os.path.exists(socket_path):
                     max_wait = 0
 
             if max_wait > 0:
                 max_wait = max_wait - 1
                 time.sleep(1)
 
     options = {
         1: "Existing MySQL server (with root password already set).",
         2: "Existing MySQL server (with unix_socket authentication plugin).",
         3: "New MySQL server (needs to be initialized)."
     }
 
     answer = 0
     if conf.mysqlserver != 'existing':
         if len([x for x in socket_paths if os.path.exists(x)]) > 0:
             if conf.mysqlserver:
                 if conf.mysqlserver == 'existing':
                     answer = 1
                 elif conf.mysqlserver == 'unix_socket':
                     answer = 2
                 elif conf.mysqlserver == 'new':
                     answer = 3
             if answer == 0:
                 answer = utils.ask_menu(_("What MySQL server are we setting up?"), options)
     else:
         answer = 1
 
     if answer == "1" or answer == 1:
         if not conf.mysqlrootpw:
-            print >> sys.stderr, utils.multiline_message(
+            print(utils.multiline_message(
                 _("""
                     Please supply the root password for MySQL, so we can set
                     up user accounts for other components that use MySQL.
                 """)
-            )
+            ), file=sys.stderr)
 
             mysql_root_password = utils.ask_question(
                 _("MySQL root password"),
                 password=True
             )
 
         else:
             mysql_root_password = conf.mysqlrootpw
 
     elif answer == "2" or answer == 2:
         mysql_root_password = 'unix_socket'
 
     else:
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
             _("""
                 Please supply a root password for MySQL. This password
                 will be the administrative user for this MySQL server,
                 and it should be kept a secret. After this setup process
                 has completed, Kolab is going to discard and forget
                 about this password, but you will need it for
                 administrative tasks in MySQL.
             """)
-        )
+        ), file=sys.stderr)
 
         mysql_root_password = utils.ask_question(
             _("MySQL root password"),
             default=utils.generate_password(),
             password=True,
             confirm=True
         )
 
         p1 = subprocess.Popen(
             [
                 'echo',
                 'UPDATE mysql.user SET Password=PASSWORD(\'%s\') WHERE User=\'root\';' % (
                     mysql_root_password
                 )
             ],
             stdout=subprocess.PIPE
         )
 
         p2 = subprocess.Popen(['mysql'], stdin=p1.stdout)
         p1.stdout.close()
         p2.communicate()
 
         p1 = subprocess.Popen(
             [
                 'echo',
                 "UPDATE mysql.user SET authentication_string=PASSWORD('%s') WHERE User='root';" % (
                     mysql_root_password
                 )
             ],
             stdout=subprocess.PIPE
         )
 
         p2 = subprocess.Popen(['mysql'], stdin=p1.stdout)
         p1.stdout.close()
         p2.communicate()
 
         p1 = subprocess.Popen(
             [
                 'echo',
                 """
                     UPDATE mysql.user
                         SET plugin='mysql_native_password'
                         WHERE User='root' AND plugin='auth_socket';
                 """
             ],
             stdout=subprocess.PIPE
         )
 
         p2 = subprocess.Popen(['mysql'], stdin=p1.stdout)
         p1.stdout.close()
         p2.communicate()
 
         p1 = subprocess.Popen(['echo', 'FLUSH PRIVILEGES;'], stdout=subprocess.PIPE)
         p2 = subprocess.Popen(['mysql'], stdin=p1.stdout)
         p1.stdout.close()
         p2.communicate()
 
     socket_path = None
     socket_paths = [
         "/var/lib/mysql/mysql.sock",
         "/var/run/mysqld/mysqld.sock",
         "/var/run/mysql/mysql.sock"
     ]
     for sp in socket_paths:
         if os.path.exists(sp):
             socket_path = sp
 
     if mysql_root_password == "unix_socket" and socket_path is not None:
         data = """
 [mysql]
 user=root
 password=
 host=localhost
 socket=%s
 """ % (socket_path)
     else:
         data = """
 [mysql]
 user=root
 password='%s'
 host=%s
 """ % (mysql_root_password, conf.mysqlhost)
 
 
     fp = open('/tmp/kolab-setup-my.cnf', 'w')
     os.chmod('/tmp/kolab-setup-my.cnf', 0o600)
     fp.write(data)
     fp.close()
 
     schema_file = None
     for root, directories, filenames in os.walk('/usr/share/doc/'):
         for filename in filenames:
             if filename.startswith('kolab_wap') and filename.endswith('.sql'):
                 # Skip the Oracle file
                 if filename.endswith('oracle.sql'):
                     continue
 
                 schema_file = os.path.join(root, filename)
 
     if schema_file is not None:
         p1 = subprocess.Popen(['echo', 'create database kolab;'], stdout=subprocess.PIPE)
         p2 = subprocess.Popen(['mysql', '--defaults-file=/tmp/kolab-setup-my.cnf'], stdin=p1.stdout)
         p1.stdout.close()
         p2.communicate()
 
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
             _("""
                 Please supply a password for the MySQL user 'kolab'.
                 This password will be used by Kolab services, such as
                 the Web Administration Panel.
             """)
-        )
+        ), file=sys.stderr)
 
         mysql_kolab_password = utils.ask_question(
             _("MySQL kolab password"),
             default=utils.generate_password(),
             password=True,
             confirm=True
         )
 
         p1 = subprocess.Popen(
             [
                 'echo',
                 "GRANT ALL PRIVILEGES ON kolab.* TO 'kolab'@'localhost' IDENTIFIED BY '%s';" % (
                     mysql_kolab_password
                 )
             ],
             stdout=subprocess.PIPE
         )
 
         p2 = subprocess.Popen(
             [
                 'mysql',
                 '--defaults-file=/tmp/kolab-setup-my.cnf'
             ],
             stdin=p1.stdout
         )
 
         p1.stdout.close()
         p2.communicate()
 
         p1 = subprocess.Popen(['cat', schema_file], stdout=subprocess.PIPE)
         p2 = subprocess.Popen(
             [
                 'mysql',
                 '--defaults-file=/tmp/kolab-setup-my.cnf',
                 'kolab'
             ],
             stdin=p1.stdout
         )
 
         p1.stdout.close()
         p2.communicate()
 
         conf.command_set(
             'kolab_wap',
             'sql_uri',
             'mysql://kolab:%s@localhost/kolab' % (mysql_kolab_password)
         )
 
         conf.command_set(
             'kolab_smtp_access_policy',
             'cache_uri',
             'mysql://kolab:%s@localhost/kolab' % (mysql_kolab_password)
         )
 
     else:
         log.warning(_("Could not find the MySQL Kolab schema file"))
diff --git a/pykolab/setup/setup_php.py b/pykolab/setup/setup_php.py
index f27dd3f..cbb93c0 100644
--- a/pykolab/setup/setup_php.py
+++ b/pykolab/setup/setup_php.py
@@ -1,138 +1,139 @@
 # -*- 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
 from augeas import Augeas
 import os
 import shutil
 import subprocess
 import tempfile
 
 import components
 
 import pykolab
 
 from pykolab import utils
 from pykolab.auth import Auth
 from pykolab.constants import *
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.setup')
 conf = pykolab.getConf()
 
 
 def __init__():
     components.register('php', execute, description=description())
 
 
 def cli_options():
     php_group = conf.add_cli_parser_option_group(_("PHP Options"))
 
     php_group.add_option(
         "--timezone",
         dest="timezone",
         action="store",
         default=None,
         help=_("Specify the timezone for PHP.")
     )
 
     php_group.add_option(
         "--with-php-ini",
         dest="php_ini_path",
         action="store",
         default=None,
         help=_("Specify the path to the php.ini file used with the webserver.")
     )
 
 
 def description():
     return _("Setup PHP.")
 
 
 def execute(*args, **kw):
     if conf.timezone is None:
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
             _("""
                 Please supply the timezone PHP should be using.
                 You have to use a Continent or Country / City locality name
                 like 'Europe/Berlin', but not just 'CEST'.
             """)
-        )
+        ), file=sys.stderr)
 
         conf.timezone = utils.ask_question(
             _("Timezone ID"),
             default="UTC"
         )
 
     if conf.php_ini_path is not None:
         if not os.path.isfile(conf.php_ini_path):
             log.error(
                 _("Cannot configure PHP through %r (No such file or directory)") % (
                     conf.php_ini_path
                 )
             )
 
             return
 
         php_ini = conf.php_ini_path
 
     else:
         # Search and destroy
         php_ini = "/etc/php.ini"
 
         if not os.path.isfile(php_ini):
             php_ini = "/etc/php/7.2/apache2/php.ini"
 
         if not os.path.isfile(php_ini):
             php_ini = "/etc/php/7.0/apache2/php.ini"
 
         if not os.path.isfile(php_ini):
             php_ini = "/etc/php5/apache2/php.ini"
 
         if not os.path.isfile(php_ini):
             log.error(_("Could not find PHP configuration file php.ini"))
             return
 
     try:
         myaugeas = Augeas()
 
         setting_base = '/files%s/' % (php_ini)
 
         setting = os.path.join(setting_base, 'Date', 'date.timezone')
         current_value = myaugeas.get(setting)
 
         if current_value is None:
             insert_paths = myaugeas.match('/files%s/Date/*' % (php_ini))
             insert_path = insert_paths[(len(insert_paths) - 1)]
             myaugeas.insert(insert_path, 'date.timezone', False)
 
         log.debug(_("Setting key %r to %r") % ('Date/date.timezone', conf.timezone), level=8)
         myaugeas.set(setting, conf.timezone)
 
         myaugeas.save()
     except IndexError:
         subprocess.Popen(
             [
                 'sed',
                 '-i',
                 '-r',
                 '-e',
                 r's|^(;*)date\.timezone.*$|date.timezone = %s|g' % (conf.timezone),
                 php_ini
             ]
         )
diff --git a/pykolab/setup/setup_roundcube.py b/pykolab/setup/setup_roundcube.py
index 869533f..b5567be 100644
--- a/pykolab/setup/setup_roundcube.py
+++ b/pykolab/setup/setup_roundcube.py
@@ -1,373 +1,375 @@
 # -*- 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 codecs
 import grp
 import hashlib
 import os
 import random
 import re
 import subprocess
 import sys
 import time
 
 from Cheetah.Template import Template
 
 import components
 
 import pykolab
 
 from pykolab import utils
 from pykolab.constants import *
 from pykolab.translate import _
 
 # pylint: disable=invalid-name
 log = pykolab.getLogger('pykolab.setup')
 conf = pykolab.getConf()
 
 
 def __init__():
     components.register('roundcube', execute, description=description(), after=['mysql', 'ldap'])
 
 
 def description():
     return _("Setup Roundcube.")
 
 
 def execute(*args, **kw):
-    print >> sys.stderr, utils.multiline_message(
+    print(utils.multiline_message(
         """
             Please supply a password for the MySQL user 'roundcube'.
             This password will be used by the Roundcube webmail
             interface.
         """
-    )
+    ), file=sys.stderr)
 
     mysql_roundcube_password = utils.ask_question(
         "MySQL roundcube password",
         default=utils.generate_password(),
         password=True,
         confirm=True
     )
 
     conf.mysql_roundcube_password = mysql_roundcube_password
 
     rc_settings = {
         'des_key': re.sub(
             r'[^a-zA-Z0-9]',
             "",
             "%s%s" % (
                 hashlib.md5("%s" % random.random()).digest().encode("base64"),
                 hashlib.md5("%s" % random.random()).digest().encode("base64")
             )
         )[:24],
 
         'imap_admin_login': conf.get('cyrus-imap', 'admin_login'),
         'imap_admin_password': conf.get('cyrus-imap', 'admin_password'),
         'ldap_base_dn': conf.get('ldap', 'base_dn'),
         'ldap_group_base_dn': conf.get('ldap', 'group_base_dn'),
         'ldap_group_filter': conf.get('ldap', 'group_filter'),
         'ldap_ldap_uri': conf.get('ldap', 'ldap_uri'),
         'ldap_resource_base_dn': conf.get('ldap', 'resource_base_dn'),
         'ldap_resource_filter': conf.get('ldap', 'resource_filter'),
         'ldap_service_bind_dn': conf.get('ldap', 'service_bind_dn'),
         'ldap_service_bind_pw': conf.get('ldap', 'service_bind_pw'),
         'ldap_user_base_dn': conf.get('ldap', 'user_base_dn'),
         'ldap_user_filter': conf.get('ldap', 'user_filter'),
         'primary_domain': conf.get('kolab', 'primary_domain'),
         'mysql_uri': 'mysqli://roundcube:%s@localhost/roundcube' % (mysql_roundcube_password),
         'conf': conf
     }
 
     rc_paths = [
         "/usr/share/roundcubemail/",
         "/usr/share/roundcube/",
         "/srv/www/roundcubemail/",
         "/var/www/roundcubemail/"
     ]
 
     rcpath = ''
     for rc_path in rc_paths:
         if os.path.isdir(rc_path):
             rcpath = rc_path
             break
 
     if not os.path.isdir(rcpath):
         log.error("Roundcube installation path not found.")
         return
 
     if os.access(rcpath + 'skins/kolab/', os.R_OK):
         rc_settings['skin'] = 'kolab'
     elif os.access(rcpath + 'skins/enterprise/', os.R_OK):
         rc_settings['skin'] = 'enterprise'
     elif os.access(rcpath + 'skins/chameleon/', os.R_OK):
         rc_settings['skin'] = 'chameleon'
     else:
         rc_settings['skin'] = 'larry'
 
     want_files = [
         'acl.inc.php',
         'calendar.inc.php',
         'config.inc.php',
         'kolab_addressbook.inc.php',
         'kolab_auth.inc.php',
         'kolab_delegation.inc.php',
         'kolab_files.inc.php',
         'kolab_folders.inc.php',
         'libkolab.inc.php',
         'managesieve.inc.php',
         'password.inc.php',
         'recipient_to_contact.inc.php',
         'terms.html',
         'terms.inc.php'
     ]
 
     for want_file in want_files:
         template_file = None
         if os.path.isfile('/etc/kolab/templates/roundcubemail/%s.tpl' % (want_file)):
             template_file = '/etc/kolab/templates/roundcubemail/%s.tpl' % (want_file)
         elif os.path.isfile('/usr/share/kolab/templates/roundcubemail/%s.tpl' % (want_file)):
             template_file = '/usr/share/kolab/templates/roundcubemail/%s.tpl' % (want_file)
 
         if template_file is not None:
             # pylint: disable=logging-not-lazy
             log.debug("Using template file %r" % (template_file), level=8)
             filep = codecs.open(template_file, 'r', encoding='utf-8')
             template_definition = filep.read()
             filep.close()
 
             t = Template(template_definition, searchList=[rc_settings])
             # pylint: disable=logging-not-lazy
             log.debug(
                 "Successfully compiled template %r, writing out to %r" % (
                     template_file,
                     want_file
                 ),
                 level=8
             )
 
             filep = None
             if os.path.isdir('/etc/roundcubemail'):
                 filep = codecs.open('/etc/roundcubemail/%s' % (want_file), 'w', encoding='utf-8')
             elif os.path.isdir('/etc/roundcube'):
                 filep = codecs.open('/etc/roundcube/%s' % (want_file), 'w', encoding='utf-8')
 
             if filep is not None:
                 filep.write(t.respond())
                 filep.close()
 
     schema_files = []
 
     # pylint: disable=too-many-nested-blocks
     for root, directories, filenames in os.walk('/usr/share/doc/'):
         directories.sort()
         for directory in directories:
             if directory.startswith("roundcubemail"):
                 for _root, _directories, _filenames in os.walk(os.path.join(root, directory)):
                     for filename in _filenames:
                         if filename.startswith('mysql.initial') and filename.endswith('.sql'):
                             schema_filepath = os.path.join(_root, filename)
                             if schema_filepath not in schema_files:
                                 schema_files.append(schema_filepath)
 
                 if schema_files:
                     break
 
         if schema_files:
             break
 
     for root, directories, filenames in os.walk(rcpath + 'plugins/calendar/drivers/kolab/'):
         for filename in filenames:
             if filename.startswith('mysql') and filename.endswith('.sql'):
                 schema_filepath = os.path.join(root, filename)
                 if schema_filepath not in schema_files:
                     schema_files.append(schema_filepath)
 
     for root, directories, filenames in os.walk(rcpath + 'plugins/libkolab/'):
         for filename in filenames:
             if filename.startswith('mysql') and filename.endswith('.sql'):
                 schema_filepath = os.path.join(root, filename)
                 if schema_filepath not in schema_files:
                     schema_files.append(schema_filepath)
 
     for root, directories, filenames in os.walk('/usr/share/doc/'):
         directories.sort()
         for directory in directories:
             if directory.startswith("chwala"):
                 for _root, _directories, _filenames in os.walk(os.path.join(root, directory)):
                     for filename in _filenames:
                         if filename.startswith('mysql.initial') and filename.endswith('.sql'):
                             schema_filepath = os.path.join(_root, filename)
                             if schema_filepath not in schema_files:
                                 schema_files.append(schema_filepath)
 
                 if len(schema_files) > 0:
                     break
         if len(schema_files) > 0:
             break
 
     if not os.path.isfile('/tmp/kolab-setup-my.cnf'):
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
                 """Please supply the MySQL root password (use 'unix_socket' for socket based authentication)"""
-            )
+            ), file=sys.stderr)
 
         mysql_root_password = utils.ask_question(
                 _("MySQL root password"),
                 password=True
             )
 
         socket_path = None
         socket_paths = [
             "/var/lib/mysql/mysql.sock",
             "/var/run/mysqld/mysqld.sock",
             "/var/run/mysql/mysql.sock"
         ]
         for sp in socket_paths:
             if os.path.exists(sp):
                 socket_path = sp
 
         if mysql_root_password == "unix_socket" and socket_path is not None:
             data = """
 [mysql]
 user=root
 password=
 host=localhost
 socket=%s
 """ % (socket_path)
         else:
             data = """
 [mysql]
 user=root
 password='%s'
 host=%s
 """ % (mysql_root_password, conf.mysqlhost)
 
         fp = open('/tmp/kolab-setup-my.cnf', 'w')
         os.chmod('/tmp/kolab-setup-my.cnf', 0o600)
         fp.write(data)
         fp.close()
 
     p1 = subprocess.Popen(['echo', 'create database roundcube;'], stdout=subprocess.PIPE)
     p2 = subprocess.Popen(['mysql', '--defaults-file=/tmp/kolab-setup-my.cnf'], stdin=p1.stdout)
     p1.stdout.close()
     p2.communicate()
 
     p1 = subprocess.Popen(
         [
             'echo',
             'GRANT ALL PRIVILEGES ON roundcube.* TO \'roundcube\'@\'localhost\' IDENTIFIED BY \'%s\';' % (
                 mysql_roundcube_password
             )
         ],
         stdout=subprocess.PIPE
     )
     p2 = subprocess.Popen(['mysql', '--defaults-file=/tmp/kolab-setup-my.cnf'], stdin=p1.stdout)
     p1.stdout.close()
     p2.communicate()
 
     for schema_file in schema_files:
         p1 = subprocess.Popen(['cat', schema_file], stdout=subprocess.PIPE)
         p2 = subprocess.Popen(
             [
                 'mysql',
                 '--defaults-file=/tmp/kolab-setup-my.cnf',
                 'roundcube'
             ],
             stdin=p1.stdout
         )
 
         p1.stdout.close()
         p2.communicate()
 
     p1 = subprocess.Popen(['echo', 'FLUSH PRIVILEGES;'], stdout=subprocess.PIPE)
     p2 = subprocess.Popen(['mysql', '--defaults-file=/tmp/kolab-setup-my.cnf'], stdin=p1.stdout)
     p1.stdout.close()
     p2.communicate()
 
     time.sleep(2)
 
     # Find Roundcube configuration that is not readable by the
     # webserver user/group.
     if os.path.isdir('/etc/roundcubemail/'):
         rccpath = "/etc/roundcubemail/"
     elif os.path.isdir('/etc/roundcube/'):
         rccpath = "/etc/roundcube"
     else:
         log.warning("Cannot find the configuration directory for roundcube.")
         rccpath = None
 
     root_uid = 0
 
     webserver_gid = None
 
     for webserver_group in ['apache', 'www-data', 'www']:
         try:
             # pylint: disable=unused-variable
             (a, b, webserver_gid, d) = grp.getgrnam(webserver_group)
             break
 
         # pylint: disable=broad-except
         except Exception:
             pass
 
     if webserver_gid is not None:
         if rccpath is not None:
             for root, directories, filenames in os.walk(rccpath):
                 for filename in filenames:
                     try:
                         os.chown(
                             os.path.join(root, filename),
                             root_uid,
                             webserver_gid
                         )
 
                     # pylint: disable=broad-except
                     except Exception:
                         pass
 
     httpservice = 'httpd.service'
 
     if os.path.isfile('/usr/lib/systemd/system/apache2.service'):
         httpservice = 'apache2.service'
 
     if os.path.isfile('/lib/systemd/system/apache2.service'): # Debian 9
         httpservice = 'apache2.service'
 
     if os.path.isdir('/lib/systemd/system/apache2.service.d'):
         httpservice = 'apache2.service'
 
     if os.path.isfile('/bin/systemctl'):
         subprocess.call(['/bin/systemctl', 'restart', httpservice])
     elif os.path.isfile('/sbin/service'):
         subprocess.call(['/sbin/service', 'httpd', 'restart'])
     elif os.path.isfile('/usr/sbin/service'):
         subprocess.call(['/usr/sbin/service', 'apache2', 'restart'])
     else:
         log.error("Could not start the webserver server service.")
 
     if os.path.isfile('/bin/systemctl'):
         subprocess.call(['/bin/systemctl', 'enable', httpservice])
     elif os.path.isfile('/sbin/chkconfig'):
         subprocess.call(['/sbin/chkconfig', 'httpd', 'on'])
     elif os.path.isfile('/usr/sbin/update-rc.d'):
         subprocess.call(['/usr/sbin/update-rc.d', 'apache2', 'defaults'])
     else:
         log.error(
             "Could not configure to start on boot, the webserver server service."
         )
diff --git a/pykolab/setup/setup_syncroton.py b/pykolab/setup/setup_syncroton.py
index 1cccd0e..8ad2d8c 100644
--- a/pykolab/setup/setup_syncroton.py
+++ b/pykolab/setup/setup_syncroton.py
@@ -1,153 +1,155 @@
 # -*- 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 os
 import subprocess
 import sys
 import time
 
 import components
 
 import pykolab
 
 from pykolab import utils
 from pykolab.constants import *
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.setup')
 conf = pykolab.getConf()
 
 
 def __init__():
     components.register(
         'syncroton',
         execute,
         description=description(),
         after=['mysql','ldap','roundcube']
     )
 
 
 def description():
     return _("Setup Syncroton.")
 
 
 def execute(*args, **kw):
     schema_files = []
     for root, directories, filenames in os.walk('/usr/share/doc/'):
         for directory in directories:
             if directory.startswith("kolab-syncroton"):
                 for root, directories, filenames in os.walk(os.path.join(root, directory)):
                     for filename in filenames:
                         if filename.startswith('mysql.initial') and filename.endswith('.sql'):
                             schema_filepath = os.path.join(root,filename)
                             if not schema_filepath in schema_files:
                                 schema_files.append(schema_filepath)
                                 break
 
                 if len(schema_files) > 0:
                     break
         if len(schema_files) > 0:
             break
 
     if not os.path.isfile('/tmp/kolab-setup-my.cnf'):
-        print >> sys.stderr, utils.multiline_message(
+        print(utils.multiline_message(
                 """Please supply the MySQL root password (use 'unix_socket' for socket based authentication)"""
-            )
+            ), file=sys.stderr)
 
         mysql_root_password = utils.ask_question(
                 _("MySQL root password"),
                 password=True
             )
 
         socket_path = None
         socket_paths = [
             "/var/lib/mysql/mysql.sock",
             "/var/run/mysqld/mysqld.sock",
             "/var/run/mysql/mysql.sock"
         ]
         for sp in socket_paths:
             if os.path.exists(sp):
                 socket_path = sp
 
         if mysql_root_password == "unix_socket" and socket_path is not None:
             data = """
 [mysql]
 user=root
 password=
 host=localhost
 socket=%s
 """ % (socket_path)
         else:
             data = """
 [mysql]
 user=root
 password='%s'
 host=%s
 """ % (mysql_root_password, conf.mysqlhost)
 
         data = """
 [mysql]
 user=root
 password='%s'
 host=%s
 """ % (mysql_root_password, conf.mysqlhost)
 
         fp = open('/tmp/kolab-setup-my.cnf', 'w')
         os.chmod('/tmp/kolab-setup-my.cnf', 0o600)
         fp.write(data)
         fp.close()
 
     for schema_file in schema_files:
         p1 = subprocess.Popen(['cat', schema_file], stdout=subprocess.PIPE)
         p2 = subprocess.Popen(['mysql', '--defaults-file=/tmp/kolab-setup-my.cnf', 'roundcube'], stdin=p1.stdout)
         p1.stdout.close()
         p2.communicate()
 
     time.sleep(2)
 
     httpservice = 'httpd.service'
 
     if os.path.isfile('/usr/lib/systemd/system/apache2.service'):
         httpservice = 'apache2.service'
 
     if os.path.isfile('/lib/systemd/system/apache2.service'): # Debian 9
         httpservice = 'apache2.service'
 
     if os.path.isdir('/lib/systemd/system/apache2.service.d'):
         httpservice = 'apache2.service'
 
     if os.path.isfile('/bin/systemctl'):
         subprocess.call(['/bin/systemctl', 'restart', httpservice])
     elif os.path.isfile('/sbin/service'):
         subprocess.call(['/sbin/service', 'httpd', 'restart'])
     elif os.path.isfile('/usr/sbin/service'):
         subprocess.call(['/usr/sbin/service','apache2','restart'])
     else:
         log.error(_("Could not start the webserver server service."))
 
     if os.path.isfile('/bin/systemctl'):
         subprocess.call(['/bin/systemctl', 'enable', httpservice])
     elif os.path.isfile('/sbin/chkconfig'):
         subprocess.call(['/sbin/chkconfig', 'httpd', 'on'])
     elif os.path.isfile('/usr/sbin/update-rc.d'):
         subprocess.call(['/usr/sbin/update-rc.d', 'apache2', 'defaults'])
     else:
         log.error(_("Could not configure to start on boot, the " + \
                 "webserver server service."))
 
diff --git a/pykolab/wap_client/__init__.py b/pykolab/wap_client/__init__.py
index 900e280..82f7ebb 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'])
+                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"
+        print("Automatically selected the only group type available")
         group_type_id = group_types.keys()[0]
 
     else:
-        print "No group types available"
+        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"
+        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 ""
+        print("")
         for key in user_types['list'].keys():
             if not key == "status":
-                print "%s) %s" % (key,user_types['list'][key]['name'])
+                print("%s) %s" % (key,user_types['list'][key]['name']))
 
-        print ""
+        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"
+        print("Automatically selected the only user type available")
         user_type_id = user_types['list'].keys()[0]
 
     else:
-        print "No user types available"
+        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"
+        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
+    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:
         # 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/saslauthd.py b/saslauthd.py
index 9f0b349..6e39ed0 100755
--- a/saslauthd.py
+++ b/saslauthd.py
@@ -1,41 +1,43 @@
 #!/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.extend(['.', '..'])
 
 from pykolab.translate import _
 
 try:
     import pykolab.logger
 except ImportError as e:
-    print >> sys.stderr, _("Cannot load pykolab/logger.py:")
-    print >> sys.stderr, "%s" % e
+    print(_("Cannot load pykolab/logger.py:"), file=sys.stderr)
+    print("%s" % e, file=sys.stderr)
     sys.exit(1)
 
 import saslauthd
 
 if __name__ == "__main__":
     saslauthd = saslauthd.SASLAuthDaemon()
     saslauthd.run()
diff --git a/saslauthd/__init__.py b/saslauthd/__init__.py
index e11296b..6d89929 100644
--- a/saslauthd/__init__.py
+++ b/saslauthd/__init__.py
@@ -1,378 +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:
             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:
             exitcode = e
         except KeyboardInterrupt:
             exitcode = 1
             log.info(_("Interrupted by user"))
         except AttributeError, e:
             exitcode = 1
             traceback.print_exc()
-            print >> sys.stderr, _("Traceback occurred, please report a " +
-                                   "bug at https://issues.kolab.org")
+            print(_("Traceback occurred, please report a " +
+                                   "bug at https://issues.kolab.org"),
+                  file=sys.stderr)
         except TypeError, e:
             exitcode = 1
             traceback.print_exc()
             log.error(_("Type Error: %s") % e)
         except:
             exitcode = 2
             traceback.print_exc()
-            print >> sys.stderr, _("Traceback occurred, please report a " +
-                                   "bug at https://issues.kolab.org")
+            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:
                     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:
                 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 >> sys.stderr, _("Group %s does not exist") % (
+                        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 >> sys.stderr, _("User %s does not exist") % (
+                        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 914be99..ef6fcb7 100755
--- a/setup-kolab.py
+++ b/setup-kolab.py
@@ -1,43 +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:
-    print >> sys.stderr, _("Cannot load pykolab/constants.py:")
-    print >> sys.stderr, "%s" % 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/tests/functional/test_kolabd/test_001_user_sync.py b/tests/functional/test_kolabd/test_001_user_sync.py
index e8c804d..8b61893 100644
--- a/tests/functional/test_kolabd/test_001_user_sync.py
+++ b/tests/functional/test_kolabd/test_001_user_sync.py
@@ -1,140 +1,140 @@
 import time
 import unittest
 
 import pykolab
 from pykolab import wap_client
 from pykolab.auth import Auth
 from pykolab.imap import IMAP
 
 conf = pykolab.getConf()
 
 
 class TestKolabDaemon(unittest.TestCase):
     @classmethod
     def setup_class(self, *args, **kw):
         from tests.functional.purge_users import purge_users
         purge_users()
 
         self.user = {
                 'local': 'john.doe',
                 'domain': 'example.org'
             }
 
         from tests.functional.user_add import user_add
         user_add("John", "Doe")
 
     @classmethod
     def teardown_class(self, *args, **kw):
         from tests.functional.purge_users import purge_users
         purge_users()
 
     def test_001_user_recipient_policy(self):
         auth = Auth()
         auth.connect()
         recipient = auth.find_recipient("%(local)s@%(domain)s" % (self.user))
         if hasattr(self, 'assertIsInstance'):
             self.assertIsInstance(recipient, str)
 
         self.assertEqual(recipient, "uid=doe,ou=People,dc=example,dc=org")
 
         result = wap_client.user_info(recipient)
 
         self.assertEqual(result['mail'], 'john.doe@example.org')
         self.assertEqual(result['alias'], ['doe@example.org', 'j.doe@example.org'])
 
     def test_002_user_recipient_policy_duplicate(self):
         from tests.functional.user_add import user_add
         user = {
                 'local': 'jane.doe',
                 'domain': 'example.org'
             }
         user_add("Jane", "Doe")
 
         time.sleep(3)
 
         auth = Auth()
         auth.connect()
         recipient = auth.find_recipient("%(local)s@%(domain)s" % (user))
         if hasattr(self, 'assertIsInstance'):
             self.assertIsInstance(recipient, str)
 
         self.assertEqual(recipient, "uid=doe2,ou=People,dc=example,dc=org")
 
         result = wap_client.user_info(recipient)
 
         if 'mailhost' not in result:
             from tests.functional.synchronize import synchronize_once
             synchronize_once()
 
         result = wap_client.user_info(recipient)
 
         self.assertEqual(result['mail'], 'jane.doe@example.org')
         self.assertEqual(result['alias'], ['doe2@example.org', 'j.doe2@example.org'])
 
     def test_003_user_mailbox_created(self):
         time.sleep(2)
         imap = IMAP()
         imap.connect()
 
         folders = imap.lm('user/%(local)s@%(domain)s' % (self.user))
         self.assertEqual(len(folders), 1)
 
     def test_004_user_additional_folders_created(self):
         time.sleep(2)
         imap = IMAP()
         imap.connect()
 
         ac_folders = conf.get_raw('kolab', 'autocreate_folders')
         exec("ac_folders = %s" % (ac_folders))
 
         folders = imap.lm('user/%(local)s/*@%(domain)s' % (self.user))
 
         self.assertEqual(len(folders), len(ac_folders.keys()))
 
     def test_005_user_folders_metadata_set(self):
         imap = IMAP()
         imap.connect()
 
         ac_folders = conf.get_raw('kolab', 'autocreate_folders')
         exec("ac_folders = %s" % (ac_folders))
 
         folders = []
         folders.extend(imap.lm('user/%(local)s@%(domain)s' % (self.user)))
         folders.extend(imap.lm('user/%(local)s/*@%(domain)s' % (self.user)))
 
         for folder in folders:
             metadata = imap.get_metadata(folder)
-            print metadata
+            print(metadata)
 
             folder_name = '/'.join(folder.split('/')[2:]).split('@')[0]
             if folder_name in ac_folders:
                 if 'annotations' in ac_folders[folder_name]:
                     for _annotation in ac_folders[folder_name]['annotations'].keys():
                         if _annotation.startswith('/private'):
                             continue
 
                         _annotation_value = ac_folders[folder_name]['annotations'][_annotation]
                         self.assertTrue(_annotation in metadata[metadata.keys().pop()])
                         self.assertEqual(_annotation_value, metadata[metadata.keys().pop()][_annotation])
 
     def test_006_user_subscriptions(self):
         imap = IMAP()
         imap.connect(login=False)
         login = conf.get('cyrus-imap', 'admin_login')
         password = conf.get('cyrus-imap', 'admin_password')
         imap.login_plain(login, password, 'john.doe@example.org')
 
         folders = imap.lm()
         self.assertTrue("INBOX" in folders)
 
         folders = imap.imap.lsub()
         self.assertTrue("Calendar" in folders)
 
     def test_011_resource_add(self):
         pass
 
     def test_012_resource_mailbox_created(self):
         pass
 
     def test_013_resource_mailbox_annotation(self):
         pass
diff --git a/tests/functional/test_kolabd/test_002_user_rename.py b/tests/functional/test_kolabd/test_002_user_rename.py
index 3ac4a02..aaea7e2 100644
--- a/tests/functional/test_kolabd/test_002_user_rename.py
+++ b/tests/functional/test_kolabd/test_002_user_rename.py
@@ -1,80 +1,80 @@
 import time
 import unittest
 
 import pykolab
 from pykolab import wap_client
 from pykolab.auth import Auth
 from pykolab.imap import IMAP
 
 conf = pykolab.getConf()
 
 
 class TestKolabDaemon(unittest.TestCase):
     @classmethod
     def setup_class(self, *args, **kw):
         from tests.functional.purge_users import purge_users
         purge_users()
 
         self.user = {
                 'local': 'john.doe',
                 'domain': 'example.org'
             }
 
         from tests.functional.user_add import user_add
         user_add("John", "Doe")
         time.sleep(2)
 
     @classmethod
     def teardown_class(self, *args, **kw):
         from tests.functional.purge_users import purge_users
         purge_users()
 
     def test_001_user_rename(self):
         """
             Rename user "Doe, John" to "Sixpack, Joe" and verify the recipient
             policy is applied, and the IMAP INBOX folder for the user is
             renamed.
         """
         auth = Auth()
         auth.connect()
         recipient = auth.find_recipient('john.doe@example.org')
         user_info = wap_client.user_info(recipient)
 
         if 'mailhost' not in user_info:
             from tests.functional.synchronize import synchronize_once
             synchronize_once()
 
         imap = IMAP()
         imap.connect()
         folders = imap.lm('user/john.doe@example.org')
         self.assertEqual(len(folders), 1)
 
         auth = Auth()
         auth.connect()
         recipient = auth.find_recipient("%(local)s@%(domain)s" % (self.user))
 
         user_info = wap_client.user_info(recipient)
         user_info['sn'] = 'Sixpack'
         user_info['givenname'] = 'Joe'
         user_info['uid'] = 'sixpack'
         user_edit = wap_client.user_edit(recipient, user_info)
 
         time.sleep(2)
 
-        print imap.lm()
+        print(imap.lm())
 
         user_info = wap_client.user_info('uid=sixpack,ou=People,dc=example,dc=org')
         if not user_info['mail'] == 'joe.sixpack@example.org':
             from tests.functional.synchronize import synchronize_once
             synchronize_once()
             user_info = wap_client.user_info('uid=sixpack,ou=People,dc=example,dc=org')
 
         self.assertEqual(user_info['mail'], 'joe.sixpack@example.org')
 
-        print imap.lm()
+        print(imap.lm())
 
         folders = imap.lm('user/john.doe@example.org')
         self.assertEqual(len(folders), 0, "INBOX for john.doe still exists")
 
         folders = imap.lm('user/joe.sixpack@example.org')
         self.assertEqual(len(folders), 1, "INBOX for joe.sixpack does not exist")
diff --git a/tests/functional/test_wallace/test_002_footer.py b/tests/functional/test_wallace/test_002_footer.py
index d6e5836..5da044a 100644
--- a/tests/functional/test_wallace/test_002_footer.py
+++ b/tests/functional/test_wallace/test_002_footer.py
@@ -1,294 +1,294 @@
 from email import message_from_string
 from email.MIMEMultipart import MIMEMultipart
 from email.MIMEBase import MIMEBase
 from email.MIMEImage import MIMEImage
 from email.MIMEText import MIMEText
 from email.Utils import COMMASPACE, formatdate
 from email import Encoders
 import os
 import smtplib
 import time
 import unittest
 
 import pykolab
 from pykolab import wap_client
 from pykolab.auth import Auth
 from pykolab.imap import IMAP
 
 conf = pykolab.getConf()
 
 
 class TestWallaceFooter(unittest.TestCase):
     user = None
 
     @classmethod
     def setUp(self):
         """ Compatibility for twisted.trial.unittest
         """
         if not self.user:
             self.setup_class()
 
     @classmethod
     def setup_class(self, *args, **kw):
         from tests.functional.purge_users import purge_users
         purge_users()
 
         self.user = {
                 'local': 'john.doe',
                 'domain': 'example.org'
             }
 
         self.footer = {}
 
         footer_html_file = conf.get('wallace', 'footer_html')
         footer_text_file = conf.get('wallace', 'footer_text')
 
         if os.path.isfile(footer_text_file):
             self.footer['plain'] = open(footer_text_file, 'r').read()
 
         if not os.path.isfile(footer_html_file):
             self.footer['html'] = '<p>' + self.footer['plain'] + '</p>'
         else:
             self.footer['html'] = open(footer_html_file, 'r').read()
 
         self.send_to = 'john.doe@example.org'
         self.send_from = 'john.doe@example.org'
 
         self.message_to = '"Doe, John" <%s>' % (self.send_to)
         self.message_from = '"Doe, John" <%s>' % (self.send_from)
 
         from tests.functional.user_add import user_add
         user_add("John", "Doe")
         time.sleep(2)
         from tests.functional.synchronize import synchronize_once
         synchronize_once()
 
     @classmethod
     def teardown_class(self, *args, **kw):
         from tests.functional.purge_users import purge_users
         purge_users()
 
     def check_message_delivered(self, subject):
         imap = IMAP()
         imap.connect()
         imap.set_acl("user/john.doe@example.org", "cyrus-admin", "lrs")
         imap.imap.m.select("user/john.doe@example.org")
 
         found = False
         max_tries = 20
 
         while not found and max_tries > 0:
             max_tries -= 1
 
             typ, data = imap.imap.m.search(None, 'ALL')
             for num in data[0].split():
                 typ, msg = imap.imap.m.fetch(num, '(RFC822)')
                 _msg = message_from_string(msg[0][1])
                 if _msg['Subject'] == subject:
                     found = True
 
             time.sleep(1)
 
         return found
 
     def html_attachment(self):
         html_body = "<html><body><p>This is an HTML attachment</p></body></html>"
         html_part = MIMEBase("text", "html")
         html_part.add_header("Content-Disposition", "attachment", filename="html_attachment.html")
         html_part.set_payload(html_body)
         return html_part
 
     def image_attachment(self):
         image_file = '/usr/share/kolab-webadmin/public_html/skins/default/images/logo_kolab.png'
         image_part = MIMEImage(open(image_file, 'r').read())
         image_part.add_header("Content-Disposition", "attachment", filename=os.path.basename(image_file))
         return image_part
 
     def message_standard_params(self, subject, msg):
         msg['From'] = self.message_from
         msg['To'] = self.message_to
 
         msg['Subject'] = subject
         msg['Date'] = formatdate(localtime=True)
 
         return msg
 
     def send_message(self, msg, _to=None, _from=None):
         smtp = smtplib.SMTP('localhost', 10026)
 
         if _to is None:
             _to = self.send_to
 
         if _from is None:
             _from = self.send_from
 
         smtp.sendmail(_from, _to, msg.as_string())
 
     def test_001_inbox_created(self):
         imap = IMAP()
         imap.connect()
 
         folders = imap.lm('user/%(local)s@%(domain)s' % (self.user))
         self.assertEqual(len(folders), 1)
 
     def test_002_send_plaintext(self):
         subject = "test_002_send_plaintext"
         body = "This is a test message"
         msg = MIMEBase("text", "plain")
         msg = self.message_standard_params(subject, msg)
 
         msg.set_payload(body)
 
         self.send_message(msg)
 
         if not self.check_message_delivered(subject):
             raise Exception
 
     def test_003_send_plaintext_with_attachment(self):
         subject = "test_003_send_plaintext_with_attachment"
         body = "This is a test message"
         msg = MIMEMultipart()
         msg = self.message_standard_params(subject, msg)
 
         msg.attach(MIMEText(body))
         msg.attach(self.image_attachment())
 
         self.send_message(msg)
 
         if not self.check_message_delivered(subject):
             raise Exception
 
     def test_004_send_html(self):
         subject = "test_004_send_html"
         body = "<html><body><p>This is a test message</p></body></html>"
         msg = MIMEBase("text", "html")
         msg = self.message_standard_params(subject, msg)
         msg.set_payload(body)
 
         self.send_message(msg)
 
         if not self.check_message_delivered(subject):
             raise Exception
 
     def test_005_send_html_with_plaintext_alternative(self):
         subject = "test_005_send_html_with_plaintext_alternative"
         html_body = "<html><body><p>This is the HTML part</p></body></html>"
         plain_body = "This is the plaintext part"
 
         msg = MIMEMultipart("alternative")
         msg = self.message_standard_params(subject, msg)
 
         html_part = MIMEBase("text", "html")
         html_part.set_payload(html_body)
         msg.attach(html_part)
 
         plain_part = MIMEText(plain_body)
         msg.attach(plain_part)
 
         self.send_message(msg)
 
         if not self.check_message_delivered(subject):
             raise Exception
 
     def test_006_send_html_with_attachment(self):
         subject = "test_006_send_html_with_attachment"
         html_body = "<html><body><p>This is the HTML part</p></body></html>"
         plain_body = "This is the plaintext part"
 
         msg = MIMEMultipart()
         msg = self.message_standard_params(subject, msg)
 
         html_part = MIMEBase("text", "html")
         html_part.set_payload(html_body)
         msg.attach(html_part)
 
         msg.attach(self.image_attachment())
 
         self.send_message(msg)
 
         if not self.check_message_delivered(subject):
             raise Exception
 
     def test_007_send_html_with_plaintext_alternative_and_attachment(self):
         subject = "test_007_send_html_with_plaintext_alternative_and_attachment"
         html_body = "<html><body><p>This is the HTML part</p></body></html>"
         plain_body = "This is the plaintext part"
 
         msg = MIMEMultipart("mixed")
         msg = self.message_standard_params(subject, msg)
 
         message_part = MIMEMultipart("alternative")
 
         html_part = MIMEBase("text", "html")
         html_part.set_payload(html_body)
         message_part.attach(html_part)
 
         plain_part = MIMEText(plain_body)
         message_part.attach(plain_part)
 
         msg.attach(message_part)
 
         msg.attach(self.image_attachment())
 
         self.send_message(msg)
 
         if not self.check_message_delivered(subject):
             raise Exception
 
     def test_008_send_plaintext_with_html_attachment(self):
         subject = "test_008_send_plaintext_with_html_attachment"
         body = "This is a plaintext message"
         msg = MIMEMultipart()
         msg = self.message_standard_params(subject, msg)
 
         msg.attach(MIMEText(body))
 
         msg.attach(self.html_attachment())
 
         self.send_message(msg)
 
         if not self.check_message_delivered(subject):
             raise Exception
 
     def test_009_send_plaintext_forwarded(self):
         subject = "test_009_send_plaintext_forwarded"
         body = "This is a plaintext message"
 
         from tests.functional.user_add import user_add
         user_add("Jane", "Doe")
 
         from tests.functional.synchronize import synchronize_once
         synchronize_once()
 
         admin_login = conf.get('cyrus-imap', 'admin_login')
         admin_password = conf.get('cyrus-imap', 'admin_password')
 
         import sievelib.factory
         script = sievelib.factory.FiltersSet("test_wallace_test_009_forward")
         script.require("copy")
         script.addfilter("forward", ["true"], [("redirect", ":copy", "john.doe@example.org")])
 
         import sievelib.managesieve
         sieveclient = sievelib.managesieve.Client('localhost', 4190, True)
         sieveclient.connect(None, None, True)
         sieveclient._plain_authentication(admin_login, admin_password, 'jane.doe@example.org')
         sieveclient.authenticated = True
 
         script_str = script.__str__()
 
-        print script_str
+        print(script_str)
 
         sieveclient.putscript("test_wallace_test_009_forward", script_str)
 
         sieveclient.setactive("test_wallace_test_009_forward")
 
         msg = MIMEText(body)
         msg['From'] = self.message_from
         msg['To'] = '"Doe, Jane" <jane.doe@example.org>'
 
         msg['Subject'] = subject
         msg['Date'] = formatdate(localtime=True)
 
         self.send_message(msg, _to='jane.doe@example.org', _from='john.doe@example.org')
 
         raise Exception
diff --git a/tests/functional/test_wallace/test_006_resource_performance.py b/tests/functional/test_wallace/test_006_resource_performance.py
index 814a111..39162c4 100644
--- a/tests/functional/test_wallace/test_006_resource_performance.py
+++ b/tests/functional/test_wallace/test_006_resource_performance.py
@@ -1,142 +1,142 @@
 import time
 import datetime
 import pykolab
 import pytz
 import uuid
 import string
 import random
 
 from pykolab.xml import Event
 from pykolab.xml import Attendee
 from pykolab.imap import IMAP
 
 from wallace import module_resources
 from twisted.trial import unittest
 
 from tests.functional import resource_func as funcs
 from tests.functional.synchronize import synchronize_once
 
 conf = pykolab.getConf()
 
 
 class TestWallacePerformance(unittest.TestCase):
 
     rooms = None
 
     @classmethod
     def setUp(self):
         """ Compatibility for twisted.trial.unittest
         """
         if not self.rooms:
             self.setup_class()
 
     @classmethod
     def setup_class(self, *args, **kw):
         funcs.purge_resources()
         self.room1 = funcs.resource_add("confroom", "Room 101")
         self.room2 = funcs.resource_add("confroom", "Conference Room B-222")
         self.rooms = funcs.resource_add("collection", "Rooms", [self.room1['dn'], self.room2['dn']])
 
         time.sleep(1)
         synchronize_once()
 
         module_resources.imap = IMAP()
         module_resources.imap.connect()
 
     def purge_mailbox(self, mailbox):
         imap = IMAP()
         imap.connect()
         imap.set_acl(mailbox, "cyrus-admin", "lrwcdest")
         imap.imap.m.select(imap.folder_quote(mailbox))
 
         typ, data = imap.imap.m.search(None, 'ALL')
         for num in data[0].split():
             imap.imap.m.store(num, '+FLAGS', '\\Deleted')
 
         imap.imap.m.expunge()
         imap.disconnect()
 
     def populate_calendar(self, resource, num=10, date=None):
         if date is None:
             date = datetime.datetime.now(pytz.timezone("Europe/London"))
 
         i = 0
         while i < num:
             offset = random.randint(-3200, 3200) * 10
             duration = random.randint(3, 72) * 10
             summary = ''.join(random.sample((string.ascii_uppercase + string.digits) * 12, random.randint(6, 18)))
             start = date + datetime.timedelta(minutes=offset)
 
             event = Event()
             event.set_summary(summary)
             event.set_start(start)
             event.set_end(start + datetime.timedelta(minutes=duration))
             saved = module_resources.save_resource_event(dict(xml=event), resource)
             i += 1
 
     def test_001_save_resource_event(self):
         event = Event()
         event.set_summary("test")
 
         date = datetime.datetime.now(pytz.timezone("Europe/London"))
         event.set_start(date)
         event.set_end(date + datetime.timedelta(hours=2))
 
         saved = module_resources.save_resource_event(dict(xml=event), self.room1)
         self.assertTrue(saved)
 
     def test_002_read_resource_calendar(self):
         self.purge_mailbox(self.room1['kolabtargetfolder'])
 
         event = Event()
         event.set_summary("test")
         event.set_start(datetime.datetime(2014, 4, 1, 12, 0, 0, tzinfo=pytz.timezone("Europe/London")))
         event.set_end(datetime.datetime(2014, 4, 1, 14, 0, 0, tzinfo=pytz.timezone("Europe/London")))
         saved = module_resources.save_resource_event(dict(xml=event), self.room1)
         self.assertTrue(saved)
         uid = event.get_uid()
 
         itip = dict(
             uid=str(uuid.uuid4()),
             sequence=0,
             start=datetime.datetime(2014, 4, 1, 13, 0, 0, tzinfo=pytz.timezone("Europe/London")),
             end=datetime.datetime(2014, 4, 1, 14, 30, 0, tzinfo=pytz.timezone("Europe/London"))
         )
 
         event.set_uid(itip['uid'])
         event.set_start(itip['start'])
         event.set_end(itip['end'])
         itip['xml'] = event
 
         res = module_resources.read_resource_calendar(self.room1, [itip])
         self.assertEqual(res, 1)
         self.assertTrue(self.room1['conflict'])
         self.assertIn(uid, self.room1['conflicting_events'])
 
     def test_003_read_time(self):
         self.purge_mailbox(self.room1['kolabtargetfolder'])
 
         # populate 5K random events
         num = 5000
         date = datetime.datetime.now(pytz.timezone("Europe/London")).replace(hour=10, minute=0, second=0, microsecond=0)
         self.populate_calendar(self.room1, num, date)
 
         itip = dict(
             uid=str(uuid.uuid4()),
             sequence=0,
             start=date,
             end=date + datetime.timedelta(minutes=90)
         )
 
         event = Event()
         event.set_uid(itip['uid'])
         event.set_start(itip['start'])
         event.set_end(itip['end'])
         itip['xml'] = event
 
         start = time.time()
         res = module_resources.read_resource_calendar(self.room1, [itip])
         self.assertEqual(res, num)
 
-        print "\nREAD TIME:", time.time() - start
-        print "CONFLICTS:", self.room1['conflicting_events']
+        print("\nREAD TIME:", time.time() - start)
+        print("CONFLICTS:", self.room1['conflicting_events'])
diff --git a/tests/functional/test_wap_client/test_002_user_add.py b/tests/functional/test_wap_client/test_002_user_add.py
index 771e700..b4d4752 100644
--- a/tests/functional/test_wap_client/test_002_user_add.py
+++ b/tests/functional/test_wap_client/test_002_user_add.py
@@ -1,78 +1,78 @@
 import time
 import unittest
 
 import pykolab
 from pykolab import wap_client
 from pykolab.auth import Auth
 from pykolab.imap import IMAP
 
 conf = pykolab.getConf()
 
 
 class TestUserAdd(unittest.TestCase):
 
     @classmethod
     def setup_class(self, *args, **kw):
         from tests.functional.purge_users import purge_users
         purge_users()
 
         self.user = {
                 'local': 'john.doe',
                 'domain': 'example.org'
             }
 
         from tests.functional.user_add import user_add
         user_add("John", "Doe")
         from tests.functional.synchronize import synchronize_once
         synchronize_once()
 
     @classmethod
     def teardown_class(self, *args, **kw):
         from tests.functional.purge_users import purge_users
         purge_users()
 
     def test_001_inbox_created(self):
         time.sleep(2)
         imap = IMAP()
         imap.connect()
 
         folders = imap.lm('user/%(local)s@%(domain)s' % (self.user))
         self.assertEqual(len(folders), 1)
 
     def test_002_autocreate_folders_created(self):
         time.sleep(2)
         imap = IMAP()
         imap.connect()
 
         exec("ac_folders = %s" % (conf.get_raw(conf.get('kolab', 'primary_domain'), 'autocreate_folders')))
 
         folders = imap.lm('user/%(local)s/*@%(domain)s' % (self.user))
 
-        print folders
-        print ac_folders.keys()
+        print(folders)
+        print(ac_folders.keys())
 
         self.assertEqual(len(folders), len(ac_folders.keys()))
 
     def test_003_folders_metadata_set(self):
         imap = IMAP()
         imap.connect()
 
         exec("ac_folders = %s" % (conf.get_raw(conf.get('kolab', 'primary_domain'), 'autocreate_folders')))
 
         folders = []
         folders.extend(imap.lm('user/%(local)s@%(domain)s' % (self.user)))
         folders.extend(imap.lm('user/%(local)s/*@%(domain)s' % (self.user)))
 
         for folder in folders:
             metadata = imap.get_metadata(folder)
 
             folder_name = '/'.join(folder.split('/')[2:]).split('@')[0]
             if folder_name in ac_folders:
                 if 'annotations' in ac_folders[folder_name]:
                     for _annotation in ac_folders[folder_name]['annotations']:
                         if _annotation.startswith('/private/'):
                             continue
 
                         _annotation_value = ac_folders[folder_name]['annotations'][_annotation]
                         self.assertTrue(_annotation in metadata[metadata.keys().pop()])
                         self.assertEqual(_annotation_value, metadata[metadata.keys().pop()][_annotation])
diff --git a/tests/functional/test_wap_client/test_003_user_add_fr_FR.py b/tests/functional/test_wap_client/test_003_user_add_fr_FR.py
index 72cf84b..497d26c 100644
--- a/tests/functional/test_wap_client/test_003_user_add_fr_FR.py
+++ b/tests/functional/test_wap_client/test_003_user_add_fr_FR.py
@@ -1,56 +1,56 @@
 # -*- coding: utf-8 -*-
 
 import time
 import unittest
 
 import pykolab
 from pykolab import wap_client
 from pykolab.auth import Auth
 from pykolab.imap import IMAP
 
 conf = pykolab.getConf()
 
 
 class TestUserAddFrFR(unittest.TestCase):
     @classmethod
     def setup_class(self, *args, **kw):
         from tests.functional.purge_users import purge_users
         purge_users()
 
         self.user = {
                 'local': 'etienne-nicolas.mehul',
                 'domain': 'example.org'
             }
 
         from tests.functional.user_add import user_add
         user_add("Étienne-Nicolas", "Méhul", 'fr_FR')
         from tests.functional.synchronize import synchronize_once
         synchronize_once()
 
     @classmethod
     def teardown_class(self, *args, **kw):
         from tests.functional.purge_users import purge_users
         purge_users()
 
     def test_001_inbox_created(self):
         time.sleep(2)
         imap = IMAP()
         imap.connect()
 
         folders = imap.lm('user/%(local)s@%(domain)s' % (self.user))
         self.assertEqual(len(folders), 1)
 
     def test_002_fr_FR_user_recipient_policy(self):
         auth = Auth()
         auth.connect()
         recipient = auth.find_recipient("%(local)s@%(domain)s" % (self.user))
         if hasattr(self, 'assertIsInstance'):
             self.assertIsInstance(recipient, str)
 
         self.assertEqual(recipient, "uid=mehul,ou=People,dc=example,dc=org")
 
         result = wap_client.user_info(recipient)
-        print result
+        print(result)
 
         self.assertEqual(result['mail'], 'etienne-nicolas.mehul@example.org')
         self.assertEqual(sorted(result['alias']), ['e.mehul@example.org', 'mehul@example.org'])
diff --git a/tests/unit/test-008-sievelib.py b/tests/unit/test-008-sievelib.py
index 9226ad6..e3395e8 100644
--- a/tests/unit/test-008-sievelib.py
+++ b/tests/unit/test-008-sievelib.py
@@ -1,57 +1,57 @@
 import sys
 import unittest
 
 sieve_scripts = [
 
 # You're average vacation script.
 """
 require [ "vacation" ];
 
 if anyof (true) {
     vacation :days 1 :subject "Out of Office" "I'm out of the office";
 }
 """,
 
 # A non-any/allof if (control) header (test) structure
 """
 require ["fileinto"];
 
 if header :contains "X-Spam-Flag" "YES" {
     fileinto "Spam";
     stop;
 }
 """,
 
 # The same, all on the same line
 """
 require ["fileinto"];
 
 if header :contains "X-Spam-Flag" "YES" { fileinto "Spam"; stop; }
 """,
 
 # A little more of a complex list of tests
 """
 require ["fileinto"];
 
 if allof (header :contains "X-Mailer" "OTRS", header :contains "X-Powered-By" "OTRS", header :contains "Organization" "Example, Inc.") { fileinto "OTRS"; stop; }
 """,
 
     ]
 
 
 class TestSievelib(unittest.TestCase):
 
     def test_001_import_sievelib(self):
         from sievelib.parser import Parser
 
     def test_002_parse_tests(self):
         from sievelib.parser import Parser
         sieve_parser = Parser(debug=True)
 
         i = 0
         for sieve_str in sieve_scripts:
             i += 1
             result = sieve_parser.parse(sieve_str)
             if not result:
-                print "Sieve line: %r" % (sieve_parser.lexer.text.split('\n')[(sieve_parser.lexer.text[:sieve_parser.lexer.pos].count('\n'))])
+                print("Sieve line: %r" % (sieve_parser.lexer.text.split('\n')[(sieve_parser.lexer.text[:sieve_parser.lexer.pos].count('\n'))]))
                 raise Exception("Failed parsing Sieve script #%d: %s" % (i, sieve_parser.error))
diff --git a/wallace.py b/wallace.py
index a45f34a..6fbcadd 100755
--- a/wallace.py
+++ b/wallace.py
@@ -1,39 +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:
-    print >> sys.stderr, _("Cannot load pykolab/constants.py:")
-    print >> sys.stderr, "%s" % 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_optout.py b/wallace/module_optout.py
index 632753f..2b9d4b2 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:
         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:
         # Some data is not JSON
-        print "Response data is not JSON"
+        print("Response data is not JSON")
 
     return response_data['result']