[PATCH 4/4] learn unicode support

Nicolas Sebrecht nicolas.s-dev at laposte.net
Wed Feb 11 15:09:28 UTC 2015


This feature is NOT WORKING. This is EXPERIMENTAL WIP.

Also, such change is very intrusive/invasive by nature. While the full ASCII
mode is still the default, it is impossible to not impact legacy code when run
without unicode support.

Signed-off-by: Nicolas Sebrecht <nicolas.s-dev at laposte.net>
---
 offlineimap.conf                        |   8 +-
 offlineimap/CustomConfig.py             |  61 ++++---
 offlineimap/__init__.py                 |   2 +-
 offlineimap/accounts.py                 | 173 +++++++++++--------
 offlineimap/folder/Base.py              | 198 +++++++++++++---------
 offlineimap/folder/Gmail.py             |  95 +++++++----
 offlineimap/folder/GmailMaildir.py      |  85 ++++++----
 offlineimap/folder/IMAP.py              | 190 ++++++++++++---------
 offlineimap/folder/LocalStatus.py       |  70 +++++---
 offlineimap/folder/LocalStatusSQLite.py | 141 +++++++++-------
 offlineimap/folder/Maildir.py           | 174 +++++++++++--------
 offlineimap/folder/UIDMaps.py           |  56 ++++---
 offlineimap/imaplibutil.py              |  32 ++--
 offlineimap/imapserver.py               | 277 ++++++++++++++++++-------------
 offlineimap/imaputil.py                 |  20 ++-
 offlineimap/init.py                     | 272 +++++++++++++++++++-----------
 offlineimap/localeval.py                |  21 ++-
 offlineimap/mbnames.py                  |  96 ++++++++---
 offlineimap/repository/Base.py          | 121 +++++++++-----
 offlineimap/repository/Gmail.py         |  24 ++-
 offlineimap/repository/IMAP.py          | 285 ++++++++++++++++++--------------
 offlineimap/repository/LocalStatus.py   |  61 ++++---
 offlineimap/repository/Maildir.py       | 175 +++++++++++++-------
 offlineimap/repository/__init__.py      |  35 ++--
 offlineimap/syncmaster.py               |  24 ++-
 offlineimap/threadutil.py               |  25 ++-
 offlineimap/ui/Curses.py                |  24 ++-
 offlineimap/ui/Machine.py               |  95 ++++++-----
 offlineimap/ui/TTY.py                   |  47 ++++--
 offlineimap/ui/UIBase.py                | 251 ++++++++++++++++------------
 offlineimap/ui/debuglock.py             |  26 ++-
 offlineimap/utils/const.py              |   9 +-
 offlineimap/utils/distro.py             |  16 +-
 33 files changed, 1968 insertions(+), 1221 deletions(-)

diff --git a/offlineimap.conf b/offlineimap.conf
index cfafb23..0ad3bcc 100644
--- a/offlineimap.conf
+++ b/offlineimap.conf
@@ -5,8 +5,14 @@
 # More details can be found in the included user documention, which is
 # also available at: http://docs.offlineimap.org/en/latest/
 
+# NOTE 0: This file is read with UTF-8 encoding when option is enabled from
+# command line. This means iso-8859-1 and ASCII charsets are natively
+# supported. Otherwise, this file is expected to be full ASCII.
+#
+# If any other charset is used somewhere with success, it's just because you're
+# lucky.
 
-# NOTE 1: Settings generally support python interpolation. This means
+# NOTE1: Settings generally support python interpolation. This means
 # values can contain python format strings which refer to other values
 # in the same section, or values in a special DEFAULT section. This
 # allows you for example to use common settings for multiple accounts:
diff --git a/offlineimap/CustomConfig.py b/offlineimap/CustomConfig.py
index 44cfcab..6b5d780 100644
--- a/offlineimap/CustomConfig.py
+++ b/offlineimap/CustomConfig.py
@@ -20,9 +20,25 @@ from sys import exc_info
 
 try:
     from ConfigParser import SafeConfigParser, Error
-except ImportError: #python3
+except ImportError: # python3
     from configparser import SafeConfigParser, Error
+
 from offlineimap.localeval import LocalEval
+from offlineimap.utils.uni import uniString, noneString
+
+
+#
+# UNICODE: it would be much better to not use the uni module at all,
+# here. This way, we could consider the classes as a low-level driver.
+#
+# Sadly, the config classes make more than just config-related tasks and
+# supports hooks.
+#
+# Hence, the Unicode strategy here is to use the uni module for the tasks not
+# related to configuration parsing. In order to keep the whole thing consistent,
+# strings are returned with low-level Python types, NOT uni objects.
+#
+
 
 class CustomConfigParser(SafeConfigParser):
     def __init__(self):
@@ -75,7 +91,7 @@ class CustomConfigParser(SafeConfigParser):
             val = self.get(section, option).strip()
             return re.split(separator_re, val)
         except re.error as e:
-            raise Error("Bad split regexp '%s': %s" % \
+            raise Error("Bad split regexp '%s': %s"%
               (separator_re, e)), None, exc_info()[2]
 
     def getdefaultlist(self, section, option, default, separator_re):
@@ -88,12 +104,14 @@ class CustomConfigParser(SafeConfigParser):
             return default
 
     def getmetadatadir(self):
+        """Returns metadatadir."""
+
         xforms = [os.path.expanduser, os.path.expandvars]
-        d = self.getdefault("general", "metadata", "~/.offlineimap")
-        metadatadir = self.apply_xforms(d, xforms)
-        if not os.path.exists(metadatadir):
-            os.mkdir(metadatadir, 0o700)
-        return metadatadir
+        d = uniString(self.getdefault("general", "metadata", "~/.offlineimap"))
+        metadatadir = uniString(self.apply_xforms(d.uni, xforms))
+        if not os.path.exists(metadatadir.fs):
+            os.mkdir(metadatadir.fs, 0o700)
+        return metadatadir.uni
 
     def getlocaleval(self):
         # We already loaded pythonfile, so return this copy.
@@ -102,10 +120,10 @@ class CustomConfigParser(SafeConfigParser):
 
         xforms = [os.path.expanduser, os.path.expandvars]
         if self.has_option("general", "pythonfile"):
-            path = self.get("general", "pythonfile")
-            path = self.apply_xforms(path, xforms)
+            path = uniString(self.apply_xforms(
+                uniString(self.get("general", "pythonfile")).uni, xforms))
         else:
-            path = None
+            path = noneString()
 
         self.localeval = LocalEval(path)
         return self.localeval
@@ -118,7 +136,7 @@ class CustomConfigParser(SafeConfigParser):
         
         For instance, for "Account Test", returns "Test"."""
 
-        key = key + ' '
+        key = key + u' '
         return [x[len(key):] for x in self.sections() \
              if x.startswith(key)]
 
@@ -132,22 +150,27 @@ class CustomConfigParser(SafeConfigParser):
             self.set(section, option, value)
 
 
-    def apply_xforms(self, string, transforms):
-        """Applies set of transformations to a string.
+    # FIXME: it could be possible that this method is used from outside, either
+    # today or later. That's why it currently fully works with the uni
+    # objects. The best would be to put this function outside of this module.
+    # 
+    # TODO: use the '_' prefix in the name or move it outside.
+    def apply_xforms(self, s, transforms):
+        """Applies set of transformations to a uni object.
 
         Arguments:
-         - string: source string; if None, then no processing will
+         - s: source uni String; if None, then no processing will
            take place.
          - transforms: iterable that returns transformation function
            on each turn.
 
-        Returns transformed string."""
+        Returns (unicode) transformed string."""
 
-        if string == None:
+        if s == None:
             return None
         for f in transforms:
-            string = f(string)
-        return string
+            s = f(s)
+        return s
 
 
 
@@ -247,7 +270,7 @@ class ConfigHelperMixin:
         """
 
         value = self.getconf(option, default)
-        return self.getconfig().apply_xforms(value, xforms)
+        return (self.getconfig().apply_xforms(value, xforms))
 
 
     def getconfboolean(self, option, default = CustomConfigDefault):
diff --git a/offlineimap/__init__.py b/offlineimap/__init__.py
index 441af44..1126781 100644
--- a/offlineimap/__init__.py
+++ b/offlineimap/__init__.py
@@ -9,7 +9,7 @@ __author__      = "John Goerzen"
 __author_email__= "john at complete.org"
 __description__ = "Disconnected Universal IMAP Mail Synchronization/Reader Support"
 __license__  = "Licensed under the GNU GPL v2 or any later version (with an OpenSSL exception)"
-__bigcopyright__ = """%(__productname__)s %(__bigversion__)s
+__bigcopyright__ = u"""%(__productname__)s %(__bigversion__)s
   %(__license__)s""" % locals()
 __homepage__ = "http://offlineimap.org"
 
diff --git a/offlineimap/accounts.py b/offlineimap/accounts.py
index 62ed5c3..b620bcf 100644
--- a/offlineimap/accounts.py
+++ b/offlineimap/accounts.py
@@ -14,6 +14,10 @@
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
+#
+# FIXME: we are acutally working with CustomConfig.blah all over the place.
+# Change the filename and class name!
+#
 from subprocess import Popen, PIPE
 from threading import Event
 import os
@@ -25,32 +29,39 @@ from offlineimap import globals
 from offlineimap.repository import Repository
 from offlineimap.ui import getglobalui
 from offlineimap.threadutil import InstanceLimitedThread
+from offlineimap.utils.uni import uniString, fsString, isASCII
+from offlineimap.utils import hack
 
 try:
     import fcntl
 except:
     pass # ok if this fails, we can do without
 
-# FIXME: spaghetti code alert!
+# FIXME: Bip, Bip! spaghetti code alert!
 def getaccountlist(customconfig):
-    # Account names in a list.
-    return customconfig.getsectionlist('Account')
-
-# FIXME: spaghetti code alert!
+    # Account sections in a list.
+    sections = customconfig.getsectionlist('Account')
+    for section in sections:
+        # section type is unicode and has ASCII restrictions.
+        isASCII(section, exception_msg="section names must be plain ASCII")
+    return sections
+
+# FIXME: Bip, Bip! spaghetti code alert!
 def AccountListGenerator(customconfig):
     """Returns a list of instanciated Account class, one per account name."""
 
-    return [Account(customconfig, accountname)
-            for accountname in getaccountlist(customconfig)]
+    return [Account(customconfig, uniString(uni_accountname))
+            for uni_accountname in getaccountlist(customconfig)]
 
-# FIXME: spaghetti code alert!
+# FIXME: Bip, Bip! spaghetti code alert!
+# FIMXE: what a poor function name!
 def AccountHashGenerator(customconfig):
-    """Returns a dict of instanciated Account class with the account name as
-    key."""
+    """Returns a dict of instanciated Account class with the account (unicode)
+    name as key."""
 
     retval = {}
-    for item in AccountListGenerator(customconfig):
-        retval[item.getname()] = item
+    for inst_account in AccountListGenerator(customconfig):
+        retval[inst_account.getname().uni] = inst_account
     return retval
 
 
@@ -71,13 +82,13 @@ class Account(CustomConfig.ConfigHelperMixin):
         :param config: Representing the offlineimap configuration file.
         :type config: :class:`offlineimap.CustomConfig.CustomConfigParser`
 
-        :param name: A (str) string denoting the name of the Account
-                     as configured.
+        :param name: A (unicode encoded) string denoting the name of the
+                     Account as configured.
         """
 
         self.config = config
         self.name = name
-        self.metadatadir = config.getmetadatadir()
+        self.metadatadir = uniString(config.getmetadatadir())
         self.localeval = config.getlocaleval()
         # current :mod:`offlineimap.ui`, can be used for logging:
         self.ui = getglobalui()
@@ -99,14 +110,19 @@ class Account(CustomConfig.ConfigHelperMixin):
         return self.name
 
     def __str__(self):
-        return self.name
+        # We should warn to never use this.
+        return self.name.std
 
     def getaccountmeta(self):
-        return os.path.join(self.metadatadir, 'Account-' + self.name)
+        return fsString(os.path.join(
+            self.metadatadir.fs, 'Account-' + self.name.fs))
 
-    # Interface from CustomConfig.ConfigHelperMixin
+    # Interface from CustomConfig.ConfigHelperMixin Return unicode string as
+    # CustomConfig.blah are doing. This is more consistent to return the same
+    # type/encoding when working on the same kind of data.
+    # FIXME: why this? Why having a method if it's used once?
     def getsection(self):
-        return 'Account ' + self.getname()
+        return u'Account ' + self.name.uni
 
     @classmethod
     def set_abort_event(cls, config, signum):
@@ -125,8 +141,8 @@ class Account(CustomConfig.ConfigHelperMixin):
 
         if signum == 1:
             # resync signal, set config option for all accounts
-            for acctsection in getaccountlist(config):
-                config.set('Account ' + acctsection, "skipsleep", '1')
+            for uni_acctsection in getaccountlist(config):
+                config.set('Account ' + uni_acctsection, "skipsleep", '1')
         elif signum == 2:
             # don't autorefresh anymore
             cls.abort_soon_signal.set()
@@ -208,13 +224,14 @@ class SyncableAccount(Account):
     def __init__(self, *args, **kwargs):
         Account.__init__(self, *args, **kwargs)
         self._lockfd = None
-        self._lockfilepath = os.path.join(
-            self.config.getmetadatadir(), "%s.lock"% self)
+        self._lockfilepath = fsString(os.path.join(
+            uniString(self.config.getmetadatadir()).fs,
+            "%s.lock"% self.name.fs))
 
     def __lock(self):
         """Lock the account, throwing an exception if it is locked already."""
 
-        self._lockfd = open(self._lockfilepath, 'w')
+        self._lockfd = open(self._lockfilepath.fs, 'w')
         try:
             fcntl.lockf(self._lockfd, fcntl.LOCK_EX|fcntl.LOCK_NB)
         except NameError:
@@ -223,7 +240,7 @@ class SyncableAccount(Account):
         except IOError:
             self._lockfd.close()
             raise OfflineImapError("Could not lock account %s. Is another "
-                "instance using this account?"% self,
+                "instance using this account?"% self.name.fs,
                 OfflineImapError.ERROR.REPO), None, exc_info()[2]
 
     def _unlock(self):
@@ -233,7 +250,7 @@ class SyncableAccount(Account):
         if self._lockfd and not self._lockfd.closed:
             self._lockfd.close()
             try:
-                os.unlink(self._lockfilepath)
+                os.unlink(self._lockfilepath.fs)
             except OSError:
                 pass    # Failed to delete for some reason.
 
@@ -241,8 +258,8 @@ class SyncableAccount(Account):
         self.ui.registerthread(self)
         try:
             accountmetadata = self.getaccountmeta()
-            if not os.path.exists(accountmetadata):
-                os.mkdir(accountmetadata, 0o700)
+            if not os.path.exists(accountmetadata.fs):
+                os.mkdir(accountmetadata.fs, 0o700)
 
             self.remoterepos = Repository(self, 'remote')
             self.localrepos  = Repository(self, 'local')
@@ -272,7 +289,7 @@ class SyncableAccount(Account):
                 self.ui.error(e, exc_info()[2])
             except Exception as e:
                 self.ui.error(e, exc_info()[2], msg=
-                    "While attempting to sync account '%s'"% self)
+                    "While attempting to sync account '%s'"% self.getname().fs)
             else:
                 # after success sync, reset the looping counter to 3
                 if self.refreshperiod:
@@ -286,9 +303,10 @@ class SyncableAccount(Account):
     def get_local_folder(self, remotefolder):
         """Return the corresponding local folder for a given remotefolder."""
 
-        return self.localrepos.getfolder(
-            remotefolder.getvisiblename().
-            replace(self.remoterepos.getsep(), self.localrepos.getsep()))
+        visiblename = remotefolder.getvisiblename()
+        visiblename.uni = visiblename.uni.replace(
+            self.remoterepos.getsep().uni, self.localrepos.getsep().uni)
+        return self.localrepos.getfolder(visiblename)
 
     def __sync(self):
         """Synchronize the account once, then return.
@@ -299,7 +317,7 @@ class SyncableAccount(Account):
 
         folderthreads = []
 
-        hook = self.getconf('presynchook', '')
+        hook = uniString(self.getconf('presynchook', ''))
         self.callhook(hook)
 
         quickconfig = self.getconfint('quick', 0)
@@ -336,20 +354,36 @@ class SyncableAccount(Account):
                 if Account.abort_NOW_signal.is_set(): break
 
                 if not remotefolder.sync_this:
-                    self.ui.debug('', "Not syncing filtered folder '%s'"
-                                  "[%s]"% (remotefolder, remoterepos))
+                    self.ui.debug('', u"Not syncing filtered folder '%s'[%s]"%
+                        (remotefolder.getname().uni, remoterepos.getname().uni))
                     continue # Ignore filtered folder
                 localfolder = self.get_local_folder(remotefolder)
                 if not localfolder.sync_this:
-                    self.ui.debug('', "Not syncing filtered folder '%s'"
-                                 "[%s]"% (localfolder, localfolder.repository))
+                    self.ui.debug('', u"Not syncing filtered folder '%s'[%s]"%
+                        (localfolder.getname().uni,
+                        localfolder.getrepository().getname().uni))
                     continue # Ignore filtered folder
                 if not globals.options.singlethreading:
-                    thread = InstanceLimitedThread(\
-                        instancename = 'FOLDER_' + self.remoterepos.getname(),
-                        target = syncfolder,
-                        name = "Folder %s [acc: %s]"% (remotefolder.getexplainedname(), self),
-                        args = (self, remotefolder, quick))
+                    reponame = self.remoterepos.getname()
+                    remotefoldername = remotefolder.getexplainedname()
+                    # UNICODE: we have to encode Unicode because Thread module
+                    # has str/ASII type assumptions.
+                    #
+                    # Also, it wouldn't much be a problem if these strings were
+                    # not used later for logging.  As a consequence, encoding
+                    # these variable here is wrong in the sense that we encode
+                    # far too much early and the logging might suffer from
+                    # replaced non-ASCII characters.
+                    #
+                    # To make it right, parameters for logging and those
+                    # dedicated to the Thread class should be uncoupled from each
+                    # other.
+                    thread = InstanceLimitedThread(
+                        instancename='FOLDER_%s'% reponame, # OK for uni object.
+                        target=syncfolder,
+                        name="Folder %s [acc: %s]"% (remotefoldername.std,
+                            self.getname().std),
+                        args=(self, remotefolder, quick))
                     thread.start()
                     folderthreads.append(thread)
                 else:
@@ -363,35 +397,37 @@ class SyncableAccount(Account):
             localrepos.forgetfolders()
             remoterepos.forgetfolders()
         except:
-            #error while syncing. Drop all connections that we have, they
-            #might be bogus by now (e.g. after suspend)
+            # Error while syncing. Drop all connections that we have, they
+            # might be bogus by now (e.g. after suspend).
             localrepos.dropconnections()
             remoterepos.dropconnections()
             raise
         else:
-            # sync went fine. Hold or drop depending on config
+            # Sync went fine. Hold or drop depending on config.
             localrepos.holdordropconnections()
             remoterepos.holdordropconnections()
 
-        hook = self.getconf('postsynchook', '')
+        hook = uniString(self.getconf('postsynchook', ''))
         self.callhook(hook)
 
     def callhook(self, cmd):
         # check for CTRL-C or SIGTERM and run postsynchook
         if Account.abort_NOW_signal.is_set():
             return
-        if not cmd:
+        # TODO: improve: "not" operation does not makes sense. Explicitly say
+        # what value it could be.
+        if not cmd.uni:
             return
         try:
-            self.ui.callhook("Calling hook: " + cmd)
+            self.ui.callhook(u"Calling hook: "+ cmd.uni)
             if self.dryrun: # don't if we are in dry-run mode
                 return
-            p = Popen(cmd, shell=True,
+            p = Popen(cmd.fs, shell=True,
                       stdin=PIPE, stdout=PIPE, stderr=PIPE,
                       close_fds=True)
-            r = p.communicate()
-            self.ui.callhook("Hook stdout: %s\nHook stderr:%s\n"% r)
-            self.ui.callhook("Hook return code: %d"% p.returncode)
+            r = fsString(p.communicate())
+            self.ui.callhook(u"Hook stdout: %s\nHook stderr:%s\n"% r.uni)
+            self.ui.callhook(u"Hook return code: %d"% p.returncode)
         except (KeyboardInterrupt, SystemExit):
             raise
         except Exception as e:
@@ -400,7 +436,8 @@ class SyncableAccount(Account):
 def syncfolder(account, remotefolder, quick):
     """Synchronizes given remote folder for the specified account.
 
-    Filtered folders on the remote side will not invoke this function."""
+    Filtered folders on the remote side will not invoke this function.
+    This method is called by each account thread."""
 
     remoterepos = account.remoterepos
     localrepos = account.localrepos
@@ -413,12 +450,13 @@ def syncfolder(account, remotefolder, quick):
         localfolder = account.get_local_folder(remotefolder)
 
         # Write the mailboxes
-        mbnames.add(account.name, localfolder.getname(),
+        mbnames.add(account.getname(), localfolder.getname(),
             localrepos.getlocalroot())
 
         # Load status folder.
-        statusfolder = statusrepos.getfolder(remotefolder.getvisiblename().
-            replace(remoterepos.getsep(), statusrepos.getsep()))
+        statusfolder = statusrepos.getfolder(uniString(
+            remotefolder.getvisiblename().uni.replace(
+            remoterepos.getsep().uni, statusrepos.getsep().uni)))
 
         if localfolder.get_uidvalidity() == None:
             # This is a new folder, so delete the status cache to be
@@ -464,24 +502,24 @@ def syncfolder(account, remotefolder, quick):
         # Load remote folder.
         ui.loadmessagelist(remoterepos, remotefolder)
         remotefolder.cachemessagelist()
-        ui.messagelistloaded(remoterepos, remotefolder,
-                             remotefolder.getmessagecount())
+        ui.messagelistloaded(
+            remoterepos, remotefolder, remotefolder.getmessagecount())
 
         # Synchronize remote changes.
         if not localrepos.getconfboolean('readonly', False):
             ui.syncingmessages(remoterepos, remotefolder, localrepos, localfolder)
             remotefolder.syncmessagesto(localfolder, statusfolder)
         else:
-            ui.debug('imap', "Not syncing to read-only repository '%s'" \
-                         % localrepos.getname())
+            ui.debug('imap', u"Not syncing to read-only repository '%s'"%
+                localrepos.getname().uni)
 
         # Synchronize local changes
         if not remoterepos.getconfboolean('readonly', False):
             ui.syncingmessages(localrepos, localfolder, remoterepos, remotefolder)
             localfolder.syncmessagesto(remotefolder, statusfolder)
         else:
-            ui.debug('', "Not syncing to read-only repository '%s'" \
-                         % remoterepos.getname())
+            ui.debug('', u"Not syncing to read-only repository '%s'"%
+                remoterepos.getname().uni)
 
         statusfolder.save()
         localrepos.restore_atime()
@@ -492,11 +530,14 @@ def syncfolder(account, remotefolder, quick):
         if e.severity > OfflineImapError.ERROR.FOLDER:
             raise
         else:
-            ui.error(e, exc_info()[2], msg = "Aborting sync, folder '%s' "
-                     "[acc: '%s']" % (localfolder, account))
+            msg = (u"Aborting sync, folder '%s' [acc: '%s']"%
+                (localfolder.getname().uni, account.getname().uni))
+            ui.error(e, exc_info()[2], msg)
     except Exception as e:
-        ui.error(e, msg = "ERROR in syncfolder for %s folder %s: %s"%
-            (account, remotefolder.getvisiblename(), traceback.format_exc()))
+        ui.error(e, msg=u"ERROR in syncfolder for %s folder %s: %s"% (
+            account.getname().fs,
+            remotefolder.getvisiblename().fs,
+            traceback.format_exc()))
     finally:
         for folder in ["statusfolder", "localfolder", "remotefolder"]:
             if folder in locals():
diff --git a/offlineimap/folder/Base.py b/offlineimap/folder/Base.py
index 3a04ef6..c513857 100644
--- a/offlineimap/folder/Base.py
+++ b/offlineimap/folder/Base.py
@@ -23,13 +23,15 @@ from offlineimap import threadutil, emailutil
 from offlineimap import globals
 from offlineimap.ui import getglobalui
 from offlineimap.error import OfflineImapError
+from offlineimap.utils.uni import noneString, uniString, fsString
 import offlineimap.accounts
 
 
 class BaseFolder(object):
     def __init__(self, name, repository):
         """
-        :param name: Path & name of folder minus root or reference
+        :param name: Path & name (unicode) of folder minus
+            root or reference
         :param repository: Repository() in which the folder is.
         """
 
@@ -37,47 +39,55 @@ class BaseFolder(object):
         # Save original name for folderfilter operations
         self.ffilter_name = name
         # Top level dir name is always ''
-        self.root = None
-        self.name = name if not name == self.getsep() else ''
+        self.root = noneString()
+        # XXX: Explain why we accept getsep() as folder name. root folder?
+        self.name = name if not name == self.getsep() else uniString(u'')
         self.repository = repository
         self.visiblename = repository.nametrans(name)
         # In case the visiblename becomes '.' or '/' (top-level) we use
         # '' as that is the name that e.g. the Maildir scanning will
         # return for the top-level dir.
         if self.visiblename == self.getsep():
-            self.visiblename = ''
+            self.visiblename = uniString(u'')
 
         self.config = repository.getconfig()
         utime_from_message_global = self.config.getdefaultboolean(
             "general", "utime_from_message", False)
-        repo = "Repository " + repository.name
-        self._utime_from_message = self.config.getdefaultboolean(repo,
+        repo = uniString(u"Repository ") + repository.getname()
+        self._utime_from_message = self.config.getdefaultboolean(repo.uni,
             "utime_from_message", utime_from_message_global)
 
         # Determine if we're running static or dynamic folder filtering
         # and check filtering status
         self._dynamic_folderfilter = self.config.getdefaultboolean(
-            repo, "dynamic_folderfilter", False)
+            repo.uni, "dynamic_folderfilter", False)
         self._sync_this = repository.should_sync_folder(self.ffilter_name)
         if self._dynamic_folderfilter:
-            self.ui.debug('', "Running dynamic folder filtering on '%s'[%s]"%
-                (self.ffilter_name, repository))
+            self.ui.debug('',
+                u"Running dynamic folder filtering on '%s'[%s]"%
+                (self.ffilter_name.uni, repository.getname().uni))
         elif not self._sync_this:
-            self.ui.debug('', "Filtering out '%s'[%s] due to folderfilter"%
-                (self.ffilter_name, repository))
+            self.ui.debug('',
+                u"Filtering out '%s'[%s] due to folderfilter"%
+                (self.ffilter_name.uni, repository.getname().uni))
 
         # Passes for syncmessagesto
-        self.syncmessagesto_passes = [('copying messages'       , self.__syncmessagesto_copy),
-                                      ('deleting messages'      , self.__syncmessagesto_delete),
-                                      ('syncing flags'          , self.__syncmessagesto_flags)]
+        self.syncmessagesto_passes = [
+            ('copying messages' , self.__syncmessagesto_copy),
+            ('deleting messages', self.__syncmessagesto_delete),
+            ('syncing flags'    , self.__syncmessagesto_flags),
+            ]
 
     def getname(self):
-        """Returns name"""
+        """Returns name (unicode)."""
+
         return self.name
 
     def __str__(self):
-        # FIMXE: remove calls of this. We have getname().
-        return self.name
+        """Returns name (filesystem encoded)."""
+
+        # XXX: Warn we should not use this because encoding is made implicit.
+        return self.name.fs
 
     @property
     def accountname(self):
@@ -96,16 +106,19 @@ class BaseFolder(object):
 
     @property
     def utime_from_message(self):
+
         return self._utime_from_message
 
     def suggeststhreads(self):
         """Returns true if this folder suggests using threads for actions;
         false otherwise.  Probably only IMAP will return true."""
+
         return 0
 
     def waitforthread(self):
         """Implements method that waits for thread to be usable.
         Should be implemented only for folders that suggest threads."""
+
         raise NotImplementedError
 
     # XXX: we may need someting like supports_quickstatus() to check
@@ -145,7 +158,9 @@ class BaseFolder(object):
         if self.name == self.visiblename:
             return self.name
         else:
-            return "%s [remote name %s]"% (self.visiblename, self.name)
+            name = uniString(u"%s [remote name %s]"%
+                (self.visiblename.uni, self.name.uni))
+            return name
 
     def getrepository(self):
         """Returns the repository object that this folder is within."""
@@ -164,20 +179,22 @@ class BaseFolder(object):
 
     def getfullname(self):
         if self.getroot():
-            return self.getroot() + self.getsep() + self.getname()
+            return uniString(
+                self.getroot() + self.getsep() + self.getname())
         else:
             return self.getname()
 
     def getfolderbasename(self):
-        """Return base file name of file to store Status/UID info in."""
+        """Return base file name (unicode) of file to store Status/UID info
+        in."""
 
-        if not self.name:
-            basename = '.'
+        if not self.name.uni:
+            basename = uniString(u'.')
         else: # Avoid directory hierarchies and file names such as '/'.
-            basename = self.name.replace('/', '.')
+            basename = uniString(self.name.replace('/', '.'))
         # Replace with literal 'dot' if final path name is '.' as '.' is
         # an invalid file name.
-        basename = re.sub('(^|\/)\.$','\\1dot', basename)
+        basename = uniString(re.sub('(^|\/)\.$','\\1dot', basename.uni))
         return basename
 
     def check_uidvalidity(self):
@@ -199,8 +216,15 @@ class BaseFolder(object):
     def _getuidfilename(self):
         """provides UIDVALIDITY cache filename for class internal purposes.
 
-        return os.path.join(self.repository.getuiddir(),
-                            self.getfolderbasename())
+        It is str type encoded to filesystem."""
+
+        uiddir = self.repository.getuiddir()
+        folderbasename = self.getfolderbasename()
+        # Fix the filename to IMAP UTF-7 encoding. This prevent from playing
+        # with multiple cache files representative of the same remote folder.
+        uidfilename = fsString(os.path.join(uiddir.fs, folderbasename.imap))
+
+        return uidfilename
 
     def get_saveduidvalidity(self):
         """Return the previously cached UIDVALIDITY value
@@ -211,12 +235,13 @@ class BaseFolder(object):
         if hasattr(self, '_base_saved_uidvalidity'):
             return self._base_saved_uidvalidity
         uidfilename = self._getuidfilename()
-        if not os.path.exists(uidfilename):
+        if not os.path.exists(uidfilename.fs):
             self._base_saved_uidvalidity = None
         else:
-            file = open(uidfilename, "rt")
+            file = open(uidfilename.fs, "rt")
             self._base_saved_uidvalidity = long(file.readline().strip())
             file.close()
+
         return self._base_saved_uidvalidity
 
     def save_uidvalidity(self):
@@ -228,9 +253,9 @@ class BaseFolder(object):
         newval = self.get_uidvalidity()
         uidfilename = self._getuidfilename()
 
-        with open(uidfilename + ".tmp", "wt") as file:
-            file.write("%d\n"% newval)
-        os.rename(uidfilename + ".tmp", uidfilename)
+        with open(uidfilename.fs + ".tmp", "wt") as fd:
+            fd.write("%d\n"% newval)
+        os.rename(uidfilename.fs + ".tmp", uidfilename.fs)
         self._base_saved_uidvalidity = newval
 
     def get_uidvalidity(self):
@@ -249,7 +274,8 @@ class BaseFolder(object):
         raise NotImplementedError
 
     def dropmessagelistcache(self):
-        raise NotImplementedException
+
+        raise NotImplementedError
 
     def getmessagelist(self):
         """Gets the current message list.
@@ -433,9 +459,14 @@ class BaseFolder(object):
         for uid in uidlist:
             self.deletemessagelabels(uid, labels)
 
+    # XXX: logic should move outside... emailutil?
     def addmessageheader(self, content, linebreak, headername, headervalue):
         """Adds new header to the provided message.
 
+        UNICODE: This method is working on email headers. Unicode support in
+        headers is supported in a full ASCII fashion. Do not work with uni
+        objects here.
+
         WARNING: This function is a bit tricky, and modifying it in the wrong way,
         may easily lead to data-loss.
 
@@ -493,20 +524,25 @@ class BaseFolder(object):
             next line\n
         """
 
-        self.ui.debug('', 'addmessageheader: called to add %s: %s'%
+        self.ui.debug('', u'addmessageheader: called to add %s: %s'%
             (headername, headervalue))
 
         insertionpoint = content.find(linebreak * 2)
         if insertionpoint == -1:
-            self.ui.debug('', 'addmessageheader: headers were missing')
+            self.ui.debug('', u'addmessageheader: headers were missing')
         else:
-            self.ui.debug('', 'addmessageheader: headers end at position %d' % insertionpoint)
+            self.ui.debug('', u'addmessageheader: headers end at position %d'%
+                insertionpoint)
             mark = '==>EOH<=='
             contextstart = max(0,            insertionpoint - 100)
             contextend   = min(len(content), insertionpoint + 100)
-            self.ui.debug('', 'addmessageheader: header/body transition context (marked by %s): %s' %
-                          (mark, repr(content[contextstart:insertionpoint]) + \
-                          mark + repr(content[insertionpoint:contextend])))
+            self.ui.debug('', u'addmessageheader: header/body transition '
+                'context (marked by %s): %s%s%s'% (
+                mark,
+                repr(content[contextstart:insertionpoint]),
+                mark,
+                repr(content[insertionpoint:contextend]))
+                )
 
         # Hoping for case #4
         prefix = linebreak
@@ -528,14 +564,14 @@ class BaseFolder(object):
             if content[0:len(linebreak)] != linebreak:
                 suffix = suffix + linebreak
 
-        self.ui.debug('', 'addmessageheader: insertionpoint = %d'% insertionpoint)
+        self.ui.debug('', u'addmessageheader: insertionpoint = %d'% insertionpoint)
         headers = content[0:insertionpoint]
-        self.ui.debug('', 'addmessageheader: headers = %s'% repr(headers))
+        self.ui.debug('', u'addmessageheader: headers = %s'% repr(headers))
         new_header = prefix + ("%s: %s" % (headername, headervalue)) + suffix
-        self.ui.debug('', 'addmessageheader: new_header = ' + repr(new_header))
+        self.ui.debug('', u'addmessageheader: new_header = %s'% repr(new_header))
         return headers + new_header + content[insertionpoint:]
 
-
+    # XXX: logic should move outside... emailutil?
     def __find_eoh(self, content):
         """ Searches for the point where mail headers end.
         Either double '\n', or end of string.
@@ -543,7 +579,8 @@ class BaseFolder(object):
         Arguments:
         - content: contents of the message to search in
         Returns: position of the first non-header byte.
-        """
+
+        UNICODE: do not work with uni objects here."""
 
         eoh_cr = content.find('\n\n')
         if eoh_cr == -1:
@@ -552,6 +589,7 @@ class BaseFolder(object):
         return eoh_cr
 
 
+    # XXX: logic should move outside... emailutil?
     def getmessageheader(self, content, name):
         """Searches for the first occurence of the given header and returns
         its value. Header name is case-insensitive.
@@ -561,21 +599,22 @@ class BaseFolder(object):
         - name: name of the header to be searched
 
         Returns: header value or None if no such header was found
-        """
 
-        self.ui.debug('', 'getmessageheader: called to get %s'% name)
+        UNICODE: do not work with uni objects here."""
+
+        self.ui.debug('', u'getmessageheader: called to get %s'% name)
         eoh = self.__find_eoh(content)
-        self.ui.debug('', 'getmessageheader: eoh = %d'% eoh)
+        self.ui.debug('', u'getmessageheader: eoh = %d'% eoh)
         headers = content[0:eoh]
-        self.ui.debug('', 'getmessageheader: headers = %s'% repr(headers))
+        self.ui.debug('', u'getmessageheader: headers = %s'% repr(headers))
 
-        m = re.search('^%s:(.*)$' % name, headers, flags = re.MULTILINE | re.IGNORECASE)
+        m = re.search(u'^%s:(.*)$'% name, headers, flags = re.MULTILINE | re.IGNORECASE)
         if m:
             return m.group(1).strip()
         else:
             return None
 
-
+    # XXX: logic should move outside... emailutil?
     def getmessageheaderlist(self, content, name):
         """Searches for the given header and returns a list of values for
         that header.
@@ -585,17 +624,18 @@ class BaseFolder(object):
         - name: name of the header to be searched
 
         Returns: list of header values or emptylist if no such header was found
-        """
 
-        self.ui.debug('', 'getmessageheaderlist: called to get %s' % name)
+        UNICODE: do not work with uni objects here."""
+
+        self.ui.debug('', u'getmessageheaderlist: called to get %s'% name)
         eoh = self.__find_eoh(content)
-        self.ui.debug('', 'getmessageheaderlist: eoh = %d' % eoh)
+        self.ui.debug('', u'getmessageheaderlist: eoh = %d'% eoh)
         headers = content[0:eoh]
-        self.ui.debug('', 'getmessageheaderlist: headers = %s' % repr(headers))
-
-        return re.findall('^%s:(.*)$' % name, headers, flags = re.MULTILINE | re.IGNORECASE)
+        self.ui.debug('', u'getmessageheaderlist: headers = %s'% repr(headers))
 
+        return re.findall(u'^%s:(.*)$'% name, headers, flags = re.MULTILINE | re.IGNORECASE)
 
+    # XXX: logic should move outside... emailutil?
     def deletemessageheaders(self, content, header_list):
         """Deletes headers in the given list from the message content.
 
@@ -608,15 +648,16 @@ class BaseFolder(object):
 
         if type(header_list) != type([]):
             header_list = [header_list]
-        self.ui.debug('', 'deletemessageheaders: called to delete %s'% (header_list))
+        self.ui.debug('', u'deletemessageheaders: called to delete %s'%
+            (header_list))
 
         if not len(header_list): return content
 
         eoh = self.__find_eoh(content)
-        self.ui.debug('', 'deletemessageheaders: end of headers = %d'% eoh)
+        self.ui.debug('', u'deletemessageheaders: end of headers = %d'% eoh)
         headers = content[0:eoh]
         rest = content[eoh:]
-        self.ui.debug('', 'deletemessageheaders: headers = %s'% repr(headers))
+        self.ui.debug('', u'deletemessageheaders: headers = %s'% repr(headers))
         new_headers = []
         for h in headers.split('\n'):
             keep_it = True
@@ -628,9 +669,6 @@ class BaseFolder(object):
 
         return ('\n'.join(new_headers) + rest)
 
-
-
-
     def change_message_uid(self, uid, new_uid):
         """Change the message from existing uid to new_uid
 
@@ -657,7 +695,7 @@ class BaseFolder(object):
         for uid in uidlist:
             self.deletemessage(uid)
 
-    def copymessageto(self, uid, dstfolder, statusfolder, register = 1):
+    def copymessageto(self, uid, dstfolder, statusfolder, register=1):
         """Copies a message from self to dst if needed, updating the status
 
         Note that this function does not check against dryrun settings,
@@ -718,8 +756,9 @@ class BaseFolder(object):
                 self.deletemessage(uid)
             else:
                 raise OfflineImapError("Trying to save msg (uid %d) on folder "
-                    "%s returned invalid uid %d"% (uid, dstfolder.getvisiblename(),
-                    new_uid), OfflineImapError.ERROR.MESSAGE)
+                    "%s returned invalid uid %d"%
+                    (uid, dstfolder.getvisiblename().fs, new_uid),
+                    OfflineImapError.ERROR.MESSAGE)
         except (KeyboardInterrupt): # bubble up CTRL-C
             raise
         except OfflineImapError as e:
@@ -727,8 +766,8 @@ class BaseFolder(object):
                 raise # bubble severe errors up
             self.ui.error(e, exc_info()[2])
         except Exception as e:
-            self.ui.error(e, exc_info()[2],
-              msg = "Copying message %s [acc: %s]"% (uid, self.accountname))
+            self.ui.error(e, exc_info()[2], msg=
+                u"Copying message %s [acc: %s]"% (uid, self.accountname.uni))
             raise    #raise on unknown errors, so we can fix those
 
     def __syncmessagesto_copy(self, dstfolder, statusfolder):
@@ -750,8 +789,11 @@ class BaseFolder(object):
             self.getmessageuidlist())
         num_to_copy = len(copylist)
         if num_to_copy and self.repository.account.dryrun:
