From a5cc274ff54b0bee51c3a901f7c9efc287b9a471 Mon Sep 17 00:00:00 2001 From: Hemna Date: Wed, 3 Feb 2021 11:00:20 -0500 Subject: [PATCH] Wrap all imap calls with try except blocks The Email Thread has been unstable due to some IMAP servers being crap. This patch wraps more of the imap server calls in try except blocks to try and trap errors. --- aprsd/email.py | 135 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 47 deletions(-) diff --git a/aprsd/email.py b/aprsd/email.py index 68481b3..921b14f 100644 --- a/aprsd/email.py +++ b/aprsd/email.py @@ -184,7 +184,12 @@ def parse_email(msgid, data, server): else: from_addr = "noaddr" LOG.debug("Got a message from '{}'".format(from_addr)) - m = server.fetch([msgid], ["RFC822"]) + try: + m = server.fetch([msgid], ["RFC822"]) + except Exception as e: + LOG.exception("Couldn't fetch email from server in parse_email", e) + return + msg = email.message_from_string(m[msgid][b"RFC822"].decode(errors="ignore")) if msg.is_multipart(): text = "" @@ -324,7 +329,12 @@ def resend_email(count, fromcall): LOG.exception("Failed to Connect to IMAP. Cannot resend email ", e) return - messages = server.search(["SINCE", today]) + try: + messages = server.search(["SINCE", today]) + except Exception as e: + LOG.exception("Couldn't search for emails in resend_email ", e) + return + # LOG.debug("%d messages received today" % len(messages)) msgexists = False @@ -332,11 +342,21 @@ def resend_email(count, fromcall): messages.sort(reverse=True) del messages[int(count) :] # only the latest "count" messages for message in messages: - for msgid, data in list(server.fetch(message, ["ENVELOPE"]).items()): + try: + parts = server.fetch(message, ["ENVELOPE"]).items() + except Exception as e: + LOG.exception("Couldn't fetch email parts in resend_email", e) + continue + + for msgid, data in list(parts): # one at a time, otherwise order is random (body, from_addr) = parse_email(msgid, data, server) # unset seen flag, will stay bold in email client - server.remove_flags(msgid, [imapclient.SEEN]) + try: + server.remove_flags(msgid, [imapclient.SEEN]) + except Exception as e: + LOG.exception("Failed to remove SEEN flag in resend_email", e) + if from_addr in shortcuts_inverted: # reverse lookup of a shortcut from_addr = shortcuts_inverted[from_addr] @@ -436,60 +456,81 @@ class APRSDEmailThread(threads.APRSDThread): continue LOG.debug("{} messages received today".format(len(messages))) - LOG.debug("Try Server.fetch.") try: - for msgid, data in server.fetch(messages, ["ENVELOPE"]).items(): - envelope = data[b"ENVELOPE"] - LOG.debug( - 'ID:%d "%s" (%s)' - % (msgid, envelope.subject.decode(), envelope.date), - ) - f = re.search( - r"'([[A-a][0-9]_-]+@[[A-a][0-9]_-\.]+)", - str(envelope.from_[0]), - ) - if f is not None: - from_addr = f.group(1) - else: - from_addr = "noaddr" + _msgs = server.fetch(messages, ["ENVELOPE"]) + except Exception as e: + LOG.exception("IMAP failed to fetch/flag messages: ", e) + continue - # LOG.debug("Message flags/tags: " + str(server.get_flags(msgid)[msgid])) - # if "APRS" not in server.get_flags(msgid)[msgid]: - # in python3, imap tags are unicode. in py2 they're strings. so .decode them to handle both - taglist = [ - x.decode(errors="ignore") - for x in server.get_flags(msgid)[msgid] - ] - if "APRS" not in taglist: - # if msg not flagged as sent via aprs - LOG.debug("Try single fetch.") + for msgid, data in _msgs.items(): + envelope = data[b"ENVELOPE"] + LOG.debug( + 'ID:%d "%s" (%s)' + % (msgid, envelope.subject.decode(), envelope.date), + ) + f = re.search( + r"'([[A-a][0-9]_-]+@[[A-a][0-9]_-\.]+)", + str(envelope.from_[0]), + ) + if f is not None: + from_addr = f.group(1) + else: + from_addr = "noaddr" + + # LOG.debug("Message flags/tags: " + str(server.get_flags(msgid)[msgid])) + # if "APRS" not in server.get_flags(msgid)[msgid]: + # in python3, imap tags are unicode. in py2 they're strings. so .decode them to handle both + taglist = [ + x.decode(errors="ignore") + for x in server.get_flags(msgid)[msgid] + ] + if "APRS" not in taglist: + # if msg not flagged as sent via aprs + LOG.debug("Try single fetch.") + try: server.fetch([msgid], ["RFC822"]) - LOG.debug("Did single fetch.") - (body, from_addr) = parse_email(msgid, data, server) - # unset seen flag, will stay bold in email client + except Exception as e: + LOG.exception("Failed single server fetch for RFC822", e) + break + + LOG.debug("Did single fetch.") + (body, from_addr) = parse_email(msgid, data, server) + # unset seen flag, will stay bold in email client + try: LOG.debug("Try remove flags.") server.remove_flags(msgid, [imapclient.SEEN]) LOG.debug("Did remove flags.") + except Exception as e: + LOG.exception("Failed to remove flags SEEN", e) + # Not much we can do here, so lets try and + # send the aprs message anyway - if from_addr in shortcuts_inverted: - # reverse lookup of a shortcut - from_addr = shortcuts_inverted[from_addr] + if from_addr in shortcuts_inverted: + # reverse lookup of a shortcut + from_addr = shortcuts_inverted[from_addr] - reply = "-" + from_addr + " " + body.decode(errors="ignore") - msg = messaging.TextMessage( - self.config["aprs"]["login"], - self.config["ham"]["callsign"], - reply, - ) - self.msg_queues["tx"].put(msg) - # flag message as sent via aprs + reply = "-" + from_addr + " " + body.decode(errors="ignore") + msg = messaging.TextMessage( + self.config["aprs"]["login"], + self.config["ham"]["callsign"], + reply, + ) + self.msg_queues["tx"].put(msg) + # flag message as sent via aprs + try: server.add_flags(msgid, ["APRS"]) # unset seen flag, will stay bold in email client + except Exception as e: + LOG.exception("Couldn't add APRS flag to email", e) + + try: server.remove_flags(msgid, [imapclient.SEEN]) - # check email more often since we just received an email - check_email_delay = 60 - except Exception as e: - LOG.exception("IMAP failed to fetch/flag messages: ", e) + except Exception as e: + LOG.exception("Couldn't remove seen flag from email", e) + + # check email more often since we just received an email + check_email_delay = 60 + # reset clock LOG.debug("Done looping over Server.fetch, logging out.") past = datetime.datetime.now()