diff --git a/offlineimap/folder/Base.py b/offlineimap/folder/Base.py index 5f07960..f757e75 100644 --- a/offlineimap/folder/Base.py +++ b/offlineimap/folder/Base.py @@ -26,7 +26,6 @@ from email import policy from email.parser import BytesParser from email.generator import BytesGenerator from email.utils import parsedate_tz, mktime_tz -from email.errors import NoBoundaryInMultipartDefect from offlineimap import threadutil from offlineimap.ui import getglobalui diff --git a/offlineimap/folder/Maildir.py b/offlineimap/folder/Maildir.py index 53525de..baabc28 100644 --- a/offlineimap/folder/Maildir.py +++ b/offlineimap/folder/Maildir.py @@ -25,6 +25,7 @@ from threading import Lock from hashlib import md5 from offlineimap import OfflineImapError from .Base import BaseFolder +from email.errors import NoBoundaryInMultipartDefect # Find the UID in a message filename re_uidmatch = re.compile(',U=(\d+)') @@ -297,24 +298,29 @@ class MaildirFolder(BaseFolder): filename = self.messagelist[uid]['filename'] filepath = os.path.join(self.getfullname(), filename) fd = open(filepath, 'rb') - retval = self.parser['8bit'].parse(fd) - try: - if len(retval.defects) > 0: - ui.warn("Message has defects: {}".format(retval.defects)) - # See if the defects are preventing us from obtaining bytes and - # handle known issues - _ = retval.as_bytes(policy=self.policy['8bit']) - except UnicodeEncodeError as err: - if any(isinstance(defect, NoBoundaryInMultipartDefect) for defect in retval.defects): - # (Hopefully) Rare instance where multipart boundary is not - # properly quoted. Solve by fixing the boundary and parsing - fd.seek(0) - _buffer = fd.read() - retval = self.parser['8bit'].parsebytes(_quote_boundary_fix(_buffer)) - else: - # Unknown issue which is causing failure of as_bytes() - ui.warn("Message has defects preventing it from being processed!") + _fd_bytes = fd.read() fd.close() + retval = self.parser['8bit'].parsebytes(_fd_bytes) + if len(retval.defects) > 0: + # We don't automatically apply fixes as to attempt to preserve the original message + self.ui.warn("UID {} has defects: {}".format(uid, retval.defects)) + if any(isinstance(defect, NoBoundaryInMultipartDefect) for defect in retval.defects): + # (Hopefully) Rare defect from a broken client where multipart boundary is + # not properly quoted. Attempt to solve by fixing the boundary and parsing + self.ui.warn(" ... applying multipart boundary fix.") + retval = self.parser['8bit'].parsebytes(self._quote_boundary_fix(_fd_bytes)) + try: + # See if the defects after fixes are preventing us from obtaining bytes + _ = retval.as_bytes(policy=self.policy['8bit']) + except UnicodeEncodeError as err: + # Unknown issue which is causing failure of as_bytes() + msg_id = self.getmessageheader(retval, "message-id") + if msg_id is None: + msg_id = '' + raise OfflineImapError( + "UID {} ({}) has defects preventing it from being processed!\n {}: {}".format( + uid, msg_id, type(err).__name__, err), + OfflineImapError.ERROR.MESSAGE) return retval # Interface from BaseFolder