-            self.ui.info("[DRYRUN] Copy {0} messages from {1}[{2}] to {3}".format(
-                num_to_copy, self, self.repository, dstfolder.repository))
+            self.ui.info(
+                u"[DRYRUN] Copy {0} messages from {1}[{2}] to {3}".format(
+                num_to_copy, self.getname().uni,
+                self.repository.getname().uni,
+                dstfolder.repository.getname().uni))
             return
         for num, uid in enumerate(copylist):
             # bail out on CTRL-C or SIGTERM
@@ -761,16 +803,17 @@ class BaseFolder(object):
             # exceptions are caught in copymessageto()
             if self.suggeststhreads() and not globals.options.singlethreading:
                 self.waitforthread()
+                fs_name = "Copy message from %s:%s"% (
+                    self.repository.getname().fs, self.getname().fs)
                 thread = threadutil.InstanceLimitedThread(\
                     self.getcopyinstancelimit(),
-                    target = self.copymessageto,
-                    name = "Copy message from %s:%s" % (self.repository, self),
-                    args = (uid, dstfolder, statusfolder))
+                    target=self.copymessageto,
+                    name=fs_name,
+                    args=(uid, dstfolder, statusfolder))
                 thread.start()
                 threads.append(thread)
             else:
-                self.copymessageto(uid, dstfolder, statusfolder,
-                                   register = 0)
+                self.copymessageto(uid, dstfolder, statusfolder, register=0)
         for thread in threads:
             thread.join()
 
@@ -840,14 +883,14 @@ class BaseFolder(object):
         for flag, uids in addflaglist.items():
             self.ui.addingflags(uids, flag, dstfolder)
             if self.repository.account.dryrun:
-                continue #don't actually add in a dryrun
+                continue # Don't actually add in a dryrun.
             dstfolder.addmessagesflags(uids, set(flag))
             statusfolder.addmessagesflags(uids, set(flag))
 
         for flag,uids in delflaglist.items():
             self.ui.deletingflags(uids, flag, dstfolder)
             if self.repository.account.dryrun:
-                continue #don't actually remove in a dryrun
+                continue # Don't actually remove in a dryrun.
             dstfolder.deletemessagesflags(uids, set(flag))
             statusfolder.deletemessagesflags(uids, set(flag))
 
@@ -899,8 +942,9 @@ class BaseFolder(object):
                     raise
                 self.ui.error(e, exc_info()[2])
             except Exception as e:
-                self.ui.error(e, exc_info()[2], "Syncing folder %s [acc: %s]" %\
-                                  (self, self.accountname))
+                self.ui.error(e, exc_info()[2],
+                    u"Syncing folder %s [acc: %s]"%
+                    (self.name.fs, self.accountname.fs))
                 raise # raise unknown Exceptions so we can fix them
 
     def __eq__(self, other):
diff --git a/offlineimap/folder/Gmail.py b/offlineimap/folder/Gmail.py
index 1afbe47..9d33cc4 100644
--- a/offlineimap/folder/Gmail.py
+++ b/offlineimap/folder/Gmail.py
@@ -21,11 +21,14 @@ from sys import exc_info
 
 from offlineimap import imaputil, OfflineImapError
 from offlineimap import imaplibutil
+from offlineimap.utils.uni import uniString, isASCII
+from offlineimap.utils import uni
 import offlineimap.accounts
 from .IMAP import IMAPFolder
 
 """Folder implementation to support features of the Gmail IMAP server."""
 
+
 class GmailFolder(IMAPFolder):
     """Folder implementation to support features of the Gmail IMAP server.
 
@@ -45,23 +48,35 @@ class GmailFolder(IMAPFolder):
     def __init__(self, imapserver, name, repository):
         super(GmailFolder, self).__init__(imapserver, name, repository)
         self.trash_folder = repository.gettrashfolder(name)
-        # Gmail will really delete messages upon EXPUNGE in these folders
-        self.real_delete_folders =  [ self.trash_folder, repository.getspamfolder() ]
+        # Gmail will really delete messages upon EXPUNGE in these folders.
+        self.real_delete_folders = [
+            self.trash_folder, repository.getspamfolder() ]
 
-        # The header under which labels are stored
-        self.labelsheader = self.repository.account.getconf('labelsheader', 'X-Keywords')
+        # The header under which labels are stored.
+        self.labelsheader = uniString(
+            self.repository.account.getconf('labelsheader', 'X-Keywords'))
+        # labelsheader is expected plain ASCII.
+        isASCII(self.labelsheader.uni, exception_msg=
+            "labelsheader must be plain ASCII")
 
         # enables / disables label sync
-        self.synclabels = self.repository.account.getconfboolean('synclabels', False)
+        self.synclabels = \
+            self.repository.account.getconfboolean('synclabels', False)
 
-        # if synclabels is enabled, add a 4th pass to sync labels
+        # If synclabels is enabled, add a 4th pass to sync labels.
         if self.synclabels:
             self.imap_query.insert(0, 'X-GM-LABELS')
-            self.syncmessagesto_passes.append(('syncing labels', self.syncmessagesto_labels))
+            self.syncmessagesto_passes.append(
+                ('syncing labels', self.syncmessagesto_labels))
 
-        # Labels to be left alone
-        ignorelabels =  self.repository.account.getconf('ignorelabels', '')
-        self.ignorelabels = set([l for l in re.split(r'\s*,\s*', ignorelabels) if len(l)])
+        # Labels to be left alone.
+        ignorelabels = uniString(
+            self.repository.account.getconf('ignorelabels', ''))
+        # ignorelabels is expected plain ASCII.
+        isASCII(ignorelabels, exception_msg=
+            "ignorelabels must be plain ASCII")
+        self.ignorelabels = set(
+            [l for l in re.split(r'\s*,\s*', ignorelabels.dbytes) if len(l)])
 
 
     def getmessage(self, uid):
@@ -71,13 +86,16 @@ class GmailFolder(IMAPFolder):
         :returns: the message body or throws and OfflineImapError
                   (probably severity MESSAGE) if e.g. no message with
                   this UID could be found.
-        """
+
+        UNICODE: this expects raw data. Don't introduce uni objects."""
+
         imapobj = self.imapserver.acquireconnection()
         try:
             data = self._fetch_from_imap(imapobj, str(uid), 2)
         finally:
             self.imapserver.releaseconnection(imapobj)
 
+        # XXX: logic on labels should go to emailutil.
         # data looks now e.g.
         #[('320 (X-GM-LABELS (...) UID 17061 BODY[] {2565}','msgbody....')]
         # we only asked for one message, and that msg is in data[0].
@@ -88,11 +106,13 @@ class GmailFolder(IMAPFolder):
         if self.synclabels:
             m = re.search('X-GM-LABELS\s*\(([^\)]*)\)', data[0][0])
             if m:
-                labels = set([imaputil.dequote(lb) for lb in imaputil.imapsplit(m.group(1))])
+                labels = set(
+                    [imaputil.dequote(lb) for lb in imaputil.imapsplit(m.group(1))])
             else:
                 labels = set()
             labels = labels - self.ignorelabels
-            labels_str = imaputil.format_labels_string(self.labelsheader, sorted(labels))
+            labels_str = imaputil.format_labels_string(
+                self.labelsheader, sorted(labels))
 
             # First remove old label headers that may be in the message content retrieved
             # from gmail Then add a labels header with current gmail labels.
@@ -104,7 +124,7 @@ class GmailFolder(IMAPFolder):
         else:
             dbg_output = body
 
-        self.ui.debug('imap', "Returned object from fetching %d: '%s'"%
+        self.ui.debug('imap', u"Returned object from fetching %d: '%s'"%
                       (uid, dbg_output))
         return body
 
@@ -141,40 +161,44 @@ class GmailFolder(IMAPFolder):
             res_type, response = imapobj.fetch("'%s'"% msgsToFetch,
               '(FLAGS X-GM-LABELS UID)')
             if res_type != 'OK':
-                raise OfflineImapError("FETCHING UIDs in folder [%s]%s failed. " % \
-                  (self.getrepository(), self) + \
-                  "Server responded '[%s] %s'" % \
+                raise OfflineImapError("FETCHING UIDs in folder [%s]%s failed. "%
+                  (self.getrepository().getname().fs, self.name.fs) +
+                  "Server responded '[%s] %s'"%
                   (res_type, response), OfflineImapError.ERROR.FOLDER), \
                   None, exc_info()[2]
         finally:
             self.imapserver.releaseconnection(imapobj)
 
         for messagestr in response:
-            # looks like: '1 (FLAGS (\\Seen Old) X-GM-LABELS (\\Inbox \\Favorites) UID 4807)' or None if no msg
+            # looks like:
+            #   '1 (FLAGS (\\Seen Old) X-GM-LABELS (\\Inbox \\Favorites) UID 4807)'
+            # or
+            #   None if no msg
             # Discard initial message number.
             if messagestr == None:
                 continue
             messagestr = messagestr.split(' ', 1)[1]
             options = imaputil.flags2hash(messagestr)
             if not 'UID' in options:
-                self.ui.warn('No UID in message with options %s' %\
-                                          str(options),
-                                          minor = 1)
+                self.ui.warn(u'No UID in message with options %s'%
+                    str(options), minor=1)
             else:
                 uid = long(options['UID'])
                 self.messagelist[uid] = self.msglist_item_initializer(uid)
                 flags = imaputil.flagsimap2maildir(options['FLAGS'])
                 m = re.search('\(([^\)]*)\)', options['X-GM-LABELS'])
                 if m:
-                    labels = set([imaputil.dequote(lb) for lb in imaputil.imapsplit(m.group(1))])
+                    labels = set(
+                        [imaputil.dequote(lb) for lb in imaputil.imapsplit(m.group(1))])
                 else:
                     labels = set()
                 labels = labels - self.ignorelabels
                 rtime = imaplibutil.Internaldate2epoch(messagestr)
-                self.messagelist[uid] = {'uid': uid, 'flags': flags, 'labels': labels, 'time': rtime}
+                self.messagelist[uid] = \
+                    {'uid': uid, 'flags': flags, 'labels': labels, 'time': rtime}
 
     def savemessage(self, uid, content, flags, rtime):
-        """Save the message on the Server
+        """Save the message on the Server.
 
         This backend always assigns a new uid, so the uid arg is ignored.
 
@@ -203,13 +227,15 @@ class GmailFolder(IMAPFolder):
         return ret
 
     def _messagelabels_aux(self, arg, uidlist, labels):
-        """Common code to savemessagelabels and addmessagelabels"""
+        """Common code to savemessagelabels and addmessagelabels."""
+
         labels = labels - self.ignorelabels
         uidlist = [uid for uid in uidlist if uid > 0]
         if len(uidlist) > 0:
             imapobj = self.imapserver.acquireconnection()
             try:
-                labels_str = '(' + ' '.join([imaputil.quote(lb) for lb in labels]) + ')'
+                labels_str = '(' + ' '.join(
+                    [imaputil.quote(lb) for lb in labels]) + ')'
                 # Coalesce uid's into ranges
                 uid_str = imaputil.uid_sequence(uidlist)
                 result = self._store_to_imap(imapobj, uid_str, arg, labels_str)
@@ -222,8 +248,10 @@ class GmailFolder(IMAPFolder):
                 self.imapserver.releaseconnection(imapobj)
 
             if result:
-                retlabels = imaputil.flags2hash(imaputil.imapsplit(result)[1])['X-GM-LABELS']
-                retlabels = set([imaputil.dequote(lb) for lb in imaputil.imapsplit(retlabels)])
+                retlabels = imaputil.flags2hash(
+                    imaputil.imapsplit(result)[1])['X-GM-LABELS']
+                retlabels = set(
+                    [imaputil.dequote(lb) for lb in imaputil.imapsplit(retlabels)])
                 return retlabels
         return None
 
@@ -232,6 +260,7 @@ class GmailFolder(IMAPFolder):
 
         Note that this function does not check against dryrun settings,
         so you need to ensure that it is never called in a dryrun mode."""
+
         if uid in self.messagelist and 'labels' in self.messagelist[uid]:
             oldlabels = self.messagelist[uid]['labels']
         else:
@@ -310,6 +339,7 @@ class GmailFolder(IMAPFolder):
 
         This function checks and protects us from action in dryrun mode.
         """
+
         # This applies the labels message by message, as this makes more sense for a
         # Maildir target. If applied with an other Gmail IMAP target it would not be
         # the fastest thing in the world though...
@@ -353,10 +383,12 @@ class GmailFolder(IMAPFolder):
                     statuslabels = set()
 
                 if selflabels != statuslabels:
-                    self.ui.settinglabels(uid, i+1, len(uidlist), sorted(selflabels), dstfolder)
+                    self.ui.settinglabels(
+                        uid, i+1, len(uidlist), sorted(selflabels), dstfolder)
                     if self.repository.account.dryrun:
                         continue #don't actually add in a dryrun
-                    dstfolder.savemessagelabels(uid, selflabels, ignorelabels = self.ignorelabels)
+                    dstfolder.savemessagelabels(
+                        uid, selflabels, ignorelabels=self.ignorelabels)
                     mtime = dstfolder.getmessagemtime(uid)
                     mtimes[uid] = mtime
                     labels[uid] = selflabels
@@ -367,4 +399,5 @@ class GmailFolder(IMAPFolder):
             statusfolder.savemessagesmtimebulk(mtimes)
 
         except NotImplementedError:
-            self.ui.warn("Can't sync labels. You need to configure a local repository of type GmailMaildir")
+            self.ui.warn(u"Can't sync labels. You need to "
+                "configure a local repository of type GmailMaildir")
diff --git a/offlineimap/folder/GmailMaildir.py b/offlineimap/folder/GmailMaildir.py
index 5ca0e1f..e21152a 100644
--- a/offlineimap/folder/GmailMaildir.py
+++ b/offlineimap/folder/GmailMaildir.py
@@ -15,22 +15,28 @@
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
-
 import os
 from sys import exc_info
+
 from .Maildir import MaildirFolder
 from offlineimap import OfflineImapError
 import offlineimap.accounts
 from offlineimap import imaputil
+from offlineimap.utils.uni import fsString, uniString, isASCII
+
 
 class GmailMaildirFolder(MaildirFolder):
-    """Folder implementation to support adding labels to messages in a Maildir.
-    """
+    """Folder implementation to support adding labels to messages in a Maildir."""
+
     def __init__(self, root, name, sep, repository):
         super(GmailMaildirFolder, self).__init__(root, name, sep, repository)
 
-        # The header under which labels are stored
-        self.labelsheader = self.repository.account.getconf('labelsheader', 'X-Keywords')
+        # The header under which labels are stored.
+        self.labelsheader = uniString(
+            self.repository.account.getconf('labelsheader', 'X-Keywords'))
+        # ...are expected plain ASCII.
+        isASCII(self.labelsheader, exception_msg=
+            "labelsheader must be plain ASCII")
 
         # enables / disables label sync
         self.synclabels = self.repository.account.getconfboolean('synclabels', 0)
@@ -40,22 +46,25 @@ class GmailMaildirFolder(MaildirFolder):
             self.syncmessagesto_passes.append(('syncing labels', self.syncmessagesto_labels))
 
     def quickchanged(self, statusfolder):
-        """Returns True if the Maildir has changed. Checks uids, flags and mtimes"""
+        """Returns True if the Maildir has changed. Checks uids, flags and mtimes."""
 
         self.cachemessagelist()
         # Folder has different uids than statusfolder => TRUE
         if sorted(self.getmessageuidlist()) != \
                 sorted(statusfolder.getmessageuidlist()):
             return True
+
         # check for flag changes, it's quick on a Maildir
         for (uid, message) in self.getmessagelist().iteritems():
             if message['flags'] != statusfolder.getmessageflags(uid):
                 return True
+
         # check for newer mtimes. it is also fast
         for (uid, message) in self.getmessagelist().iteritems():
             if message['mtime'] > statusfolder.getmessagemtime(uid):
                 return True
-        return False  #Nope, nothing changed
+
+        return False  # Nope, nothing changed.
 
 
     # Interface from BaseFolder
@@ -68,25 +77,27 @@ class GmailMaildirFolder(MaildirFolder):
         if self.messagelist is None:
             self.messagelist = self._scanfolder()
 
-        # Get mtimes
+        # Get mtimes.
         if self.synclabels:
             for uid, msg in self.messagelist.items():
-                filepath = os.path.join(self.getfullname(), msg['filename'])
-                msg['mtime'] = long(os.stat(filepath).st_mtime)
+                fs_filepath = os.path.join(
+                    self.getfullname().fs, msg['filename'].fs)
+                msg['mtime'] = long(os.stat(fs_filepath).st_mtime)
 
 
     def getmessagelabels(self, uid):
         # Labels are not cached in cachemessagelist because it is too slow.
         if not self.messagelist[uid]['labels_cached']:
             filename = self.messagelist[uid]['filename']
-            filepath = os.path.join(self.getfullname(), filename)
+            filepath = fsString(
+                os.path.join(self.getfullname().fs, filename.fs))
 
-            if not os.path.exists(filepath):
+            if not os.path.exists(filepath.fs):
                 return set()
 
-            file = open(filepath, 'rt')
-            content = file.read()
-            file.close()
+            f = open(filepath.fs, 'rt')
+            content = f.read()
+            f.close()
 
             self.messagelist[uid]['labels'] = set()
             for hstr in self.getmessageheaderlist(content, self.labelsheader):
@@ -121,9 +132,10 @@ class GmailMaildirFolder(MaildirFolder):
 
         # Update the mtime and labels
         filename = self.messagelist[uid]['filename']
-        filepath = os.path.join(self.getfullname(), filename)
-        self.messagelist[uid]['mtime'] = long(os.stat(filepath).st_mtime)
+        fs_filepath = os.path.join(self.getfullname().fs, filename.fs)
+        self.messagelist[uid]['mtime'] = long(os.stat(fs_filepath).st_mtime)
         self.messagelist[uid]['labels'] = labels
+
         return ret
 
     def savemessagelabels(self, uid, labels, ignorelabels=set()):
@@ -133,11 +145,11 @@ class GmailMaildirFolder(MaildirFolder):
         so you need to ensure that it is never called in a dryrun mode."""
 
         filename = self.messagelist[uid]['filename']
-        filepath = os.path.join(self.getfullname(), filename)
+        filepath = fsString(os.path.join(self.getfullname().fs, filename.fs))
 
-        file = open(filepath, 'rt')
-        content = file.read()
-        file.close()
+        f = open(filepath.fs, 'rt')
+        content = f.read()
+        f.close()
 
         oldlabels = set()
         for hstr in self.getmessageheaderlist(content, self.labelsheader):
@@ -164,21 +176,22 @@ class GmailMaildirFolder(MaildirFolder):
         # write file with new labels to a unique file in tmp
         messagename = self.new_message_filename(uid, set())
         tmpname = self.save_to_tmp_file(messagename, content)
-        tmppath = os.path.join(self.getfullname(), tmpname)
+        tmppath = fsString(os.path.join(self.getfullname().fs, tmpname.fs))
 
         # move to actual location
         try:
-            os.rename(tmppath, filepath)
+            os.rename(tmppath.fs, filepath.fs)
         except OSError as e:
-            raise OfflineImapError("Can't rename file '%s' to '%s': %s" % \
-              (tmppath, filepath, e[1]), OfflineImapError.ERROR.FOLDER), \
+            raise OfflineImapError("Can't rename file '%s' to '%s': %s"%
+              (tmppath.fs, filepath.fs, e[1]),
+              OfflineImapError.ERROR.FOLDER), \
               None, exc_info()[2]
 
         if rtime != None:
-            os.utime(filepath, (rtime, rtime))
+            os.utime(filepath.fs, (rtime, rtime))
 
         # save the new mtime and labels
-        self.messagelist[uid]['mtime'] = long(os.stat(filepath).st_mtime)
+        self.messagelist[uid]['mtime'] = long(os.stat(filepath.fs).st_mtime)
         self.messagelist[uid]['labels'] = labels
 
     def copymessageto(self, uid, dstfolder, statusfolder, register = 1):
@@ -198,7 +211,8 @@ class GmailMaildirFolder(MaildirFolder):
         realcopy = uid > 0 and not dstfolder.uidexists(uid)
 
         # first copy the message
-        super(GmailMaildirFolder, self).copymessageto(uid, dstfolder, statusfolder, register)
+        super(GmailMaildirFolder, self).copymessageto(
+            uid, dstfolder, statusfolder, register)
 
         # sync labels and mtime now when the message is new (the embedded labels are up to date,
         # and have already propagated to the remote server.
@@ -207,7 +221,8 @@ class GmailMaildirFolder(MaildirFolder):
         if realcopy and self.synclabels:
             try:
                 labels = dstfolder.getmessagelabels(uid)
-                statusfolder.savemessagelabels(uid, labels, mtime=self.getmessagemtime(uid))
+                statusfolder.savemessagelabels(
+                    uid, labels, mtime=self.getmessagemtime(uid))
 
             # dstfolder is not GmailMaildir.
             except NotImplementedError:
@@ -226,6 +241,7 @@ class GmailMaildirFolder(MaildirFolder):
 
         This function checks and protects us from action in ryrun mode.
         """
+
         # For each label, we store a list of uids to which it should be
         # added.  Then, we can call addmessageslabels() to apply them in
         # bulk, rather than one call per message.
@@ -256,7 +272,7 @@ class GmailMaildirFolder(MaildirFolder):
                     uidlist.append(uid)
 
 
-            self.ui.collectingdata(uidlist, self)
+            self.ui.collectingdata(uidlist, self.getname())
             # This can be slow if there is a lot of modified files
             for uid in uidlist:
                 # bail out on CTRL-C or SIGTERM
@@ -290,7 +306,7 @@ class GmailMaildirFolder(MaildirFolder):
 
                 self.ui.addinglabels(uids, lb, dstfolder)
                 if self.repository.account.dryrun:
-                    continue #don't actually add in a dryrun
+                    continue # don't actually add in a dryrun
                 dstfolder.addmessageslabels(uids, set([lb]))
                 statusfolder.addmessageslabels(uids, set([lb]))
 
@@ -317,11 +333,12 @@ class GmailMaildirFolder(MaildirFolder):
                     continue #don't actually update statusfolder
 
                 filename = self.messagelist[uid]['filename']
-                filepath = os.path.join(self.getfullname(), filename)
-                mtimes[uid] = long(os.stat(filepath).st_mtime)
+                fs_filepath = os.path.join(self.getfullname().fs, filename.fs)
+                mtimes[uid] = long(os.stat(fs_filepath).st_mtime)
 
             # finally update statusfolder in a single DB transaction
             statusfolder.savemessagesmtimebulk(mtimes)
 
         except NotImplementedError:
-            self.ui.warn("Can't sync labels. You need to configure a remote repository of type Gmail.")
+            self.ui.warn(u"Can't sync labels. You need to configure a remote "
+                "repository of type Gmail.")
diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py
index c7e6516..1f93e11 100644
--- a/offlineimap/folder/IMAP.py
+++ b/offlineimap/folder/IMAP.py
@@ -22,8 +22,8 @@ import time
 from sys import exc_info
 
 from .Base import BaseFolder
-from offlineimap import imaputil, imaplibutil, emailutil, OfflineImapError
-from offlineimap import globals
+from offlineimap import imaputil, imaplibutil, emailutil, OfflineImapError, globals
+from offlineimap.utils.uni import noneString, uniString, imapString
 from offlineimap.imaplib2 import MonthNames
 
 
@@ -42,19 +42,22 @@ class IMAPFolder(BaseFolder):
     def __init__(self, imapserver, name, repository):
         # FIXME: decide if unquoted name is from the responsability of the
         # caller or not, but not both.
-        name = imaputil.dequote(name)
-        self.sep = imapserver.delim
+        # TODO: dequoting has definetly to be done as soon as possible: who
+        # wants to work with quoted strings?
+        name = imapString(imaputil.dequote(name.imap))
+        self.sep = imapserver.getdelim()
         super(IMAPFolder, self).__init__(name, repository)
         self.expunge = repository.getexpunge()
-        self.root = None # imapserver.root
+        self.root = noneString() # imapserver.root
         self.imapserver = imapserver
         self.messagelist = None
         self.randomgenerator = random.Random()
-        #self.ui is set in BaseFolder
+        # UNICODE: this is IMAP query string in a list, don't use uni objects.
         self.imap_query = ['BODY.PEEK[]']
+        # self.ui is set in BaseFolder
 
-        fh_conf = self.repository.account.getconf('filterheaders', '')
-        self.filterheaders = [h for h in re.split(r'\s*,\s*', fh_conf) if h]
+        uni_fh_conf = self.repository.account.getconf('filterheaders', '')
+        self.filterheaders = [h for h in re.split(r'\s*,\s*', uni_fh_conf) if h]
 
 
     def __selectro(self, imapobj, force=False):
@@ -66,10 +69,12 @@ class IMAPFolder(BaseFolder):
         .. todo: Still valid? Needs verification
         :param: Enforce new SELECT even if we are on that folder already.
         :returns: raises :exc:`OfflineImapError` severity FOLDER on error"""
+
         try:
-            imapobj.select(self.getfullname(), force = force)
+            fullname = self.getfullname()
+            imapobj.select(fullname.imap, force=force)
         except imapobj.readonly:
-            imapobj.select(self.getfullname(), readonly = True, force = force)
+            imapobj.select(fullname.imap, readonly=True, force=force)
 
     # Interface from BaseFolder
     def suggeststhreads(self):
@@ -81,7 +86,9 @@ class IMAPFolder(BaseFolder):
 
     # Interface from BaseFolder
     def getcopyinstancelimit(self):
-        return 'MSGCOPY_' + self.repository.getname()
+        # XXX: has to be documented.
+        # I think to remember about that in init.py. Should check.
+        return uniString(u'MSGCOPY_' + self.repository.getname().uni)
 
     # Interface from BaseFolder
     def get_uidvalidity(self):
@@ -116,8 +123,8 @@ class IMAPFolder(BaseFolder):
             imapobj = self.imapserver.acquireconnection()
             try:
                 # Select folder and get number of messages
-                restype, imapdata = imapobj.select(self.getfullname(), True,
-                                                   True)
+                restype, imapdata = imapobj.select(
+                    self.getfullname().imap, True, True)
                 self.imapserver.releaseconnection(imapobj)
             except OfflineImapError as e:
                 # retry on dropped connections, raise otherwise
@@ -156,25 +163,26 @@ class IMAPFolder(BaseFolder):
         Returns: range(s) for messages or None if no messages
         are to be fetched."""
 
-        res_type, imapdata = imapobj.select(self.getfullname(), True, True)
+        res_type, imapdata = imapobj.select(
+            self.getfullname().imap, True, True)
         if imapdata == [None] or imapdata[0] == '0':
             # Empty folder, no need to populate message list
             return None
 
-        # By default examine all messages in this folder
+        # By default examine all messages in this folder.
         msgsToFetch = '1:*'
 
         maxage = self.config.getdefaultint(
-            "Account %s"% self.accountname, "maxage", -1)
+            uniString(u"Account %s").uni % self.accountname.uni, "maxage", -1)
         maxsize = self.config.getdefaultint(
-            "Account %s"% self.accountname, "maxsize", -1)
+            uniString(u"Account %s").uni % self.accountname.uni, "maxsize", -1)
 
-        # Build search condition
+        # Build search condition.
         if (maxage != -1) | (maxsize != -1):
             search_cond = "(";
 
             if(maxage != -1):
-                #find out what the oldest message is that we should look at
+                # Find out what the oldest message is that we should look at.
                 oldest_struct = time.gmtime(time.time() - (60*60*24*maxage))
                 if oldest_struct[0] < 1900:
                     raise OfflineImapError("maxage setting led to year %d. "
@@ -196,7 +204,8 @@ class IMAPFolder(BaseFolder):
             if res_type != 'OK':
                 raise OfflineImapError("SEARCH in folder [%s]%s failed. "
                     "Search string was '%s'. Server responded '[%s] %s'"% (
-                    self.getrepository(), self, search_cond, res_type, res_data),
+                    self.getrepository().fs, self.getname().fs,
+                    search_cond, res_type, res_data),
                     OfflineImapError.ERROR.FOLDER)
 
             # Resulting MSN are separated by space, coalesce into ranges
@@ -223,9 +232,11 @@ class IMAPFolder(BaseFolder):
             # imaplib2 from quoting the sequence.
             res_type, response = imapobj.fetch("'%s'"%
                 msgsToFetch, '(FLAGS UID)')
+            response = imapString(response)
             if res_type != 'OK':
                 raise OfflineImapError("FETCHING UIDs in folder [%s]%s failed. "
-                    "Server responded '[%s] %s'"% (self.getrepository(), self,
+                    "Server responded '[%s] %s'"% (
+                    self.getrepository().getname().fs, self.name.fs,
                     res_type, response), OfflineImapError.ERROR.FOLDER)
         finally:
             self.imapserver.releaseconnection(imapobj)
@@ -238,9 +249,8 @@ class IMAPFolder(BaseFolder):
             messagestr = messagestr.split(' ', 1)[1]
             options = imaputil.flags2hash(messagestr)
             if not 'UID' in options:
-                self.ui.warn('No UID in message with options %s'% \
-                                          str(options),
-                                          minor = 1)
+                self.ui.warn(u'No UID in message with options %s'%
+                    str(options), minor=1)
             else:
                 uid = long(options['UID'])
                 self.messagelist[uid] = self.msglist_item_initializer(uid)
@@ -282,8 +292,8 @@ class IMAPFolder(BaseFolder):
         else:
             dbg_output = data
 
-        self.ui.debug('imap', "Returned object from fetching %d: '%s'"%
-                      (uid, dbg_output))
+        self.ui.debug('imap', u"Returned object from fetching %d: '%s'"%
+            (uid, dbg_output))
 
         return data
 
@@ -324,7 +334,8 @@ class IMAPFolder(BaseFolder):
 
 
     def __savemessage_searchforheader(self, imapobj, headername, headervalue):
-        self.ui.debug('imap', '__savemessage_searchforheader called for %s: %s'% \
+        self.ui.debug('imap',
+            u'__savemessage_searchforheader called for %s: %s'%
             (headername, headervalue))
         # Now find the UID it got.
         headervalue = imapobj._quote(headervalue)
@@ -333,21 +344,27 @@ class IMAPFolder(BaseFolder):
                 headername, headervalue)[1][0]
         except imapobj.error as err:
             # IMAP server doesn't implement search or had a problem.
-            self.ui.debug('imap', "__savemessage_searchforheader: got IMAP error '%s' while attempting to UID SEARCH for message with header %s"% (err, headername))
+            self.ui.debug('imap', u"__savemessage_searchforheader: "
+                "got IMAP error '%s' while attempting to UID SEARCH "
+                "for message with header %s"% (err, headername))
             return 0
-        self.ui.debug('imap', '__savemessage_searchforheader got initial matchinguids: ' + repr(matchinguids))
+        self.ui.debug('imap', u'__savemessage_searchforheader got '
+            'initial matchinguids: %s'% repr(matchinguids))
 
         if matchinguids == '':
-            self.ui.debug('imap', "__savemessage_searchforheader: UID SEARCH for message with header %s yielded no results"% headername)
+            self.ui.debug('imap', u"__savemessage_searchforheader: "
+                "UID SEARCH for message with header %s yielded no results"%
+                headername)
             return 0
 
         matchinguids = matchinguids.split(' ')
-        self.ui.debug('imap', '__savemessage_searchforheader: matchinguids now ' + \
-                 repr(matchinguids))
+        self.ui.debug('imap', u'__savemessage_searchforheader: '
+            'matchinguids now %s'% repr(matchinguids))
         if len(matchinguids) != 1 or matchinguids[0] == None:
             raise ValueError("While attempting to find UID for message with "
-                             "header %s, got wrong-sized matchinguids of %s"%\
-                                 (headername, str(matchinguids)))
+                "header %s, got wrong-sized matchinguids of %s"%
+                (headername, str(matchinguids)))
+
         return long(matchinguids[0])
 
     def __savemessage_fetchheaders(self, imapobj, headername, headervalue):
