''' mail_iterator.py - The module contains the MailIterator class. Copyright (c) 2012 Intra2net AG Author: Plamen Dimitrov This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ''' import imaplib import re import time import logging MAILBOX_RESP = re.compile(r'\((?P.*?)\) "(?P.*)" (?P.*)') UIDVAL_RESP = re.compile(r'(?P.*) \(UIDVALIDITY (?P.*)\)') class MailIterator: """This class communicates with the e-mail server.""" # class attributes # IMAP4_SSL for connection with an IMAP server mail_con = None # list of tuples (uidvalidity, mailboxname) for the retrieved mailboxes mailboxes = None def __init__(self, server, username, password): """Creates a connection and a user session.""" self.mail_con = imaplib.IMAP4_SSL(server) result, data = self.mail_con.login(username, password) if(result!="OK"): raise UserWarning("Could not log in as user " + username + ". " + data) result, self.mailboxes = self.mail_con.list() if(result!="OK"): raise UserWarning("Could not retrieve mailboxes for user " + username + ".") def __del__(self): """Closes the connection and the user session.""" self.mail_con.close() self.mail_con.logout() def __iter__(self): """Iterates through all mailboxes, returns (uidval,name).""" for mailbox in self.mailboxes: logging.debug("Checking mailbox %s.", mailbox) mailbox = MAILBOX_RESP.match(mailbox.decode('iso-8859-1')).groups() result, data = self.mail_con.status(mailbox[2], '(UIDVALIDITY)') if(result!="OK"): raise UserWarning("Could not retrieve mailbox uidvalidity.") uidval = UIDVAL_RESP.match(data[0].decode('iso-8859-1')).groups() logging.debug("Extracted mailbox info is %s %s.", data[0], uidval) self.mail_con.select(mailbox[2]) yield (mailbox[2], uidval[1]) def fetch_messages(self): """Fetches the messages from the current mailbox, return list of uids.""" result, data = self.mail_con.uid('search', None, "ALL") if(result!="OK"): raise UserWarning("Could not fetch messages.") mailid_list = data[0].split() return mailid_list def fetch_internal_date(self, mid): """Fetches the internal date of a message, returns a time tuple.""" result, data = self.mail_con.uid('fetch', mid, '(INTERNALDATE)') if(result!="OK"): raise UserWarning("Could not fetch the internal date of message" + mid + ".") internal_date = imaplib.Internaldate2tuple(data[0]) return internal_date def fetch_received_date(self, mid): """Fetches the received date of a message, returns bytes reponse.""" result, data = self.mail_con.uid('fetch', mid, '(BODY.PEEK[HEADER.FIELDS (RECEIVED)])') if(result!="OK"): raise UserWarning("Could not fetch the received header of message" + mid + ".") return data[0][1].decode('iso-8859-1') def update_message(self, mid, mailbox, internal_date): """Replaces a message with one with correct internal date.""" internal_date_seconds = time.mktime(internal_date.timetuple()) internal_date_str = imaplib.Time2Internaldate(internal_date_seconds) result, data = self.mail_con.uid('fetch', mid, '(RFC822)') if(result!="OK"): raise UserWarning("Could not retrieve the entire e-mail" + mid + ".") #logging.debug("Entire e-mail is: %s", data[0][1]) fetched_flags = self.mail_con.uid('fetch', mid, '(FLAGS)')[1][0] parsed_flags = imaplib.ParseFlags(fetched_flags) flags_str = " ".join(flag.decode('iso-8859-1') for flag in parsed_flags) result, data = self.mail_con.append(mailbox, flags_str, internal_date_str, data[0][1]) logging.debug("Adding corrected copy of the message reponse: %s %s", result, data) if(result!="OK"): raise UserWarning("Could not replace the e-mail" + mid + ".") else: result, data = self.mail_con.uid('STORE', mid, '+FLAGS', r'(\Deleted)') logging.debug("Removing old copy of the message reponse: %s %s", result, data) if(result!="OK"): raise UserWarning("Could not delete the e-mail" + mid + ".") else: self.mail_con.expunge() return