Correcting an issue where dbg_output was not defined when the server was

unreachable due to an optimization in PR#56.  Since message-id is more
useful to better pin point the correct message, removing dbg_output.

Also fixing https://github.com/OfflineIMAP/offlineimap3/issues/62 by
correcting broken multipart boundaries or raising an error if as_bytes()
fails.  Related python bug submitted: https://bugs.python.org/issue43818
although this workaround should be sufficent in the interim.

Signed-off-by: Joseph Ishac <jishac@nasa.gov>
This commit is contained in:
Joseph Ishac 2021-04-14 14:54:25 -04:00
parent f024bb9e4c
commit a4532294ae
2 changed files with 23 additions and 18 deletions

View File

@ -26,7 +26,6 @@ from email import policy
from email.parser import BytesParser from email.parser import BytesParser
from email.generator import BytesGenerator from email.generator import BytesGenerator
from email.utils import parsedate_tz, mktime_tz from email.utils import parsedate_tz, mktime_tz
from email.errors import NoBoundaryInMultipartDefect
from offlineimap import threadutil from offlineimap import threadutil
from offlineimap.ui import getglobalui from offlineimap.ui import getglobalui

View File

@ -25,6 +25,7 @@ from threading import Lock
from hashlib import md5 from hashlib import md5
from offlineimap import OfflineImapError from offlineimap import OfflineImapError
from .Base import BaseFolder from .Base import BaseFolder
from email.errors import NoBoundaryInMultipartDefect
# Find the UID in a message filename # Find the UID in a message filename
re_uidmatch = re.compile(',U=(\d+)') re_uidmatch = re.compile(',U=(\d+)')
@ -297,24 +298,29 @@ class MaildirFolder(BaseFolder):
filename = self.messagelist[uid]['filename'] filename = self.messagelist[uid]['filename']
filepath = os.path.join(self.getfullname(), filename) filepath = os.path.join(self.getfullname(), filename)
fd = open(filepath, 'rb') fd = open(filepath, 'rb')
retval = self.parser['8bit'].parse(fd) _fd_bytes = fd.read()
try: fd.close()
retval = self.parser['8bit'].parsebytes(_fd_bytes)
if len(retval.defects) > 0: if len(retval.defects) > 0:
ui.warn("Message has defects: {}".format(retval.defects)) # We don't automatically apply fixes as to attempt to preserve the original message
# See if the defects are preventing us from obtaining bytes and self.ui.warn("UID {} has defects: {}".format(uid, retval.defects))
# handle known issues 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']) _ = retval.as_bytes(policy=self.policy['8bit'])
except UnicodeEncodeError as err: 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() # Unknown issue which is causing failure of as_bytes()
ui.warn("Message has defects preventing it from being processed!") msg_id = self.getmessageheader(retval, "message-id")
fd.close() if msg_id is None:
msg_id = '<unknown-message-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 return retval
# Interface from BaseFolder # Interface from BaseFolder