@@ -373,8 +390,8 @@ class IMAPFolder(BaseFolder):
 
         Returns UID when found, 0 when not found."""
 
-        self.ui.debug('imap', '__savemessage_fetchheaders called for %s: %s'% \
-                 (headername, headervalue))
+        self.ui.debug('imap', u'__savemessage_fetchheaders called for %s: %s'%
+            (headername, headervalue))
 
         # run "fetch X:* rfc822.header"
         # since we stored the mail we are looking for just recently, it would
@@ -395,17 +412,18 @@ class IMAPFolder(BaseFolder):
 
         result = imapobj.uid('FETCH', bytearray('%d:*'% start), 'rfc822.header')
         if result[0] != 'OK':
-            raise OfflineImapError('Error fetching mail headers: %s'%
-                '. '.join(result[1]), OfflineImapError.ERROR.MESSAGE)
+            joint_result = imapString('. '.join(result[1]))
+            raise OfflineImapError("Error fetching mail headers: %s"%
+                joint_result.fs, OfflineImapError.ERROR.MESSAGE)
 
+        # UNICODE: this block works on raw IMAP data.
         result = result[1]
-
         found = 0
         for item in result:
             if found == 0 and type(item) == type( () ):
                 # Walk just tuples
-                if re.search("(?:^|\\r|\\n)%s:\s*%s(?:\\r|\\n)"% (headername, headervalue),
-                        item[1], flags=re.IGNORECASE):
+                if re.search("(?:^|\\r|\\n)%s:\s*%s(?:\\r|\\n)"%
+                    (headername, headervalue), item[1], flags=re.IGNORECASE):
                     found = 1
             elif found == 1:
                 if type(item) == type (""):
@@ -413,14 +431,18 @@ class IMAPFolder(BaseFolder):
                     if uid:
                         return int(uid.group(1))
                     else:
-                        self.ui.warn("Can't parse FETCH response, can't find UID: %s", result.__repr__())
+                        self.ui.warn(
+                            u"Can't parse FETCH response, can't find UID: %s",
+                            result.__repr__())
                 else:
-                    self.ui.warn("Can't parse FETCH response, we awaited string: %s", result.__repr__())
+                    self.ui.warn(
+                        u"Can't parse FETCH response, we awaited string: %s",
+                        result.__repr__())
 
         return 0
 
     def __getmessageinternaldate(self, content, rtime=None):
-        """Parses mail and returns an INTERNALDATE string
+        """Parses mail and returns an INTERNALDATE string.
 
         It will use information in the following order, falling back as an
         attempt fails:
@@ -471,7 +493,7 @@ class IMAPFolder(BaseFolder):
             # or something.  Argh.  It seems that Time2Internaldate
             # will rause a ValueError if the year is 0102 but not 1902,
             # but some IMAP servers nonetheless choke on 1902.
-            self.ui.debug('imap', "Message with invalid date %s. "
+            self.ui.debug('imap', u"Message with invalid date %s. "
                 "Server will use local time."% datetuple)
             return None
 
@@ -550,20 +572,22 @@ class IMAPFolder(BaseFolder):
                     # insert a random unique header that we can fetch later
                     (headername, headervalue) = self.__generate_randomheader(
                         content)
-                    self.ui.debug('imap', 'savemessage: header is: %s: %s'%
+                    self.ui.debug('imap', u'savemessage: header is: %s: %s'%
                         (headername, headervalue))
-                    content = self.addmessageheader(content, CRLF, headername, headervalue)
+                    content = self.addmessageheader(
+                        content, CRLF, headername, headervalue)
 
                 if len(content)>200:
                     dbg_output = "%s...%s"% (content[:150], content[-50:])
                 else:
                     dbg_output = content
-                self.ui.debug('imap', "savemessage: date: %s, content: '%s'"%
+                self.ui.debug('imap', u"savemessage: date: %s, content: '%s'"%
                     (date, dbg_output))
 
+                fullname = self.getfullname()
                 try:
                     # Select folder for append and make the box READ-WRITE
-                    imapobj.select(self.getfullname())
+                    imapobj.select(fullname.imap)
                 except imapobj.readonly:
                     # readonly exception. Return original uid to notify that
                     # we did not save the message. (see savemessage in Base.py)
@@ -572,7 +596,7 @@ class IMAPFolder(BaseFolder):
 
                 #Do the APPEND
                 try:
-                    (typ, dat) = imapobj.append(self.getfullname(),
+                    (typ, dat) = imapobj.append(fullname.imap,
                         imaputil.flagsmaildir2imap(flags), date, content)
                     # This should only catch 'NO' responses since append()
                     # will raise an exception for 'BAD' responses:
@@ -584,9 +608,11 @@ class IMAPFolder(BaseFolder):
                         # In this case, we should immediately abort the repository sync
                         # and continue with the next account.
                         msg = \
-                            "Saving msg (%s) in folder '%s', repository '%s' failed (abort). " \
+                            "Saving msg (%s) in folder '%s', " \
+                            "repository '%s' failed (abort). " \
                             "Server responded: %s %s\n"% \
-                            (msg_id, self, self.getrepository(), typ, dat)
+                            (msg_id, self.name.fs,
+                            self.getrepository().getname().fs, typ, dat)
                         raise OfflineImapError(msg, OfflineImapError.ERROR.REPO)
                     retry_left = 0 # Mark as success
                 except imapobj.abort as e:
@@ -596,11 +622,13 @@ class IMAPFolder(BaseFolder):
                     imapobj = self.imapserver.acquireconnection()
                     if not retry_left:
                         raise OfflineImapError("Saving msg (%s) in folder '%s', "
-                              "repository '%s' failed (abort). Server responded: %s\n"
-                              "Message content was: %s"%
-                              (msg_id, self, self.getrepository(), str(e), dbg_output),
-                                               OfflineImapError.ERROR.MESSAGE), \
-                              None, exc_info()[2]
+                            "repository '%s' failed (abort). Server responded: %s\n"
+                            "Message content was: %s"% (msg_id,
+                            self.name.fs,
+                            self.getrepository().getname().fs,
+                            str(e), dbg_output),
+                            OfflineImapError.ERROR.MESSAGE), \
+                            None, exc_info()[2]
                     # XXX: is this still needed?
                     self.ui.error(e, exc_info()[2])
                 except imapobj.error as e: # APPEND failed
@@ -611,12 +639,13 @@ class IMAPFolder(BaseFolder):
                     imapobj = None
                     raise OfflineImapError("Saving msg (%s) folder '%s', repo '%s'"
                         "failed (error). Server responded: %s\nMessage content was: "
-                        "%s" % (msg_id, self, self.getrepository(), str(e), dbg_output),
-                            OfflineImapError.ERROR.MESSAGE), None, exc_info()[2]
+                        "%s"% (msg_id, self.name.fs,
+                        self.getrepository().getname().fs, str(e), dbg_output),
+                        OfflineImapError.ERROR.MESSAGE), None, exc_info()[2]
             # Checkpoint. Let it write out stuff, etc. Eg searches for
             # just uploaded messages won't work if we don't do this.
-            (typ,dat) = imapobj.check()
-            assert(typ == 'OK')
+            (typ, dat) = imapobj.check()
+            assert (typ == 'OK')
 
             # get the new UID, do we use UIDPLUS?
             if use_uidplus:
@@ -628,12 +657,12 @@ class IMAPFolder(BaseFolder):
                 # data. TODO
                 resp = imapobj._get_untagged_response('APPENDUID')
                 if resp == [None] or resp is None:
-                    self.ui.warn("Server supports UIDPLUS but got no APPENDUID "
+                    self.ui.warn(u"Server supports UIDPLUS but got no APPENDUID "
                         "appending a message.")
                     return 0
                 uid = long(resp[-1].split(' ')[1])
                 if uid == 0:
-                    self.ui.warn("savemessage: Server supports UIDPLUS, but"
+                    self.ui.warn(u"savemessage: Server supports UIDPLUS, but"
                         " we got no usable uid back. APPENDUID reponse was "
                         "'%s'"% str(resp))
             else:
@@ -643,11 +672,11 @@ class IMAPFolder(BaseFolder):
                 # See docs for savemessage in Base.py for explanation
                 # of this and other return values
                 if uid == 0:
-                    self.ui.debug('imap', 'savemessage: attempt to get new UID '
+                    self.ui.debug('imap', u'savemessage: attempt to get new UID '
                         'UID failed. Search headers manually.')
-                    uid = self.__savemessage_fetchheaders(imapobj, headername,
-                        headervalue)
-                    self.ui.warn('imap', "savemessage: Searching mails for new "
+                    uid = self.__savemessage_fetchheaders(
+                        imapobj, headername, headervalue)
+                    self.ui.warn('imap', u"savemessage: Searching mails for new "
                         "Message-ID failed. Could not determine new UID.")
         finally:
             if imapobj: self.imapserver.releaseconnection(imapobj)
@@ -656,7 +685,7 @@ class IMAPFolder(BaseFolder):
             self.messagelist[uid] = self.msglist_item_initializer(uid)
             self.messagelist[uid]['flags'] = flags
 
-        self.ui.debug('imap', 'savemessage: returning new UID %d'% uid)
+        self.ui.debug('imap', u'savemessage: returning new UID %d'% uid)
         return uid
 
 
@@ -674,7 +703,7 @@ class IMAPFolder(BaseFolder):
         fails_left = retry_num # retry on dropped connection
         while fails_left:
             try:
-                imapobj.select(self.getfullname(), readonly = True)
+                imapobj.select(self.getfullname().imap, readonly=True)
                 res_type, data = imapobj.uid('fetch', uids, query)
                 fails_left = 0
             except imapobj.abort as e:
@@ -690,19 +719,20 @@ class IMAPFolder(BaseFolder):
             #IMAP server says bad request or UID does not exist
             severity = OfflineImapError.ERROR.MESSAGE
             reason = "IMAP server '%s' failed to fetch messages UID '%s'."\
-                "Server responded: %s %s"% (self.getrepository(), uids,
-                                             res_type, data)
+                "Server responded: %s %s"% (
+                self.getrepository().getname().fs, uids, res_type, data)
             if data == [None]:
                 #IMAP server did not find a message with this UID
                 reason = "IMAP server '%s' does not have a message "\
-                    "with UID '%s'" % (self.getrepository(), uids)
+                    "with UID '%s'"% (
+                    self.getrepository().getname().fs, uids)
             raise OfflineImapError(reason, severity)
 
         return data
 
 
     def _store_to_imap(self, imapobj, uid, field, data):
-        """Stores data to IMAP server
+        """Stores data to IMAP server.
 
         Arguments:
         - imapobj: instance of IMAPlib to use
@@ -710,14 +740,16 @@ class IMAPFolder(BaseFolder):
         - field: field name to be stored/updated
         - data: field contents
         """
-        imapobj.select(self.getfullname())
+
+        imapobj.select(self.getfullname().imap)
         res_type, retdata = imapobj.uid('store', uid, field, data)
         if res_type != 'OK':
             severity = OfflineImapError.ERROR.MESSAGE
             reason = "IMAP server '%s' failed to store %s for message UID '%d'."\
-                     "Server responded: %s %s"% (
-                    self.getrepository(), field, uid, res_type, retdata)
+                "Server responded: %s %s"% (self.getrepository().getname().fs,
+                field, uid, res_type, retdata)
             raise OfflineImapError(reason, severity)
+
         return retdata[0]
 
     # Interface from BaseFolder
@@ -771,13 +803,13 @@ class IMAPFolder(BaseFolder):
         imapobj = self.imapserver.acquireconnection()
         try:
             try:
-                imapobj.select(self.getfullname())
+                imapobj.select(self.getfullname().imap)
             except imapobj.readonly:
                 self.ui.flagstoreadonly(self, uidlist, flags)
                 return
             r = imapobj.uid('store',
                 imaputil.uid_sequence(uidlist), operation + 'FLAGS',
-                    imaputil.flagsmaildir2imap(flags))
+                imaputil.flagsmaildir2imap(flags))
             assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
             r = r[1]
         finally:
@@ -845,7 +877,7 @@ class IMAPFolder(BaseFolder):
         imapobj = self.imapserver.acquireconnection()
         try:
             try:
-                imapobj.select(self.getfullname())
+                imapobj.select(self.getfullname().imap)
             except imapobj.readonly:
                 self.ui.deletereadonly(self, uidlist)
                 return
diff --git a/offlineimap/folder/LocalStatus.py b/offlineimap/folder/LocalStatus.py
index c753f63..e6d47ac 100644
--- a/offlineimap/folder/LocalStatus.py
+++ b/offlineimap/folder/LocalStatus.py
@@ -19,6 +19,7 @@ from sys import exc_info
 import os
 import threading
 
+from offlineimap.utils.uni import uniString, fsString, isASCII
 from .Base import BaseFolder
 
 
@@ -29,10 +30,14 @@ class LocalStatusFolder(BaseFolder):
     magicline = "OFFLINEIMAP LocalStatus CACHE DATA - DO NOT MODIFY - FORMAT %d"
 
     def __init__(self, name, repository):
-        self.sep = '.' #needs to be set before super.__init__()
+        self.sep = uniString(u'.') # needs to be set before super.__init__()
         super(LocalStatusFolder, self).__init__(name, repository)
-        self.root = repository.root
-        self.filename = os.path.join(self.getroot(), self.getfolderbasename())
+        self.root = repository.getlocalroot() # XXX: what root looks like?
+        # Fix the filename to the IMAP UTF-7 encoding version. This prevents
+        # from playing with multiple cache files representative of the same
+        # remote folder.
+        self.filename = fsString(os.path.join(
+            self.root.fs, self.getfolderbasename().imap))
         self.messagelist = {}
         self.savelock = threading.Lock()
         # Should we perform fsyncs as often as possible?
@@ -44,15 +49,16 @@ class LocalStatusFolder(BaseFolder):
         return 0
 
     def isnewfolder(self):
-        return not os.path.exists(self.filename)
+        return not os.path.exists(self.filename.fs)
 
     # Interface from BaseFolder
     def getfullname(self):
         return self.filename
 
     def deletemessagelist(self):
+
         if not self.isnewfolder():
-            os.unlink(self.filename)
+            os.unlink(self.filename.fs)
 
     # Interface from BaseFolder
     def msglist_item_initializer(self, uid):
@@ -72,10 +78,16 @@ class LocalStatusFolder(BaseFolder):
                 uid = long(uid)
                 flags = set(flags)
             except ValueError as e:
-                errstr = "Corrupt line '%s' in cache file '%s'" % \
-                    (line, self.filename)
+                # Mixing encoding:
+                # - line: should be ASCII but unexpected bug could make it
+                #   string of bytes with unkown encoding.
+                # - self.filename: filesystem encoded.
+                # Converting from unkown encoding is worse than keeping things
+                # as-is.
+                errstr = uniString(u"Corrupt line '%s' in cache file '%s'"%
+                    (line, self.filename.uni))
                 self.ui.warn(errstr)
-                raise ValueError(errstr), None, exc_info()[2]
+                raise ValueError(errstr.fs), None, exc_info()[2]
             self.messagelist[uid] = self.msglist_item_initializer(uid)
             self.messagelist[uid]['flags'] = flags
 
@@ -93,12 +105,19 @@ class LocalStatusFolder(BaseFolder):
                 uid = long(uid)
                 flags = set(flags)
                 mtime = long(mtime)
-                labels = set([lb.strip() for lb in labels.split(',') if len(lb.strip()) > 0])
+                labels = set([
+                    lb.strip() for lb in labels.split(',') if len(lb.strip()) > 0])
             except ValueError as e:
-                errstr = "Corrupt line '%s' in cache file '%s'"% \
-                    (line, self.filename)
+                # Mixing encoding:
+                # - line: should be ASCII but unexpected bug could make it
+                #   string of bytes with unkown encoding.
+                # - self.filename: filesystem encoded.
+                # Converting from unkown encoding is worse than keeping things
+                # as-is.
+                errstr = uniString(u"Corrupt line '%s' in cache file '%s'"%
+                    (line, self.filename.uni))
                 self.ui.warn(errstr)
-                raise ValueError(errstr), None, exc_info()[2]
+                raise ValueError(errstr.fs), None, exc_info()[2]
             self.messagelist[uid] = self.msglist_item_initializer(uid)
             self.messagelist[uid]['flags'] = flags
             self.messagelist[uid]['mtime'] = mtime
@@ -123,8 +142,9 @@ class LocalStatusFolder(BaseFolder):
 
             # Convert from format v1.
             elif line == (self.magicline % 1):
-                self.ui._msg('Upgrading LocalStatus cache from version 1'
-                    'to version 2 for %s:%s'% (self.repository, self))
+                self.ui._msg(u'Upgrading LocalStatus cache from version 1'
+                    'to version 2 for %s:%s'%
+                    (self.repository.getname().uni, self.name.uni))
                 self.readstatus_v1(cachefd)
                 cachefd.close()
                 self.save()
@@ -139,14 +159,16 @@ class LocalStatusFolder(BaseFolder):
 
             # Something is wrong.
             else:
-                errstr = "Unrecognized cache magicline in '%s'" % self.filename
+                # Keep string filesystem encoded.
+                errstr = uniString(
+                    u"Unrecognized cache magicline in '%s'"% self.filename.uni)
                 self.ui.warn(errstr)
-                raise ValueError(errstr)
+                raise ValueError(errstr.fs)
 
         if not line:
             # The status file is empty - should not have happened,
             # but somehow did.
-            errstr = "Cache file '%s' is empty."% self.filename
+            errstr = uniString(u"Cache file '%s' is empty."% self.filename.uni)
             self.ui.warn(errstr)
             cachefd.close()
             return
@@ -172,15 +194,23 @@ class LocalStatusFolder(BaseFolder):
             for msg in self.messagelist.values():
                 flags = ''.join(sorted(msg['flags']))
                 labels = ', '.join(sorted(msg['labels']))
-                cachefd.write("%s|%s|%d|%s\n" % (msg['uid'], flags, msg['mtime'], labels))
+                data = u"%s|%s|%d|%s\n"% (msg['uid'], flags, msg['mtime'], labels)
+                # Ensure all data is full ASCII.
+                isASCII(data, exception_msg=
+                    "unsupported character was about to go to the cache file %s"%
+                    self.filename.fs)
+                cachefd.write(data)
             cachefd.flush()
             if self.doautosave:
                 os.fsync(cachefd.fileno())
             cachefd.close()
-            os.rename(self.filename + ".tmp", self.filename)
+            os.rename(self.filename.fs + ".tmp", self.filename.fs)
 
             if self.doautosave:
-                fd = os.open(os.path.dirname(self.filename), os.O_RDONLY)
+                # fsync on directory ensure that a newly created file will exist
+                # in the directory. The filesystem must support this feature.
+                p = os.path.dirname(self.filename.fs)
+                fd = os.open(p, os.O_RDONLY)
                 os.fsync(fd)
                 os.close(fd)
 
diff --git a/offlineimap/folder/LocalStatusSQLite.py b/offlineimap/folder/LocalStatusSQLite.py
index 8a3b9df..f0800f8 100644
--- a/offlineimap/folder/LocalStatusSQLite.py
+++ b/offlineimap/folder/LocalStatusSQLite.py
@@ -20,68 +20,77 @@ from threading import Lock
 try:
     import sqlite3 as sqlite
 except:
-    pass #fail only if needed later on, not on import
+    pass # Fail only if needed later on, not on import.
 
 from .Base import BaseFolder
+from offlineimap.utils.uni import uniString, fsString
 
 
 class LocalStatusSQLiteFolder(BaseFolder):
-    """LocalStatus backend implemented with an SQLite database
+    """LocalStatus backend implemented with an SQLite database.
 
     As python-sqlite currently does not allow to access the same sqlite
     objects from various threads, we need to open get and close a db
     connection and cursor for all operations. This is a big disadvantage
     and we might want to investigate if we cannot hold an object open
     for a thread somehow."""
-    #though. According to sqlite docs, you need to commit() before
-    #the connection is closed or your changes will be lost!"""
-    #get db connection which autocommits
-    #connection = sqlite.connect(self.filename, isolation_level=None)
-    #cursor = connection.cursor()
-    #return connection, cursor
-
-    #current version of our db format
+
+    # Though. According to sqlite docs, you need to commit() before
+    # the connection is closed or your changes will be lost!
+    # Get db connection which autocommits
+    # connection = sqlite.connect(self.filename.fs, isolation_level=None)
+    # cursor = connection.cursor()
+    # return connection, cursor
+
+    # Current version of our db format.
     cur_version = 2
 
     def __init__(self, name, repository):
-        self.sep = '.' #needs to be set before super.__init__()
+        self.sep = uniString(u'.') # needs to be set before super.__init__()
         super(LocalStatusSQLiteFolder, self).__init__(name, repository)
-        self.root = repository.root
-        self.filename = os.path.join(self.getroot(), self.getfolderbasename())
+        self.root = repository.getlocalroot()
+        folderbasename = self.getfolderbasename()
+        # Fix the filename to the IMAP UTF-7 encoding version. This prevents
+        # from playing with multiple cache files representative of the same
+        # remote folder.
+        self.filename = fsString(os.path.join(
+            self.getroot().fs, self.getfolderbasename().imap))
         self.messagelist = {}
-
         self._newfolder = False        # flag if the folder is new
 
-        dirname = os.path.dirname(self.filename)
-        if not os.path.exists(dirname):
-            os.makedirs(dirname)
-        if not os.path.isdir(dirname):
+        dirname = fsString(os.path.dirname(self.filename.fs))
+        if not os.path.exists(dirname.fs):
+            os.makedirs(dirname.fs)
+        if not os.path.isdir(dirname.fs):
+            # dirname is expected filesystem encoded.
             raise UserWarning("SQLite database path '%s' is not a directory."%
-                dirname)
+                dirname.fs)
 
         # dblock protects against concurrent writes in same connection
         self._dblock = Lock()
 
-        #Try to establish connection, no need for threadsafety in __init__
+        # Try to establish connection, no need for threadsafety in __init__.
         try:
-            self.connection = sqlite.connect(self.filename, check_same_thread=False)
+            self.connection = sqlite.connect(
+                self.filename.fs, check_same_thread=False)
         except NameError:
             # sqlite import had failed
             raise UserWarning('SQLite backend chosen, but no sqlite python '
                 'bindings available. Please install.'), None, exc_info()[2]
 
-        #Make sure sqlite is in multithreading SERIALIZE mode
+        # Make sure sqlite is in multithreading SERIALIZE mode.
         assert sqlite.threadsafety == 1, 'Your sqlite is not multithreading safe.'
 
-        #Test if db version is current enough and if db is readable.
+        # Test if db version is current enough and if db is readable.
         try:
             cursor = self.connection.execute(
                 "SELECT value from metadata WHERE key='db_version'")
         except sqlite.DatabaseError:
-            #db file missing or corrupt, recreate it.
+            # db file missing or corrupt, recreate it.
+            # XXX: warn the user about that.
             self.__create_db()
         else:
-            # fetch db version and upgrade if needed
+            # fetch db version and upgrade if needed.
             version = int(cursor.fetchone()[0])
             if version < LocalStatusSQLiteFolder.cur_version:
                 self.__upgrade_db(version)
@@ -144,27 +153,31 @@ class LocalStatusSQLiteFolder(BaseFolder):
         return cursor
 
     def __upgrade_db(self, from_ver):
-        """Upgrade the sqlite format from version 'from_ver' to current"""
+        """Upgrade the sqlite format from version 'from_ver' to current."""
 
         if hasattr(self, 'connection'):
-            self.connection.close() #close old connections first
+            self.connection.close() # Close old connections first.
         self.connection = sqlite.connect(self.filename,
-                                         check_same_thread = False)
+            check_same_thread=False)
 
         # Upgrade from database version 1 to version 2
-        # This change adds labels and mtime columns, to be used by Gmail IMAP and Maildir folders.
+        # This change adds labels and mtime columns, to be used by Gmail IMAP
+        # and Maildir folders.
+
         if from_ver <= 1:
-            self.ui._msg('Upgrading LocalStatus cache from version 1 to version 2 for %s:%s'%
-                (self.repository, self))
-            self.connection.executescript("""ALTER TABLE status ADD mtime INTEGER DEFAULT 0;
-                                             ALTER TABLE status ADD labels VARCHAR(256) DEFAULT '';
-                                             UPDATE metadata SET value='2' WHERE key='db_version';
-                                          """)
+            self.ui._msg(u'Upgrading LocalStatus cache from version 1 '
+                'to version 2 for %s:%s'%
+                (self.repository.getname().uni, self.name.uni))
+            self.connection.executescript("""
+ALTER TABLE status ADD mtime INTEGER DEFAULT 0;
+ALTER TABLE status ADD labels VARCHAR(256) DEFAULT '';
+UPDATE metadata SET value='2' WHERE key='db_version';
+                """)
             self.connection.commit()
 
         # Future version upgrades come here...
-        # if from_ver <= 2: ... #upgrade from 2 to 3
-        # if from_ver <= 3: ... #upgrade from 3 to 4
+        # if from_ver <= 2: ... # upgrade from 2 to 3
+        # if from_ver <= 3: ... # upgrade from 3 to 4
 
 
     def __create_db(self):
@@ -172,12 +185,13 @@ class LocalStatusSQLiteFolder(BaseFolder):
 
         self.connection must point to the opened and valid SQlite
         database connection."""
-        self.ui._msg('Creating new Local Status db for %s:%s' \
-                         % (self.repository, self))
+
+        self.ui._msg(u"Creating new Local Status db for %s:%s to '%s'"%
+            (self.repository.getname().uni, self.name.uni, self.filename.uni))
         self.connection.executescript("""
-        CREATE TABLE metadata (key VARCHAR(50) PRIMARY KEY, value VARCHAR(128));
-        INSERT INTO metadata VALUES('db_version', '2');
-        CREATE TABLE status (id INTEGER PRIMARY KEY, flags VARCHAR(50), mtime INTEGER, labels VARCHAR(256));
+CREATE TABLE metadata (key VARCHAR(50) PRIMARY KEY, value VARCHAR(128));
+INSERT INTO metadata VALUES('db_version', '2');
+CREATE TABLE status (id INTEGER PRIMARY KEY, flags VARCHAR(50), mtime INTEGER, labels VARCHAR(256));
         """)
         self.connection.commit()
         self._newfolder = True
@@ -223,7 +237,6 @@ class LocalStatusSQLiteFolder(BaseFolder):
             '(id,flags,mtime,labels) VALUES (?,?,?,?)',
             data, executemany=True)
 
-
     # Following some pure SQLite functions, where we chose to use
     # BaseFolder() methods instead. Doing those on the in-memory list is
     # quicker anyway. If our db becomes so big that we don't want to
@@ -235,8 +248,10 @@ class LocalStatusSQLiteFolder(BaseFolder):
     #    with conn:
     #        cursor.execute('SELECT id FROM status WHERE id=:id',{'id': uid})
     #        return cursor.fetchone()
+    #
     # This would be the pure SQLite solution, use BaseFolder() method,
     # to avoid threading with sqlite...
+    #
     #def getmessageuidlist(self):
     #    conn, cursor = self.get_cursor()
     #    with conn:
@@ -245,16 +260,18 @@ class LocalStatusSQLiteFolder(BaseFolder):
     #        for row in cursor:
     #            r.append(row[0])
     #        return r
+    #
     #def getmessagecount(self):
     #    conn, cursor = self.get_cursor()
     #    with conn:
     #        cursor.execute('SELECT count(id) from status');
     #        return cursor.fetchone()[0]
+    #
     #def getmessageflags(self, uid):
     #    conn, cursor = self.get_cursor()
     #    with conn:
     #        cursor.execute('SELECT flags FROM status WHERE id=:id',
-    #                        {'id': uid})
+    #            {'id': uid})
     #        for row in cursor:
     #            flags = [x for x in row[0]]
     #            return flags
@@ -286,8 +303,10 @@ class LocalStatusSQLiteFolder(BaseFolder):
         self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime, 'mtime': mtime, 'labels': labels}
         flags = ''.join(sorted(flags))
         labels = ', '.join(sorted(labels))
-        self.__sql_write('INSERT INTO status (id,flags,mtime,labels) VALUES (?,?,?,?)',
-                         (uid,flags,mtime,labels))
+        self.__sql_write(
+            'INSERT INTO status (id,flags,mtime,labels) VALUES (?,?,?,?)',
+            (uid,flags,mtime,labels))
+
         return uid
 
 
@@ -296,7 +315,7 @@ class LocalStatusSQLiteFolder(BaseFolder):
         assert self.uidexists(uid)
         self.messagelist[uid]['flags'] = flags
         flags = ''.join(sorted(flags))
-        self.__sql_write('UPDATE status SET flags=? WHERE id=?',(flags,uid))
+        self.__sql_write('UPDATE status SET flags=? WHERE id=?', (flags,uid))
 
 
     def getmessageflags(self, uid):
@@ -309,18 +328,20 @@ class LocalStatusSQLiteFolder(BaseFolder):
 
         labels = ', '.join(sorted(labels))
         if mtime:
-            self.__sql_write('UPDATE status SET labels=?, mtime=? WHERE id=?',(labels,mtime,uid))
+            self.__sql_write(
+                'UPDATE status SET labels=?, mtime=? WHERE id=?',
+                (labels,mtime,uid))
         else:
-            self.__sql_write('UPDATE status SET labels=? WHERE id=?',(labels,uid))
+            self.__sql_write('UPDATE status SET labels=? WHERE id=?',
+                (labels,uid))
 
 
     def savemessageslabelsbulk(self, labels):
-        """
-        Saves labels from a dictionary in a single database operation.
-        
-        """
+        """Saves labels from a dictionary in a single database operation."""
+
         data = [(', '.join(sorted(l)), uid) for uid, l in labels.items()]
-        self.__sql_write('UPDATE status SET labels=? WHERE id=?', data, executemany=True)
+        self.__sql_write(
+            'UPDATE status SET labels=? WHERE id=?', data, executemany=True)
         for uid, l in labels.items():
             self.messagelist[uid]['labels'] = l
 
@@ -330,7 +351,8 @@ class LocalStatusSQLiteFolder(BaseFolder):
         for uid in uids:
             newlabels = self.messagelist[uid]['labels'] | labels
             data.append((', '.join(sorted(newlabels)), uid))
-        self.__sql_write('UPDATE status SET labels=? WHERE id=?', data, executemany=True)
+        self.__sql_write(
+            'UPDATE status SET labels=? WHERE id=?', data, executemany=True)
         for uid in uids:
             self.messagelist[uid]['labels'] = self.messagelist[uid]['labels'] | labels
 
@@ -340,7 +362,8 @@ class LocalStatusSQLiteFolder(BaseFolder):
         for uid in uids:
             newlabels = self.messagelist[uid]['labels'] - labels
             data.append((', '.join(sorted(newlabels)), uid))
-        self.__sql_write('UPDATE status SET labels=? WHERE id=?', data, executemany=True)
+        self.__sql_write(
+            'UPDATE status SET labels=? WHERE id=?', data, executemany=True)
         for uid in uids:
             self.messagelist[uid]['labels'] = self.messagelist[uid]['labels'] - labels
 
@@ -353,7 +376,8 @@ class LocalStatusSQLiteFolder(BaseFolder):
         """Saves mtimes from the mtimes dictionary in a single database operation."""
 
         data = [(mt, uid) for uid, mt in mtimes.items()]
-        self.__sql_write('UPDATE status SET mtime=? WHERE id=?', data, executemany=True)
+        self.__sql_write(
+            'UPDATE status SET mtime=? WHERE id=?', data, executemany=True)
         for uid, mt in mtimes.items():
             self.messagelist[uid]['mtime'] = mt
 
@@ -382,6 +406,7 @@ class LocalStatusSQLiteFolder(BaseFolder):
         if not len(uidlist):
             return
         # arg2 needs to be an iterable of 1-tuples [(1,),(2,),...]
-        self.__sql_write('DELETE FROM status WHERE id=?', zip(uidlist, ), True)
+        self.__sql_write(
+            'DELETE FROM status WHERE id=?', zip(uidlist, ), True)
         for uid in uidlist:
             del(self.messagelist[uid])
diff --git a/offlineimap/folder/Maildir.py b/offlineimap/folder/Maildir.py
index b35bfc2..136209c 100644
--- a/offlineimap/folder/Maildir.py
+++ b/offlineimap/folder/Maildir.py
@@ -32,6 +32,8 @@ except NameError:
     from sets import Set as set
 
 from offlineimap import OfflineImapError
+from offlineimap.utils.uni import uniString, fsString
+
 
 # Find the UID in a message filename
 re_uidmatch = re.compile(',U=(\d+)')
@@ -57,8 +59,22 @@ def _gettimeseq():
     finally:
         timelock.release()
 
+#
+# XXX: some methods do a lot of os.path.join()/os.path.split(). Check if it can
+# be factorized in methods with names explaining WHAT are the returned values.
+# I mean, not a generic "splitted_blah()" thing but rather something like
+# "dirname_tmp_filename()".
+#
+
 class MaildirFolder(BaseFolder):
     def __init__(self, root, name, sep, repository):
+        """:params:
+        - root: path
+        - name: folder name
+        - sep: sep character.
+        - repository: repository instance.
+        """
+
         self.sep = sep # needs to be set before super().__init__
         super(MaildirFolder, self).__init__(name, repository)
         self.dofsync = self.config.getdefaultboolean("general", "fsync", True)
@@ -66,21 +82,24 @@ class MaildirFolder(BaseFolder):
         self.messagelist = None
         # check if we should use a different infosep to support Win file systems
         self.wincompatible = self.config.getdefaultboolean(
-            "Account "+self.accountname, "maildir-windows-compatible", False)
-        self.infosep = '!' if self.wincompatible else ':'
+            "Account " + self.accountname.dbytes,
+            "maildir-windows-compatible", False)
+        self.infosep = uniString(u'!') if self.wincompatible else uniString(u':')
         """infosep is the separator between maildir name and flag appendix"""
-        self.re_flagmatch = re.compile('%s2,(\w*)'% self.infosep)
+        self.re_flagmatch = re.compile('%s2,(\w*)'% self.infosep.dbytes)
         #self.ui is set in BaseFolder.init()
         # Everything up to the first comma or colon (or ! if Windows):
-        self.re_prefixmatch = re.compile('([^'+ self.infosep + ',]*)')
-        #folder's md, so we can match with recorded file md5 for validity
-        self._foldermd5 = md5(self.getvisiblename()).hexdigest()
+        self.re_prefixmatch = re.compile('([^'+ self.infosep.dbytes + ',]*)')
+        # folder's md, so we can match with recorded file md5 for validity
+        self._foldermd5 = md5(self.getvisiblename().imap).hexdigest()
         # Cache the full folder path, as we use getfullname() very often
-        self._fullname = os.path.join(self.getroot(), self.getname())
+        self._fullname = fsString(os.path.join(
+            self.root.fs, self.name.fs))
 
     # Interface from BaseFolder
     def getfullname(self):
         """Return the absolute file path to the Maildir folder (sans cur|new)"""
+
         return self._fullname
 
     # Interface from BaseFolder
@@ -89,14 +108,15 @@ class MaildirFolder(BaseFolder):
 
         Maildirs have no notion of uidvalidity, so we just return a magic
         token."""
+
         return 42
 
-    #Checks to see if the given message is within the maximum age according
-    #to the maildir name which should begin with a timestamp
+    # Checks to see if the given message is within the maximum age according
+    # to the maildir name which should begin with a timestamp.
     def _iswithinmaxage(self, messagename, maxage):
