diff --git a/offlineimap/imapserver.py b/offlineimap/imapserver.py index 3970541..bd377f7 100644 --- a/offlineimap/imapserver.py +++ b/offlineimap/imapserver.py @@ -310,12 +310,8 @@ class IMAPServer: # This is a behavior we got from pykerberos. First byte is one, # first four bytes are preserved (pykerberos calls this a length). # Any additional bytes are username. - reply = [] - reply[0:4] = response.message[0:4] - reply[0] = '\x01' - if self.username: - reply[5:] = self.username - reply = ''.join(reply) + reply = b'\x01' + response.message[1:4] + reply += bytes(self.username, 'utf-8') response = self.gss_vc.wrap(reply, response.encrypted) return response.message if response.message else "" diff --git a/offlineimap/imaputil.py b/offlineimap/imaputil.py index 4083d34..758e78c 100644 --- a/offlineimap/imaputil.py +++ b/offlineimap/imaputil.py @@ -18,6 +18,7 @@ import re import binascii import codecs +from typing import Tuple from offlineimap.ui import getglobalui # Globals @@ -352,42 +353,45 @@ def decode_mailbox_name(name): def IMAP_utf8(foldername): """Convert IMAP4_utf_7 encoded string to utf-8""" - return foldername.decode('imap4-utf-7').encode('utf-8') + return codecs.decode( + foldername.encode(), + 'imap4-utf-7' + ).encode('utf-8').decode() def utf8_IMAP(foldername): """Convert utf-8 encoded string to IMAP4_utf_7""" - return foldername.decode('utf-8').encode('imap4-utf-7') + return codecs.decode( + foldername.encode(), + 'utf-8' + ).encode('imap4-utf-7').decode() # Codec definition - def modified_base64(s): s = s.encode('utf-16be') - return binascii.b2a_base64(s).rstrip('\n=').replace('/', ',') + return binascii.b2a_base64(s).rstrip(b'\n=').replace(b'/', b',') def doB64(_in, r): if _in: - r.append('&%s-' % modified_base64(''.join(_in))) + r.append(b'&%s-' % modified_base64(''.join(_in))) del _in[:] -def encoder(s): +def utf7m_encode(text: str) -> Tuple[bytes, int]: r = [] _in = [] - for c in s: - ordC = ord(c) - if 0x20 <= ordC <= 0x25 or 0x27 <= ordC <= 0x7e: + + for c in text: + if 0x20 <= ord(c) <= 0x7e: doB64(_in, r) - r.append(c) - elif c == '&': - doB64(_in, r) - r.append('&-') + r.append(b'&-' if c == '&' else c.encode()) else: _in.append(c) + doB64(_in, r) - return str(''.join(r)), len(s) + return b''.join(r), len(text) # decoding @@ -396,10 +400,10 @@ def modified_unbase64(s): return str(b, 'utf-16be') -def decoder(s): +def utf7m_decode(binary: bytes) -> Tuple[str, int]: r = [] decode = [] - for c in s: + for c in binary.decode(): if c == '&' and not decode: decode.append('&') elif c == '-' and decode: @@ -415,26 +419,31 @@ def decoder(s): if decode: r.append(modified_unbase64(''.join(decode[1:]))) - bin_str = ''.join(r) - return bin_str, len(s) + + return ''.join(r), len(binary) class StreamReader(codecs.StreamReader): def decode(self, s, errors='strict'): - return decoder(s) + return utf7m_decode(s) class StreamWriter(codecs.StreamWriter): def decode(self, s, errors='strict'): - return encoder(s) + return utf7m_encode(s) -def imap4_utf_7(name): - if name == 'imap4-utf-7': - return encoder, decoder, StreamReader, StreamWriter +def utf7m_search_function(name): + return codecs.CodecInfo( + utf7m_encode, + utf7m_decode, + StreamReader, + StreamWriter, + name='imap4-utf-7' + ) -codecs.register(imap4_utf_7) +codecs.register(utf7m_search_function) def foldername_to_imapname(folder_name): @@ -454,4 +463,4 @@ def foldername_to_imapname(folder_name): if any((c in atom_specials) for c in folder_name): folder_name = '"' + folder_name + '"' - return folder_name \ No newline at end of file + return folder_name diff --git a/offlineimap/repository/IMAP.py b/offlineimap/repository/IMAP.py index b732243..c86f129 100644 --- a/offlineimap/repository/IMAP.py +++ b/offlineimap/repository/IMAP.py @@ -206,12 +206,23 @@ class IMAPRepository(BaseRepository): Returns: Returns the remoteusereval or remoteuser or netrc user value. """ - localeval = self.localeval - if self.config.has_option(self.getsection(), 'remoteusereval'): user = self.getconf('remoteusereval') if user is not None: - return localeval.eval(user).encode('UTF-8') + l_user = self.localeval.eval(user) + + # We need a str username + if isinstance(l_user, bytes): + return l_user.decode(encoding='utf-8') + elif isinstance(l_user, str): + return l_user + + # If is not bytes or str, we have a problem + raise OfflineImapError("Could not get a right username format for" + " repository %s. Type found: %s. " + "Please, open a bug." % + (self.name, type(l_user)), + OfflineImapError.ERROR.FOLDER) if self.config.has_option(self.getsection(), 'remoteuser'): # Assume the configuration file to be UTF-8 encoded so we must not @@ -590,7 +601,20 @@ class IMAPRepository(BaseRepository): encoding='utf-8') password = file_desc.readline().strip() file_desc.close() - return password.encode('UTF-8') + + # We need a str password + if isinstance(password, bytes): + return password.decode(encoding='utf-8') + elif isinstance(password, str): + return password + + # If is not bytes or str, we have a problem + raise OfflineImapError("Could not get a right password format for" + " repository %s. Type found: %s. " + "Please, open a bug." % + (self.name, type(password)), + OfflineImapError.ERROR.FOLDER) + # 4. Read password from ~/.netrc. try: netrcentry = netrc.netrc().authenticators(self.gethost())