From 032376efadad56a52f4cd6ee55c216d80b464cca Mon Sep 17 00:00:00 2001 From: Urs Liska Date: Mon, 2 Oct 2017 01:06:18 +0200 Subject: [PATCH 1/8] utf8: Add imap4_utf_7 codec Add code to reencode IMAP folder names to regular utf-8. This starts an implementation that will add a new config option `utf8foldernames` on account level which will fix #299 and on the long run replace the current `decodefoldernames` option. This commit introduces code to register an `imap4_utf_7` codec on which two-way conversion methods will later be built. Original code by (https://www.blogger.com/profile/16648963337079496096), taken from http://piao-tech.blogspot.no/2010/03/get-offlineimap-working-with-non-ascii.html In the comment http://piao-tech.blogspot.com/2010/03/get-offlineimap-working-with-non-ascii.html?showComment=1316041409339#c669880170006851138 indicates that this code is expected to be incorporated into offlineIMAP and therefore the author implicitly agrees to put it under this license. Signed-off-by: Urs Liska Signed-off-by: Nicolas Sebrecht --- offlineimap/imaputil.py | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/offlineimap/imaputil.py b/offlineimap/imaputil.py index cc51531..1299cdd 100644 --- a/offlineimap/imaputil.py +++ b/offlineimap/imaputil.py @@ -17,6 +17,8 @@ import re import string +import binascii +import codecs from offlineimap.ui import getglobalui @@ -370,3 +372,73 @@ def decode_mailbox_name(name): return ret.decode('utf-7').encode('utf-8') except (UnicodeDecodeError, UnicodeEncodeError): return name + +# Functionality to convert folder names encoded in IMAP_utf_7 to utf_8. +# This is achieved by defining 'imap4_utf_7' as a proper encoding scheme. + +def modified_base64(s): + s = s.encode('utf-16be') + return binascii.b2a_base64(s).rstrip('\n=').replace('/', ',') + +def doB64(_in, r): + if _in: + r.append('&%s-' % modified_base64(''.join(_in))) + del _in[:] + +def encoder(s): + r = [] + _in = [] + for c in s: + ordC = ord(c) + if 0x20 <= ordC <= 0x25 or 0x27 <= ordC <= 0x7e: + doB64(_in, r) + r.append(c) + elif c == '&': + doB64(_in, r) + r.append('&-') + else: + _in.append(c) + doB64(_in, r) + return (str(''.join(r)), len(s)) + +# decoding +def modified_unbase64(s): + b = binascii.a2b_base64(s.replace(',', '/') + '===') + return unicode(b, 'utf-16be') + +def decoder(s): + r = [] + decode = [] + for c in s: + if c == '&' and not decode: + decode.append('&') + elif c == '-' and decode: + if len(decode) == 1: + r.append('&') + else: + r.append(modified_unbase64(''.join(decode[1:]))) + decode = [] + elif decode: + decode.append(c) + else: + r.append(c) + + if decode: + r.append(modified_unbase64(''.join(decode[1:]))) + bin_str = ''.join(r) + return (bin_str, len(s)) + +class StreamReader(codecs.StreamReader): + def decode(self, s, errors='strict'): + return decoder(s) + +class StreamWriter(codecs.StreamWriter): + def decode(self, s, errors='strict'): + return encoder(s) + +def imap4_utf_7(name): + if name == 'imap4-utf-7': + return (encoder, decoder, StreamReader, StreamWriter) + + +codecs.register(imap4_utf_7) From 24b3f27e5f5dd7f643c3dc7694009cf125b32ace Mon Sep 17 00:00:00 2001 From: Urs Liska Date: Tue, 26 Sep 2017 11:26:33 +0200 Subject: [PATCH 2/8] utf8: Add utf-7/8 conversion functions This commit adds two functions - imaputil.IMAP_utf8() - imaputil.utf8_IMAP() as an interface to the new imap4_utf_7 codec. Signed-off-by: Urs Liska Signed-off-by: Nicolas Sebrecht --- offlineimap/imaputil.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/offlineimap/imaputil.py b/offlineimap/imaputil.py index 1299cdd..39bd8d4 100644 --- a/offlineimap/imaputil.py +++ b/offlineimap/imaputil.py @@ -376,6 +376,18 @@ def decode_mailbox_name(name): # Functionality to convert folder names encoded in IMAP_utf_7 to utf_8. # This is achieved by defining 'imap4_utf_7' as a proper encoding scheme. +# Public API, to be used in repository definitions + +def IMAP_utf8(foldername): + """Convert IMAP4_utf_7 encoded string to utf-8""" + return foldername.decode('imap4-utf-7').encode('utf-8') + +def utf8_IMAP(foldername): + """Convert utf-8 encoded string to IMAP4_utf_7""" + return foldername.decode('utf-8').encode('imap4-utf-7') + +# Codec definition + def modified_base64(s): s = s.encode('utf-16be') return binascii.b2a_base64(s).rstrip('\n=').replace('/', ',') From dca5f1846df678baf6d71fb565080c77fc43d057 Mon Sep 17 00:00:00 2001 From: Urs Liska Date: Tue, 26 Sep 2017 15:16:15 +0200 Subject: [PATCH 3/8] utf8 (aside): Move code for decodefoldernames While intending *not* to change the behaviour of the existing decodefoldernames option this commit transparently improves the coding. So far this worked by overriding the folder's getvisiblename() method which reads self.visiblename from and applies the conversion on *every* invocation of getvisiblename(). This commit does the calculation once in the IMAPFolder's __init__. Signed-off-by: Urs Liska Signed-off-by: Nicolas Sebrecht --- offlineimap/folder/IMAP.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py index a46ef31..a23708c 100644 --- a/offlineimap/folder/IMAP.py +++ b/offlineimap/folder/IMAP.py @@ -47,6 +47,8 @@ class IMAPFolder(BaseFolder): name = imaputil.dequote(name) self.sep = imapserver.delim super(IMAPFolder, self).__init__(name, repository) + if repository.getdecodefoldernames(): + self.visiblename = imaputil.decode_mailbox_name(self.visiblename) self.idle_mode = False self.expunge = repository.getexpunge() self.root = None # imapserver.root @@ -290,13 +292,6 @@ class IMAPFolder(BaseFolder): 'keywords': keywords} self.ui.messagelistloaded(self.repository, self, self.getmessagecount()) - # Interface from BaseFolder - def getvisiblename(self): - vname = super(IMAPFolder, self).getvisiblename() - if self.repository.getdecodefoldernames(): - return imaputil.decode_mailbox_name(vname) - return vname - # Interface from BaseFolder def getmessage(self, uid): """Retrieve message with UID from the IMAP server (incl body). From 14d83dbf48b9596022f6e40168bad22728b810b2 Mon Sep 17 00:00:00 2001 From: Urs Liska Date: Tue, 26 Sep 2017 15:31:52 +0200 Subject: [PATCH 4/8] utf8: avoid dequoting IMAP folder names twice Signed-off-by: Urs Liska Signed-off-by: Nicolas Sebrecht --- offlineimap/repository/IMAP.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/offlineimap/repository/IMAP.py b/offlineimap/repository/IMAP.py index 7581858..010d6e8 100644 --- a/offlineimap/repository/IMAP.py +++ b/offlineimap/repository/IMAP.py @@ -480,8 +480,7 @@ 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, + retval.append(self.getfoldertype()(self.imapserver, name, self)) # Add all folderincludes if len(self.folderincludes): From 36d726763d0097a53cedfff35b01424c0dcef33b Mon Sep 17 00:00:00 2001 From: Urs Liska Date: Mon, 2 Oct 2017 01:26:29 +0200 Subject: [PATCH 5/8] utf8: implement utf8foldernames option If utf8foldernames is enabled on account level all folder names read from the IMAP server will immediately be reencoded to UTF-8. Names will be treated as UTF-8 as long as the IMAP server isn't contacted again, for which they are reencoded to IMAP4-UTF-7. This means that any further processing such as nametrans, folderfilter etc. will act upon the UTF-8 names, which will have to be documented carefully. NOTE 1: GMail repositories and folders inherit from the IMAP... classes, so I don't know yet if these changes have ugly side-effects. But web research suggests that GMail IMAP folders are equally encoded in UTF-7 so that should work identically here and incorporate the same improvements. NOTE 2: I could not test the behaviour with idlefolders as I didn't get this option to work at all, not even with the latest stable version. NOTE 3: I *did* test to sync an IMAP repository against another IMAP repository. Signed-off-by: Urs Liska Signed-off-by: Nicolas Sebrecht --- offlineimap/accounts.py | 6 +++-- offlineimap/folder/IMAP.py | 40 ++++++++++++++++++++++------------ offlineimap/folder/UIDMaps.py | 6 ++--- offlineimap/imapserver.py | 2 +- offlineimap/repository/Base.py | 2 +- offlineimap/repository/IMAP.py | 11 ++++++---- 6 files changed, 42 insertions(+), 25 deletions(-) diff --git a/offlineimap/accounts.py b/offlineimap/accounts.py index f50aa78..7f10aed 100644 --- a/offlineimap/accounts.py +++ b/offlineimap/accounts.py @@ -69,6 +69,8 @@ class Account(CustomConfig.ConfigHelperMixin): self.name = name self.metadatadir = config.getmetadatadir() self.localeval = config.getlocaleval() + # Store utf-8 support as a property of Account object + self.utf_8_support = self.getconfboolean('utf8foldernames', False) # Current :mod:`offlineimap.ui`, can be used for logging: self.ui = getglobalui() self.refreshperiod = self.getconffloat('autorefresh', 0.0) @@ -360,7 +362,7 @@ class SyncableAccount(Account): if not remotefolder.sync_this: self.ui.debug('', "Not syncing filtered folder '%s'" - "[%s]"% (remotefolder, remoterepos)) + "[%s]"% (remotefolder.getname(), remoterepos)) continue # Ignore filtered folder. # The remote folder names must not have the local sep char in @@ -378,7 +380,7 @@ class SyncableAccount(Account): localfolder = self.get_local_folder(remotefolder) if not localfolder.sync_this: self.ui.debug('', "Not syncing filtered folder '%s'" - "[%s]"% (localfolder, localfolder.repository)) + "[%s]"% (localfolder.getname(), localfolder.repository)) continue # Ignore filtered folder. if not globals.options.singlethreading: diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py index a23708c..b9d4be8 100644 --- a/offlineimap/folder/IMAP.py +++ b/offlineimap/folder/IMAP.py @@ -41,10 +41,17 @@ MSGCOPY_NAMESPACE = 'MSGCOPY_' 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. + def __init__(self, imapserver, name, repository, decode=True): + # decode the folder name from IMAP4_utf_7 to utf_8 if + # - utf8foldernames is enabled for the *account* + # - the decode argument is given + # (default True is used when the folder name is the result of + # querying the IMAP server, while False is used when creating + # a folder object from a locally available utf_8 name) + # In any case the given name is first dequoted. name = imaputil.dequote(name) + if decode and repository.account.utf_8_support: + name = imaputil.IMAP_utf8(name) self.sep = imapserver.delim super(IMAPFolder, self).__init__(name, repository) if repository.getdecodefoldernames(): @@ -69,7 +76,6 @@ class IMAPFolder(BaseFolder): if self.repository.getidlefolders(): self.idle_mode = True - def __selectro(self, imapobj, force=False): """Select this folder when we do not need write access. @@ -80,9 +86,15 @@ class IMAPFolder(BaseFolder): :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) + imapobj.select(self.getfullIMAPname(), force = force) except imapobj.readonly: - imapobj.select(self.getfullname(), readonly = True, force = force) + imapobj.select(self.getfullIMAPname(), readonly = True, force = force) + + def getfullIMAPname(self): + name = self.getfullname() + if self.repository.account.utf_8_support: + name = imaputil.utf8_IMAP(name) + return name # Interface from BaseFolder def suggeststhreads(self): @@ -147,7 +159,7 @@ class IMAPFolder(BaseFolder): imapobj = self.imapserver.acquireconnection() try: # Select folder and get number of messages. - restype, imapdata = imapobj.select(self.getfullname(), True, + restype, imapdata = imapobj.select(self.getfullIMAPname(), True, True) self.imapserver.releaseconnection(imapobj) except OfflineImapError as e: @@ -213,7 +225,7 @@ class IMAPFolder(BaseFolder): res_data.remove(0) return res_data - res_type, imapdata = imapobj.select(self.getfullname(), True, True) + res_type, imapdata = imapobj.select(self.getfullIMAPname(), True, True) if imapdata == [None] or imapdata[0] == '0': # Empty folder, no need to populate message list. return None @@ -630,7 +642,7 @@ class IMAPFolder(BaseFolder): try: # Select folder for append and make the box READ-WRITE. - imapobj.select(self.getfullname()) + imapobj.select(self.getfullIMAPname()) except imapobj.readonly: # readonly exception. Return original uid to notify that # we did not save the message. (see savemessage in Base.py) @@ -639,7 +651,7 @@ class IMAPFolder(BaseFolder): # Do the APPEND. try: - (typ, dat) = imapobj.append(self.getfullname(), + (typ, dat) = imapobj.append(self.getfullIMAPname(), imaputil.flagsmaildir2imap(flags), date, content) # This should only catch 'NO' responses since append() # will raise an exception for 'BAD' responses: @@ -753,7 +765,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.getfullIMAPname(), readonly=True) res_type, data = imapobj.uid('fetch', uids, query) break except imapobj.abort as e: @@ -813,7 +825,7 @@ class IMAPFolder(BaseFolder): - field: field name to be stored/updated - data: field contents """ - imapobj.select(self.getfullname()) + imapobj.select(self.getfullIMAPname()) res_type, retdata = imapobj.uid('store', uid, field, data) if res_type != 'OK': severity = OfflineImapError.ERROR.MESSAGE @@ -874,7 +886,7 @@ class IMAPFolder(BaseFolder): imapobj = self.imapserver.acquireconnection() try: try: - imapobj.select(self.getfullname()) + imapobj.select(self.getfullIMAPname()) except imapobj.readonly: self.ui.flagstoreadonly(self, uidlist, flags) return @@ -949,7 +961,7 @@ class IMAPFolder(BaseFolder): imapobj = self.imapserver.acquireconnection() try: try: - imapobj.select(self.getfullname()) + imapobj.select(self.getfullIMAPname()) except imapobj.readonly: self.ui.deletereadonly(self, uidlist) return diff --git a/offlineimap/folder/UIDMaps.py b/offlineimap/folder/UIDMaps.py index 48d1e96..e89a121 100644 --- a/offlineimap/folder/UIDMaps.py +++ b/offlineimap/folder/UIDMaps.py @@ -40,8 +40,8 @@ class MappedIMAPFolder(IMAPFolder): diskr2l: dict mapping message uids: self.r2l[remoteuid]=localuid diskl2r: dict mapping message uids: self.r2l[localuid]=remoteuid""" - def __init__(self, *args, **kwargs): - IMAPFolder.__init__(self, *args, **kwargs) + def __init__(self, imapserver, name, repository, decode=True): + IMAPFolder.__init__(self, imapserver, name, repository, decode=False) self.dryrun = self.config.getdefaultboolean("general", "dry-run", True) self.maplock = Lock() self.diskr2l, self.diskl2r = self._loadmaps() @@ -49,7 +49,7 @@ class MappedIMAPFolder(IMAPFolder): # Representing the local IMAP Folder using local UIDs. # XXX: This should be removed since we inherit from IMAPFolder. # See commit 3ce514e92ba7 to know more. - self._mb = IMAPFolder(*args, **kwargs) + self._mb = IMAPFolder(imapserver, name, repository, decode=False) def _getmapfilename(self): return os.path.join(self.repository.getmapdir(), diff --git a/offlineimap/imapserver.py b/offlineimap/imapserver.py index 71c325f..8971298 100644 --- a/offlineimap/imapserver.py +++ b/offlineimap/imapserver.py @@ -797,7 +797,7 @@ class IdleThread(object): localrepos = account.localrepos remoterepos = account.remoterepos statusrepos = account.statusrepos - remotefolder = remoterepos.getfolder(self.folder) + remotefolder = remoterepos.getfolder(self.folder, decode=False) hook = account.getconf('presynchook', '') account.callhook(hook) diff --git a/offlineimap/repository/Base.py b/offlineimap/repository/Base.py index 0c3d16f..127ea79 100644 --- a/offlineimap/repository/Base.py +++ b/offlineimap/repository/Base.py @@ -242,7 +242,7 @@ class BaseRepository(CustomConfig.ConfigHelperMixin): # Get IMAPFolder and see if the reverse nametrans works fine. # TODO: getfolder() works only because we succeed in getting # inexisting folders which I would like to change. Take care! - tmp_remotefolder = remote_repo.getfolder(remote_name) + tmp_remotefolder = remote_repo.getfolder(remote_name, decode=False) loop_name = tmp_remotefolder.getvisiblename().replace( remote_repo.getsep(), local_repo.getsep()) if local_name != loop_name: diff --git a/offlineimap/repository/IMAP.py b/offlineimap/repository/IMAP.py index 010d6e8..02c8368 100644 --- a/offlineimap/repository/IMAP.py +++ b/offlineimap/repository/IMAP.py @@ -428,10 +428,10 @@ class IMAPRepository(BaseRepository): # No strategy yielded a password! return None - def getfolder(self, foldername): + def getfolder(self, foldername, decode=True): """Return instance of OfflineIMAP representative folder.""" - return self.getfoldertype()(self.imapserver, foldername, self) + return self.getfoldertype()(self.imapserver, foldername, self, decode) def getfoldertype(self): return folder.IMAP.IMAPFolder @@ -488,7 +488,7 @@ class IMAPRepository(BaseRepository): try: for foldername in self.folderincludes: try: - imapobj.select(foldername, readonly=True) + imapobj.select(imaputil.utf8_IMAP(foldername), readonly=True) except OfflineImapError as e: # couldn't select this folderinclude, so ignore folder. if e.severity > OfflineImapError.ERROR.FOLDER: @@ -497,7 +497,7 @@ class IMAPRepository(BaseRepository): 'Invalid folderinclude:') continue retval.append(self.getfoldertype()( - self.imapserver, foldername, self)) + self.imapserver, foldername, self, decode=False)) finally: self.imapserver.releaseconnection(imapobj) @@ -555,6 +555,9 @@ class IMAPRepository(BaseRepository): return imapobj = self.imapserver.acquireconnection() try: + if self.account.utf_8_support: + foldername = imaputil.utf8_IMAP(foldername) + result = imapobj.create(foldername) if result[0] != 'OK': raise OfflineImapError("Folder '%s'[%s] could not be created. " From ef3299b7ce949a273882f08b836d17eb9caad254 Mon Sep 17 00:00:00 2001 From: Urs Liska Date: Mon, 2 Oct 2017 01:27:51 +0200 Subject: [PATCH 6/8] Remove some unnecessary whitespace (in existing code) Addresses https://github.com/OfflineIMAP/offlineimap/pull/498#discussion_r141672756 Signed-off-by: Urs Liska Signed-off-by: Nicolas Sebrecht --- offlineimap/folder/IMAP.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py index b9d4be8..49104c8 100644 --- a/offlineimap/folder/IMAP.py +++ b/offlineimap/folder/IMAP.py @@ -86,9 +86,9 @@ class IMAPFolder(BaseFolder): :param: Enforce new SELECT even if we are on that folder already. :returns: raises :exc:`OfflineImapError` severity FOLDER on error""" try: - imapobj.select(self.getfullIMAPname(), force = force) + imapobj.select(self.getfullIMAPname(), force=force) except imapobj.readonly: - imapobj.select(self.getfullIMAPname(), readonly = True, force = force) + imapobj.select(self.getfullIMAPname(), readonly=True, force=force) def getfullIMAPname(self): name = self.getfullname() From 8b398f3aa89c3d06edf87fc4c4cb464c7ac1b8f5 Mon Sep 17 00:00:00 2001 From: Urs Liska Date: Thu, 28 Sep 2017 23:48:54 +0200 Subject: [PATCH 7/8] utf8: Safeguard against use of old *and* new option The new 'utf8foldernames' will not work together with the existing 'decodefoldernames' option (which will be documented in the next commit). Therefore this commit will check for this condition and abort the synchronization of a misconfigured account before doing any changes. Other accounts are not affected. Signed-off-by: Urs Liska Signed-off-by: Nicolas Sebrecht --- offlineimap/accounts.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/offlineimap/accounts.py b/offlineimap/accounts.py index 7f10aed..3aa13e2 100644 --- a/offlineimap/accounts.py +++ b/offlineimap/accounts.py @@ -274,6 +274,21 @@ class SyncableAccount(Account): raise return + if self.utf_8_support and self.remoterepos.getdecodefoldernames(): + e = OfflineImapError("Configuration mismatch in account " + + "'%s'. "% self.getname() + + "\nAccount setting 'utf8foldernames' and repository " + + "setting 'decodefoldernames'\nmay not be used at the " + + "same time. This account has not been synchronized.\n" + + "Please check the configuration and documentation.", + OfflineImapError.ERROR.REPO) + self.ui.error(e, exc_info()[2], + msg="Configuration mismatch in account " + + "'%s'. "% self.getname()) + # Abort *this* account without doing any changes. + # Other accounts are not affected. + return + # Loop account sync if needed (bail out after 3 failures). looping = 3 while looping: From f5198794e5e2c110156f6731353a06ae2ee15e42 Mon Sep 17 00:00:00 2001 From: Urs Liska Date: Mon, 2 Oct 2017 01:18:41 +0200 Subject: [PATCH 8/8] utf8: document new feature, deprecate old one - Document the new utf8foldernames config option - Deprecate the old decodefoldernames option Update its documentation, discussing the limitations. Signed-off-by: Urs Liska Signed-off-by: Nicolas Sebrecht --- offlineimap.conf | 67 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/offlineimap.conf b/offlineimap.conf index b7c9fbd..f6b5438 100644 --- a/offlineimap.conf +++ b/offlineimap.conf @@ -432,6 +432,55 @@ remoterepository = RemoteExample #proxy = SOCKS5:IP:9999 +# EXPERIMENTAL: This option stands in the [Account Test] section. +# +# IMAP defines an encoding for non-ASCII ("international") characters, and most +# IMAP servers store folder names in this encoding. Note that the IMAP 4rev1 +# specification (RFC 3501) allows both UTF-8 and modified UTF-7 folder names +# so it is *possible* that an IMAP server already uses UTF-8 encoded folder +# names. But usually Folders that are shown as, say, "Gäste" will be represented +# as "G&AOQ-ste", and by default will be synchronized like this by offlineIMAP. +# +# This option converts IMAP folder names from IMAP4-UTF-7 to UTF-8 and back +# in order to have nicely readable UTF-8 folder names in the local copy. +# +# WARNING: with this option enabled: +# - compatibility with any other version is NOT GUARANTED (including newer); +# - existing set-ups will probably break. +# - no support is provided. +# +# IMPORTANT: READ THIS SECTION if you intend to enable this feature for an +# EXISTING ACCOUNT that has already been synchronized! +# Enabling UTF-8 encoded folder names will change many things on the local +# repository of an account, so you really have to create a new local repository +# and review the configuration. The least that would happen otherwise is a +# duplication of all folders containing non-ASCII characters. +# But also the following functionality may change, so the configuration in the +# remote repository configuration has to be reviewed/updated: +# - decodefoldernames +# This option is replaced by utf8foldernames and must be removed +# If both utf8foldernames and decodefoldernames are enabled the synchronization +# for the given account is aborted before doing any changes. +# - nametrans +# With utf8foldernames enabled any nametrans function will operate on the +# UTF-8 encoded folder names, while even with decodefoldernames enabled they +# operate on the original IMAP4-UTF-7 encoded names. +# - folderfilter +# Folder filters still work on the untranslated names before applying a +# nametrans function, but still this operates on the UTF-8 encoded names. +# - folderincludes +# With utf8foldernames enabled this function expects UTF-8 encoded folder +# names. +# - foldersort +# With utf8foldernames enabled the folder names passed to the sorting routine +# will be the UTF encoded names. +# - idlefolders +# With utf8foldernames enabled folders passed to this function are expected to +# be UTF-8 encoded. +# +#utf8foldernames = no + + # TESTING: This option stands in the [Account Test] section. # # Use authproxy connection for this account. Useful to bypass the GFW in China. @@ -1005,7 +1054,7 @@ remoteuser = username #reference = Mail -# This option stands in the [Repository RemoteExample] section. +# DEPRECATED: This option stands in the [Repository RemoteExample] section. # # IMAP defines an encoding for non-ASCII ("international") characters. Enable # this option if you want to decode them to the nowadays ubiquitous UTF-8. @@ -1013,10 +1062,26 @@ remoteuser = username # Note that the IMAP 4rev1 specification (RFC 3501) allows both UTF-8 and # modified UTF-7 folder names. # +# This option converts IMAP folder names from IMAP4-UTF-7 to UTF-8. +# +# NOTE/LIMITATION: +# - The reencoding is applied *after* a nametrans function that may be given, +# so it is important to note that nametrans will work on the undecoded +# UTF-7 names. +# - This option only works from a remote IMAP to a local Maildir repository +# - It only works *once*, so it can only be used for one-off backups +# (see https://github.com/OfflineIMAP/offlineimap/issues/299 and especially +# https://github.com/OfflineIMAP/offlineimap/issues/299#issuecomment-331243827) +# # WARNING: with this option enabled: # - compatibility with any other version is NOT GUARANTED (including newer); # - no support is provided. # +# DEPRECATION: +# This option is only there for backward compatibility with existing set-ups. +# For newly created accounts please use the utf8foldernames option on account +# level. +# # This feature was merged because it's small changes in the code. However, this # might seriously decrease the stability of the program. That's why it will # likely never be marked stable. The approach is: if it works for you, you're