-        #In order to have the same behaviour as SINCE in an IMAP search
-        #we must convert this to the oldest time and then strip off hrs/mins
-        #from that day
+        # In order to have the same behaviour as SINCE in an IMAP search
+        # we must convert this to the oldest time and then strip off hrs/mins
+        # from that day
         oldest_time_utc = time.time() - (60*60*24*maxage)
         oldest_time_struct = time.gmtime(oldest_time_utc)
         oldest_time_today_seconds = ((oldest_time_struct[3] * 3600) \
@@ -132,18 +152,18 @@ class MaildirFolder(BaseFolder):
         """
 
         prefix, uid, fmd5, flags = None, None, None, set()
-        prefixmatch = self.re_prefixmatch.match(filename)
+        prefixmatch = self.re_prefixmatch.match(filename.fs)
         if prefixmatch:
             prefix = prefixmatch.group(1)
         folderstr = ',FMD5=%s'% self._foldermd5
-        foldermatch = folderstr in filename
+        foldermatch = folderstr in filename.fs
         # If there was no folder MD5 specified, or if it mismatches,
         # assume it is a foreign (new) message and ret: uid, fmd5 = None, None
         if foldermatch:
-            uidmatch = re_uidmatch.search(filename)
+            uidmatch = re_uidmatch.search(filename.fs)
             if uidmatch:
                 uid = long(uidmatch.group(1))
-        flagmatch = self.re_flagmatch.search(filename)
+        flagmatch = self.re_flagmatch.search(filename.fs)
         if flagmatch:
             # Filter out all lowercase (custom maildir) flags. We don't
             # handle them yet.
@@ -158,44 +178,45 @@ class MaildirFolder(BaseFolder):
         :returns: dict that can be used as self.messagelist.
         """
 
-        maxage = self.config.getdefaultint("Account " + self.accountname,
-                                           "maxage", None)
-        maxsize = self.config.getdefaultint("Account " + self.accountname,
-                                            "maxsize", None)
+        maxage = self.config.getdefaultint(
+            "Account " + self.accountname.dbytes, "maxage", None)
+        maxsize = self.config.getdefaultint(
+            "Account " + self.accountname.dbytes, "maxsize", None)
         retval = {}
         files = []
         nouidcounter = -1          # Messages without UIDs get negative UIDs.
         for dirannex in ['new', 'cur']:
-            fulldirname = os.path.join(self.getfullname(), dirannex)
-            files.extend((dirannex, filename) for
-                         filename in os.listdir(fulldirname))
+            fs_fulldirname = os.path.join(self.getfullname().fs, dirannex)
+            files.extend((dirannex, fs_filename) for
+                fs_filename in os.listdir(fs_fulldirname))
 
-        for dirannex, filename in files:
-            # We store just dirannex and filename, ie 'cur/123...'
-            filepath = os.path.join(dirannex, filename)
+        for dirannex, fs_filename in files:
+            # We store just dirannex and fs_filename, ie 'cur/123...'
+            fs_filepath = os.path.join(dirannex, fs_filename)
             # check maxage/maxsize if this message should be considered
-            if maxage and not self._iswithinmaxage(filename, maxage):
+            if maxage and not self._iswithinmaxage(fs_filename, maxage):
                 continue
-            if maxsize and (os.path.getsize(os.path.join(
-                        self.getfullname(), filepath)) > maxsize):
+            if maxsize and (os.path.getsize(
+                os.path.join(self.getfullname().fs, fs_filepath)) > maxsize):
                 continue
 
-            (prefix, uid, fmd5, flags) = self._parse_filename(filename)
+            (prefix, uid, fmd5, flags) = self._parse_filename(
+                fsString(fs_filename))
             if uid is None: # assign negative uid to upload it.
                 uid = nouidcounter
                 nouidcounter -= 1
             else:                       # It comes from our folder.
-                uidmatch = re_uidmatch.search(filename)
+                uidmatch = re_uidmatch.search(fs_filename)
                 uid = None
                 if not uidmatch:
                     uid = nouidcounter
                     nouidcounter -= 1
                 else:
                     uid = long(uidmatch.group(1))
-            # 'filename' is 'dirannex/filename', e.g. cur/123,U=1,FMD5=1:2,S
+            # 'fs_filename' is 'dirannex/fs_filename', e.g. cur/123,U=1,FMD5=1:2,S
             retval[uid] = self.msglist_item_initializer(uid)
             retval[uid]['flags'] = flags
-            retval[uid]['filename'] = filepath
+            retval[uid]['filename'] = fsString(fs_filepath)
         return retval
 
     # Interface from BaseFolder
@@ -235,10 +256,10 @@ class MaildirFolder(BaseFolder):
         """Return the content of the message."""
 
         filename = self.messagelist[uid]['filename']
-        filepath = os.path.join(self.getfullname(), filename)
-        file = open(filepath, 'rt')
-        retval = file.read()
-        file.close()
+        fs_filepath = os.path.join(self.getfullname().fs, filename.fs)
+        f = open(fs_filepath, 'rt')
+        retval = f.read()
+        f.close()
         #TODO: WHY are we replacing \r\n with \n here? And why do we
         #      read it as text?
         return retval.replace("\r\n", "\n")
@@ -246,8 +267,8 @@ class MaildirFolder(BaseFolder):
     # Interface from BaseFolder
     def getmessagetime(self, uid):
         filename = self.messagelist[uid]['filename']
-        filepath = os.path.join(self.getfullname(), filename)
-        return os.path.getmtime(filepath)
+        fs_filepath = os.path.join(self.getfullname().fs, filename.fs)
+        return os.path.getmtime(fs_filepath)
 
     def new_message_filename(self, uid, flags=set()):
         """Creates a new unique Maildir filename
@@ -273,14 +294,14 @@ class MaildirFolder(BaseFolder):
         Returns: relative path to the temporary file
         that was created."""
 
-        tmpname = os.path.join('tmp', filename)
+        tmpname = fsString(os.path.join('tmp', filename.fs))
         # open file and write it out
         tries = 7
         while tries:
             tries = tries - 1
             try:
-                fd = os.open(os.path.join(self.getfullname(), tmpname),
-                             os.O_EXCL|os.O_CREAT|os.O_WRONLY, 0o666)
+                fs_path = os.path.join(self.getfullname().fs, tmpname.fs)
+                fd = os.open(fs_path, os.O_EXCL|os.O_CREAT|os.O_WRONLY, 0o666)
                 break
             except OSError as e:
                 if e.errno == e.EEXIST:
@@ -288,8 +309,9 @@ class MaildirFolder(BaseFolder):
                         time.sleep(0.23)
                         continue
                     severity = OfflineImapError.ERROR.MESSAGE
-                    raise OfflineImapError("Unique filename %s already exists." % \
-                      filename, severity), None, exc_info()[2]
+                    # filename is expected filesystem encoded.
+                    raise OfflineImapError("Unique filename %s already exists."%
+                      filename.fs, severity), None, exc_info()[2]
                 else:
                     raise
 
@@ -303,7 +325,6 @@ class MaildirFolder(BaseFolder):
 
         return tmpname
 
-
     # Interface from BaseFolder
     def savemessage(self, uid, content, flags, rtime):
         """Writes a new message, with the specified uid.
@@ -311,9 +332,10 @@ class MaildirFolder(BaseFolder):
         See folder/Base for detail. Note that savemessage() does not
         check against dryrun settings, so you need to ensure that
         savemessage is never called in a dryrun mode."""
+
         # This function only ever saves to tmp/,
         # but it calls savemessageflags() to actually save to cur/ or new/.
-        self.ui.savemessage('maildir', uid, flags, self)
+        self.ui.savemessage('maildir', uid, flags, self.name)
         if uid < 0:
             # We cannot assign a new uid.
             return uid
@@ -325,18 +347,20 @@ class MaildirFolder(BaseFolder):
 
         # Otherwise, save the message in tmp/ and then call savemessageflags()
         # to give it a permanent home.
-        tmpdir = os.path.join(self.getfullname(), 'tmp')
+        fs_tmpdir = os.path.join(self.getfullname().fs, 'tmp') # FIXME: never used!
         messagename = self.new_message_filename(uid, flags)
         tmpname = self.save_to_tmp_file(messagename, content)
         if rtime != None:
-            os.utime(os.path.join(self.getfullname(), tmpname), (rtime, rtime))
+            os.utime(os.path.join(self.getfullname().fs, tmpname.fs),
+                (rtime, rtime))
 
         self.messagelist[uid] = self.msglist_item_initializer(uid)
         self.messagelist[uid]['flags'] = flags
         self.messagelist[uid]['filename'] = tmpname
         # savemessageflags moves msg to 'cur' or 'new' as appropriate
         self.savemessageflags(uid, flags)
-        self.ui.debug('maildir', 'savemessage: returning uid %d' % uid)
+        self.ui.debug('maildir', u'savemessage: returning uid %d'% uid)
+
         return uid
 
     # Interface from BaseFolder
@@ -357,30 +381,32 @@ class MaildirFolder(BaseFolder):
         assert uid in self.messagelist
 
         oldfilename = self.messagelist[uid]['filename']
-        dir_prefix, filename = os.path.split(oldfilename)
+        # UNICODE: Keep those as Python types.
+        fs_dir_prefix, fs_filename = os.path.split(oldfilename.fs)
         # If a message has been seen, it goes into 'cur'
-        dir_prefix = 'cur' if 'S' in flags else 'new'
+        fs_dir_prefix = 'cur' if 'S' in flags else 'new'
 
         if flags != self.messagelist[uid]['flags']:
-            # Flags have actually changed, construct new filename Strip
+            # Flags have actually changed, construct new filename. Strip
             # off existing infostring (possibly discarding small letter
-            # flags that dovecot uses TODO)
-            infomatch = self.re_flagmatch.search(filename)
+            # flags that dovecot uses TODO).
+            infomatch = self.re_flagmatch.search(fs_filename)
             if infomatch:
-                filename = filename[:-len(infomatch.group())] #strip off
-            infostr = '%s2,%s'% (self.infosep, ''.join(sorted(flags)))
-            filename += infostr
+                fs_filename = fs_filename[:-len(infomatch.group())] # strip off
+            infostr = '%s2,%s'% (self.infosep.fs, ''.join(sorted(flags)))
+            fs_filename += infostr
 
-        newfilename = os.path.join(dir_prefix, filename)
+        newfilename = fsString(os.path.join(fs_dir_prefix, fs_filename))
         if (newfilename != oldfilename):
             try:
-                os.rename(os.path.join(self.getfullname(), oldfilename),
-                          os.path.join(self.getfullname(), newfilename))
+                fs_old = os.path.join(self.getfullname().fs, oldfilename.fs)
+                fs_new = os.path.join(self.getfullname().fs, newfilename.fs)
+                os.rename(fs_old, fs_new)
             except OSError as e:
-                raise OfflineImapError("Can't rename file '%s' to '%s': %s" % (
-                                       oldfilename, newfilename, e[1]),
-                                       OfflineImapError.ERROR.FOLDER), \
-                      None, exc_info()[2]
+                raise OfflineImapError("Can't rename file '%s' to '%s': %s"% (
+                    oldfilename.fs, newfilename.fs, e[1]),
+                    OfflineImapError.ERROR.FOLDER), \
+                    None, exc_info()[2]
 
             self.messagelist[uid]['flags'] = flags
             self.messagelist[uid]['filename'] = newfilename
@@ -400,12 +426,13 @@ class MaildirFolder(BaseFolder):
         if uid == new_uid: return
 
         oldfilename = self.messagelist[uid]['filename']
-        dir_prefix, filename = os.path.split(oldfilename)
+        fs_dir_prefix = os.path.split(oldfilename.fs)[0]
         flags = self.getmessageflags(uid)
-        newfilename = os.path.join(dir_prefix,
-          self.new_message_filename(new_uid, flags))
-        os.rename(os.path.join(self.getfullname(), oldfilename),
-                  os.path.join(self.getfullname(), newfilename))
+        newfilename = fsString(os.path.join(
+            fs_dir_prefix, self.new_message_filename(new_uid, flags).fs))
+        fs_old = os.path.join(self.getfullname().fs, oldfilename.fs)
+        fs_new = os.path.join(self.getfullname().fs, newfilename.fs)
+        os.rename(fs_old, fs_new)
         self.messagelist[new_uid] = self.messagelist[uid]
         self.messagelist[new_uid]['filename'] = newfilename
         del self.messagelist[uid]
@@ -419,19 +446,20 @@ class MaildirFolder(BaseFolder):
         :return: Nothing, or an Exception if UID but no corresponding file
                  found.
         """
+
         if not self.uidexists(uid):
             return
 
         filename = self.messagelist[uid]['filename']
-        filepath = os.path.join(self.getfullname(), filename)
+        fs_filepath = os.path.join(self.getfullname().fs, filename.fs)
         try:
-            os.unlink(filepath)
+            os.unlink(fs_filepath)
         except OSError:
             # Can't find the file -- maybe already deleted?
             newmsglist = self._scanfolder()
             if uid in newmsglist:       # Nope, try new filename.
                 filename = newmsglist[uid]['filename']
-                filepath = os.path.join(self.getfullname(), filename)
-                os.unlink(filepath)
+                fs_filepath = os.path.join(self.getfullname().fs, filename.fs)
+                os.unlink(fs_filepath)
             # Yep -- return.
         del(self.messagelist[uid])
diff --git a/offlineimap/folder/UIDMaps.py b/offlineimap/folder/UIDMaps.py
index e8ca9a7..c1069f3 100644
--- a/offlineimap/folder/UIDMaps.py
+++ b/offlineimap/folder/UIDMaps.py
@@ -16,10 +16,15 @@
 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
 from sys import exc_info
+import os.path
+import codecs
 from threading import Lock
+
 from offlineimap import OfflineImapError
 from .IMAP import IMAPFolder
-import os.path
+from offlineimap.utils import uni
+from offlineimap.utils.uni import fsString, uniString
+
 
 class MappedIMAPFolder(IMAPFolder):
     """IMAP class to map between Folder() instances where both side assign a uid
@@ -42,27 +47,27 @@ class MappedIMAPFolder(IMAPFolder):
         """Representing the local IMAP Folder using local UIDs"""
 
     def _getmapfilename(self):
-        return os.path.join(self.repository.getmapdir(),
-                            self.getfolderbasename())
+        return fsString(os.path.join(
+            self.repository.getmapdir().fs, self.getfolderbasename().fs))
 
     def _loadmaps(self):
         self.maplock.acquire()
         try:
             mapfilename = self._getmapfilename()
-            if not os.path.exists(mapfilename):
+            if not os.path.exists(mapfilename.fs):
                 return ({}, {})
-            file = open(mapfilename, 'rt')
+            mapfile = open(mapfilename.fs, 'rt')
             r2l = {}
             l2r = {}
             while 1:
-                line = file.readline()
+                line = mapfile.readline()
                 if not len(line):
                     break
                 try:
                     line = line.strip()
                 except ValueError:
                     raise Exception("Corrupt line '%s' in UID mapping file '%s'"%
-                        (line, mapfilename)), None, exc_info()[2]
+                        (line, mapfilename.fs)), None, exc_info()[2]
                 (str1, str2) = line.split(':')
                 loc = long(str1)
                 rem = long(str2)
@@ -76,11 +81,16 @@ class MappedIMAPFolder(IMAPFolder):
         mapfilename = self._getmapfilename()
         if dolock: self.maplock.acquire()
         try:
-            file = open(mapfilename + ".tmp", 'wt')
+            tmpname = fsString(mapfilename.fs + ".tmp")
+            if uni.use_unicode():
+                mapfile = codecs.open(tmpname.fs, 'wt', uni.ENCODING)
+            else:
+                mapfile = open(tmpname.fs, 'wt')
             for (key, value) in self.diskl2r.iteritems():
-                file.write("%d:%d\n"% (key, value))
-            file.close()
-            os.rename(mapfilename + '.tmp', mapfilename)
+                data = uniString("%d:%d\n"% (key, value))
+                mapfile.write(data.dbytes)
+            mapfile.close()
+            os.rename(tmpname.fs, mapfilename.fs)
         finally:
             if dolock: self.maplock.release()
 
@@ -90,7 +100,7 @@ class MappedIMAPFolder(IMAPFolder):
         except KeyError as e:
             raise OfflineImapError("Could not find UID for msg '{0}' (f:'{1}'."
                 " This is usually a bad thing and should be reported on the ma"
-                "iling list.".format(e.args[0], self),
+                "iling list.".format(e.args[0], self.name.fs),
                 OfflineImapError.ERROR.MESSAGE), None, exc_info()[2]
 
     # Interface from BaseFolder
@@ -131,6 +141,7 @@ class MappedIMAPFolder(IMAPFolder):
     # Interface from BaseFolder
     def uidexists(self, ruid):
         """Checks if the (remote) UID exists in this Folder"""
+
         # This implementation overrides the one in BaseFolder, as it is
         # much more efficient for the mapped case.
         return ruid in self.r2l
@@ -139,6 +150,7 @@ class MappedIMAPFolder(IMAPFolder):
     def getmessageuidlist(self):
         """Gets a list of (remote) UIDs.
         You may have to call cachemessagelist() before calling this function!"""
+
         # This implementation overrides the one in BaseFolder, as it is
         # much more efficient for the mapped case.
         return self.r2l.keys()
@@ -147,6 +159,7 @@ class MappedIMAPFolder(IMAPFolder):
     def getmessagecount(self):
         """Gets the number of messages in this folder.
         You may have to call cachemessagelist() before calling this function!"""
+
         # This implementation overrides the one in BaseFolder, as it is
         # much more efficient for the mapped case.
         return len(self.r2l)
@@ -180,6 +193,7 @@ class MappedIMAPFolder(IMAPFolder):
     # Interface from BaseFolder
     def getmessage(self, uid):
         """Returns the content of the specified message."""
+
         return self._mb.getmessage(self.r2l[uid])
 
     # Interface from BaseFolder
@@ -200,8 +214,8 @@ class MappedIMAPFolder(IMAPFolder):
 
         See folder/Base for details. Note that savemessage() does not
         check against dryrun settings, so you need to ensure that
-        savemessage is never called in a dryrun mode.
-        """
+        savemessage is never called in a dryrun mode."""
+
         self.ui.savemessage('imap', uid, flags, self)
         # Mapped UID instances require the source to already have a
         # positive UID, so simply return here.
@@ -238,11 +252,10 @@ class MappedIMAPFolder(IMAPFolder):
 
     # Interface from BaseFolder
     def savemessageflags(self, uid, flags):
-        """
-
-        Note that this function does not check against dryrun settings,
+        """Note that this function does not check against dryrun settings,
         so you need to ensure that it is never called in a
         dryrun mode."""
+
         self._mb.savemessageflags(self.r2l[uid], flags)
 
     # Interface from BaseFolder
@@ -251,8 +264,8 @@ class MappedIMAPFolder(IMAPFolder):
 
     # Interface from BaseFolder
     def addmessagesflags(self, uidlist, flags):
-        self._mb.addmessagesflags(self._uidlist(self.r2l, uidlist),
-                                  flags)
+        self._mb.addmessagesflags(
+            self._uidlist(self.r2l, uidlist), flags)
 
     # Interface from BaseFolder
     def change_message_uid(self, ruid, new_ruid):
@@ -261,6 +274,7 @@ class MappedIMAPFolder(IMAPFolder):
         :param new_uid: The old remote UID will be changed to a new
             UID. The UIDMaps case handles this efficiently by simply
             changing the mappings file."""
+
         if ruid not in self.r2l:
             raise OfflineImapError("Cannot change unknown Maildir UID %s"%
                 ruid, OfflineImapError.ERROR.MESSAGE)
@@ -303,8 +317,8 @@ class MappedIMAPFolder(IMAPFolder):
 
     # Interface from BaseFolder
     def deletemessagesflags(self, uidlist, flags):
-        self._mb.deletemessagesflags(self._uidlist(self.r2l, uidlist),
-                                     flags)
+        self._mb.deletemessagesflags(
+            self._uidlist(self.r2l, uidlist), flags)
 
     # Interface from BaseFolder
     def deletemessage(self, uid):
diff --git a/offlineimap/imaplibutil.py b/offlineimap/imaplibutil.py
index 83ffe9a..dd3c96c 100644
--- a/offlineimap/imaplibutil.py
+++ b/offlineimap/imaplibutil.py
@@ -27,6 +27,11 @@ from offlineimap import OfflineImapError
 from offlineimap.imaplib2 import IMAP4, IMAP4_SSL, zlib, InternalDate, Mon2num
 
 
+#
+# UNICODE: consider this module to work with IMAP encoded data. Features here
+# are low-level drivers. Do not import uni module.
+#
+
 class UsefulIMAPMixIn(object):
     def __getselectedfolder(self):
         if self.state == 'SELECTED':
@@ -54,12 +59,12 @@ class UsefulIMAPMixIn(object):
         except self.abort as e:
             # self.abort is raised when we are supposed to retry
             errstr = "Server '%s' closed connection, error on SELECT '%s'. Ser"\
-                "ver said: %s" % (self.host, mailbox, e.args[0])
+                "ver said: %s"% (self.host, mailbox, e.args[0])
             severity = OfflineImapError.ERROR.FOLDER_RETRY
             raise OfflineImapError(errstr, severity), None, exc_info()[2]
         if result[0] != 'OK':
             #in case of error, bail out with OfflineImapError
-            errstr = "Error SELECTing mailbox '%s', server reply:\n%s" %\
+            errstr = "Error SELECTing mailbox '%s', server reply:\n%s"%\
                 (mailbox, result)
             severity = OfflineImapError.ERROR.FOLDER
             raise OfflineImapError(errstr, severity)
@@ -134,7 +139,8 @@ def new_mesg(self, s, tn=None, secs=None):
             if tn is None:
                 tn = threading.currentThread().getName()
             tm = time.strftime('%M:%S', time.localtime(secs))
-            getglobalui().debug('imap', '  %s.%02d %s %s' % (tm, (secs*100)%100, tn, s))
+            getglobalui().debug(u'imap', u'  %s.%02d %s %s'%
+                (tm, (secs*100)%100, tn, s))
 
 
 class WrappedIMAP4_SSL(UsefulIMAPMixIn, IMAP4_SSL):
@@ -151,21 +157,23 @@ class WrappedIMAP4_SSL(UsefulIMAPMixIn, IMAP4_SSL):
     def open(self, host=None, port=None):
         if not self.ca_certs and not self._fingerprint:
             raise OfflineImapError("No CA certificates "
-              "and no server fingerprints configured.  "
-              "You must configure at least something, otherwise "
-              "having SSL helps nothing.", OfflineImapError.ERROR.REPO)
+                "and no server fingerprints configured.  "
+                "You must configure at least something, otherwise "
+                "having SSL helps nothing.",
+                OfflineImapError.ERROR.REPO)
         super(WrappedIMAP4_SSL, self).open(host, port)
         if self._fingerprint:
             # compare fingerprints
             fingerprint = sha1(self.sock.getpeercert(True)).hexdigest()
             if fingerprint not in self._fingerprint:
                 raise OfflineImapError("Server SSL fingerprint '%s' "
-                      "for hostname '%s' "
-                      "does not match configured fingerprint(s) %s.  "
-                      "Please verify and set 'cert_fingerprint' accordingly "
-                      "if not set yet."%
-                      (fingerprint, host, self._fingerprint),
-                      OfflineImapError.ERROR.REPO)
+                    "for hostname '%s' "
+                    "does not match configured fingerprint(s) %s.  "
+                    "Please verify and set 'cert_fingerprint' accordingly "
+                    "if not set yet."%
+                    (fingerprint, host,
+                    self._fingerprint),
+                    OfflineImapError.ERROR.REPO)
 
 
 class WrappedIMAP4(UsefulIMAPMixIn, IMAP4):
diff --git a/offlineimap/imapserver.py b/offlineimap/imapserver.py
index 3d69426..5b55173 100644
--- a/offlineimap/imapserver.py
+++ b/offlineimap/imapserver.py
@@ -15,10 +15,6 @@
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
-from offlineimap import imaplibutil, imaputil, threadutil, OfflineImapError
-from offlineimap.ui import getglobalui
-from threading import Lock, BoundedSemaphore, Thread, Event, currentThread
-import offlineimap.accounts
 import hmac
 import socket
 import base64
@@ -28,6 +24,12 @@ from sys import exc_info
 from socket import gaierror
 from ssl import SSLError, cert_time_to_seconds
 
+from offlineimap import imaplibutil, imaputil, threadutil, OfflineImapError
+from offlineimap.ui import getglobalui
+from threading import Lock, BoundedSemaphore, Thread, Event, currentThread
+import offlineimap.accounts
+from offlineimap.utils.uni import noneString, uniString, dbytesString, fsString, imapString
+
 try:
     # do we have a recent pykerberos?
     have_gss = False
@@ -37,8 +39,16 @@ try:
 except ImportError:
     pass
 
+
+#
+# XXX: 'repos' is a variable name currently used as alias to 'repository'.
+# This is bad because it lies about what could be understood as explicit plural
+# (a list of repositories). Either use 'repo' or 'repository' but NOT 'repos'.
+#
+# XXX: if it's actually just about initialization, call it InitIMAPServer.
+#
 class IMAPServer:
-    """Initializes all variables from an IMAPRepository() instance
+    """Initializes all variables from an IMAPRepository() instance.
 
     Various functions, such as acquireconnection() return an IMAP4
     object on which we can operate.
@@ -56,39 +66,40 @@ class IMAPServer:
 
         self.preauth_tunnel = repos.getpreauthtunnel()
         self.transport_tunnel = repos.gettransporttunnel()
-        if self.preauth_tunnel and self.transport_tunnel:
-            raise OfflineImapError('%s: '% repos +
+        if self.preauth_tunnel.uni and self.transport_tunnel:
+            raise OfflineImapError('%s: '% repos.getname().fs +
                 'you must enable precisely one '
                 'type of tunnel (preauth or transport), '
                 'not both', OfflineImapError.ERROR.REPO)
         self.tunnel = \
-            self.preauth_tunnel if self.preauth_tunnel \
+            self.preauth_tunnel if self.preauth_tunnel.uni \
             else self.transport_tunnel
 
         self.username = \
-            None if self.preauth_tunnel else repos.getuser()
+            noneString() if self.preauth_tunnel.uni else repos.getuser()
         self.user_identity = repos.get_remote_identity()
         self.authmechs = repos.get_auth_mechanisms()
-        self.password = None
-        self.passworderror = None
-        self.goodpassword = None
+        self.password = noneString()
+        self.passworderror = noneString()
+        self.goodpassword = noneString()
 
         self.usessl = repos.getssl()
         self.hostname = \
-            None if self.preauth_tunnel else repos.gethost()
+            noneString() if self.preauth_tunnel.uni else repos.gethost()
         self.port = repos.getport()
         if self.port == None:
             self.port = 993 if self.usessl else 143
         self.sslclientcert = repos.getsslclientcert()
         self.sslclientkey = repos.getsslclientkey()
         self.sslcacertfile = repos.getsslcacertfile()
-        if self.sslcacertfile is None:
+        if self.sslcacertfile.uni is None:
             self.__verifycert = None # disable cert verification
-        self.fingerprint = repos.get_ssl_fingerprint()
+        # List of str values.
+        self.fingerprints = repos.get_ssl_fingerprints()
         self.sslversion = repos.getsslversion()
 
-        self.delim = None
-        self.root = None
+        self.delim = noneString()
+        self.root = noneString()
         self.maxconnections = repos.getmaxconnections()
         self.availableconnections = []
         self.assignedconnections = []
@@ -103,26 +114,30 @@ class IMAPServer:
 
     def __getpassword(self):
         """Returns the server password or None"""
-        if self.goodpassword != None: # use cached good one first
+
+        if self.goodpassword.value != None: # use cached good one first
             return self.goodpassword
 
-        if self.password != None and self.passworderror == None:
+        if self.password.uni != None and self.passworderror.uni == None:
             return self.password # non-failed preconfigured one
 
         # get 1) configured password first 2) fall back to asking via UI
-        self.password = self.repos.getpassword() or \
-            self.ui.getpass(self.repos.getname(), self.config,
-                self.passworderror)
-        self.passworderror = None
+        self.password = self.repos.getpassword()
+        if not self.password.uni:
+            self.ui.getpass(self.repos.getname().uni, self.config,
+                self.passworderror.uni)
+        self.passworderror = noneString()
         return self.password
 
-    # XXX: is this function used anywhere?
+
     def getroot(self):
         """Returns this server's folder root. Can only be called after one
         or more calls to acquireconnection."""
 
         return self.root
 
+    def getdelim(self):
+        return self.delim
 
     def releaseconnection(self, connection, drop_conn=False):
         """Releases a connection, returning it to the pool.
@@ -130,7 +145,7 @@ class IMAPServer:
         :param drop_conn: If True, the connection will be released and
            not be reused. This can be used to indicate broken connections."""
 
-        if connection is None: return #noop on bad connection
+        if connection is None: return # noop on bad connection
         self.connectionlock.acquire()
         self.assignedconnections.remove(connection)
         # Don't reuse broken connections
@@ -142,44 +157,51 @@ class IMAPServer:
         self.semaphore.release()
 
     def __md5handler(self, response):
+        """This method is a hook. input/ouput are basic str python type."""
+
         challenge = response.strip()
-        self.ui.debug('imap', '__md5handler: got challenge %s'% challenge)
+        self.ui.debug('imap', u'__md5handler: got challenge %s'% challenge)
 
-        passwd = self.__getpassword()
-        retval = self.username + ' ' + hmac.new(passwd, challenge).hexdigest()
-        self.ui.debug('imap', '__md5handler: returning %s'% retval)
+        passwd = self.__getpassword().value
+        retval = self.username.dbytes + ' ' + \
+            hmac.new(passwd, challenge).hexdigest()
+        self.ui.debug('imap', u'__md5handler: returning %s'% retval)
         return retval
 
     def __loginauth(self, imapobj):
         """ Basic authentication via LOGIN command."""
 
-        self.ui.debug('imap', 'Attempting IMAP LOGIN authentication')
-        imapobj.login(self.username, self.__getpassword())
-
+        self.ui.debug('imap', u'Attempting IMAP LOGIN authentication')
+        imapobj.login(self.username.dbytes, self.__getpassword().dbytes)
 
     def __plainhandler(self, response):
-        """Implements SASL PLAIN authentication, RFC 4616,
-          http://tools.ietf.org/html/rfc4616"""
+        """This method is a hook. input/ouput are basic str python type.
+
+        Implements SASL PLAIN authentication, RFC 4616,
+          http://tools.ietf.org/html/rfc4616
+        """
 
-        authc = self.username
-        passwd = self.__getpassword()
+        authc = self.username.dbytes
+        passwd = self.__getpassword().value
         authz = ''
-        if self.user_identity != None:
-            authz = self.user_identity
-        NULL = u'\x00'
-        retval = NULL.join((authz, authc, passwd)).encode('utf-8')
-        self.ui.debug('imap', '__plainhandler: returning %s' % retval)
+        if self.user_identity.uni != None:
+            authz = self.user_identity.imap
+        NULL = '\x00'
+        retval = NULL.join((authz, authc, passwd))
+        self.ui.debug('imap', u'__plainhandler: returning %s'%
+            dbytesString(retval).uni)
         return retval
 
-
     # XXX: describe function
     def __gssauth(self, response):
+        """This method is a hook. input/ouput are basic str python type."""
+
         data = base64.b64encode(response)
         try:
             if self.gss_step == self.GSS_STATE_STEP:
                 if not self.gss_vc:
                     rc, self.gss_vc = kerberos.authGSSClientInit(
-                        'imap@' + self.hostname)
+                        'imap@' + self.hostname.dbytes)
                     response = kerberos.authGSSClientResponse(self.gss_vc)
                 rc = kerberos.authGSSClientStep(self.gss_vc, data)
                 if rc != kerberos.AUTH_GSS_CONTINUE:
@@ -188,12 +210,12 @@ class IMAPServer:
                 rc = kerberos.authGSSClientUnwrap(self.gss_vc, data)
                 response = kerberos.authGSSClientResponse(self.gss_vc)
                 rc = kerberos.authGSSClientWrap(
-                    self.gss_vc, response, self.username)
+                    self.gss_vc, response, self.username.dbytes)
             response = kerberos.authGSSClientResponse(self.gss_vc)
         except kerberos.GSSError as err:
             # Kerberos errored out on us, respond with None to cancel the
             # authentication
-            self.ui.debug('imap', '%s: %s'% (err[0][0], err[1][0]))
+            self.ui.debug('imap', u'%s: %s'% err[0][0], err[1][0])
             return None
 
         if not response:
@@ -203,7 +225,7 @@ class IMAPServer:
 
     def __start_tls(self, imapobj):
         if 'STARTTLS' in imapobj.capabilities and not self.usessl:
-            self.ui.debug('imap', 'Using STARTTLS connection')
+            self.ui.debug('imap', u'Using STARTTLS connection')
             try:
                 imapobj.starttls()
             except imapobj.error as e:
@@ -305,7 +327,7 @@ class IMAPServer:
         for m in mechs:
             if m not in auth_methods:
                 raise Exception("Bad authentication method %s, "
-                  "please, file OfflineIMAP bug" % m)
+                    "please, file OfflineIMAP bug"% m)
 
             func, tryTLS, check_cap = auth_methods[m]
 
@@ -321,13 +343,12 @@ class IMAPServer:
                     continue
 
             tried_to_authn = True
-            self.ui.debug('imap', u'Attempting '
-              '%s authentication'% m)
+            self.ui.debug('imap', u'Attempting %s authentication'% m)
             try:
                 if func(imapobj):
                     return
             except (imapobj.error, OfflineImapError) as e:
-                self.ui.warn('%s authentication failed: %s'% (m, e))
+                self.ui.warn(u'%s authentication failed: %s'% (m, e))
                 exc_stack.append((m, e))
 
         if len(exc_stack):
@@ -343,14 +364,13 @@ class IMAPServer:
               lambda x: x[5:], filter(lambda x: x[0:5] == "AUTH=",
                imapobj.capabilities)
             ))
