diff --git a/offlineimap.conf b/offlineimap.conf index 69ede92..b0ef554 100644 --- a/offlineimap.conf +++ b/offlineimap.conf @@ -762,6 +762,20 @@ remoteuser = username #reference = Mail +# 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. +# +# Note that the IMAP 4rev1 specification (RFC 3501) allows both UTF-8 and +# modified UTF-7 folder names. +# +# This option is disabled by default to retain compatibility with older versions +# of offlineimap. +# +#decodefoldernames = no + + # This option stands in the [Repository RemoteExample] section. # # In between synchronisations, OfflineIMAP can monitor mailboxes for new diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py index 9ed7fec..659ed5f 100644 --- a/offlineimap/folder/IMAP.py +++ b/offlineimap/folder/IMAP.py @@ -258,6 +258,13 @@ class IMAPFolder(BaseFolder): def dropmessagelistcache(self): self.messagelist = {} + # 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 getmessagelist(self): return self.messagelist diff --git a/offlineimap/imaputil.py b/offlineimap/imaputil.py index f1f287b..6d8b444 100644 --- a/offlineimap/imaputil.py +++ b/offlineimap/imaputil.py @@ -25,6 +25,9 @@ from offlineimap.ui import getglobalui # Message headers that use space as the separator (for label storage) SPACE_SEPARATED_LABEL_HEADERS = ('X-Label', 'Keywords') +# Find the modified UTF-7 shifts of an international mailbox name. +MUTF7_SHIFT_RE = re.compile(r'&[^-]*-|\+') + def __debug(*args): msg = [] @@ -328,3 +331,28 @@ def labels_from_header(header_name, header_value): return labels + +def decode_mailbox_name(name): + """Decodes a modified UTF-7 mailbox name. + + If the string cannot be decoded, it is returned unmodified. + + See RFC 3501, sec. 5.1.3. + + Arguments: + - name: string, possibly encoded with modified UTF-7 + + Returns: decoded UTF-8 string. + """ + def demodify(m): + s = m.group() + if s == '+': + return '+-' + return '+' + s[1:-1].replace(',', '/') + '-' + + ret = MUTF7_SHIFT_RE.sub(demodify, name) + + try: + return ret.decode('utf-7').encode('utf-8') + except UnicodeEncodeError: + return name diff --git a/offlineimap/repository/IMAP.py b/offlineimap/repository/IMAP.py index 68c8e33..b1ee473 100644 --- a/offlineimap/repository/IMAP.py +++ b/offlineimap/repository/IMAP.py @@ -261,6 +261,9 @@ class IMAPRepository(BaseRepository): def getreference(self): return self.getconf('reference', '') + def getdecodefoldernames(self): + return self.getconfboolean('decodefoldernames', 0) + def getidlefolders(self): localeval = self.localeval return localeval.eval(self.getconf('idlefolders', '[]'))