diff options
Diffstat (limited to 'ctec/logic.py')
-rw-r--r-- | ctec/logic.py | 119 |
1 files changed, 93 insertions, 26 deletions
diff --git a/ctec/logic.py b/ctec/logic.py index 1769e47..d151a0f 100644 --- a/ctec/logic.py +++ b/ctec/logic.py @@ -1,15 +1,13 @@ import imaplib import mailbox import pathlib -import pprint -import re -from typing import List, Union, Tuple +from typing import List, Tuple from . import import_or_install, imap_response mailbox.Maildir.colon = '!' -FLAGS = re.compile(rb'FLAGS \(([^)]+?)\)') +MESSAGE_DOWNLOAD_BATCH = 1 def percent_encode(c: str) -> str: return '%' + hex(ord(c))[2:] @@ -48,29 +46,98 @@ class Account: raise NotImplementedError(f'who the hell uses {repr(folder_info.delimiter)} as a delimiter') self.mailbox.add_folder(clean_folder_name(folder_info.name, folder_info.delimiter)) - def fetch_inbox(self): - with imaplib.IMAP4_SSL(self.info['imap host']) as M: - M.login(self.address, self.info['password']) - inbox = self.mailbox.add_folder('Inbox') - M.select() - typ, data = M.search(None, 'ALL') - for num in data[0].split(): - typ, data = M.fetch(num, '(FLAGS RFC822)') - for prefix, message in data[:-1]: - flags = FLAGS.search(prefix).group(1).split() - message = mailbox.MaildirMessage(message) - print(message['Subject'], flags) - if rb'\Seen' in flags: - message.add_flag('S') - message.set_subdir('cur') - if 'Message-ID' in message: - message_id = message['Message-ID'].strip() + def fetch_folder(self, folder_name: str): + folder = self.mailbox.add_folder(folder_name) + message_id_to_key = dict((message['Message-ID'], key) for key, message in folder.iteritems() if 'Message-ID' in message) + typ, count_data = self.connection.select(dirty_folder_name(folder_name)) + if typ != 'OK': + print(typ, count_data) + count = int(count_data[0].decode('ASCII')) + needs_fetching = [] + if count > 0: + typ, data = self.connection.fetch('1:*', '(FLAGS ENVELOPE)') + if typ != 'OK': + print(typ, data) + pending: List[bytes] = [] + for fetched_line in data: + if isinstance(fetched_line, tuple): + pending.append(b'\r\n'.join(fetched_line)) + elif isinstance(fetched_line, bytes): + pending.append(fetched_line) + else: + raise TypeError(f'what the hell is a {repr(fetched_line)} and why is it in the fetch response') + try: + parsed_data = imap_response.MessageData.parse(b''.join(pending)) + number = parsed_data.number + if parsed_data.envelope is None: + print('hold the fuck up where did that envelope go', parsed_data) else: - print(message) - raise KeyError('No message ID header') - if not any(x['Message-ID'].strip() == message_id for x in inbox): - inbox.add(message) - M.close() + message_id = parsed_data.envelope.get('message_id', None) + if message_id is None: + print('no message ID for', parsed_data) + elif message_id in message_id_to_key: + message: mailbox.MaildirMessage = folder[message_id_to_key[message_id]] + flags_list = parsed_data.flags or [] + flags = '' + if r'\Seen' in flags_list: + flags += 'S' + if r'\Answered' in flags_list: + flags += 'R' # Replied to + if r'\Flagged' in flags_list: + flags += 'F' + if r'\Deleted' in flags_list: + flags += 'T' # Trashed + if r'\Draft' in flags_list: + flags += 'D' + message.set_flags(flags) + else: + needs_fetching.append(number) + pending = [] + except ValueError: + continue + if len(pending) > 0: + imap_response.MessageData.parse(b''.join(pending)) + + if len(needs_fetching) > 0: + for fetch_start in range(0, len(needs_fetching), MESSAGE_DOWNLOAD_BATCH): + typ, data = self.connection.fetch(','.join(str(x) for x in needs_fetching[fetch_start:fetch_start + MESSAGE_DOWNLOAD_BATCH]), '(FLAGS BODY.PEEK[])') + if typ != 'OK': + print(typ, data) + pending = [] + for fetched_line in data: + if isinstance(fetched_line, tuple): + pending.append(b'\r\n'.join(fetched_line)) + elif isinstance(fetched_line, bytes): + pending.append(fetched_line) + else: + raise TypeError(f'what the hell is a {repr(fetched_line)} and why is it in the fetch response') + try: + parsed_data = imap_response.MessageData.parse(b''.join(pending)) + if parsed_data.body_all_sections is None: + print('hold the fuck up where did that body go', parsed_data) + else: + message = mailbox.MaildirMessage(parsed_data.body_all_sections) + flags_list = parsed_data.flags or [] + flags = '' + if r'\Seen' in flags_list: + flags += 'S' + if r'\Answered' in flags_list: + flags += 'R' # Replied to + if r'\Flagged' in flags_list: + flags += 'F' + if r'\Deleted' in flags_list: + flags += 'T' # Trashed + if r'\Draft' in flags_list: + flags += 'D' + message.set_flags(flags) + folder.add(message) + pending = [] + except ValueError: + continue + if len(pending) > 0: + imap_response.MessageData.parse(b''.join(pending)) + + self.connection.close() def folders(self) -> List[Tuple[str, mailbox.Maildir]]: return [(folder, self.mailbox.get_folder(folder)) for folder in self.mailbox.list_folders()] |