-            raise OfflineImapError(u"Repository %s: no supported "
+            raise OfflineImapError("Repository %s: no supported "
               "authentication mechanisms found; configured %s, "
-              "server advertises %s"% (self.repos,
+              "server advertises %s"% (self.repos.getname().fs,
               ", ".join(self.authmechs), methods),
               OfflineImapError.ERROR.REPO)
 
 
-    # XXX: move above, closer to releaseconnection()
     def acquireconnection(self):
         """Fetches a connection from the pool, making sure to create a new one
         if needed, to obey the maximum connection limits, etc.
@@ -359,7 +379,7 @@ class IMAPServer:
 
         self.semaphore.acquire()
         self.connectionlock.acquire()
-        curThread = currentThread()
+        curThread = currentThread() # .getNames() -> 'Account sync $accountname'
         imapobj = None
 
         if len(self.availableconnections): # One is available.
@@ -385,40 +405,48 @@ class IMAPServer:
 
         # Must be careful here that if we fail we should bail out gracefully
         # and release locks / threads so that the next attempt can try...
-        success = 0
+        success = False
         try:
             while not success:
                 # Generate a new connection.
-                if self.tunnel:
-                    self.ui.connecting('tunnel', self.tunnel)
-                    imapobj = imaplibutil.IMAP4_Tunnel(self.tunnel,
-                                                       timeout=socket.getdefaulttimeout())
-                    success = 1
+                # 0. tunnel
+                if self.tunnel.uni:
+                    self.ui.connecting(u'tunnel', self.tunnel.uni)
+                    imapobj = imaplibutil.IMAP4_Tunnel(self.tunnel.uni,
+                        timeout=socket.getdefaulttimeout())
+                    success = True
+                # 1. tunnel
                 elif self.usessl:
-                    self.ui.connecting(self.hostname, self.port)
-                    imapobj = imaplibutil.WrappedIMAP4_SSL(self.hostname,
-                                                           self.port,
-                                                           self.sslclientkey,
-                                                           self.sslclientcert,
-                                                           self.sslcacertfile,
-                                                           self.__verifycert,
-                                                           self.sslversion,
-                                                           timeout=socket.getdefaulttimeout(),
-                                                           fingerprint=self.fingerprint
-                                                           )
+                    self.ui.connecting(self.hostname.uni, self.port)
+                    imapobj = imaplibutil.WrappedIMAP4_SSL(
+                        self.hostname.dbytes,
+                        self.port,
+                        self.sslclientkey.dbytes,
+                        self.sslclientcert.dbytes,
+                        self.sslcacertfile.fs,
+                        self.__verifycert,
+                        self.sslversion.dbytes,
+                        timeout=socket.getdefaulttimeout(),
+                        fingerprint=self.fingerprints
+                        )
+                # 2. ?
                 else:
-                    self.ui.connecting(self.hostname, self.port)
-                    imapobj = imaplibutil.WrappedIMAP4(self.hostname, self.port,
-                                                       timeout=socket.getdefaulttimeout())
-
-                if not self.preauth_tunnel:
+                    self.ui.connecting(self.hostname.uni, self.port)
+                    imapobj = imaplibutil.WrappedIMAP4(
+                        self.hostname.dbytes,
+                        self.port,
+                        timeout=socket.getdefaulttimeout())
+
+                # 3. authn helper
+                if not self.preauth_tunnel.uni:
                     try:
                         self.__authn_helper(imapobj)
                         self.goodpassword = self.password
-                        success = 1
+                        success = True
                     except OfflineImapError as e:
-                        self.passworderror = str(e)
+                        self.passworderror = fsString("%s"% e)
                         raise
+            # Ok, we could login.
 
             # Enable compression
             if self.repos.getconfboolean('usecompression', 0):
@@ -429,29 +457,30 @@ class IMAPServer:
             if dat != [None]:
                 imapobj.capabilities = tuple(dat[-1].upper().split())
 
-            if self.delim == None:
-                listres = imapobj.list(self.reference, '""')[1]
+            if self.delim.uni == None:
+                listres = imapobj.list(self.reference.imap, '""')[1]
                 if listres == [None] or listres == None:
                     # Some buggy IMAP servers do not respond well to LIST "" ""
                     # Work around them.
-                    listres = imapobj.list(self.reference, '"*"')[1]
+                    listres = imapobj.list(self.reference.imap, '"*"')[1]
                 if listres == [None] or listres == None:
                     # No Folders were returned. This occurs, e.g. if the
                     # 'reference' prefix does not exist on the mail
                     # server. Raise exception.
-                    err = "Server '%s' returned no folders in '%s'"% \
-                        (self.repos.getname(), self.reference)
-                    self.ui.warn(err)
-                    raise Exception(err)
-                self.delim, self.root = \
-                     imaputil.imapsplit(listres[0])[1:]
-                self.delim = imaputil.dequote(self.delim)
-                self.root = imaputil.dequote(self.root)
+                    err = uniString(u"Server '%s' returned no folders in '%s'"%
+                        (self.repos.getname().uni, self.reference.uni))
+                    self.ui.warn(err.uni)
+                    raise Exception(err.fs)
+                imap_delim, imap_root = \
+                    imaputil.imapsplit(listres[0])[1:]
+                self.delim = imapString(imaputil.dequote(imap_delim))
+                self.root = imapString(imaputil.dequote(imap_root))
 
             with self.connectionlock:
                 self.assignedconnections.append(imapobj)
                 self.lastowner[imapobj] = curThread.ident
             return imapobj
+
         except Exception as e:
             """If we are here then we did not succeed in getting a
             connection - we should clean up and then re-raise the
@@ -466,7 +495,7 @@ class IMAPServer:
                 reason = "Could not resolve name '%s' for repository "\
                          "'%s'. Make sure you have configured the ser"\
                          "ver name correctly and that you are online."%\
-                         (self.hostname, self.repos)
+                         (self.hostname.fs, self.repos.getname().fs)
                 raise OfflineImapError(reason, severity), None, exc_info()[2]
 
             elif isinstance(e, SSLError) and e.errno == errno.EPERM:
@@ -475,11 +504,11 @@ class IMAPServer:
                 if self.port != 993:
                     reason = "Could not connect via SSL to host '%s' and non-s"\
                         "tandard ssl port %d configured. Make sure you connect"\
-                        " to the correct port."% (self.hostname, self.port)
+                        " to the correct port."% (self.hostname.fs, self.port)
                 else:
                     reason = "Unknown SSL protocol connecting to host '%s' for "\
-                         "repository '%s'. OpenSSL responded:\n%s"\
-                         % (self.hostname, self.repos, e)
+                        "repository '%s'. OpenSSL responded:\n%s"%\
+                        (self.hostname.fs, self.repos.getname().fs, e)
                 raise OfflineImapError(reason, severity), None, exc_info()[2]
 
             elif isinstance(e, socket.error) and e.args[0] == errno.ECONNREFUSED:
@@ -488,14 +517,15 @@ class IMAPServer:
                 reason = "Connection to host '%s:%d' for repository '%s' was "\
                     "refused. Make sure you have the right host and port "\
                     "configured and that you are actually able to access the "\
-                    "network."% (self.hostname, self.port, self.repos)
+                    "network."% (self.hostname.fs, self.port,
+                    self.repos.getname().fs)
                 raise OfflineImapError(reason, severity), None, exc_info()[2]
             # Could not acquire connection to the remote;
             # socket.error(last_error) raised
             if str(e)[:24] == "can't open socket; error":
-                raise OfflineImapError("Could not connect to remote server '%s' "\
-                    "for repository '%s'. Remote does not answer."
-                    % (self.hostname, self.repos),
+                raise OfflineImapError("Could not connect to remote server '%s' "
+                    "for repository '%s'. Remote does not answer."%
+                    (self.hostname.fs, self.repos.getname().fs),
                     OfflineImapError.ERROR.REPO), None, exc_info()[2]
             else:
                 # re-raise all other errors
@@ -541,7 +571,7 @@ class IMAPServer:
         is expected to be invoked in a separate thread, which should be join()'d
         after the event is set."""
 
-        self.ui.debug('imap', 'keepalive thread started')
+        self.ui.debug('imap', u'keepalive thread started')
         while not event.isSet():
             self.connectionlock.acquire()
             numconnections = len(self.assignedconnections) + \
@@ -550,7 +580,8 @@ class IMAPServer:
 
             threads = []
             for i in range(numconnections):
-                self.ui.debug('imap', 'keepalive: processing connection %d of %d'% (i, numconnections))
+                self.ui.debug('imap', u'keepalive: processing connection '
+                    '%d of %d'% (i, numconnections))
                 if len(self.idlefolders) > i:
                     # IDLE thread
                     idler = IdleThread(self, self.idlefolders[i])
@@ -560,23 +591,26 @@ class IMAPServer:
                 idler.start()
                 threads.append(idler)
 
-            self.ui.debug('imap', 'keepalive: waiting for timeout')
+            self.ui.debug('imap', u'keepalive: waiting for timeout')
             event.wait(timeout)
-            self.ui.debug('imap', 'keepalive: after wait')
+            self.ui.debug('imap', u'keepalive: after wait')
 
             for idler in threads:
                 # Make sure all the commands have completed.
                 idler.stop()
                 idler.join()
-            self.ui.debug('imap', 'keepalive: all threads joined')
-        self.ui.debug('imap', 'keepalive: event is set; exiting')
+            self.ui.debug('imap', u'keepalive: all threads joined')
+        self.ui.debug('imap', u'keepalive: event is set; exiting')
         return
 
     def __verifycert(self, cert, hostname):
         """Verify that cert (in socket.getpeercert() format) matches hostname.
 
         CRLs are not handled.
-        Returns error message if any problems are found and None on success."""
+        Returns error message if any problems are found and None on success.
+        
+        We don't call this function ourself. It is passed to low-level drivers
+        as a hook. Do NOT work with unicode nor uni String, here."""
 
         errstr = "CA Cert verifying failed: "
         if not cert:
@@ -616,15 +650,26 @@ class IdleThread(object):
     def __init__(self, parent, folder=None):
         """If invoked without 'folder', perform a NOOP and wait for
         self.stop() to be called. If invoked with folder, switch to IDLE
-        mode and synchronize once we have a new message"""
+        mode and synchronize once we have a new message.
+
+        XXX: requires better class comments:
+        - why is it here?
+        - what is threaded?
+        IOW, explain WHAT it does and HOW it works.
+        The name is lying around. It is composed with Thread, it doesn't inherit from
+        a Thread class/subclass. Call it IdleMode, IdleSync or whatever but NOT
+        IdleTread."""
 
         self.parent = parent
         self.folder = folder
         self.stop_sig = Event()
         self.ui = getglobalui()
-        if folder is None:
+        if folder.uni is None:
             self.thread = Thread(target=self.noop)
         else:
+            # Oh no... Passing a 'hidden' function to something outside is BAD.
+            # It's like accessing a private method/attribute of an encapsulated
+            # object...
             self.thread = Thread(target=self.__idle)
         self.thread.setDaemon(1)
 
@@ -648,7 +693,7 @@ class IdleThread(object):
         try:
             imapobj.noop()
         except imapobj.abort:
-            self.ui.warn('Attempting NOOP on dropped connection %s'%
+            self.ui.warn(u'Attempting NOOP on dropped connection %s'%
                 imapobj.identifier)
             self.parent.releaseconnection(imapobj, True)
             imapobj = None
@@ -665,17 +710,20 @@ class IdleThread(object):
         statusrepos = account.statusrepos
         remotefolder = remoterepos.getfolder(self.folder)
 
-        hook = account.getconf('presynchook', '')
+        hook = uniString(account.getconf('presynchook', ''))
         account.callhook(hook)
+
         offlineimap.accounts.syncfolder(account, remotefolder, quick=False)
-        hook = account.getconf('postsynchook', '')
+
+        hook = uniString(account.getconf('postsynchook', ''))
         account.callhook(hook)
 
         ui = getglobalui()
-        ui.unregisterthread(currentThread()) #syncfolder registered the thread
+        ui.unregisterthread(currentThread()) # syncfolder registered the thread
 
     def __idle(self):
-        """Invoke IDLE mode until timeout or self.stop() is invoked"""
+        """Invoke IDLE mode until timeout or self.stop() is invoked."""
+
         def callback(args):
             """IDLE callback function invoked by imaplib2
 
@@ -683,6 +731,7 @@ class IdleThread(object):
             while in IDLE mode, b) we get an Exception (e.g. on dropped
             connections, or c) the standard imaplib IDLE timeout of 29
             minutes kicks in."""
+
             result, cb_arg, exc_data = args
             if exc_data is None and not self.stop_sig.isSet():
                 # No Exception, and we are not supposed to stop:
@@ -696,7 +745,7 @@ class IdleThread(object):
             while not success:
                 imapobj = self.parent.acquireconnection()
                 try:
-                    imapobj.select(self.folder)
+                    imapobj.select(self.folder.imap)
                 except OfflineImapError as e:
                     if e.severity == OfflineImapError.ERROR.FOLDER_RETRY:
                         # Connection closed, release connection and retry
@@ -709,7 +758,7 @@ class IdleThread(object):
             if "IDLE" in imapobj.capabilities:
                 imapobj.idle(callback=callback)
             else:
-                self.ui.warn("IMAP IDLE not supported on server '%s'."
+                self.ui.warn(u"IMAP IDLE not supported on server '%s'."
                     "Sleep until next refresh cycle."% imapobj.identifier)
                 imapobj.noop()
             self.stop_sig.wait() # self.stop() or IDLE callback are invoked
@@ -717,7 +766,7 @@ class IdleThread(object):
                 # End IDLE mode with noop, imapobj can point to a dropped conn.
                 imapobj.noop()
             except imapobj.abort:
-                self.ui.warn('Attempting NOOP on dropped connection %s'%
+                self.ui.warn(u'Attempting NOOP on dropped connection %s'%
                     imapobj.identifier)
                 self.parent.releaseconnection(imapobj, True)
             else:
diff --git a/offlineimap/imaputil.py b/offlineimap/imaputil.py
index f1f287b..e77affb 100644
--- a/offlineimap/imaputil.py
+++ b/offlineimap/imaputil.py
@@ -17,8 +17,13 @@
 
 import re
 import string
+
 from offlineimap.ui import getglobalui
 
+#
+# UNICODE: This module makes low-level jobs for IMAP objects. Do NOT import uni
+# module. Everything is expected IMAP encoded.
+#
 
 ## Globals
 
@@ -30,7 +35,7 @@ def __debug(*args):
     msg = []
     for arg in args:
         msg.append(str(arg))
-    getglobalui().debug('imap', " ".join(msg))
+    getglobalui().debug('imap', u" ".join(msg))
 
 def dequote(s):
     """Takes string which may or may not be quoted and unquotes it.
@@ -77,7 +82,7 @@ def __options2hash(list):
     while (counter < len(list)):
         retval[list[counter]] = list[counter + 1]
         counter += 2
-    __debug("__options2hash returning:", retval)
+    __debug(u"__options2hash returning:", retval)
     return retval
 
 def flags2hash(flags):
@@ -99,7 +104,7 @@ def imapsplit(imapstring):
     ['(\\HasNoChildren)', '"."', '"INBOX.Sent"']"""
 
     if not isinstance(imapstring, basestring):
-        __debug("imapsplit() got a non-string input; working around.")
+        __debug(u"imapsplit() got a non-string input; working around.")
         # Sometimes, imaplib will throw us a tuple if the input
         # contains a literal.  See Python bug
         # #619732 at https://sourceforge.net/tracker/index.php?func=detail&aid=619732&group_id=5470&atid=105470
@@ -121,7 +126,7 @@ def imapsplit(imapstring):
                 arg = arg.replace('\\', '\\\\')
                 arg = arg.replace('"', '\\"')
                 arg = '"%s"' % arg
-                __debug("imapsplit() non-string [%d]: Appending %s"% (i, arg))
+                __debug(u"imapsplit() non-string [%d]: Appending %s"% (i, arg))
                 retval.append(arg)
             else:
                 # Even -- we have a string that ends with a literal
@@ -130,10 +135,10 @@ def imapsplit(imapstring):
                 # Recursion to the rescue.
                 arg = imapstring[i]
                 arg = re.sub('\{\d+\}$', '', arg)
-                __debug("imapsplit() non-string [%d]: Feeding %s to recursion"%\
+                __debug(u"imapsplit() non-string [%d]: Feeding %s to recursion"%
                     (i, arg))
                 retval.extend(imapsplit(arg))
-        __debug("imapsplit() non-string: returning %s" % str(retval))
+        __debug(u"imapsplit() non-string: returning %s"% str(retval))
         return retval
 
     workstr = imapstring.strip()
@@ -254,7 +259,8 @@ def __split_quoted(s):
     while True:
         next_q = rest.find(q)
         if next_q == -1:
-            raise ValueError("can't find ending quote '%s' in '%s'"% (q, s))
+            raise ValueError("can't find ending quote '%s' in '%s'"%
+                (q, s))
         # If quote is preceeded by even number of backslashes,
         # then it is the ending quote, otherwise the quote
         # character is escaped by backslash, so we should
diff --git a/offlineimap/init.py b/offlineimap/init.py
index 7f6a679..25b1bcd 100644
--- a/offlineimap/init.py
+++ b/offlineimap/init.py
@@ -22,14 +22,17 @@ import offlineimap.imaplib2 as imaplib
 import signal
 import socket
 import logging
-from optparse import OptionParser
+import codecs
+from optparse import OptionParser, SUPPRESS_HELP
 
 import offlineimap
-from offlineimap import accounts, threadutil, syncmaster
 from offlineimap import globals
+from offlineimap import accounts, threadutil, syncmaster
 from offlineimap.ui import UI_LIST, setglobalui, getglobalui
 from offlineimap.CustomConfig import CustomConfigParser
-from offlineimap.utils import stacktrace
+from offlineimap.utils import stacktrace, uni
+from offlineimap.utils.uni import fsString, uniString, isASCII
+from offlineimap.utils import hack
 
 
 class OfflineImap:
@@ -52,9 +55,8 @@ class OfflineImap:
 
     def __parse_cmd_options(self):
         parser = OptionParser(version=offlineimap.__bigversion__,
-                              description="%s.\n\n%s" %
-                              (offlineimap.__copyright__,
-                               offlineimap.__license__))
+            description="%s.\n\n%s" %
+            (offlineimap.__copyright__, offlineimap.__license__))
         parser.add_option("--dry-run",
                   action="store_true", dest="dryrun",
                   default=False,
@@ -82,6 +84,15 @@ class OfflineImap:
               "maxsyncaccounts and all maxconnections configuration file "
               "variables to 1.")
 
+        parser.add_option("--unicode", dest="unicode_help",
+            action='store_true',
+            help="Enable Unicode support (EXPERIMENTAL).")
+        parser.add_option("--enable-unicode",
+            action='store_true', dest='use_unicode', help=SUPPRESS_HELP)
+        parser.add_option("--no-unicode",
+            action='store_false', dest='use_unicode',
+            help="Disable Unicode support (default).")
+
         parser.add_option("-P", dest="profiledir", metavar="DIR",
                   help="Sets OfflineIMAP into profile mode. The program "
               "will create DIR (it must not already exist). "
@@ -92,7 +103,8 @@ class OfflineImap:
               "specific reason to do so. It will significantly "
               "decrease program performance, may reduce reliability, "
               "and can generate huge amounts of data. This option "
-              "implies the -1 option.")
+              "implies the -1 option. "
+              "Support only ASCII encoded path.")
 
         parser.add_option("-a", dest="accounts", metavar="ACCOUNTS",
                   help="Overrides the accounts section in the config file. "
@@ -123,6 +135,7 @@ class OfflineImap:
                   help="Log to FILE")
 
         parser.add_option("-f", dest="folders", metavar="folder1,[folder2...]",
+                  type=str,
                   help="Only sync the specified folders. The folder names "
               "are the *untranslated* foldernames of the remote repository. "
               "This command-line option overrides any 'folderfilter' "
@@ -130,6 +143,7 @@ class OfflineImap:
 
         parser.add_option("-k", dest="configoverride",
                   action="append",
+                  type=str,
                   metavar="[section:]option=value",
                   help=
               """Override configuration file option. If"section" is
@@ -161,71 +175,96 @@ class OfflineImap:
               "not usable. Possible interface choices are: %s " %
               ", ".join(UI_LIST.keys()))
 
+        parser.set_defaults(use_unicode=False)
         (options, args) = parser.parse_args()
-        globals.set_options (options)
+        # UNICODE: REMEMBER that globals.options keeps options filesystem encoded.
+        globals.set_options(options)
+        # Set unicode context as early as possible! This impacts a lot of
+        # things.
+        uni.set_unicode_context(options.use_unicode)
 
-        #read in configuration file
+        if options.unicode_help:
+            uni.help_message()
+            sys.exit(0)
+
+        # Read in configuration file.
         if not options.configfile:
+            # Keep options.configfile is str filesystem encoded.
             # Try XDG location, then fall back to ~/.offlineimaprc
-            xdg_var = 'XDG_CONFIG_HOME'
-            if not xdg_var in os.environ or not os.environ[xdg_var]:
-                xdg_home = os.path.expanduser('~/.config')
+            xdg_var_fs = 'XDG_CONFIG_HOME'
+            # Expansion on non-ASCII HOME path might be hurting.
+            xdg_home_default = uniString(u'~/.config')
+            configfilename_default = uniString(u'~/.offlineimaprc')
+            # Elements of os.environ are filesystem encoded.
+            if not xdg_var_fs in os.environ or not os.environ[xdg_var_fs]:
+                # os.path.expanduser keeps given encoding.
+                xdg_home = fsString(os.path.expanduser(xdg_home_default.fs))
             else:
-                xdg_home = os.environ[xdg_var]
-            options.configfile = os.path.join(xdg_home, "offlineimap", "config")
+                xdg_home = fsString(os.environ[xdg_var_fs])
+            options.configfile = os.path.join(xdg_home.fs, "offlineimap", "config")
             if not os.path.exists(options.configfile):
-                options.configfile = os.path.expanduser('~/.offlineimaprc')
-            configfilename = options.configfile
+                options.configfile = os.path.expanduser(configfilename_default.fs)
+            configfilename = fsString(options.configfile)
         else:
-            configfilename = os.path.expanduser(options.configfile)
+            configfilename = fsString(os.path.expanduser(options.configfile))
 
         config = CustomConfigParser()
-        if not os.path.exists(configfilename):
-            # TODO, initialize and make use of chosen ui for logging
-            logging.error(" *** Config file '%s' does not exist; aborting!"%
-                          configfilename)
+
+        if not os.path.exists(configfilename.fs):
+            # TODO, initialize and make use of chosen ui for logging.
+            logging.error(u" *** Config file '%s' does not exist; aborting!"%
+                configfilename.uni)
             sys.exit(1)
-        config.read(configfilename)
+        if uni.use_unicode():
+            config.readfp(codecs.open(configfilename.fs, 'rt', uni.ENCODING))
+        else:
+            config.read(configfilename.fs)
 
-        #profile mode chosen?
+        # Profile mode chosen?
         if options.profiledir:
+            profiledir = fsString(options.profiledir)
             if not options.singlethreading:
                 # TODO, make use of chosen ui for logging
-                logging.warn("Profile mode: Forcing to singlethreaded.")
+                logging.warn(u"Profile mode: Forcing to singlethreaded.")
                 options.singlethreading = True
-            if os.path.exists(options.profiledir):
+            if os.path.exists(profiledir.fs):
                 # TODO, make use of chosen ui for logging
-                logging.warn("Profile mode: Directory '%s' already exists!"%
-                             options.profiledir)
+                logging.warn(u"Profile mode: Directory '%s' already exists!"%
+                    profiledir.uni)
             else:
-                os.mkdir(options.profiledir)
-            threadutil.ExitNotifyThread.set_profiledir(options.profiledir)
+                os.mkdir(profiledir.fs)
+            # profiledir is still str filesystem encoded, change that.
+            threadutil.ExitNotifyThread.set_profiledir(profiledir)
             # TODO, make use of chosen ui for logging
-            logging.warn("Profile mode: Potentially large data will be "
-                         "created in '%s'"% options.profiledir)
+            logging.warn(u"Profile mode: Potentially large data will be "
+                "created in '%s'"% profiledir.fs)
 
-        #override a config value
+        # Override a config value.
         if options.configoverride:
-            for option in options.configoverride:
+            configoverride = fsString(options.configoverride)
+            for option in configoverride.uni:
                 (key, value) = option.split('=', 1)
                 if ':' in key:
                     (secname, key) = key.split(':', 1)
                     section = secname.replace("_", " ")
                 else:
                     section = "general"
-                config.set(section, key, value)
+                # Configuration file is Unicode.
+                config.set(uniString(section).uni, uniString(key).uni,
+                    uniString(value).uni)
 
-        #which ui to use? cmd line option overrides config file
-        ui_type = config.getdefault('general', 'ui', 'ttyui')
+        # Which ui to use? cmd line option overrides config file.
+        ui_type = uniString(config.getdefault('general', 'ui', 'ttyui'))
         if options.interface != None:
-            ui_type = options.interface
-        if '.' in ui_type:
-            #transform Curses.Blinkenlights -> Blinkenlights
-            ui_type = ui_type.split('.')[-1]
+            ui_type.fs = options.interface
+        if '.' in ui_type.uni:
+            # Transform Curses.Blinkenlights -> Blinkenlights
+            ui_type.uni = ui_type.split('.')[-1]
             # TODO, make use of chosen ui for logging
-            logging.warning('Using old interface name, consider using one '
-                            'of %s'% ', '.join(UI_LIST.keys()))
-        if options.diagnostics: ui_type = 'basic' # enforce basic UI for --info
+            logging.warn(u'Using old interface name, consider using one '
+                'of %s'% ', '.join(UI_LIST.keys()))
+        if options.diagnostics:
+            ui_type.uni = u'basic' # enforce basic UI for --info
 
         # dry-run? Set [general]dry-run=True
         if options.dryrun:
@@ -233,79 +272,96 @@ class OfflineImap:
         config.set_if_not_exists('general', 'dry-run', 'False')
 
         try:
-            # create the ui class
-            self.ui = UI_LIST[ui_type.lower()](config)
+            # Create the ui class.
+            self.ui = UI_LIST[ui_type.dbytes.lower()](config)
         except KeyError:
-            logging.error("UI '%s' does not exist, choose one of: %s"% \
-                              (ui_type, ', '.join(UI_LIST.keys())))
+            logging.error(u"UI '%s' does not exist, choose one of: %s"%
+                (ui_type.fs, ', '.join(UI_LIST.keys())))
             sys.exit(1)
         setglobalui(self.ui)
 
-        #set up additional log files
+        # Set up additional log files.
         if options.logfile:
-            self.ui.setlogfile(options.logfile)
+            self.ui.setlogfile(fsString(options.logfile))
 
-        #welcome blurb
+        # Welcome blurb.
         self.ui.init_banner()
+        if options.use_unicode:
+            self.ui.info(u"*** Unicode support is enabled ***")
 
         if options.debugtype:
             self.ui.logger.setLevel(logging.DEBUG)
             if options.debugtype.lower() == 'all':
                 options.debugtype = 'imap,maildir,thread'
-            #force single threading?
+            # Force single threading?
             if not ('thread' in options.debugtype.split(',') \
                     and not options.singlethreading):
-                self.ui._msg("Debug mode: Forcing to singlethreaded.")
+                self.ui._msg(u"Debug mode: Forcing to singlethreaded.")
                 options.singlethreading = True
 
             debugtypes = options.debugtype.split(',') + ['']
             for dtype in debugtypes:
                 dtype = dtype.strip()
                 self.ui.add_debug(dtype)
-                if dtype.lower() == u'imap':
+                if dtype.lower() == 'imap':
                     imaplib.Debug = 5
 
         if options.runonce:
-            # FIXME: spaghetti code alert!
+            # It's not possible to get that from config, instead?
+            # XXX: warn about what we do.
             for section in accounts.getaccountlist(config):
                 config.remove_option('Account ' + section, "autorefresh")
 
         if options.quick:
+            # It's not possible to get that from config, instead?
+            # XXX: warn about what we do.
             for section in accounts.getaccountlist(config):
                 config.set('Account ' + section, "quick", '-1')
 
-        #custom folder list specified?
+        # Custom folder list specified?
         if options.folders:
-            foldernames = options.folders.split(",")
-            folderfilter = "lambda f: f in %s"% foldernames
+            # Each folder is filesystem encoded, change that.
+            foldernames = []
+            for fs_foldername in options.folders.split(","):
+                foldernames.append(fsString(fs_foldername).uni)
+
+            folderfilter = uniString(u"lambda f: f in %s"% foldernames)
             folderincludes = "[]"
+            # accountname is full ASCII in unicode.
+            # It's not possible to get that from config, instead?
             for accountname in accounts.getaccountlist(config):
-                account_section = 'Account ' + accountname
-                remote_repo_section = 'Repository ' + \
-                    config.get(account_section, 'remoterepository')
-                config.set(remote_repo_section, "folderfilter", folderfilter)
+                uni_account_section = u'Account ' + accountname.uni
+                remoterepository = uniString(
+                    config.get(uni_account_section, 'remoterepository'))
+                remote_repo_section = u'Repository ' + remoterepository.uni
+                config.set(remote_repo_section, u"folderfilter", folderfilter)
                 config.set(remote_repo_section, "folderincludes",
-                           folderincludes)
+                    folderincludes)
 
         if options.logfile:
-            sys.stderr = self.ui.logfile
+            # sys.stderr expects filesystem encoding. The erasing should honor
+            # this encoding.
+            sys.stderr = codecs.open(self.ui.logfile, 'wb', uni.FS_ENCODING)
 
         socktimeout = config.getdefaultint("general", "socktimeout", 0)
         if socktimeout > 0:
             socket.setdefaulttimeout(socktimeout)
 
-        threadutil.initInstanceLimit('ACCOUNTLIMIT',
+        threadutil.initInstanceLimit(uniString(u'ACCOUNTLIMIT'),
             config.getdefaultint('general', 'maxsyncaccounts', 1))
 
         for reposname in config.getsectionlist('Repository'):
-            for instancename in ["FOLDER_" + reposname,
-                                 "MSGCOPY_" + reposname]:
+            # FIXME: What's that (FOLDER_, MSGCOPY_)?
+            # Boing! concatenation of str on list?
+            # ...Oh, yeah... forgot we have magic python for OfflineImap! :-D
+            for instancename in [u"FOLDER_" + reposname, u"MSGCOPY_" + reposname]:
                 if options.singlethreading:
-                    threadutil.initInstanceLimit(instancename, 1)
+                    threadutil.initInstanceLimit(uniString(instancename), 1)
                 else:
-                    threadutil.initInstanceLimit(instancename,
+                    threadutil.initInstanceLimit(uniString(instancename),
                         config.getdefaultint('Repository ' + reposname,
-                                                  'maxconnections', 2))
+                            'maxconnections', 2))
+
         self.config = config
         return (options, args)
 
@@ -314,36 +370,47 @@ class OfflineImap:
 
         self.config is supposed to have been correctly initialized
         already."""
+
         try:
-            pidfd = open(self.config.getmetadatadir() + "/pid", "w")
+            pidfd = open(self.config.getmetadatadir().fs + "/pid", "w")
             pidfd.write(str(os.getpid()) + "\n")
             pidfd.close()
         except:
             pass
 
         try:
-            # Honor CLI --account option, only.
-            # Accounts to sync are put into syncaccounts variable.
-            activeaccounts = self.config.get("general", "accounts")
+            # Honor CLI --account option.
+            activeaccounts = uniString(self.config.get("general", "accounts"))
+            # Check all declared accounts are full ASCII.
+            # FIXME: put this into CustomConfigParser.
+            isASCII(activeaccounts.uni, exception_msg=
+                "configuration: non ASCII character in accounts")
             if options.accounts:
-                activeaccounts = options.accounts
+                activeaccounts = fsString(options.accounts)
+            # Remove whitespaces.
             activeaccounts = activeaccounts.replace(" ", "")
+            # Make it a list with unicode values.
             activeaccounts = activeaccounts.split(",")
+            # Dict of instanciated accounts with (unicode) account name as key.
+            # FIXME: why would we need to get instanciated accounts here? Hmm...
+            # ...Oh, yeah... forgot we have magic logic too! :-o
             allaccounts = accounts.AccountHashGenerator(self.config)
 
+            # Accounts to sync are put into syncaccounts, a list of (unicode)
+            # values.
             syncaccounts = []
-            for account in activeaccounts:
-                if account not in allaccounts:
+            for accountname in activeaccounts:
+                if accountname.uni not in allaccounts:
                     if len(allaccounts) == 0:
-                        errormsg = "The account '%s' does not exist because no" \
-                            " accounts are defined!"% account
+                        uni_errormsg = (u"The account '%s' does not exist because no"
+                            " accounts are defined!"% accountname.uni)
                     else:
-                        errormsg = "The account '%s' does not exist.  Valid ac" \
-                            "counts are: %s"% \
-                            (account, ", ".join(allaccounts.keys()))
-                    self.ui.terminate(1, errormsg=errormsg)
-                if account not in syncaccounts:
-                    syncaccounts.append(account)
+                        uni_errormsg = (u"The account '%s' does not exist.  Valid ac"
+                            "counts are: %s"% (accountname.uni,
+                            u", ".join([a.uni for a in allaccounts])))
+                    self.ui.terminate(1, errormsg=uni_errormsg)
+                if accountname not in syncaccounts:
+                    syncaccounts.append(accountname)
 
             def sig_handler(sig, frame):
                 if sig == signal.SIGUSR1:
@@ -355,8 +422,8 @@ class OfflineImap:
                     accounts.Account.set_abort_event(self.config, 2)
                 elif sig in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP):
                     # tell each account to ABORT ASAP (ctrl-c)
-                    getglobalui().warn("Terminating NOW (this may "\
-                                       "take a few seconds)...")
+                    getglobalui().warn("Terminating NOW (this may "
+                        "take a few seconds)...")
                     accounts.Account.set_abort_event(self.config, 3)
                 elif sig == signal.SIGQUIT:
                     stacktrace.dump(sys.stderr)
@@ -369,44 +436,53 @@ class OfflineImap:
             signal.signal(signal.SIGINT, sig_handler)
             signal.signal(signal.SIGQUIT, sig_handler)
 
-            #various initializations that need to be performed:
+            # Various initializations that need to be performed:
+            # XXX: is there any reason to no import mbnames in imports?
             offlineimap.mbnames.init(self.config, syncaccounts)
 
             if options.singlethreading:
-                #singlethreaded
+                # singlethreaded
                 self.__sync_singlethreaded(syncaccounts)
             else:
                 # multithreaded
+                # ExitNotifyThread is inherited from threading.Thread.
                 t = threadutil.ExitNotifyThread(target=syncmaster.syncitall,
-                                 name='Sync Runner',
-                                 kwargs = {'accounts': syncaccounts,
-                                           'config': self.config})
+                    name='Sync Runner',
+                    kwargs={'accounts': syncaccounts, 'config': self.config})
                 t.start()
                 threadutil.exitnotifymonitorloop(threadutil.threadexited)
             self.ui.terminate()
         except (SystemExit):
             raise
         except Exception as e:
+            # FIXME: UNICODE: This helps having good traces. There are some
+            # Exception with missing of full traces or other informations when
+            # not set. This should be improved.
+            if uni.use_unicode():
+                hack.inspect_crash()
+                raise
             self.ui.error(e)
             self.ui.terminate()
 
     def __sync_singlethreaded(self, accs):
         """Executed if we do not want a separate syncmaster thread
 
-        :param accs: A list of accounts that should be synced
-        """
+        :param accs: A list of accounts that should be synced."""
+
         for accountname in accs:
             account = offlineimap.accounts.SyncableAccount(self.config,
-                                                           accountname)
-            threading.currentThread().name = "Account sync %s"% accountname
+                accountname)
+            threading.currentThread().name = "Account sync %s"% accountname.dbytes
             account.syncrunner()
 
     def __serverdiagnostics(self, options):
-        activeaccounts = self.config.get("general", "accounts")
+        """Start server diagnostic for active accounts."""
+
+        activeaccounts = uniString(self.config.get("general", "accounts"))
         if options.accounts:
-            activeaccounts = options.accounts
+            activeaccounts = fsString(options.accounts)
         activeaccounts = activeaccounts.split(",")
         allaccounts = accounts.AccountListGenerator(self.config)
         for account in allaccounts:
-            if account.name not in activeaccounts: continue
+            if account.getname() not in activeaccounts: continue
             account.serverdiagnostics()
diff --git a/offlineimap/localeval.py b/offlineimap/localeval.py
index a9494fb..2188fdd 100644
--- a/offlineimap/localeval.py
+++ b/offlineimap/localeval.py
@@ -17,6 +17,10 @@
 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
 import imp
+import codecs
+
+from offlineimap.utils import uni
+from offlineimap.utils.uni import noneString
 try:
     import errno
 except:
@@ -25,24 +29,31 @@ except:
 class LocalEval:
     """Here is a powerfull but very dangerous option, of course."""
 
-    def __init__(self, path=None):
+    def __init__(self, path=noneString):
         self.namespace = {}
 
-        if path is not None:
+        if path.uni is not None:
             # FIXME: limit opening files owned by current user with rights set
             # to fixed mode 644.
-            foo = open(path, 'r')
+            if uni.use_unicode():
+                foo = codecs.open(path.fs, 'r', uni.ENCODING)
+            else:
+                foo = open(path.fs, 'r')
             module = imp.load_module(
                 '<none>',
                 foo,
-                path,
+                path.fs,
                 ('', 'r', imp.PY_SOURCE))
             for attr in dir(module):
                 self.namespace[attr] = getattr(module, attr)
 
     def eval(self, text, namespace=None):
