From f9dde27aff8bf77568d8272a5e143418c72fc456 Mon Sep 17 00:00:00 2001 From: Timo Kankare Date: Mon, 5 Sep 2016 22:35:34 +0300 Subject: [PATCH] Extracted list_message code to account and backends. --- Mailnag/backends/imap.py | 32 ++++++++++++++++ Mailnag/backends/pop3.py | 29 +++++++++++++- Mailnag/common/accounts.py | 8 +++- Mailnag/daemon/mails.py | 77 ++++++++------------------------------ 4 files changed, 82 insertions(+), 64 deletions(-) diff --git a/Mailnag/backends/imap.py b/Mailnag/backends/imap.py index feb07b3..7fe460d 100644 --- a/Mailnag/backends/imap.py +++ b/Mailnag/backends/imap.py @@ -23,6 +23,7 @@ # MA 02110-1301, USA. # +import email import logging import re import Mailnag.common.imaplib2 as imaplib @@ -95,6 +96,37 @@ class ImapBackend: (not self._conn.Terminate) + def list_messages(self): + conn = self._conn + if len(self.folders) == 0: + folder_list = [ 'INBOX' ] + else: + folder_list = self.folders + + for folder in folder_list: + # select IMAP folder + conn.select(folder, readonly = True) + try: + status, data = conn.search(None, 'UNSEEN') # ALL or UNSEEN + except: + logging.warning('Folder %s does not exist.', folder) + continue + + if status != 'OK' or None in [d for d in data]: + logging.debug('Folder %s in status %s | Data: %s', (folder, status, data)) + continue # Bugfix LP-735071 + for num in data[0].split(): + typ, msg_data = conn.fetch(num, '(BODY.PEEK[HEADER])') # header only (without setting READ flag) + for response_part in msg_data: + if isinstance(response_part, tuple): + try: + msg = email.message_from_string(response_part[1]) + except: + logging.debug("Couldn't get IMAP message.") + continue + yield (folder, msg) + + def request_folders(self): lst = [] diff --git a/Mailnag/backends/pop3.py b/Mailnag/backends/pop3.py index 2a5ba99..20872dc 100644 --- a/Mailnag/backends/pop3.py +++ b/Mailnag/backends/pop3.py @@ -23,6 +23,7 @@ # MA 02110-1301, USA. # +import email import logging import poplib @@ -82,10 +83,36 @@ class Pop3Backend: def close(self): self._conn.quit() + def has_connection(self): return (self._conn != None) and \ ('sock' in self._conn.__dict__) - + + + def list_messages(self): + conn = self._conn + folder = '' + # number of mails on the server + mail_total = len(conn.list()[1]) + for i in range(1, mail_total + 1): # for each mail + try: + # header plus first 0 lines from body + message = conn.top(i, 0)[1] + except: + logging.debug("Couldn't get POP message.") + continue + + # convert list to string + message_string = '\n'.join(message) + + try: + # put message into email object and make a dictionary + msg = dict(email.message_from_string(message_string)) + except: + logging.debug("Couldn't get msg from POP message.") + continue + yield (folder, msg) + def request_folders(self): lst = [] diff --git a/Mailnag/common/accounts.py b/Mailnag/common/accounts.py index 8af8b55..0eb7350 100644 --- a/Mailnag/common/accounts.py +++ b/Mailnag/common/accounts.py @@ -91,8 +91,12 @@ class Account: return self._has_IMAP_connection() else: return self._has_POP3_connection() - - + + + def list_messages(self): + return self.backend.list_messages() + + # Requests folder names (list) from a server. # Relevant for IMAP accounts only. # Returns an empty list when used on POP3 accounts. diff --git a/Mailnag/daemon/mails.py b/Mailnag/daemon/mails.py index 5c550aa..8fdb625 100644 --- a/Mailnag/daemon/mails.py +++ b/Mailnag/daemon/mails.py @@ -70,70 +70,25 @@ class MailCollector: continue if acc.imap: # IMAP - if len(acc.folders) == 0: - folder_list = [ 'INBOX' ] - else: - folder_list = acc.folders - - for folder in folder_list: - # select IMAP folder - conn.select(folder, readonly = True) - try: - status, data = conn.search(None, 'UNSEEN') # ALL or UNSEEN - except: - logging.warning('Folder %s does not exist.', folder) - continue - - if status != 'OK' or None in [d for d in data]: - logging.debug('Folder %s in status %s | Data: %s', (folder, status, data)) - continue # Bugfix LP-735071 - for num in data[0].split(): - typ, msg_data = conn.fetch(num, '(BODY.PEEK[HEADER])') # header only (without setting READ flag) - for response_part in msg_data: - if isinstance(response_part, tuple): - try: - msg = email.message_from_string(response_part[1]) - except: - logging.debug("Couldn't get IMAP message.") - continue - - sender, subject, datetime, msgid = self._get_header(msg) - id = self._get_id(msgid, acc, folder, sender, subject, datetime) - - # Discard mails with identical IDs (caused - # by mails with a non-unique fallback ID, - # i.e. mails received in the same folder with - # identical sender and subject but *no datetime*, - # see _get_id()). - # Also filter duplicates caused by Gmail labels. - if id not in mail_ids: - mail_list.append(Mail(datetime, subject, \ - sender, id, acc)) - mail_ids[id] = None + for folder, msg in acc.list_messages(): + sender, subject, datetime, msgid = self._get_header(msg) + id = self._get_id(msgid, acc, folder, sender, subject, datetime) + + # Discard mails with identical IDs (caused + # by mails with a non-unique fallback ID, + # i.e. mails received in the same folder with + # identical sender and subject but *no datetime*, + # see _get_id()). + # Also filter duplicates caused by Gmail labels. + if id not in mail_ids: + mail_list.append(Mail(datetime, subject, \ + sender, id, acc)) + mail_ids[id] = None else: # POP - # number of mails on the server - mail_total = len(conn.list()[1]) - for i in range(1, mail_total + 1): # for each mail - try: - # header plus first 0 lines from body - message = conn.top(i, 0)[1] - except: - logging.debug("Couldn't get POP message.") - continue - - # convert list to string - message_string = '\n'.join(message) - - try: - # put message into email object and make a dictionary - msg = dict(email.message_from_string(message_string)) - except: - logging.debug("Couldn't get msg from POP message.") - continue - + for folder, msg in acc.list_messages(): sender, subject, datetime, msgid = self._get_header(msg) - id = self._get_id(msgid, acc, '', sender, subject, datetime) + id = self._get_id(msgid, acc, folder, sender, subject, datetime) # Discard mails with identical IDs (caused # by mails with a non-unique fallback ID,