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)