+        """eval() builtin use Latin-1 or UTF-8 arguments, also does this one.
+
+        :param: text is a uni object."""
+
         names = {}
         names.update(self.namespace)
         if namespace is not None:
             names.update(namespace)
-        return eval(text, names)
+        return eval(text.uni, names)
diff --git a/offlineimap/mbnames.py b/offlineimap/mbnames.py
index 936a110..bf095d2 100644
--- a/offlineimap/mbnames.py
+++ b/offlineimap/mbnames.py
@@ -19,67 +19,113 @@
 import os.path
 import re                               # for folderfilter
 from threading import Lock
+import codecs
 
+from offlineimap.utils import uni
+from offlineimap.utils.uni import uniString
+
+
+# Format of boxes is:
+# - keys: account names (uni objects)
+# - values: list of folder names (uni objects)
 boxes = {}
+# Format of localroots is:
+# - keys: account names (uni objects)
+# - values: list of local repository roots (uni objects)
 localroots = {}
-config = None
+# List of (unicode) (sync)-account names.
 accounts = None
+
+config = None
 mblock = Lock()
 
 def init(conf, accts):
+    """Initialize mbnames.
+
+    :param accts: list of (unicode) (syncable) accounts.
+    """
+
     global config, accounts
     config = conf
     accounts = accts
 
 def add(accountname, foldername, localfolders):
+    """Add the infos to the boxes and localroots variables."""
+
     if not accountname in boxes:
-        boxes[accountname] = []
+        boxes[accountname] = [] # Add account name key if missing.
+        # XXX: Erase (?) local repository root.
         localroots[accountname] = localfolders
     if not foldername in boxes[accountname]:
+        # Add folder name to the list.
         boxes[accountname].append(foldername)
 
 def write():
     # See if we're ready to write it out.
-    for account in accounts:
-        if account not in boxes:
+    for uni_accountname in accounts:
+        if uni_accountname not in boxes:
             return
 
     __genmbnames()
 
 def __genmbnames():
-    """Takes a configparser object and a boxlist, which is a list of hashes
-    containing 'accountname' and 'foldername' keys."""
+    """Takes a configparser object and a boxlist, which is a list of dicts
+    containing 'accountname.uni' and 'foldername.uni' keys."""
 
     xforms = [os.path.expanduser, os.path.expandvars]
     mblock.acquire()
     try:
-        localeval = config.getlocaleval()
         if not config.getdefaultboolean("mbnames", "enabled", 0):
             return
-        path = config.apply_xforms(config.get("mbnames", "filename"), xforms)
-        file = open(path, "wt")
-        file.write(localeval.eval(config.get("mbnames", "header")))
+
+        localeval = config.getlocaleval()
+        path = uniString(config.get("mbnames", "filename"))
+        path = uniString(config.apply_xforms(path, xforms))
+        if uni.use_unicode():
+            fd_mbnames = codecs.open(path.fs, "wt", uni.ENCODING)
+        else:
+            fd_mbnames = open(path.fs, "wt")
+
+        mbnames_header = uniString(config.get("mbnames", "header"))
+        # Write to file with default encoding.
+        fd_mbnames.write(localeval.eval(mbnames_header).dbytes)
+
+        # Default folderfilter.
         folderfilter = lambda accountname, foldername: 1
+        # User folderfilter.
         if config.has_option("mbnames", "folderfilter"):
-            folderfilter = localeval.eval(config.get("mbnames", "folderfilter"),
-                                          {'re': re})
+            mbnames_folderfilter = uniString(
+                config.get("mbnames", "folderfilter"))
+            folderfilter = localeval.eval(mbnames_folderfilter, {'re': re})
+
+        # Default sort_keyfunc.
         mb_sort_keyfunc = lambda d: (d['accountname'], d['foldername'])
+        # User sort_keyfunc.
         if config.has_option("mbnames", "sort_keyfunc"):
-            mb_sort_keyfunc = localeval.eval(config.get("mbnames", "sort_keyfunc"),
-                                         {'re': re})
+            mbnames_sort_keyfunc = uniString(
+                config.get("mbnames", "sort_keyfunc"))
+            mb_sort_keyfunc = localeval.eval(mbnames_sort_keyfunc, {'re': re})
+
         itemlist = []
         for accountname in boxes.keys():
-            localroot = localroots[accountname]
+            localfolders = localroots[accountname]
             for foldername in boxes[accountname]:
-                if folderfilter(accountname, foldername):
-                    itemlist.append({'accountname': accountname,
-                                     'foldername': foldername,
-                                     'localfolders': localroot})
-        itemlist.sort(key = mb_sort_keyfunc)
-        format_string = config.get("mbnames", "peritem", raw=1)
-        itemlist = [format_string % d for d in itemlist]
-        file.write(localeval.eval(config.get("mbnames", "sep")).join(itemlist))
-        file.write(localeval.eval(config.get("mbnames", "footer")))
-        file.close()
+                if folderfilter(accountname.uni, foldername.uni):
+                    itemlist.append({'accountname': accountname.uni,
+                                     'foldername': foldername.uni,
+                                     'localfolders': localfolders.uni})
+        itemlist.sort(key=mb_sort_keyfunc)
+        uni_format_string = config.get("mbnames", "peritem", raw=1)
+        itemlist = [uni_format_string % d for d in itemlist]
+
+        mbnames_sep = uniString(config.get("mbnames", "sep"))
+        mbnames_footer = uniString(config.get("mbnames", "footer"))
+
+        uni_eval_mbnames_sep = localeval.eval(mbnames_sep).join(itemlist)
+        uni_eval_mbnames_footer = localeval.eval(mbnames_footer)
+
+        fd_mbnames.write(uni_eval_mbnames_sep)
+        fd_mbnames.write(uni_eval_mbnames_footer)
+        fd_mbnames.close()
     finally:
         mblock.release()
diff --git a/offlineimap/repository/Base.py b/offlineimap/repository/Base.py
index 0cf44f8..76fc0e8 100644
--- a/offlineimap/repository/Base.py
+++ b/offlineimap/repository/Base.py
@@ -21,7 +21,10 @@ from sys import exc_info
 
 from offlineimap import CustomConfig
 from offlineimap.ui import getglobalui
+from offlineimap.utils.uni import fsString, uniString, noneString, use_unicode
 from offlineimap.error import OfflineImapError
+from offlineimap.utils import hack
+
 
 class BaseRepository(CustomConfig.ConfigHelperMixin, object):
 
@@ -33,33 +36,54 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
         self.localeval = account.getlocaleval()
         self._accountname = self.account.getname()
         self._readonly = self.getconfboolean('readonly', False)
-        self.uiddir = os.path.join(self.config.getmetadatadir(), 'Repository-' + self.name)
-        if not os.path.exists(self.uiddir):
-            os.mkdir(self.uiddir, 0o700)
-        self.mapdir = os.path.join(self.uiddir, 'UIDMapping')
-        if not os.path.exists(self.mapdir):
-            os.mkdir(self.mapdir, 0o700)
+        metadatapath = uniString(self.config.getmetadatadir())
+        # FIXME: self.uiddir variable name is lying about itself.
+        self.uiddir = fsString(os.path.join(
+            metadatapath.fs, 'Repository-' + self.name.fs))
+        if not os.path.exists(self.uiddir.fs):
+            os.mkdir(self.uiddir.fs, 0o700)
+        self.mapdir = fsString(os.path.join(self.uiddir.fs, 'UIDMapping'))
+        if not os.path.exists(self.mapdir.fs):
+            os.mkdir(self.mapdir.fs, 0o700)
         # FIXME: self.uiddir variable name is lying about itself.
-        self.uiddir = os.path.join(self.uiddir, 'FolderValidity')
-        if not os.path.exists(self.uiddir):
-            os.mkdir(self.uiddir, 0o700)
+        self.uiddir = fsString(os.path.join(self.uiddir.fs, 'FolderValidity'))
+        if not os.path.exists(self.uiddir.fs):
+            os.mkdir(self.uiddir.fs, 0o700)
 
+        # Expects Unicode syntaxt u''.
         self.nametrans = lambda foldername: foldername
+        # Expects Unicode syntaxt u''.
         self.folderfilter = lambda foldername: 1
+        # Expects Unicode syntaxt u''.
         self.folderincludes = []
+        # Expects Unicode syntaxt u''.
         self.foldersort = None
         if self.config.has_option(self.getsection(), 'nametrans'):
             self.nametrans = self.localeval.eval(
-                self.getconf('nametrans'), {'re': re})
+                uniString(self.getconf('nametrans')), {'re': re})
         if self.config.has_option(self.getsection(), 'folderfilter'):
             self.folderfilter = self.localeval.eval(
-                self.getconf('folderfilter'), {'re': re})
+                uniString(self.getconf('folderfilter')), {'re': re})
         if self.config.has_option(self.getsection(), 'folderincludes'):
-            self.folderincludes = self.localeval.eval(
-                self.getconf('folderincludes'), {'re': re})
+            folderincludes = self.localeval.eval(
+                uniString(self.getconf('folderincludes')), {'re': re})
+            self.folderincludes = [uniString(f) for f in folderincludes]
         if self.config.has_option(self.getsection(), 'foldersort'):
             self.foldersort = self.localeval.eval(
-                self.getconf('foldersort'), {'re': re})
+                uniString(self.getconf('foldersort')), {'re': re})
+        # Check if user defined stuff don't even run.
+        if not use_unicode():
+            try:
+                for funcname in ['folderfilter', 'foldersort', 'nametrans']:
+                    func = self.__dict__[funcname]
+                    if func:
+                        func('ascii')
+                for funcname in ['folderincludes']:
+                    'ascii' in self.__dict__[funcname]
+            except Exception as e:
+                raise Exception("function %s failed to run. Is there any "
+                    "Unicode string in it?"% funcname)
+
 
     def restore_atime(self):
         """Sets folders' atime back to their values after a sync
@@ -91,7 +115,8 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
         return self.name
 
     def __str__(self):
-        return self.name
+        # Warn to not use this because encoding is made implicit.
+        return self.name.fs
 
     @property
     def accountname(self):
@@ -106,7 +131,7 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
 
     # Interface from CustomConfig.ConfigHelperMixin
     def getsection(self):
-        return 'Repository ' + self.name
+        return uniString(u'Repository ' + self.name.uni).uni
 
     # Interface from CustomConfig.ConfigHelperMixin
     def getconfig(self):
@@ -135,7 +160,7 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
     def should_sync_folder(self, fname):
         """Should this folder be synced?"""
 
-        return fname in self.folderincludes or self.folderfilter(fname)
+        return fname.uni in self.folderincludes or self.folderfilter(fname)
 
     def get_create_folders(self):
         """Is folder creation enabled on this repository?
@@ -148,12 +173,15 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
 
     def makefolder(self, foldername):
         """Create a new folder."""
+
         raise NotImplementedError
 
     def deletefolder(self, foldername):
+
         raise NotImplementedError
 
     def getfolder(self, foldername):
+
         raise NotImplementedError
 
     def sync_folder_structure(self, dst_repo, status_repo):
@@ -179,43 +207,48 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
         src_hash = {}
         for folder in src_folders:
             src_hash[folder.getvisiblename().replace(
-                    src_repo.getsep(), dst_repo.getsep())] = folder
+                src_repo.getsep().uni, dst_repo.getsep().uni)] = folder
         dst_hash = {}
         for folder in dst_folders:
             dst_hash[folder.getvisiblename().replace(
-                    dst_repo.getsep(), src_repo.getsep())] = folder
+                dst_repo.getsep().uni, src_repo.getsep().uni)] = folder
 
         # Find new folders on src_repo.
-        for src_name_t, src_folder in src_hash.iteritems():
+        for uni_src_name_t, src_folder in src_hash.iteritems():
+            src_name_t = uniString(uni_src_name_t)  # Fix type.
+
             # Don't create on dst_repo, if it is readonly
             if not dst_repo.get_create_folders():
                 break
-            if src_folder.sync_this and not src_name_t in dst_folders:
+            if src_folder.sync_this and not src_name_t.uni in dst_hash:
                 try:
                     dst_repo.makefolder(src_name_t)
                     dst_haschanged = True # Need to refresh list
                 except OfflineImapError as e:
-                    self.ui.error(e, exc_info()[2],
-                         "Creating folder %s on repository %s"%
-                         (src_name_t, dst_repo))
+                    self.ui.error(e, exc_info()[2], 
+                        u"Creating folder %s on repository %s"%
+                        (src_name_t.uni, dst_repo.getname().uni))
                     raise
-                status_repo.makefolder(src_name_t.replace(dst_repo.getsep(),
-                                                   status_repo.getsep()))
+                status_repo.makefolder(uniString(src_name_t.replace(
+                    dst_repo.getsep(), status_repo.getsep())))
+
         # Find new folders on dst_repo.
         for dst_name_t, dst_folder in dst_hash.iteritems():
+
             if not src_repo.get_create_folders():
                 # Don't create missing folder on readonly repo.
                 break
 
-            if dst_folder.sync_this and not dst_name_t in src_folders:
+            if dst_folder.sync_this and not dst_name_t in src_hash:
                 # nametrans sanity check!
                 # Does nametrans back&forth lead to identical names?
                 # 1) would src repo filter out the new folder name? In this
                 # case don't create it on it:
                 if not self.should_sync_folder(dst_name_t):
-                    self.ui.debug('', "Not creating folder '%s' (repository '%s"
-                        "') as it would be filtered out on that repository."%
-                        (dst_name_t, self))
+                    self.ui.debug('',
+                        u"Not creating folder '%s' (repository '%s') as it "
+                        "would be filtered out on that repository."%
+                        (dst_name_t.uni, self.name.uni))
                     continue
                 # get IMAPFolder and see if the reverse nametrans
                 # works fine TODO: getfolder() works only because we
@@ -223,9 +256,10 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
                 # like to change. Take care!
                 folder = self.getfolder(dst_name_t)
                 # apply reverse nametrans to see if we end up with the same name
-                newdst_name = folder.getvisiblename().replace(
-                    src_repo.getsep(), dst_repo.getsep())
-                if dst_folder.name != newdst_name:
+                newdst_name = uniString(folder.getvisiblename().uni.replace(
+                    src_repo.getsep().uni, dst_repo.getsep().uni))
+                #hack.debugger()
+                if dst_folder.getname().imap != newdst_name.imap:
                     raise OfflineImapError("INFINITE FOLDER CREATION DETECTED! "
                         "Folder '%s' (repository '%s') would be created as fold"
                         "er '%s' (repository '%s'). The latter becomes '%s' in "
@@ -234,23 +268,25 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
                         "itories so they lead to identical names if applied bac"
                         "k and forth. 2) Use folderfilter settings on a reposit"
                         "ory to prevent some folders from being created on the "
-                        "other side." % (dst_folder.name, dst_repo, dst_name_t,
-                                         src_repo, newdst_name),
-                                           OfflineImapError.ERROR.REPO)
+                        "other side."% (dst_folder.getname().fs,
+                        dst_repo.getname().fs, dst_name_t.fs,
+                        src_repo.getname().fs, newdst_name.fs),
+                        OfflineImapError.ERROR.REPO)
                 # end sanity check, actually create the folder
                 try:
                     src_repo.makefolder(dst_name_t)
                     src_haschanged = True # Need to refresh list
                 except OfflineImapError as e:
-                    self.ui.error(e, exc_info()[2], "Creating folder %s on "
-                                  "repository %s" % (dst_name_t, src_repo))
+                    self.ui.error(e, exc_info()[2],
+                        u"Creating folder %s on repository %s"%
+                        (dst_name_t.fs, src_repo.getname().fs))
                     raise
-                status_repo.makefolder(dst_name_t.replace(
-                                src_repo.getsep(), status_repo.getsep()))
+                status_repo.makefolder(uniString(dst_name_t.replace(
+                    src_repo.getsep(), status_repo.getsep())))
         # Find deleted folders.
         # TODO: We don't delete folders right now.
 
-        #Forget old list of cached folders so we get new ones if needed
+        # Forget old list of cached folders so we get new ones if needed.
         if src_haschanged:
             self.forgetfolders()
         if dst_haschanged:
@@ -268,5 +304,6 @@ class BaseRepository(CustomConfig.ConfigHelperMixin, object):
     def getlocalroot(self):
     	""" Local root folder for storing messages.
     	Will not be set for remote repositories."""
-        return None
+
+        return noneString()
 
diff --git a/offlineimap/repository/Gmail.py b/offlineimap/repository/Gmail.py
index 2e23e62..2ff67fd 100644
--- a/offlineimap/repository/Gmail.py
+++ b/offlineimap/repository/Gmail.py
@@ -17,6 +17,8 @@
 
 from offlineimap.repository.IMAP import IMAPRepository
 from offlineimap import folder, OfflineImapError
+from offlineimap.utils.uni import uniString
+
 
 class GmailRepository(IMAPRepository):
     """Gmail IMAP repository.
@@ -24,6 +26,7 @@ class GmailRepository(IMAPRepository):
     Falls back to hard-coded gmail host name and port, if none were specified:
     http://mail.google.com/support/bin/answer.py?answer=78799&topic=12814
     """
+
     # Gmail IMAP server hostname
     HOSTNAME = "imap.gmail.com"
     # Gmail IMAP server port
@@ -31,44 +34,49 @@ class GmailRepository(IMAPRepository):
 
     def __init__(self, reposname, account):
         """Initialize a GmailRepository object."""
+
         # Enforce SSL usage
-        account.getconfig().set('Repository ' + reposname,
-                                'ssl', 'yes')
+        account.getconfig().set(uniString(
+            'Repository ' + reposname.dbytes, 'ssl', 'yes')).dbytes
         IMAPRepository.__init__(self, reposname, account)
 
-
     def gethost(self):
         """Return the server name to connect to.
 
         Gmail implementation first checks for the usual IMAP settings
         and falls back to imap.gmail.com if not specified."""
+
         try:
             return super(GmailRepository, self).gethost()
         except OfflineImapError:
             # nothing was configured, cache and return hardcoded one
-            self._host = GmailRepository.HOSTNAME
+            self._host = uniString(GmailRepository.HOSTNAME)
             return self._host
 
     def getport(self):
+
         return GmailRepository.PORT
 
     def getssl(self):
+
         return 1
 
     def getpreauthtunnel(self):
+
         return None
 
     def getfolder(self, foldername):
-        return self.getfoldertype()(self.imapserver, foldername,
-                                    self)
+
+        return self.getfoldertype()(
+            self.imapserver, foldername, self)
 
     def getfoldertype(self):
         return folder.Gmail.GmailFolder
 
     def gettrashfolder(self, foldername):
         #: Where deleted mail should be moved
-        return  self.getconf('trashfolder','[Gmail]/Trash')
+        return uniString(self.getconf('trashfolder','[Gmail]/Trash'))
 
     def getspamfolder(self):
         #: Gmail also deletes messages upon EXPUNGE in the Spam folder
-        return  self.getconf('spamfolder','[Gmail]/Spam')
+        return uniString(self.getconf('spamfolder','[Gmail]/Spam'))
diff --git a/offlineimap/repository/IMAP.py b/offlineimap/repository/IMAP.py
index b109546..3ad8bb6 100644
--- a/offlineimap/repository/IMAP.py
+++ b/offlineimap/repository/IMAP.py
@@ -26,27 +26,33 @@ from offlineimap import folder, imaputil, imapserver, OfflineImapError
 from offlineimap.folder.UIDMaps import MappedIMAPFolder
 from offlineimap.threadutil import ExitNotifyThread
 from offlineimap.utils.distro import get_os_sslcertfile, get_os_sslcertfile_searchpath
+from offlineimap.utils.uni import noneString, uniString, dbytesString, \
+    imapString, valueString, isASCII
+from offlineimap.utils import hack
 
 
 class IMAPRepository(BaseRepository):
     def __init__(self, reposname, account):
         """Initialize an IMAPRepository object."""
+
         BaseRepository.__init__(self, reposname, account)
         # self.ui is being set by the BaseRepository
-        self._host = None
+        self._host = noneString()
         self.imapserver = imapserver.IMAPServer(self)
         self.folders = None
         if self.getconf('sep', None):
-            self.ui.info("The 'sep' setting is being ignored for IMAP "
-                         "repository '%s' (it's autodetected)"% self)
+            # FIXME: should warn, instead.
+            self.ui.info(u"The 'sep' setting is being ignored for IMAP "
+                "repository '%s' (it's autodetected)"% self.name.uni)
 
     def startkeepalive(self):
         keepalivetime = self.getkeepalive()
         if not keepalivetime: return
         self.kaevent = Event()
-        self.kathread = ExitNotifyThread(target = self.imapserver.keepalive,
-                                         name = "Keep alive " + self.getname(),
-                                         args = (keepalivetime, self.kaevent))
+        self.kathread = ExitNotifyThread(
+            target=self.imapserver.keepalive,
+            name="Keep alive " + self.name.fs,
+            args=(keepalivetime, self.kaevent))
         self.kathread.setDaemon(1)
         self.kathread.start()
 
@@ -83,46 +89,50 @@ class IMAPRepository(BaseRepository):
 
         This requires that self.imapserver has been initialized with an
         acquireconnection() or it will still be `None`"""
-        assert self.imapserver.delim != None, "'%s' " \
-            "repository called getsep() before the folder separator was " \
-            "queried from the server"% self
-        return self.imapserver.delim
+
+        delim = self.imapserver.getdelim()
+        assert delim.uni != None, ("'%s' "
+            "repository called getsep() before the folder separator was "
+            "queried from the server"% self.name.fs)
+        return delim
 
     def gethost(self):
         """Return the configured hostname to connect to
 
         :returns: hostname as string or throws Exception"""
-        if self._host:  # use cached value if possible
+
+        if self._host.uni:  # Use cached value if possible.
             return self._host
 
         # 1) check for remotehosteval setting
         if self.config.has_option(self.getsection(), 'remotehosteval'):
-            host = self.getconf('remotehosteval')
+            host = noneString()
+            hosteval = uniString(self.getconf('remotehosteval'))
             try:
-                host = self.localeval.eval(host)
+                host = uniString(self.localeval.eval(hosteval))
             except Exception as e:
                 raise OfflineImapError("remotehosteval option for repository "
-                    "'%s' failed:\n%s"% (self, e), OfflineImapError.ERROR.REPO), \
-                    None, exc_info()[2]
-            if host:
+                    "'%s' failed:\n%s"% (self.name.fs, e),
+                    OfflineImapError.ERROR.REPO), None, exc_info()[2]
+            if host.uni:
                 self._host = host
                 return self._host
         # 2) check for plain remotehost setting
-        host = self.getconf('remotehost', None)
-        if host != None:
+        host = uniString(self.getconf('remotehost', None))
+        if host.uni != None:
             self._host = host
             return self._host
 
         # no success
-        raise OfflineImapError("No remote host for repository "
-            "'%s' specified."% self, OfflineImapError.ERROR.REPO)
+        raise OfflineImapError("No remote host for repository '%s' "
+            "specified."% self.name.fs, OfflineImapError.ERROR.REPO)
 
     def get_remote_identity(self):
         """Remote identity is used for certain SASL mechanisms
         (currently -- PLAIN) to inform server about the ID
         we want to authorize as instead of our login name."""
 
-        return self.getconf('remote_identity', default=None)
+        return uniString(self.getconf('remote_identity', default=None))
 
     def get_auth_mechanisms(self):
         supported = ["GSSAPI", "CRAM-MD5", "PLAIN", "LOGIN"]
@@ -132,58 +142,66 @@ class IMAPRepository(BaseRepository):
         # TODO: due to the chosen-plaintext resistance.
         default = ["GSSAPI", "CRAM-MD5", "PLAIN", "LOGIN"]
 
-        mechs = self.getconflist('auth_mechanisms', r',\s*',
-          default)
+        mechs = self.getconflist('auth_mechanisms', r',\s*', default)
 
         for m in mechs:
+            isASCII(m, exception_msg=
+                "auth_mechanisms must be plain ASCII")
             if m not in supported:
-                raise OfflineImapError("Repository %s: "% self + \
-                  "unknown authentication mechanism '%s'"% m,
-                  OfflineImapError.ERROR.REPO)
+                raise OfflineImapError("Repository %s: unknown "
+                    "authentication mechanism '%s'"%
+                    self.name.fs, m, OfflineImapError.ERROR.REPO)
+
+        self.ui.debug('imap', u"Using authentication mechanisms %s"% mechs)
 
-        self.ui.debug('imap', "Using authentication mechanisms %s" % mechs)
         return mechs
 
 
     def getuser(self):
-        user = None
+        def getnetrcuser(path=None):
+            try:
+                if path:
+                    netrcentry = netrc.netrc('/etc/netrc').authenticators(self.gethost())
+                else:
+                    netrcentry = netrc.netrc().authenticators(self.gethost())
+            except IOError as inst:
+                if inst.errno != errno.ENOENT:
+                    raise
+            else:
+                if netrcentry:
+                    # Couldn't find the expected encoding of netrc. Assume it to
+                    # be uni.ENCODING (UTF-8) because it's the less offending.
+                    return dbytesString(netrcentry[0])
+            return noneString()
+
+        user = noneString()
         localeval = self.localeval
 
         if self.config.has_option(self.getsection(), 'remoteusereval'):
-            user = self.getconf('remoteusereval')
-        if user != None:
-            return localeval.eval(user)
+            user = uniString(self.getconf('remoteusereval'))
+        if user.uni != None:
+            return uniString(localeval.eval(user))
 
-        user = self.getconf('remoteuser')
-        if user != None:
+        user = uniString(self.getconf('remoteuser'))
+        if user.uni != None:
             return user
 
-        try:
-            netrcentry = netrc.netrc().authenticators(self.gethost())
-        except IOError as inst:
-            if inst.errno != errno.ENOENT:
-                raise
-        else:
-            if netrcentry:
-                return netrcentry[0]
+        user = getnetrcuser('/etc/netrc')
+        if user.dbytes != None:
+            return user
 
-        try:
-            netrcentry = netrc.netrc('/etc/netrc').authenticators(self.gethost())
-        except IOError as inst:
-            if inst.errno not in (errno.ENOENT, errno.EACCES):
-                raise
-        else:
-            if netrcentry:
-                return netrcentry[0]
+        user = getnetrcuser()
+        if user.dbytes != None:
+            return user
 
+        return user
 
     def getport(self):
-        port = None
-
         if self.config.has_option(self.getsection(), 'remoteporteval'):
-            port = self.getconf('remoteporteval')
-        if port != None:
-            return self.localeval.eval(port)
+            porteval = uniString(self.getconf('remoteporteval'))
+            if porteval.uni != None:
+                # XXX: we should be type checking of what eval() returns...
+                return self.localeval.eval(porteval)
 
         return self.getconfint('remoteport', None)
 
@@ -192,11 +210,11 @@ class IMAPRepository(BaseRepository):
 
     def getsslclientcert(self):
         xforms = [os.path.expanduser, os.path.expandvars, os.path.abspath]
-        return self.getconf_xform('sslclientcert', xforms, None)
+        return uniString(self.getconf_xform('sslclientcert', xforms, None))
 
     def getsslclientkey(self):
         xforms = [os.path.expanduser, os.path.expandvars, os.path.abspath]
-        return self.getconf_xform('sslclientkey', xforms, None)
+        return uniString(self.getconf_xform('sslclientkey', xforms, None))
 
     def getsslcacertfile(self):
         """Determines CA bundle.
@@ -217,52 +235,61 @@ class IMAPRepository(BaseRepository):
         """
 
         xforms = [os.path.expanduser, os.path.expandvars, os.path.abspath]
-        cacertfile = self.getconf_xform('sslcacertfile', xforms, None)
-        if self.getconf('sslcacertfile', None) == "OS-DEFAULT":
-            cacertfile = get_os_sslcertfile()
-            if cacertfile == None:
+
+        cacertfile = uniString(self.getconf_xform(
+            'sslcacertfile', xforms, None))
+        if uniString(self.getconf('sslcacertfile', None)).dbytes == "OS-DEFAULT":
+            cacertfile = dbytesString(get_os_sslcertfile())
+            if cacertfile.uni == None:
                 searchpath = get_os_sslcertfile_searchpath()
+                # searchpath is iterable of plain ASCII paths.
+                isASCII(', '.join(searchpath), exception_msg=
+                    "OS-DEFAULT paths must be plain ASCII")
                 if searchpath:
                     reason = "Default CA bundle was requested, "\
                              "but no existing locations available.  "\
-                             "Tried %s." % (", ".join(searchpath))
+                             "Tried %s."% (", ".join(searchpath))
                 else:
                     reason = "Default CA bundle was requested, "\
                              "but OfflineIMAP doesn't know any for your "\
                              "current operating system."
                 raise OfflineImapError(reason, OfflineImapError.ERROR.REPO)
-        if cacertfile is None:
-            return None
-        if not os.path.isfile(cacertfile):
+        if cacertfile.uni is None:
+            return noneString()
+        if not os.path.isfile(cacertfile.fs):
             reason = "CA certfile for repository '%s' couldn't be found.  "\
-                     "No such file: '%s'" % (self.name, cacertfile)
+                "No such file: '%s'"% (self.name.fs, cacertfile.fs)
             raise OfflineImapError(reason, OfflineImapError.ERROR.REPO)
         return cacertfile
 
     def getsslversion(self):
-        return self.getconf('ssl_version', None)
+        return uniString(self.getconf('ssl_version', None))
 
-    def get_ssl_fingerprint(self):
-        """Return array of possible certificate fingerprints.
+    def get_ssl_fingerprints(self):
+        """Return array of possible certificate fingerprints (type str).
 
         Configuration item cert_fingerprint can contain multiple
         comma-separated fingerprints in hex form."""
 
-        value = self.getconf('cert_fingerprint', "")
+        # Encode back to what user actually wrote.
+        value = uniString(self.getconf('cert_fingerprint', "")).dbytes
         return [f.strip().lower() for f in value.split(',') if f]
 
     def getpreauthtunnel(self):
-        return self.getconf('preauthtunnel', None)
+        return uniString(self.getconf('preauthtunnel', None))
 
     def gettransporttunnel(self):
-        return self.getconf('transporttunnel', None)
+        return uniString(self.getconf('transporttunnel', None))
 
     def getreference(self):
-        return self.getconf('reference', '')
+        return uniString(self.getconf('reference', '""'))
 
     def getidlefolders(self):
+        """Return a list of idle folders."""
+
         localeval = self.localeval
-        return localeval.eval(self.getconf('idlefolders', '[]'))
+        # XXX: we should be type checking of what eval() returns...
+        return localeval.eval(uniString(self.getconf('idlefolders', '[]')))
 
     def getmaxconnections(self):
         num1 = len(self.getidlefolders())
@@ -286,45 +313,50 @@ class IMAPRepository(BaseRepository):
         On success we return the password.
         If all strategies fail we return None."""
 
+        def getnetrcpass(path):
+            try:
+                if path:
+                    netrcentry = netrc.netrc('/etc/netrc').authenticators(
+                        self.gethost())
+                else:
+                    netrcentry = netrc.netrc().authenticators(self.gethost())
+            except IOError as inst:
+                if inst.errno != errno.ENOENT:
+                    raise
+                else:
+                    if netrcentry:
+                        # Assume netrc use UTF-8 encoding. This has to be
+                        # checked.
+                        user = uniString(self.getconf('remoteuser'))
+                        if user.uni == None or user.dbytes == netrcentry[0]:
+                            return valueString(netrcentry[2])
+            return noneString()
+
         # 1. evaluate Repository 'remotepasseval'
-        passwd = self.getconf('remotepasseval', None)
-        if passwd != None:
-            return self.localeval.eval(passwd)
+        passwdeval = uniString(self.getconf('remotepasseval', None))
+        if passwdeval.value != None:
+            return valueString(self.localeval.eval(passwdeval))
         # 2. read password from Repository 'remotepass'
-        password = self.getconf('remotepass', None)
-        if password != None:
-            return password
+        password = uniString(self.getconf('remotepass', None))
+        if password.uni != None:
+            return valueString(password.dbytes)
         # 3. read password from file specified in Repository 'remotepassfile'
-        passfile = self.getconf('remotepassfile', None)
-        if passfile != None:
-            fd = open(os.path.expanduser(passfile))
-            password = fd.readline().strip()
+        passfile = uniString(self.getconf('remotepassfile', None))
+        if passfile.uni != None:
+            fs_file = os.path.expanduser(passfile.fs)
+            fd = open(fs_file, 'rb')
+            password = dbytesString(fd.readline().strip())
             fd.close()
-            return password
+            return valueString(password.dbytes)
         # 4. read password from ~/.netrc
-        try:
-            netrcentry = netrc.netrc().authenticators(self.gethost())
-        except IOError as inst:
-            if inst.errno != errno.ENOENT:
-                raise
-        else:
-            if netrcentry:
-                user = self.getconf('remoteuser')
-                if user == None or user == netrcentry[0]:
-                    return netrcentry[2]
+        password = getnetrcpass()
+        if password.value is not None:
+            return password
         # 5. read password from /etc/netrc
-        try:
-            netrcentry = netrc.netrc('/etc/netrc').authenticators(self.gethost())
-        except IOError as inst:
-            if inst.errno not in (errno.ENOENT, errno.EACCES):
-                raise
-        else:
-            if netrcentry:
-                user = self.getconf('remoteuser')
-                if user == None or user == netrcentry[0]:
-                    return netrcentry[2]
-        # no strategy yielded a password!
-        return None
+        password = getnetrcpass('/etc/netrc')
+        if password.value is not None:
+            return password
+        return noneString()
 
     def getfolder(self, foldername):
         """Return instance of OfflineIMAP representative folder."""
@@ -341,6 +373,7 @@ class IMAPRepository(BaseRepository):
     def forgetfolders(self):
         self.folders = None
 
+    # XXX: make it clear it returns instances and not names.
     def getfolders(self):
         """Return a list of instances of OfflineIMAP representative folder."""
 
@@ -348,17 +381,17 @@ class IMAPRepository(BaseRepository):
             return self.folders
         retval = []
         imapobj = self.imapserver.acquireconnection()
-        # check whether to list all folders, or subscribed only
+        # Check whether to list all folders, or subscribed only.
         listfunction = imapobj.list
         if self.getconfboolean('subscribedonly', False):
             listfunction = imapobj.lsub
         try:
-            listresult = listfunction(directory = self.imapserver.reference)[1]
+            ref = self.imapserver.reference
+            listresult = listfunction(directory=ref)[1]
         finally:
             self.imapserver.releaseconnection(imapobj)
         for s in listresult:
-            if s == None or \
-                   (isinstance(s, basestring) and s == ''):
+            if s == None or (isinstance(s, basestring) and s == ''):
                 # Bug in imaplib: empty strings in results from
                 # literals. TODO: still relevant?
                 continue
@@ -366,22 +399,22 @@ class IMAPRepository(BaseRepository):
             flaglist = [x.lower() for x in imaputil.flagsplit(flags)]
             if '\\noselect' in flaglist:
                 continue
-            foldername = imaputil.dequote(name)
-            retval.append(self.getfoldertype()(self.imapserver, foldername,
-                                               self))
-        # Add all folderincludes
+            foldername = imapString(imaputil.dequote(name))
+            retval.append(
+                self.getfoldertype()(self.imapserver, foldername, self))
+        # Add all folderincludes.
         if len(self.folderincludes):
             imapobj = self.imapserver.acquireconnection()
             try:
                 for foldername in self.folderincludes:
                     try:
-                        imapobj.select(foldername, readonly = True)
+                        imapobj.select(foldername.imap, readonly=True)
                     except OfflineImapError as e:
-                        # couldn't select this folderinclude, so ignore folder.
+                        # Couldn't select this folderinclude, so ignore folder.
                         if e.severity > OfflineImapError.ERROR.FOLDER:
                             raise
                         self.ui.error(e, exc_info()[2],
-                                      'Invalid folderinclude:')
+                            'Invalid folderinclude: %s'% foldername.fs)
                         continue
                     retval.append(self.getfoldertype()(
                         self.imapserver, foldername, self))
@@ -389,10 +422,10 @@ class IMAPRepository(BaseRepository):
                 self.imapserver.releaseconnection(imapobj)
 
         if self.foldersort is None:
-            # default sorting by case insensitive transposed name
-            retval.sort(key=lambda x: str.lower(x.getvisiblename()))
+            # Default sorting by case insensitive transposed name.
+            retval.sort(key=lambda x: x.getvisiblename().lower())
         else:
-            # do foldersort in a python3-compatible way
+            # Do foldersort in a python3-compatible way.
             # http://bytes.com/topic/python/answers/844614-python-3-sorting-comparison-function
             def cmp2key(mycmp):
                 """Converts a cmp= function into a key= function
@@ -417,20 +450,20 @@ class IMAPRepository(BaseRepository):
 
         :param foldername: Full path of the folder to be created."""
 
-        if self.getreference():
+        if self.getreference().uni:
             foldername = self.getreference() + self.getsep() + foldername
-        if not foldername: # Create top level folder as folder separator
+        if not foldername.uni: # Create top level folder as folder separator.
             foldername = self.getsep()
-        self.ui.makefolder(self, foldername)
+        self.ui.makefolder(self, foldername.uni)
         if self.account.dryrun:
             return
         imapobj = self.imapserver.acquireconnection()
         try:
-            result = imapobj.create(foldername)
+            result = imapobj.create(foldername.imap)
             if result[0] != 'OK':
                 raise OfflineImapError("Folder '%s'[%s] could not be created. "
-                    "Server responded: %s"% (foldername, self, str(result)),
-                    OfflineImapError.ERROR.FOLDER)
+                    "Server responded: %s"% (foldername.imap, self.name.fs,
+                    str(result)), OfflineImapError.ERROR.FOLDER)
         finally:
             self.imapserver.releaseconnection(imapobj)
 
diff --git a/offlineimap/repository/LocalStatus.py b/offlineimap/repository/LocalStatus.py
index fc34a55..6a89a6f 100644
--- a/offlineimap/repository/LocalStatus.py
+++ b/offlineimap/repository/LocalStatus.py
@@ -20,6 +20,8 @@ import os
 from offlineimap.folder.LocalStatus import LocalStatusFolder
 from offlineimap.folder.LocalStatusSQLite import LocalStatusSQLiteFolder
 from offlineimap.repository.Base import BaseRepository
+from offlineimap.utils.uni import uniString, fsString
+
 
 class LocalStatusRepository(BaseRepository):
     def __init__(self, reposname, account):
@@ -29,32 +31,35 @@ class LocalStatusRepository(BaseRepository):
         self.backends = {}
         self.backends['sqlite'] = {
             'class': LocalStatusSQLiteFolder,
-            'root': os.path.join(account.getaccountmeta(), 'LocalStatus-sqlite')
+            'root': fsString(os.path.join(
+                account.getaccountmeta().fs, 'LocalStatus-sqlite'))
         }
 
         self.backends['plain'] = {
             'class': LocalStatusFolder,
-            'root': os.path.join(account.getaccountmeta(), 'LocalStatus')
+            'root': fsString(os.path.join(
+                account.getaccountmeta().fs, 'LocalStatus'))
         }
 
-        # Set class and root for the configured backend
-        self.setup_backend(self.account.getconf('status_backend', 'plain'))
+        # Set class and root for the configured backend.
+        self.setup_backend(uniString(
+            self.account.getconf('status_backend', 'plain')))
 
-        if not os.path.exists(self.root):
-            os.mkdir(self.root, 0o700)
+        if not os.path.exists(self.root.fs):
+            os.mkdir(self.root.fs, 0o700)
 
         # self._folders is a dict of name:LocalStatusFolders()
         self._folders = {}
 
     def setup_backend(self, backend):
-        if backend in self.backends.keys():
+        if backend.dbytes in self.backends.keys():
             self._backend = backend
-            self.root = self.backends[backend]['root']
-            self.LocalStatusFolderClass = self.backends[backend]['class']
+            self.root = self.backends[backend.dbytes]['root']
+            self.LocalStatusFolderClass = self.backends[backend.dbytes]['class']
 
         else:
             raise SyntaxWarning("Unknown status_backend '%s' for account '%s'"%
-                (backend, self.account.name))
+                (backend.fs, self.account.getname().fs))
 
     def import_other_backend(self, folder):
         for bk, dic in self.backends.items():
@@ -62,15 +67,16 @@ class LocalStatusRepository(BaseRepository):
             if dic['class'] == type(folder):
                 continue
 
-            repobk = LocalStatusRepository(self.name, self.account)
+            repobk = LocalStatusRepository(self.getname(), self.account)
             repobk.setup_backend(bk)      # fake the backend
-            folderbk = dic['class'](folder.name, repobk)
+            folderbk = dic['class'](folder.getname(), repobk)
 
             # if backend contains data, import it to folder.
             if not folderbk.isnewfolder():
-                self.ui._msg('Migrating LocalStatus cache from %s to %s " \
-                    "status folder for %s:%s'%
-                    (bk, self._backend, self.name, folder.name))
+                self.ui._msg(u"Migrating LocalStatus cache from %s to %s "
+                    "status folder for %s:%s"%
+                    (bk.getname().uni, self._backend.uni,
+                    self.name.uni, folder.getname().uni))
 
                 folderbk.cachemessagelist()
                 folder.messagelist = folderbk.messagelist
@@ -78,7 +84,7 @@ class LocalStatusRepository(BaseRepository):
                 break
 
     def getsep(self):
-        return '.'
+        return uniString(u'.')
 
     def makefolder(self, foldername):
         """Create a LocalStatus Folder."""
@@ -88,7 +94,22 @@ class LocalStatusRepository(BaseRepository):
 
         # Create an empty StatusFolder
         folder = self.LocalStatusFolderClass(foldername, self)
-        folder.save()
+        # With Unicode, makefolder() might be raised while cache file already
+        # exists. Calling makefolder() let us update the folder name on disk in
+        # a Maildir if the folder name is not full ASCII.
+        #
+        # Avoid clearing the folder cache file if it already exists. We don't
+        # want to re-download emails from that folder if we already have a cache
+        # file.
+        #
+        # Make the check here to not annoy callers with this issue and because
+        # we can't insert it in the save() method simply (missing of context).
+        #
+        # FIXME: why this call to save() in the first place? We have NOTHING to
+        # save at all. This will only create the cache file which case should be
+        # handled from within the class...
+        if folder.isnewfolder():
+            folder.save()
 
         # Invalidate the cache.
         self.forgetfolders()
@@ -96,8 +117,8 @@ class LocalStatusRepository(BaseRepository):
     def getfolder(self, foldername):
         """Return the Folder() object for a foldername."""
 
-        if foldername in self._folders:
-            return self._folders[foldername]
+        if foldername.uni in self._folders:
+            return self._folders[foldername.uni]
 
         folder = self.LocalStatusFolderClass(foldername, self)
 
@@ -105,7 +126,7 @@ class LocalStatusRepository(BaseRepository):
         if folder.isnewfolder():
             self.import_other_backend(folder)
 
-        self._folders[foldername] = folder
+        self._folders[foldername.uni] = folder
         return folder
 
     def getfolders(self):
diff --git a/offlineimap/repository/Maildir.py b/offlineimap/repository/Maildir.py
index 0262ba2..d07f0be 100644
--- a/offlineimap/repository/Maildir.py
+++ b/offlineimap/repository/Maildir.py
@@ -15,12 +15,17 @@
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
+import os
+from stat import *
+
 from offlineimap import folder
 from offlineimap.ui import getglobalui
 from offlineimap.error import OfflineImapError
 from offlineimap.repository.Base import BaseRepository
-import os
-from stat import *
+from offlineimap.utils import uni
+from offlineimap.utils.uni import isASCII, fsString, uniString, noneString
+from offlineimap.utils import hack
+
 
 class MaildirRepository(BaseRepository):
     def __init__(self, reposname, account):
@@ -32,20 +37,25 @@ class MaildirRepository(BaseRepository):
         self.root = self.getlocalroot()
         self.folders = None
         self.ui = getglobalui()
-        self.debug("MaildirRepository initialized, sep is %s"% repr(self.getsep()))
+        self.debug(u"MaildirRepository initialized, sep is %s"% repr(self.getsep()))
         self.folder_atimes = []
 
         # Create the top-level folder if it doesn't exist
-        if not os.path.isdir(self.root):
-            os.mkdir(self.root, 0o700)
+        if not os.path.isdir(self.root.fs):
+            os.mkdir(self.root.fs, 0o700)
 
     def _append_folder_atimes(self, foldername):
-        """Store the atimes of a folder's new|cur in self.folder_atimes"""
+        """Store the atimes of a folder's new|cur in self.folder_atimes.
+
+        :params foldername: filsystem encoded."""
 
-        p = os.path.join(self.root, foldername)
-        new = os.path.join(p, 'new')
-        cur = os.path.join(p, 'cur')
-        atimes = (p, os.path.getatime(new), os.path.getatime(cur))
+        # self.root is already encoded... It's a path.
+        # We assume foldername is filesystem encoded.
+        p = fsString(os.path.join(self.root.fs, foldername.fs))
+        fs_new = os.path.join(p.fs, 'new')
+        fs_cur = os.path.join(p.fs, 'cur')
+        # Later use of the path expects filesystem encoding.
+        atimes = (p, os.path.getatime(fs_new), os.path.getatime(fs_cur))
         self.folder_atimes.append(atimes)
 
     def restore_atime(self):
@@ -57,20 +67,24 @@ class MaildirRepository(BaseRepository):
             return # not configured to restore
 
         for (dirpath, new_atime, cur_atime) in self.folder_atimes:
-            new_dir = os.path.join(dirpath, 'new')
-            cur_dir = os.path.join(dirpath, 'cur')
-            os.utime(new_dir, (new_atime, os.path.getmtime(new_dir)))
-            os.utime(cur_dir, (cur_atime, os.path.getmtime(cur_dir)))
+            # dirpath is already filesystem encoded, see
+            #  _append_folder_atimes()
+            fs_new_dir = os.path.join(dirpath.fs, 'new')
+            fs_cur_dir = os.path.join(dirpath.fs, 'cur')
+            os.utime(fs_new_dir, (new_atime, os.path.getmtime(fs_new_dir)))
+            os.utime(fs_cur_dir, (cur_atime, os.path.getmtime(fs_cur_dir)))
 
     def getlocalroot(self):
         xforms = [os.path.expanduser]
-        return self.getconf_xform('localfolders', xforms)
+        localroot = uniString(self.getconf_xform('localfolders', xforms))
+        # encode local root path as soon as possible.
+        return localroot
 
     def debug(self, msg):
         self.ui.debug('maildir', msg)
 
     def getsep(self):
-        return self.getconf('sep', '.').strip()
+        return uniString(self.getconf('sep', '.').strip())
 
     def makefolder(self, foldername):
         """Create new Maildir folder if necessary
@@ -79,48 +93,58 @@ class MaildirRepository(BaseRepository):
         need to invoke :meth:`forgetfolders` to force new caching when
         you are done creating folders yourself.
 
-        :param foldername: A relative mailbox name. The maildir will be
-            created in self.root+'/'+foldername. All intermediate folder
-            levels will be created if they do not exist yet. 'cur',
-            'tmp', and 'new' subfolders will be created in the maildir.
+        :param foldername: A relative mailbox name (unicode).
+            The maildir will be created in self.root+'/'+foldername.
+            All intermediate folder levels will be created if they do
+            not exist yet. 'cur', 'tmp', and 'new' subfolders will be
+            created in the maildir.
         """
 
-        self.ui.makefolder(self, foldername)
+        self.ui.makefolder(self, foldername.uni)
         if self.account.dryrun:
             return
-        full_path = os.path.abspath(os.path.join(self.root, foldername))
 
         # sanity tests
-        if self.getsep() == '/':
+        if self.getsep().uni == '/':
             for component in foldername.split('/'):
-                assert not component in ['new', 'cur', 'tmp'],\
+                assert not component.uni in ['new', 'cur', 'tmp'],\
                     "When using nested folders (/ as a Maildir separator), "\
                     "folder names may not contain 'new', 'cur', 'tmp'."
-        assert foldername.find('../') == -1, "Folder names may not contain ../"
-        assert not foldername.startswith('/'), "Folder names may not begin with /"
+        assert foldername.find(u'../') == -1, "Folder names may not contain ../"
+        assert not foldername.startswith(u'/'), "Folder names may not begin with /"
+
+        diverged, fs_unexpected, fs_expected = uni.diverged_foldernames(
+            foldername)
+        if diverged:
+            uni.rename_diverged(self.root, fs_unexpected, fs_expected)
+            self.debug(u"  makefolder : renamed '%s' to '%s'"%
+                (fs_unexpected.uni, fsString(fs_expected).uni))
+
+        full_path = fsString(os.path.abspath(os.path.join(
+            self.root.fs, fs_expected)))
 
         # If we're using hierarchical folders, it's possible that
         # sub-folders may be created before higher-up ones.
-        self.debug("makefolder: calling makedirs '%s'"% full_path)
+        self.debug(u"makefolder: calling makedirs '%s'"% full_path.uni)
         try:
-            os.makedirs(full_path, 0o700)
+            os.makedirs(full_path.fs, 0o700)
         except OSError as e:
-            if e.errno == 17 and os.path.isdir(full_path):
-                self.debug("makefolder: '%s' already a directory"% foldername)
+            if e.errno == 17 and os.path.isdir(full_path.fs):
+                self.debug(u"makefolder: '%s' already a directory"% foldername.fs)
             else:
                 raise
-        for subdir in ['cur', 'new', 'tmp']:
+        for fs_subdir in ['cur', 'new', 'tmp']:
             try:
-                os.mkdir(os.path.join(full_path, subdir), 0o700)
+                os.mkdir(os.path.join(full_path.fs, fs_subdir), 0o700)
             except OSError as e:
                 if e.errno == 17 and os.path.isdir(full_path):
-                    self.debug("makefolder: '%s' already has subdir %s"%
-                        (foldername, subdir))
+                    self.debug(u"makefolder: '%s' already has subdir %s"%
+                        (foldername.uni, fs_subdir))
                 else:
                     raise
 
     def deletefolder(self, foldername):
-        self.ui.warn("NOT YET IMPLEMENTED: DELETE FOLDER %s"% foldername)
+        self.ui.warn(u"NOT YET IMPLEMENTED: DELETE FOLDER %s"% foldername.uni)
 
     def getfolder(self, foldername):
         """Return a Folder instance of this Maildir
@@ -129,69 +153,92 @@ class MaildirRepository(BaseRepository):
         we only return existing folders and that 2 calls with the same
         name will return the same object."""
 
-        # getfolders() will scan and cache the values *if* necessary
+        # getfolders() will scan and cache the values *if* necessary.
         folders = self.getfolders()
         for f in folders:
-            if foldername == f.name:
+            if foldername.uni == f.name:
                 return f
         raise OfflineImapError("getfolder() asked for a nonexisting "
-                               "folder '%s'."% foldername,
-                               OfflineImapError.ERROR.FOLDER)
+            "folder '%s'."% foldername.fs, OfflineImapError.ERROR.FOLDER)
 
     def _getfolders_scandir(self, root, extension=None):
         """Recursively scan folder 'root'; return a list of MailDirFolder
 
-        :param root: (absolute) path to Maildir root
-        :param extension: (relative) subfolder to examine within root"""
+        :param root: (absolute) path to Maildir root, uni object.
+        :param extension: (relative) subfolder to examine within root, uni
+            object.
+        """
 
-        self.debug("_GETFOLDERS_SCANDIR STARTING. root = %s, extension = %s"%
-                   (root, extension))
+        if type(extension) != type(noneString()):
+            extension = noneString()
+        self.debug(u"_GETFOLDERS_SCANDIR STARTING. root = %s, extension = %s"%
+            (root.uni, extension.uni))
         retval = []
 
         # Configure the full path to this repository -- "toppath"
-        if extension:
-            toppath = os.path.join(root, extension)
+        if extension.uni:
+            toppath = fsString(os.path.join(root.fs, extension.fs))
         else:
             toppath = root
-        self.debug("  toppath = %s"% toppath)
+        self.debug(u"  toppath = %s"% (toppath.uni))
 
         # Iterate over directories in top & top itself.
-        for dirname in os.listdir(toppath) + ['']:
-            self.debug("  dirname = %s"% dirname)
-            if dirname == '' and extension is not None:
-                self.debug('  skip this entry (already scanned)')
+        # os.listdir() returns str when the path is str and unicode if the path
+        # is unicode. Here, we assume filsystem encoding.
+        for fs_dirname in os.listdir(toppath.fs) + ['']:
+            # Sanity check for legacy mode.
+            if not uni.use_unicode() and not isASCII(fs_dirname):
+                self.debug(u"  skip dirname (unexpected character) = %s"%
+                    dirname.std)
                 continue
-            if dirname in ['cur', 'new', 'tmp']:
-                self.debug("  skip this entry (Maildir special)")
+
+            dirname = fsString(fs_dirname)
+            if dirname.fs == '' and extension.uni is not None:
+                self.debug(u'  skip this entry (already scanned)')
+                continue
+
+            # If we run without Unicode support, folders are IMAP UTF-7 encoded
+            # (full ASCII).
+            if dirname.fs != '':
+                diverged, fs_unexpected, fs_expected = uni.diverged_foldernames(dirname)
+                if diverged:
+                    uni.rename_diverged(toppath, fs_unexpected, fs_expected)
+                    self.debug(u"  scandir folders: renamed '%s' to '%s'"%
+                        (fsString(fs_unexpected).uni, fsString(fs_expected).uni))
+
+            self.debug(u"  dirname = %s"% dirname.uni)
+            if dirname.fs in ['cur', 'new', 'tmp']:
+                self.debug(u"  skip this entry (Maildir special)")
                 # Bypass special files.
                 continue
-            fullname = os.path.join(toppath, dirname)
-            if not os.path.isdir(fullname):
-                self.debug("  skip this entry (not a directory)")
+
+            fullname = fsString(os.path.join(toppath.fs, dirname.fs))
+            if not os.path.isdir(fullname.fs):
+                self.debug(u"  skip this entry (not a directory)")
                 # Not a directory -- not a folder.
                 continue
             # extension can be None.
-            if extension:
-                foldername = os.path.join(extension, dirname)
+            if extension.uni:
+                foldername = fsString(os.path.join(extension.fs, dirname.fs))
             else:
                 foldername = dirname
 
-            if (os.path.isdir(os.path.join(fullname, 'cur')) and
-                os.path.isdir(os.path.join(fullname, 'new')) and
-                os.path.isdir(os.path.join(fullname, 'tmp'))):
+            if (os.path.isdir(os.path.join(fullname.fs, 'cur')) and
+                os.path.isdir(os.path.join(fullname.fs, 'new')) and
+                os.path.isdir(os.path.join(fullname.fs, 'tmp'))):
                 # This directory has maildir stuff -- process
-                self.debug("  This is maildir folder '%s'."% foldername)
+                self.debug(u"  This is maildir folder '%s'."% foldername.uni)
                 if self.getconfboolean('restoreatime', False):
                     self._append_folder_atimes(foldername)
                 fd = self.getfoldertype()(self.root, foldername,
-                                          self.getsep(), self)
+                    self.getsep(), self)
                 retval.append(fd)
 
             if self.getsep() == '/' and dirname != '':
                 # Recursively check sub-directories for folders too.
                 retval.extend(self._getfolders_scandir(root, foldername))
-        self.debug("_GETFOLDERS_SCANDIR RETURNING %s"% \
-                   repr([x.getname() for x in retval]))
+        self.debug(u"_GETFOLDERS_SCANDIR RETURNING %s"%
+            repr([x.getname().uni for x in retval]))
         return retval
 
     def getfolders(self):
diff --git a/offlineimap/repository/__init__.py b/offlineimap/repository/__init__.py
index 0fbbc13..e726e2f 100644
--- a/offlineimap/repository/__init__.py
+++ b/offlineimap/repository/__init__.py
@@ -28,6 +28,8 @@ from offlineimap.repository.Maildir import MaildirRepository
 from offlineimap.repository.GmailMaildir import GmailMaildirRepository
 from offlineimap.repository.LocalStatus import LocalStatusRepository
 from offlineimap.error import OfflineImapError
+from offlineimap.utils.uni import uniString, isASCII
+from offlineimap.utils import hack
 
 
 class Repository(object):
@@ -41,43 +43,46 @@ class Repository(object):
         :param regtype: 'remote', 'local', or 'status'"""
 
         if reqtype == 'remote':
-            name = account.getconf('remoterepository')
+            name = uniString(account.getconf('remoterepository'))
             # We don't support Maildirs on the remote side.
             typemap = {'IMAP': IMAPRepository,
                 'Gmail': GmailRepository}
 
         elif reqtype == 'local':
-            name = account.getconf('localrepository')
+            name = uniString(account.getconf('localrepository'))
             typemap = {'IMAP': MappedIMAPRepository,
                 'Maildir': MaildirRepository,
                 'GmailMaildir': GmailMaildirRepository}
 
         elif reqtype == 'status':
             # create and return a LocalStatusRepository.
-            name = account.getconf('localrepository')
+            name = uniString(account.getconf('localrepository'))
             return LocalStatusRepository(name, account)
 
         else:
-            errstr = "Repository type %s not supported" % reqtype
+            errstr = "Repository type %s not supported"% reqtype
             raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO)
 
         # Get repository type.
         config = account.getconfig()
         try:
-            repostype = config.get('Repository ' + name, 'type').strip()
+            repostype = uniString(config.get(
+                'Repository ' + name.uni, 'type').strip())
+            isASCII(repostype.uni, exception_msg=
+                "repository type must be plain ASCII")
         except NoSectionError as e:
-            errstr = ("Could not find section '%s' in configuration. Required "
-                      "for account '%s'." % ('Repository %s' % name, account))
-            raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO), \
-                None, exc_info()[2]
+            errstr = ("Could not find section Repository '%s' in configuration."
+                " Required for account '%s'."% (name.fs, account.getname().fs))
+            raise OfflineImapError(
+                errstr, OfflineImapError.ERROR.REPO), None, exc_info()[2]
 
         try:
-            repo = typemap[repostype]
+            repo = typemap[repostype.dbytes]
         except KeyError:
-            errstr = "'%s' repository not supported for '%s' repositories."% \
-                (repostype, reqtype)
-            raise OfflineImapError(errstr, OfflineImapError.ERROR.REPO), \
-                None, exc_info()[2]
+            errstr = ("'%s' repository not supported for '%s' repositories."%
+                (repostype.fs, reqtype))
+            raise OfflineImapError(
+                errstr, OfflineImapError.ERROR.REPO), None, exc_info()[2]
 
         return repo(name, account)
 
@@ -87,6 +92,6 @@ class Repository(object):
         executed instead of this stub
 
         :param account: :class:`Account`
-        :param regtype: 'remote', 'local', or 'status'
+        :param regtype: uni object of 'remote', 'local', or 'status'
         """
         pass
diff --git a/offlineimap/syncmaster.py b/offlineimap/syncmaster.py
index 5fd0dee..e6a4648 100644
--- a/offlineimap/syncmaster.py
+++ b/offlineimap/syncmaster.py
@@ -16,24 +16,34 @@
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
+from threading import currentThread
+
 from offlineimap.threadutil import threadlist, InstanceLimitedThread
 from offlineimap.accounts import SyncableAccount
-from threading import currentThread
+from offlineimap.utils.uni import uniString
+
 
 def syncaccount(threads, config, accountname):
     account = SyncableAccount(config, accountname)
-    thread = InstanceLimitedThread(instancename = 'ACCOUNTLIMIT',
-                                   target = account.syncrunner,
-                                   name = "Account sync %s" % accountname)
+    thread = InstanceLimitedThread(
+        instancename=uniString(u'ACCOUNTLIMIT'), # Not redirected to Thread.
+        target=account.syncrunner,
+        name="Account sync %s"% accountname.std)
     thread.setDaemon(True)
+    # Actually start thread.
     thread.start()
+    # Add started thread to the list.
     threads.add(thread)
 
 def syncitall(accounts, config):
-    # Special exit message for SyncRunner thread, so main thread can exit
+    # Special exit message for SyncRunner thread, so main thread can exit.
     currentThread().exit_message = 'SYNCRUNNER_EXITED_NORMALLY'
+    # Retrieve all prepared threads.
     threads = threadlist()
-    for accountname in accounts:
-        syncaccount(threads, config, accountname)
+    # accounts is a list of (unicode) names.
+    for uni_accountname in accounts:
+        syncaccount(threads, config, uniString(uni_accountname))
     # Wait for the threads to finish.
+    # FIXME: should actually be called 'join' (much more undersantable), instead
+    # of 'reset'.
     threads.reset()
diff --git a/offlineimap/threadutil.py b/offlineimap/threadutil.py
index f69f8a6..294a45b 100644
--- a/offlineimap/threadutil.py
+++ b/offlineimap/threadutil.py
@@ -15,7 +15,7 @@
 #    along with this program; if not, write to the Free Software
 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
-from threading import Lock, Thread, BoundedSemaphore, currentThread
+from threading import Lock, Thread, BoundedSemaphore
 try:
     from Queue import Queue, Empty
 except ImportError: # python3
@@ -23,8 +23,14 @@ except ImportError: # python3
 import traceback
 import os.path
 import sys
+
 from offlineimap.ui import getglobalui
 
+#
+# UNICODE: Thread library has ASCII expectations.
+#
+
+
 ######################################################################
 # General utilities
 ######################################################################
@@ -70,6 +76,7 @@ class threadlist:
         finally:
             self.lock.release()
 
+    # Fix name.
     def reset(self):
         while 1:
             thread = self.pop()
@@ -109,11 +116,11 @@ def exitnotifymonitorloop(callback):
             # we need a timeout in the get() call, so that ctrl-c can throw
             # a SIGINT (http://bugs.python.org/issue1360). A timeout with empty
             # Queue will raise `Empty`.
-            thrd = exitthreads.get(True, 60)
+            thrd = exitthreads.get(True, 10)
             # request to abort when callback returns true
             do_loop = (callback(thrd) != True)
         except Empty:
-            pass
+            pass    # ???
 
 def threadexited(thread):
     """Called when a thread exits.
@@ -133,12 +140,12 @@ def threadexited(thread):
         return True
     else:
         ui.threadExited(thread)
-        return False
+    return False
 
 class ExitNotifyThread(Thread):
     """This class is designed to alert a "monitor" to the fact that a
     thread has exited and to provide for the ability for it to find out
-    why.  All instances are made daemon threads (setDaemon(True), so we
+    why. All instances are made daemon threads (setDaemon(True), so we
     bail out when the mainloop dies.
 
     The thread can set instance variables self.exit_message for a human
@@ -171,8 +178,8 @@ class ExitNotifyThread(Thread):
                     prof = prof.runctx("Thread.run(self)", globals(), locals())
                 except SystemExit:
                     pass
-                prof.dump_stats(os.path.join(ExitNotifyThread.profiledir,
-                                "%s_%s.prof"% (self.ident, self.getName())))
+                prof.dump_stats(os.path.join(ExitNotifyThread.profiledir.fs,
+                     "%s_%s.prof"% (self.ident.fs, self.getName().fs)))
         except Exception as e:
             # Thread exited with Exception, store it
             tb = traceback.format_exc()
@@ -218,7 +225,9 @@ instancelimitedlock = Lock()
 
 def initInstanceLimit(instancename, instancemax):
     """Initialize the instance-limited thread implementation to permit
-    up to intancemax threads with the given instancename."""
+    up to intancemax threads with the given instancename.
+
+    :param instancename: instance name (uni object)."""
 
     instancelimitedlock.acquire()
     if not instancename in instancelimitedsems:
diff --git a/offlineimap/ui/Curses.py b/offlineimap/ui/Curses.py
index ddc05ea..e4214ab 100644
--- a/offlineimap/ui/Curses.py
+++ b/offlineimap/ui/Curses.py
@@ -25,6 +25,8 @@ import logging
 
 from offlineimap.ui.UIBase import UIBase
 from offlineimap.threadutil import ExitNotifyThread
+from offlineimap.utils import uni
+from offlineimap.utils.uni import fsString
 import offlineimap
 
 
@@ -180,7 +182,8 @@ class CursesAccountFrame:
         # if this belongs to an Account (and not *Control), set the
         # skipsleep pref
         if isinstance(self.account, offlineimap.accounts.Account):
-            self.ui.info("Requested synchronization for acc: %s"% self.account)
+            self.ui.info(u"Requested synchronization for acc: %s"%
+                self.account)
             self.account.config.set('Account %s'% self.account.name,
                 'skipsleep', '1')
 
@@ -348,7 +351,12 @@ class Blinkenlights(UIBase, CursesUtil):
         ch = CursesLogHandler()
         #ch.setLevel(logging.DEBUG)
         # create formatter and add it to the handlers
-        self.formatter = logging.Formatter("%(message)s")
+        if uni.use_unicode():
+            encode_function = uni.uni2fs
+        else:
+            encode_function = None
+        self.formatter = uni.UnicodeFormatter("%(message)s",
+            encode_function=encode_function)
         ch.setFormatter(self.formatter)
         # add the handlers to the logger
         self.logger.addHandler(ch)
@@ -511,7 +519,7 @@ class Blinkenlights(UIBase, CursesUtil):
             return
         if chr(key) == 'q':
             # Request to quit completely.
-            self.warn("Requested shutdown via 'q'")
+            self.warn(u"Requested shutdown via 'q'")
             offlineimap.accounts.Account.set_abort_event(self.config, 3)
         try:
             index = int(chr(key))
@@ -525,7 +533,7 @@ class Blinkenlights(UIBase, CursesUtil):
 
     def sleep(self, sleepsecs, account):
         self.gettf().setcolor('red')
-        self.info("Next sync in %d:%02d"% (sleepsecs / 60, sleepsecs % 60))
+        self.info(u"Next sync in %d:%02d"% (sleepsecs / 60, sleepsecs % 60))
         return super(Blinkenlights, self).sleep(sleepsecs, account)
 
     def sleeping(self, sleepsecs, remainingsecs):
@@ -551,15 +559,15 @@ class Blinkenlights(UIBase, CursesUtil):
         self.lock()
         try:
             #s.gettf().setcolor('white')
-            self.warn(" *** Input Required")
-            self.warn(" *** Please enter password for account %s: " % \
-                          accountname)
+            self.warn(u" *** Input Required")
+            self.warn(u" *** Please enter password for account %s: "%
+                accountname)
             self.logwin.refresh()
             password = self.logwin.getstr()
         finally:
             self.unlock()
             self.inputhandler.input_release()
-        return password
+        return fsString(password)
 
     def setupwindows(self, resize=False):
         """Setup and draw bannerwin and logwin.
diff --git a/offlineimap/ui/Machine.py b/offlineimap/ui/Machine.py
index dc650c3..cda1f1b 100644
--- a/offlineimap/ui/Machine.py
+++ b/offlineimap/ui/Machine.py
@@ -20,14 +20,31 @@ except ImportError: # python3
 import sys
 import time
 import logging
+
 from threading import currentThread
 from offlineimap.ui.UIBase import UIBase
+from offlineimap.utils import uni
+from offlineimap.utils.uni import dbytesString
 import offlineimap
 
 protocol = '7.0.0'
 
-class MachineLogFormatter(logging.Formatter):
+#
+# UNICODE: This module is a low-level UI driver. Work with Unicode strings.
+#
+
+
+def getThreadname(thread):
+    return dbytesString(thread.getName()).uni
+
+
+class MachineLogFormatter(uni.UnicodeFormatter):
     """urlencodes any outputted line, to avoid multi-line output"""
+
+    def __init__(self, fmt, datefmt=None):
+        uni.UnicodeFormatter.__init__(self, fmt, datefmt,
+            encode_function=uni.uni2bytes)
+
     def format(s, record):
         # Mapping of log levels to historic tag names
         severity_map = {
@@ -43,10 +60,10 @@ class MachineLogFormatter(logging.Formatter):
             whoami = record.machineui["id"]
         else:
             command = ""
-            whoami = currentThread().getName()
+            whoami = getThreadname(currentThread())
 
-        prefix = "%s:%s"% (command, urlencode([('', whoami)])[1:])
-        return "%s:%s:%s"% (severity, prefix, urlencode([('', line)])[1:])
+        prefix = u"%s:%s"% (command, urlencode([(u'', whoami)])[1:])
+        return u"%s:%s:%s"% (severity, prefix, urlencode([(u'', line)])[1:])
 
 
 class MachineUI(UIBase):
@@ -67,7 +84,7 @@ class MachineUI(UIBase):
                 extra = {
                   'machineui': {
                    'command': command,
-                   'id': currentThread().getName(),
+                   'id': getThreadname(currentThread())
                   }
                 })
 
@@ -84,7 +101,7 @@ class MachineUI(UIBase):
 
     def unregisterthread(s, thread):
         UIBase.unregisterthread(s, thread)
-        s._printData(s.logger.info, 'unregisterthread', thread.getName())
+        s._printData(s.logger.info, 'unregisterthread', getThreadname(thread))
 
     def debugging(s, debugtype):
         s._printData(s.logger.debug, 'debugging', debugtype)
@@ -96,79 +113,80 @@ class MachineUI(UIBase):
         s._printData(s.logger.info, 'acctdone', accountname)
 
     def validityproblem(s, folder):
-        s._printData(s.logger.warning, 'validityproblem', "%s\n%s\n%s\n%s"%
-                (folder.getname(), folder.getrepository().getname(),
+        s._printData(s.logger.warning, 'validityproblem', u"%s\n%s\n%s\n%s"%
+                (folder.getname().uni, folder.getrepository().getname().uni,
                  folder.get_saveduidvalidity(), folder.get_uidvalidity()))
 
-    def connecting(s, hostname, port):
-        s._printData(s.logger.info, 'connecting', "%s\n%s"% (hostname, str(port)))
+    def connecting(s, uni_hostname, port):
+        s._printData(s.logger.info, 'connecting', u"%s\n%s"%
+            (uni_hostname, str(port)))
 
     def syncfolders(s, srcrepos, destrepos):
-        s._printData(s.logger.info, 'syncfolders', "%s\n%s"% (s.getnicename(srcrepos),
-                                                s.getnicename(destrepos)))
+        s._printData(s.logger.info, 'syncfolders', u"%s\n%s"%
+            (s.getnicename(srcrepos), s.getnicename(destrepos)))
 
     def syncingfolder(s, srcrepos, srcfolder, destrepos, destfolder):
-        s._printData(s.logger.info, 'syncingfolder', "%s\n%s\n%s\n%s\n"%
+        s._printData(s.logger.info, 'syncingfolder', u"%s\n%s\n%s\n%s\n"%
                 (s.getnicename(srcrepos), srcfolder.getname(),
                  s.getnicename(destrepos), destfolder.getname()))
 
     def loadmessagelist(s, repos, folder):
-        s._printData(s.logger.info, 'loadmessagelist', "%s\n%s"% (s.getnicename(repos),
-                                                    folder.getvisiblename()))
+        s._printData(s.logger.info, 'loadmessagelist', u"%s\n%s"%
+            (s.getnicename(repos), folder.getvisiblename().uni))
 
     def messagelistloaded(s, repos, folder, count):
-        s._printData(s.logger.info, 'messagelistloaded', "%s\n%s\n%d"%
-                (s.getnicename(repos), folder.getname(), count))
+        s._printData(s.logger.info, 'messagelistloaded', u"%s\n%s\n%d"%
+                (s.getnicename(repos), folder.getname().uni, count))
 
     def syncingmessages(s, sr, sf, dr, df):
-        s._printData(s.logger.info, 'syncingmessages', "%s\n%s\n%s\n%s\n"%
-                (s.getnicename(sr), sf.getname(), s.getnicename(dr),
-                 df.getname()))
+        s._printData(s.logger.info, 'syncingmessages', u"%s\n%s\n%s\n%s\n"%
+                (s.getnicename(sr), sf.getname().uni, s.getnicename(dr),
+                 df.getname().uni))
 
     def copyingmessage(s, uid, num, num_to_copy, srcfolder, destfolder):
-        s._printData(s.logger.info, 'copyingmessage', "%d\n%s\n%s\n%s[%s]"%
-                (uid, s.getnicename(srcfolder), srcfolder.getname(),
+        s._printData(s.logger.info, 'copyingmessage', u"%d\n%s\n%s\n%s[%s]"%
+                (uid, s.getnicename(srcfolder), srcfolder.getname().uni,
                  s.getnicename(destfolder), destfolder))
 
     def folderlist(s, list):
-        return ("\f".join(["%s\t%s"% (s.getnicename(x), x.getname()) for x in list]))
+        return (u"\f".join([u"%s\t%s"% (s.getnicename(x), x.getname().uni) for x in list]))
 
     def uidlist(s, list):
-        return ("\f".join([str(u) for u in list]))
+        return (u"\f".join([str(u) for u in list]))
 
     def deletingmessages(s, uidlist, destlist):
         ds = s.folderlist(destlist)
-        s._printData(s.logger.info, 'deletingmessages', "%s\n%s"% (s.uidlist(uidlist), ds))
+        s._printData(s.logger.info, 'deletingmessages', u"%s\n%s"%
+            (s.uidlist(uidlist), ds))
 
     def addingflags(s, uidlist, flags, dest):
-        s._printData(s.logger.info, "addingflags", "%s\n%s\n%s"% (s.uidlist(uidlist),
-                                                    "\f".join(flags),
-                                                    dest))
+        s._printData(s.logger.info, "addingflags", u"%s\n%s\n%s"%
+            (s.uidlist(uidlist), u"\f".join(flags), dest))
 
     def deletingflags(s, uidlist, flags, dest):
-        s._printData(s.logger.info, 'deletingflags', "%s\n%s\n%s"% (s.uidlist(uidlist),
-                                                      "\f".join(flags),
-                                                      dest))
+        s._printData(s.logger.info, 'deletingflags', u"%s\n%s\n%s"%
+            (s.uidlist(uidlist), u"\f".join(flags), dest))
 
     def threadException(s, thread):
-        s._printData(s.logger.warning, 'threadException', "%s\n%s"%
-                     (thread.getName(), s.getThreadExceptionString(thread)))
+        s._printData(s.logger.warning, 'threadException', u"%s\n%s"%
+             (getThreadname(thread), s.getThreadExceptionString(thread)))
         s.delThreadDebugLog(thread)
         s.terminate(100)
 
     def terminate(s, exitstatus=0, errortitle='', errormsg=''):
-        s._printData(s.logger.info, 'terminate', "%d\n%s\n%s"% (exitstatus, errortitle, errormsg))
+        s._printData(s.logger.info, 'terminate', u"%d\n%s\n%s"%
+            (exitstatus, errortitle, errormsg))
         sys.exit(exitstatus)
 
     def mainException(s):
         s._printData(s.logger.warning, 'mainException', s.getMainExceptionString())
 
     def threadExited(s, thread):
-        s._printData(s.logger.info, 'threadExited', thread.getName())
+        s._printData(s.logger.info, 'threadExited', getThreadname(thread))
         UIBase.threadExited(s, thread)
 
     def sleeping(s, sleepsecs, remainingsecs):
-        s._printData(s.logger.info, 'sleeping', "%d\n%d"% (sleepsecs, remainingsecs))
+        s._printData(s.logger.info, 'sleeping', u"%d\n%d"% (sleepsecs, remainingsecs))
         if sleepsecs > 0:
             time.sleep(sleepsecs)
         return 0
@@ -176,9 +194,8 @@ class MachineUI(UIBase):
 
     def getpass(s, accountname, config, errmsg=None):
         if errmsg:
-            s._printData(s.logger.warning,
-              'getpasserror', "%s\n%s"% (accountname, errmsg),
-              False)
+            s._printData(s.logger.warning, 'getpasserror', u"%s\n%s"%
+                (accountname, errmsg), False)
 
         s._log_con_handler.acquire() # lock the console output
         try:
diff --git a/offlineimap/ui/TTY.py b/offlineimap/ui/TTY.py
index 0b5aa6a..ae2459a 100644
--- a/offlineimap/ui/TTY.py
+++ b/offlineimap/ui/TTY.py
@@ -19,31 +19,45 @@ import logging
 import sys
 import time
 from getpass import getpass
+
 from offlineimap import banner
 from offlineimap.ui.UIBase import UIBase
+from offlineimap.utils import uni
+from offlineimap.utils.uni import fsString
+
 
-class TTYFormatter(logging.Formatter):
+class TTYFormatter(uni.UnicodeFormatter):
     """Specific Formatter that adds thread information to the log output."""
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, fmt, datefmt=None):
         #super() doesn't work in py2.6 as 'logging' uses old-style class
-        logging.Formatter.__init__(self, *args, **kwargs)
+        if uni.use_unicode():
+            encode_function = uni.uni2fs
+        else:
+            encode_function = None
+        uni.UnicodeFormatter.__init__(self, fmt, datefmt,
+            encode_function=encode_function)
         self._last_log_thread = None
 
     def format(self, record):
-        """Override format to add thread information."""
+        """Override format to add thread information.
 
-        #super() doesn't work in py2.6 as 'logging' uses old-style class
-        log_str = logging.Formatter.format(self, record)
+        We are near to output. It's convenient to encode everything here."""
+
+        # super() doesn't work in py2.6 as 'logging' uses old-style class.
+        log_str = uni.UnicodeFormatter.format(self, record)
         # If msg comes from a different thread than our last, prepend
         # thread info.  Most look like 'Account sync foo' or 'Folder
         # sync foo'.
-        t_name = record.threadName
+        #
+        # Because the Thread module does not support Unicode, threadName is
+        # encoded with uni.ENCODING. Re-encode to filesystem.
+        t_name = uni.uni2fs(uni.bytes2uni(record.threadName))
         if t_name == 'MainThread':
-            return log_str # main thread doesn't get things prepended
+            return log_str # Main thread doesn't get things prepended.
         if t_name != self._last_log_thread:
             self._last_log_thread = t_name
-            log_str = "%s:\n %s" % (t_name, log_str)
+            log_str = "%s:\n %s"% (t_name, log_str)
         else:
             log_str = " %s"% log_str
         return log_str
@@ -60,7 +74,7 @@ class TTYUI(UIBase):
         ch = logging.StreamHandler()
         #ch.setLevel(logging.DEBUG)
         # create formatter and add it to the handlers
-        self.formatter = TTYFormatter("%(message)s")
+        self.formatter = TTYFormatter(u"%(message)s")
         ch.setFormatter(self.formatter)
         # add the handlers to the logger
         self.logger.addHandler(ch)
@@ -78,17 +92,18 @@ class TTYUI(UIBase):
         """TTYUI backend is capable of querying the password."""
 
         if errmsg:
-            self.warn("%s: %s"% (accountname, errmsg))
+            self.warn(u"%s: %s"% (accountname, errmsg))
         self._log_con_handler.acquire() # lock the console output
         try:
-            return getpass("Enter password for account '%s': " % accountname)
+            return fsString(getpass(
+                u"Enter password for account '%s': "% accountname))
         finally:
             self._log_con_handler.release()
 
     def mainException(self):
         if isinstance(sys.exc_info()[1], KeyboardInterrupt):
-            self.logger.warn("Timer interrupted at user request; program "
-                "terminating.\n")
+            self.logger.warn(u"Timer interrupted at user request; program "
+                u"terminating.\n")
             self.terminate()
         else:
             UIBase.mainException(self)
@@ -105,7 +120,7 @@ class TTYUI(UIBase):
 
         if sleepsecs > 0:
             if remainingsecs//60 != (remainingsecs-sleepsecs)//60:
-                self.logger.info("Next refresh in %.1f minutes" % (
-                    remainingsecs/60.0))
+                self.logger.info(u"Next refresh in %.1f minutes"%
+                    (remainingsecs/60.0))
             time.sleep(sleepsecs)
         return 0
diff --git a/offlineimap/ui/UIBase.py b/offlineimap/ui/UIBase.py
index 9285b51..75ee3fd 100644
--- a/offlineimap/ui/UIBase.py
+++ b/offlineimap/ui/UIBase.py
@@ -26,8 +26,14 @@ try:
 except ImportError: #python3
     from queue import Queue
 from collections import deque
+
+from offlineimap import globals
 from offlineimap.error import OfflineImapError
 import offlineimap
+from offlineimap.utils import uni
+from offlineimap.utils.uni import fsString
+from offlineimap.utils import hack
+
 
 debugtypes = {'':'Other offlineimap related sync messages',
               'imap': 'IMAP protocol debugging',
@@ -82,7 +88,12 @@ class UIBase(object):
         ch = logging.StreamHandler(sys.stdout)
         #ch.setLevel(logging.DEBUG)
         # create formatter and add it to the handlers
-        self.formatter = logging.Formatter("%(message)s")
+        if globals.options.use_unicode:
+            encode_function = uni.uni2fs
+        else:
+            encode_function = None
+        self.formatter = uni.UnicodeFormatter("%(message)s",
+                encode_function=encode_function)
         ch.setFormatter(self.formatter)
         # add the handlers to the logger
         self.logger.addHandler(ch)
@@ -92,16 +103,22 @@ class UIBase(object):
     def setlogfile(self, logfile):
         """Create file handler which logs to file."""
 
-        fh = logging.FileHandler(logfile, 'at')
-        file_formatter = logging.Formatter("%(asctime)s %(levelname)s: "
-            "%(message)s", '%Y-%m-%d %H:%M:%S')
+        if globals.options.use_unicode:
+            encode_function = uni.uni2bytes
+        # logfile was kept encoded.
+        else:
+            encoding = uni.uni2std
+        fh = logging.FileHandler(logfile, 'at', encoding=encoding)
+        file_formatter = uni.UnicodeFormatter(u"%(asctime)s %(levelname)s: "
+            u"%(message)s", '%Y-%m-%d %H:%M:%S',
+            encode_function=None)
         fh.setFormatter(file_formatter)
         self.logger.addHandler(fh)
         # write out more verbose initial info blurb on the log file
         p_ver = ".".join([str(x) for x in sys.version_info[0:3]])
-        msg = "OfflineImap %s starting...\n  Python: %s Platform: %s\n  "\
-              "Args: %s"% (offlineimap.__bigversion__, p_ver, sys.platform,
-                            " ".join(sys.argv))
+        msg = u"OfflineImap %s starting...\n  Python: %s Platform: %s\n  "\
+            u"Args: %s"% (offlineimap.__bigversion__, p_ver, sys.platform,
+            u" ".join(sys.argv))
         self.logger.info(msg)
 
     def _msg(self, msg):
@@ -142,10 +159,12 @@ class UIBase(object):
            ui.error(exc, sys.exc_info()[2], msg="While syncing Folder %s in "
                "repo %s")
         """
+
         if msg:
-            self.logger.error("ERROR: %s\n  %s" % (msg, exc))
+            self.logger.error(u"ERROR: %s\n  %s"%
+                (msg, fsString(str(exc)).uni))
         else:
-            self.logger.error("ERROR: %s" % (exc))
+            self.logger.error(u"ERROR: %s"% (fsString(str(exc)).uni))
 
         instant_traceback = exc_traceback
         if not self.debuglist:
@@ -160,14 +179,15 @@ class UIBase(object):
         """Register current thread as being associated with an account name."""
 
         cur_thread = threading.currentThread()
+        thr_name = cur_thread.getName()
         if cur_thread in self.threadaccounts:
             # was already associated with an old account, update info
-            self.debug('thread', "Register thread '%s' (previously '%s', now "
-                    "'%s')" % (cur_thread.getName(),
-                               self.getthreadaccount(cur_thread), account))
+            self.debug('thread', u"Register thread '%s' "
+                u"(previously '%s', now '%s')"% (thr_name,
+                self.getthreadaccount(cur_thread), account))
         else:
-            self.debug('thread', "Register new thread '%s' (account '%s')"%
-                (cur_thread.getName(), account))
+            self.debug('thread', u"Register new thread '%s' (account '%s')"%
+                (thr_name, account))
         self.threadaccounts[cur_thread] = account
 
     def unregisterthread(self, thr):
@@ -175,7 +195,8 @@ class UIBase(object):
 
         if thr in self.threadaccounts:
             del self.threadaccounts[thr]
-        self.debug('thread', "Unregister thread '%s'" % thr.getName())
+        thr_name = thr.getName()
+        self.debug('thread', u"Unregister thread '%s'"% thr_name)
 
     def getthreadaccount(self, thr=None):
         """Get Account() for a thread (current if None)
@@ -195,14 +216,14 @@ class UIBase(object):
             # introduced in p2.6 only, so we'll need to work around and
             # shorten our debugmsg list manually :-(
             self.debugmessages[cur_thread] = deque()
-        self.debugmessages[cur_thread].append("%s: %s" % (debugtype, msg))
+        self.debugmessages[cur_thread].append(u"%s: %s"% (debugtype, msg))
 
         # Shorten queue if needed
         if len(self.debugmessages[cur_thread]) > self.debugmsglen:
             self.debugmessages[cur_thread].popleft()
 
         if debugtype in self.debuglist: # log if we are supposed to do so
-            self.logger.debug("[%s]: %s" % (debugtype, msg))
+            self.logger.debug("u[%s]: %s"% (debugtype, msg))
 
     def add_debug(self, debugtype):
         global debugtypes
@@ -215,18 +236,18 @@ class UIBase(object):
 
     def debugging(self, debugtype):
         global debugtypes
-        self.logger.debug("Now debugging for %s: %s" % (debugtype,
-                                                        debugtypes[debugtype]))
+        msg = u"Now debugging for %s: %s"% (debugtype, debugtypes[debugtype])
+        self.logger.debug(msg)
 
     def invaliddebug(self, debugtype):
-        self.warn("Invalid debug type: %s" % debugtype)
+        self.warn(u"Invalid debug type: %s"% debugtype)
 
-    def getnicename(self, object):
+    def getnicename(self, obj):
         """Return the type of a repository or Folder as string.
 
         (IMAP, Gmail, Maildir, etc...)"""
 
-        prelimname = object.__class__.__name__.split('.')[-1]
+        prelimname = obj.__class__.__name__.split('.')[-1]
         # Strip off extra stuff.
         return re.sub('(Folder|Repository)', '', prelimname)
 
@@ -239,20 +260,20 @@ class UIBase(object):
 
     ################################################## INPUT
 
-    def getpass(self, accountname, config, errmsg = None):
+    def getpass(self, accountname, config, errmsg=None):
         raise NotImplementedError("Prompting for a password is not supported"
             " in this UI backend.")
 
     def folderlist(self, folder_list):
-        return ', '.join(["%s[%s]"% \
-            (self.getnicename(x), x.getname()) for x in folder_list])
+        return u', '.join([u"%s[%s]"% \
+            (self.getnicename(x), x.getname().uni) for x in folder_list])
 
     ################################################## WARNINGS
     def msgtoreadonly(self, destfolder, uid, content, flags):
         if self.config.has_option('general', 'ignore-readonly') and \
             self.config.getboolean('general', 'ignore-readonly'):
             return
-        self.warn("Attempted to synchronize message %d to folder %s[%s], "
+        self.warn(u"Attempted to synchronize message %d to folder %s[%s], "
             "but that folder is read-only.  The message will not be "
             "copied to that folder."% (
             uid, self.getnicename(destfolder), destfolder))
@@ -261,7 +282,7 @@ class UIBase(object):
         if self.config.has_option('general', 'ignore-readonly') and \
                 self.config.getboolean('general', 'ignore-readonly'):
             return
-        self.warn("Attempted to modify flags for messages %s in folder %s[%s], "
+        self.warn(u"Attempted to modify flags for messages %s in folder %s[%s], "
             "but that folder is read-only.  No flags have been modified "
             "for that message."% (
             str(uidlist), self.getnicename(destfolder), destfolder))
@@ -270,7 +291,7 @@ class UIBase(object):
         if self.config.has_option('general', 'ignore-readonly') and \
                 self.config.getboolean('general', 'ignore-readonly'):
             return
-        self.warn("Attempted to modify labels for messages %s in folder %s[%s], "
+        self.warn(u"Attempted to modify labels for messages %s in folder %s[%s], "
             "but that folder is read-only.  No labels have been modified "
             "for that message."% (
             str(uidlist), self.getnicename(destfolder), destfolder))
@@ -279,7 +300,7 @@ class UIBase(object):
         if self.config.has_option('general', 'ignore-readonly') and \
                 self.config.getboolean('general', 'ignore-readonly'):
             return
-        self.warn("Attempted to delete messages %s in folder %s[%s], but that "
+        self.warn(u"Attempted to delete messages %s in folder %s[%s], but that "
             "folder is read-only.  No messages have been deleted in that "
             "folder."% (str(uidlist), self.getnicename(destfolder),
             destfolder))
@@ -298,162 +319,173 @@ class UIBase(object):
 
         if not self.logger.isEnabledFor(logging.INFO): return
         displaystr = ''
-        hostname = hostname if hostname else ''
-        port = "%s"% port if port else ''
+        hostname = hostname if hostname else u''
+        port = u"%s"% port if port else u''
         if hostname:
-            displaystr = ' to %s:%s' % (hostname, port)
-        self.logger.info("Establishing connection%s" % displaystr)
+            displaystr = u' to %s:%s'% (hostname, port)
+        self.logger.info(u"Establishing connection%s"% displaystr)
 
     def acct(self, account):
         """Output that we start syncing an account (and start counting)."""
 
         self.acct_startimes[account] = time.time()
-        self.logger.info("*** Processing account %s" % account)
+        self.logger.info(u"*** Processing account %s"% account)
 
     def acctdone(self, account):
         """Output that we finished syncing an account (in which time)."""
 
         sec = time.time() - self.acct_startimes[account]
         del self.acct_startimes[account]
-        self.logger.info("*** Finished account '%s' in %d:%02d"%
+        self.logger.info(u"*** Finished account '%s' in %d:%02d"%
             (account, sec // 60, sec % 60))
 
     def syncfolders(self, src_repo, dst_repo):
         """Log 'Copying folder structure...'."""
 
         if self.logger.isEnabledFor(logging.DEBUG):
-            self.debug('', "Copying folder structure from %s to %s" %\
-                           (src_repo, dst_repo))
+            self.debug('', u"Copying folder structure from %s to %s"%
+                (src_repo, dst_repo))
 
     ############################## Folder syncing
     def makefolder(self, repo, foldername):
         """Called when a folder is created."""
 
-        prefix = "[DRYRUN] " if self.dryrun else ""
-        self.info(("{0}Creating folder {1}[{2}]".format(
+        prefix = u"[DRYRUN] " if self.dryrun else u""
+        self.info((u"{0}Creating folder {1}[{2}]".format(
             prefix, foldername, repo)))
 
     def syncingfolder(self, srcrepos, srcfolder, destrepos, destfolder):
         """Called when a folder sync operation is started."""
 
-        self.logger.info("Syncing %s: %s -> %s"% (srcfolder,
+        self.logger.info(u"Syncing %s: %s -> %s"% (srcfolder,
             self.getnicename(srcrepos),
             self.getnicename(destrepos)))
 
     def skippingfolder(self, folder):
         """Called when a folder sync operation is started."""
-        self.logger.info("Skipping %s (not changed)" % folder)
+
+        self.logger.info(u"Skipping %s (not changed)"% folder)
 
     def validityproblem(self, folder):
-        self.logger.warning("UID validity problem for folder %s (repo %s) "
-                            "(saved %d; got %d); skipping it. Please see FAQ "
-                            "and manual on how to handle this."% \
-               (folder, folder.getrepository(),
-                folder.get_saveduidvalidity(), folder.get_uidvalidity()))
+        msg = u"UID validity problem for folder %s (repo %s) " \
+            "(saved %d; got %d); skipping it. Please see FAQ " \
+            "and manual on how to handle this."% \
+            (folder, folder.getrepository(),
+            folder.get_saveduidvalidity(),
+            folder.get_uidvalidity())
+        self.logger.warning(msg)
 
     def loadmessagelist(self, repos, folder):
-        self.logger.debug("Loading message list for %s[%s]"% (
-                self.getnicename(repos),
-                folder))
+        msg = u"Loading message list for %s[%s]"% (
+            self.getnicename(repos), folder)
+        self.logger.debug(msg)
 
     def messagelistloaded(self, repos, folder, count):
-        self.logger.debug("Message list for %s[%s] loaded: %d messages" % (
-                self.getnicename(repos), folder, count))
+        self.logger.debug(u"Message list for %s[%s] loaded: %d messages"%
+            (self.getnicename(repos), folder, count))
 
     ############################## Message syncing
 
     def syncingmessages(self, sr, srcfolder, dr, dstfolder):
-        self.logger.debug("Syncing messages %s[%s] -> %s[%s]" % (
-                self.getnicename(sr), srcfolder,
-                self.getnicename(dr), dstfolder))
+        msg = u"Syncing messages %s[%s] -> %s[%s]"% (
+            self.getnicename(sr), srcfolder,
+            self.getnicename(dr), dstfolder)
+        self.logger.debug(msg)
 
     def copyingmessage(self, uid, num, num_to_copy, src, destfolder):
-        """Output a log line stating which message we copy"""
-        self.logger.info("Copy message %s (%d of %d) %s:%s -> %s" % (
+        """Output a log line stating which message we copy. """
+
+        self.logger.info(u"Copy message %s (%d of %d) %s:%s -> %s"% (
                 uid, num, num_to_copy, src.repository, src,
                 destfolder.repository))
 
     def deletingmessages(self, uidlist, destlist):
         ds = self.folderlist(destlist)
-        prefix = "[DRYRUN] " if self.dryrun else ""
-        self.info("{0}Deleting {1} messages ({2}) in {3}".format(
+        prefix = u"[DRYRUN] " if self.dryrun else u""
+        self.info(u"{0}Deleting {1} messages ({2}) in {3}".format(
             prefix, len(uidlist),
             offlineimap.imaputil.uid_sequence(uidlist), ds))
 
     def addingflags(self, uidlist, flags, dest):
-        self.logger.info("Adding flag %s to %d messages on %s" % (
-                ", ".join(flags), len(uidlist), dest))
+        msg = u"Adding flag %s to %d messages on %s"% (
+            u", ".join(flags), len(uidlist), dest)
+        self.logger.info(msg)
 
     def deletingflags(self, uidlist, flags, dest):
-        self.logger.info("Deleting flag %s from %d messages on %s" % (
-                ", ".join(flags), len(uidlist), dest))
+        msg = u"Deleting flag %s from %d messages on %s"% (
+            u", ".join(flags), len(uidlist), dest)
+        self.logger.info(msg)
 
     def addinglabels(self, uidlist, label, dest):
-        self.logger.info("Adding label %s to %d messages on %s" % (
-                label, len(uidlist), dest))
+        msg = u"Adding label %s to %d messages on %s"% (
+            label, len(uidlist), dest)
+        self.logger.info(msg)
 
     def deletinglabels(self, uidlist, label, dest):
-        self.logger.info("Deleting label %s from %d messages on %s" % (
-                label, len(uidlist), dest))
+        msg = u"Deleting label %s from %d messages on %s"% (
+            label, len(uidlist), dest)
+        self.logger.info(msg)
 
     def settinglabels(self, uid, num, num_to_set, labels, dest):
-        self.logger.info("Setting labels to message %d on %s (%d of %d): %s" % (
-                uid, dest, num, num_to_set, ", ".join(labels)))
+        msg = u"Setting labels to message %d on %s (%d of %d): %s"% (
+            uid, dest, num, num_to_set, ", ".join(labels))
+        self.logger.info()
 
     def collectingdata(self, uidlist, source):
         if uidlist:
-            self.logger.info("Collecting data from %d messages on %s"% (
-                len(uidlist), source))
+            msg = u"Collecting data from %d messages on %s"% (
+                len(uidlist), source)
         else:
-            self.logger.info("Collecting data from messages on %s"% source)
+            msg = u"Collecting data from messages on %s"% source
+        self.logger.info(msg)
 
     def serverdiagnostics(self, repository, type):
         """Connect to repository and output useful information for debugging."""
 
         conn = None
-        self._msg("%s repository '%s': type '%s'" % (type, repository.name,
-                  self.getnicename(repository)))
+        self._msg(u"%s repository '%s': type '%s'"% (
+            type, repository.getname().uni, self.getnicename(repository)))
         try:
             if hasattr(repository, 'gethost'): # IMAP
-                self._msg("Host: %s Port: %s SSL: %s"% (repository.gethost(),
+                self._msg(u"Host: %s Port: %s SSL: %s"% (repository.gethost().uni,
                     repository.getport(), repository.getssl()))
                 try:
                     conn = repository.imapserver.acquireconnection()
                 except OfflineImapError as e:
-                    self._msg("Failed to connect. Reason %s" % e)
+                    self._msg(u"Failed to connect. Reason %s"% e)
                 else:
                     if 'ID' in conn.capabilities:
-                        self._msg("Server supports ID extension.")
+                        self._msg(u"Server supports ID extension.")
                         #TODO: Debug and make below working, it hangs Gmail
                         #res_type, response = conn.id((
                         #    'name', offlineimap.__productname__,
                         #    'version', offlineimap.__bigversion__))
-                        #self._msg("Server ID: %s %s" % (res_type, response[0]))
-                    self._msg("Server welcome string: %s" % str(conn.welcome))
-                    self._msg("Server capabilities: %s\n" % str(conn.capabilities))
+                        #self._msg(u"Server ID: %s %s" % (res_type, response[0]))
+                    self._msg(u"Server welcome string: %s"% str(conn.welcome))
+                    self._msg(u"Server capabilities: %s\n"% str(conn.capabilities))
                     repository.imapserver.releaseconnection(conn)
             if type != 'Status':
                 folderfilter = repository.getconf('folderfilter', None)
                 if folderfilter:
-                    self._msg("folderfilter= %s\n" % folderfilter)
+                    self._msg(u"folderfilter= %s\n"% folderfilter)
                 folderincludes = repository.getconf('folderincludes', None)
                 if folderincludes:
-                    self._msg("folderincludes= %s\n" % folderincludes)
+                    self._msg(u"folderincludes= %s\n"% folderincludes)
                 nametrans = repository.getconf('nametrans', None)
                 if nametrans:
-                    self._msg("nametrans= %s\n" % nametrans)
+                    self._msg(u"nametrans= %s\n"% nametrans)
 
                 folders = repository.getfolders()
-                foldernames = [(f.name, f.getvisiblename(), f.sync_this)
+                foldernames = [(f.getname().uni, f.getvisiblename().uni, f.sync_this)
                     for f in folders]
                 folders = []
                 for name, visiblename, sync_this in foldernames:
-                    syncstr = "" if sync_this else " (disabled)"
-                    if name == visiblename: folders.append("%s%s" % (name,
-                                                                     syncstr))
-                    else: folders.append("%s -> %s%s" % (name,
-                                                       visiblename, syncstr))
-                self._msg("Folderlist:\n %s\n" % "\n ".join(folders))
+                    syncstr = u"" if sync_this else u" (disabled)"
+                    if name == visiblename: folders.append(u"%s%s"%
+                        (name, syncstr))
+                    else: folders.append(u"%s -> %s%s"%
+                        (name, visiblename, syncstr))
+                self._msg(u"Folderlist:\n %s\n"% u"\n ".join(folders))
         finally:
             if conn: #release any existing IMAP connection
                 repository.imapserver.close()
@@ -462,18 +494,18 @@ class UIBase(object):
         """Output a log line stating that we save a msg."""
 
         self.debug(debugtype, "Write mail '%s:%d' with flags %s"%
-            (folder, uid, repr(flags)))
+            (folder.getname().uni, uid, repr(flags)))
 
     ################################################## Threads
 
     def getThreadDebugLog(self, thread):
+        thr_name = thread.getName()
         if thread in self.debugmessages:
-            message = "\nLast %d debug messages logged for %s prior to exception:\n"\
-                       % (len(self.debugmessages[thread]), thread.getName())
-            message += "\n".join(self.debugmessages[thread])
+            message = u"\nLast %d debug messages logged for %s prior to" \
+                " exception:\n"% (len(self.debugmessages[thread]), thr_name)
+            message += u"\n".join(self.debugmessages[thread])
         else:
-            message = "\nNo debug messages were logged for %s."% \
-                thread.getName()
+            message = u"\nNo debug messages were logged for %s."% thr_name
         return message
 
     def delThreadDebugLog(self, thread):
@@ -481,9 +513,10 @@ class UIBase(object):
             del self.debugmessages[thread]
 
     def getThreadExceptionString(self, thread):
-        message = "Thread '%s' terminated with exception:\n%s"% \
-            (thread.getName(), thread.exit_stacktrace)
-        message += "\n" + self.getThreadDebugLog(thread)
+        thr_name = thread.getName()
+        message = u"Thread '%s' terminated with exception:\n%s"% (
+            thr_name, thread.exit_stacktrace)
+        message += u"\n" + self.getThreadDebugLog(thread)
         return message
 
     def threadException(self, thread):
@@ -497,23 +530,23 @@ class UIBase(object):
     def terminate(self, exitstatus = 0, errortitle = None, errormsg = None):
         """Called to terminate the application."""
 
-        #print any exceptions that have occurred over the run
+        # print any exceptions that have occurred over the run
         if not self.exc_queue.empty():
-           self.warn("ERROR: Exceptions occurred during the run!")
+           self.warn(u"ERROR: Exceptions occurred during the run!")
         while not self.exc_queue.empty():
             msg, exc, exc_traceback = self.exc_queue.get()
             if msg:
-                self.warn("ERROR: %s\n  %s"% (msg, exc))
+                self.warn(u"ERROR: %s\n  %s"% (msg, fsString(str(exc)).uni))
             else:
-                self.warn("ERROR: %s"% (exc))
+                self.warn(u"ERROR: %s"% (fsString(str(exc)).uni))
             if exc_traceback:
-                self.warn("\nTraceback:\n%s"% "".join(
-                        traceback.format_tb(exc_traceback)))
+                self.warn(u"\nTraceback:\n%s"% u"".join(
+                    traceback.format_tb(exc_traceback)))
 
         if errormsg and errortitle:
-            self.warn('ERROR: %s\n\n%s\n'% (errortitle, errormsg))
+            self.warn(u'ERROR: %s\n\n%s\n'% (errortitle, errormsg))
         elif errormsg:
-                self.warn('%s\n'% errormsg)
+            self.warn(u'%s\n'% errormsg)
         sys.exit(exitstatus)
 
     def threadExited(self, thread):
@@ -528,7 +561,7 @@ class UIBase(object):
 
     def callhook(self, msg):
         if self.dryrun:
-            self.info("[DRYRUN] {0}".format(msg))
+            self.info(u"[DRYRUN] {0}".format(msg))
         else:
             self.info(msg)
 
@@ -566,7 +599,7 @@ class UIBase(object):
 
         if sleepsecs > 0:
             if remainingsecs//60 != (remainingsecs-sleepsecs)//60:
-                self.logger.debug("Next refresh in %.1f minutes" % (
-                        remainingsecs/60.0))
+                self.logger.debug(u"Next refresh in %.1f minutes"% (
+                    remainingsecs/60.0))
             time.sleep(sleepsecs)
         return 0
diff --git a/offlineimap/ui/debuglock.py b/offlineimap/ui/debuglock.py
index 673efb0..96609af 100644
--- a/offlineimap/ui/debuglock.py
+++ b/offlineimap/ui/debuglock.py
@@ -17,7 +17,14 @@
 
 from threading import Lock, currentThread
 import traceback
-logfile = open("/tmp/logfile", "wt")
+import codecs
+
+from offlineimap.utils import uni
+
+if uni.use_unicode():
+    logfile = codecs.open("/tmp/logfile", "wt", uni.ENCODING)
+else:
+    logfile = open("/tmp/logfile", "wt")
 loglock = Lock()
 
 class DebuggingLock:
@@ -26,24 +33,27 @@ class DebuggingLock:
         self.name = name
 
     def acquire(self, blocking = 1):
-        self.print_tb("Acquire lock")
+        self.print_tb(u"Acquire lock")
         self.lock.acquire(blocking)
-        self.logmsg("===== %s: Thread %s acquired lock\n"%
+        self.logmsg(u"===== %s: Thread %s acquired lock\n"%
             (self.name, currentThread().getName()))
 
     def release(self):
-        self.print_tb("Release lock")
+        self.print_tb(u"Release lock")
         self.lock.release()
 
     def logmsg(self, msg):
         loglock.acquire()
-        logfile.write(msg + "\n")
+        msg = msg + u"\n"
+        if uni.use_unicode():
+            msg = uni.uni2bytes(msg)
+        logfile.write(msg)
         logfile.flush()
         loglock.release()
 
     def print_tb(self, msg):
-        self.logmsg(".... %s: Thread %s attempting to %s\n"% \
-                    (self.name, currentThread().getName(), msg) + \
-                    "\n".join(traceback.format_list(traceback.extract_stack())))
+        self.logmsg(u".... %s: Thread %s attempting to %s\n"% \
+            (self.name, currentThread().getName(), msg) + \
+            u"\n".join(traceback.format_list(traceback.extract_stack())))
 
 
diff --git a/offlineimap/utils/const.py b/offlineimap/utils/const.py
index f4584bc..80fdd47 100644
--- a/offlineimap/utils/const.py
+++ b/offlineimap/utils/const.py
@@ -5,6 +5,11 @@
 
 import copy
 
+#
+# UNICODE: assume variables to be full ASCII. Should be fine while
+# metaprogramming.
+#
+
 class ConstProxy(object):
     """Implements read-only access to a given object
     that can be attached to each instance only once."""
@@ -21,12 +26,12 @@ class ConstProxy(object):
 
 
     def __setattr__(self, name, value):
-        raise AttributeError("tried to set '%s' to '%s' for constant object"% \
+        raise AttributeError("tried to set '%s' to '%s' for constant object"%
             (name, value))
 
 
     def __delattr__(self, name):
-        raise RuntimeError("tried to delete field '%s' from constant object"% \
+        raise RuntimeError("tried to delete field '%s' from constant object"%
             (name))
 
 
diff --git a/offlineimap/utils/distro.py b/offlineimap/utils/distro.py
index 8cd2b79..3d35223 100644
--- a/offlineimap/utils/distro.py
+++ b/offlineimap/utils/distro.py
@@ -11,6 +11,8 @@ import os
 # For the former we will just return the value, for an iterable
 # we will walk through the values and will return the first
 # one that corresponds to the existing file.
+#
+# UNICODE: all paths must be full ASCII.
 __DEF_OS_LOCATIONS = {
     'freebsd': '/usr/local/share/certs/ca-root-nss.crt',
     'openbsd': '/etc/ssl/cert.pem',
@@ -35,9 +37,8 @@ def get_os_name():
     the first component of platform.linux_distribution.
 
     Return value will be all-lowercase to avoid confusion about
-    proper name capitalisation.
+    proper name capitalisation."""
 
-    """
     OS = platform.system().lower()
 
     if OS.startswith('linux'):
@@ -55,8 +56,7 @@ def get_os_sslcertfile_searchpath():
     that they can iterate over result.
 
     Returned value of None means that there is no search path
-    at all.
-    """
+    at all."""
 
     OS = get_os_name()
 
@@ -83,9 +83,9 @@ def get_os_sslcertfile():
         return None
 
     for f in l:
-      assert (type(f) == type(""))
-      if os.path.exists(f) and \
-        (os.path.isfile(f) or os.path.islink(f)):
-          return f
+        assert (type(f) == type(""))
+        if os.path.exists(f) and (
+            os.path.isfile(f) or os.path.islink(f)):
+            return f
 
     return None
-- 
2.2.2




More information about the OfflineIMAP-project mailing list