''' The module contains the MailIterator class. Copyright (c) 2012 Intra2net AG Author: Plamen Dimitrov and Thomas Jarosch 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 sys import socket, imaplib import re import logging MAILBOX_RESP = re.compile(r'\((?P.*?)\) "(?P.*)" (?P.*)') UIDVAL_RESP = re.compile(r'(?P.*) \(UIDVALIDITY (?P.*)\)') ACLS_RESP = re.compile(b'(?P\S+) (?P\S+)') #imaplib.Debug = 4 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 # logged in status logged_in = None def __init__(self, server, username, password): """Creates a connection and a user session.""" # connect to server try: self.mail_con = imaplib.IMAP4(server) # socket functionality (currently unavailable) #imap_socket = socket.socket(socket.AF_UNIX) #imap_socket.connect("/var/imap/socket/imap") #self.mail_con.sock = imap_socket #self.mail_con.file = self.mail_con.sock.makefile('rb') logging.info("Connected to %s", server) except (socket.error, self.mail_con.error) as ex: logging.error("Could not connect to host: %s", ex) sys.exit() # log in try: # proxy authentication #self.mail_con.login("cyrus", "geheim") #self.mail_con.proxyauth(username) self.mail_con.login(username, password) self.logged_in = True logging.info("Logged in as %s.", username) except self.mail_con.error as ex: self.logged_in = False logging.error("Could not log in as user %s: %s", username, ex) sys.exit() # list mailboxes try: _result, mailboxes = self.mail_con.list() except self.mail_con.error as ex: logging.warning("Could not retrieve mailboxes for user %s: %s", username, ex) self.mailboxes = [] for mailbox in mailboxes: mailbox = MAILBOX_RESP.match(mailbox.decode('iso-8859-1')).groups() self.mailboxes.append(mailbox) self.mailboxes = sorted(self.mailboxes, key=lambda box: box[2], reverse=True) return def __del__(self): """Closes the connection and the user session.""" if self.logged_in: self.mail_con.logout() def clear_inbox_acls(self, user): """Resets the inbox acls for a given user.""" try: _result, inbox_acls = self.mail_con.getacl("INBOX") except self.mail_con.error as ex: logging.warning("Could not get the acls of INBOX: %s", ex) return inbox_acls = ACLS_RESP.findall(inbox_acls[0][6:]) logging.debug("Retrieved acls from INBOX are %s", inbox_acls) for acl_ref in inbox_acls: if acl_ref[0].decode('iso-8859-1') != user: try: self.mail_con.deleteacl("INBOX", acl_ref[0]) logging.debug("Reset acls on INBOX for user %s", acl_ref[0].decode('iso-8859-1')) except self.mail_con.error as ex: logging.warning("Could not reset acls on INBOX for user %s: %s", acl_ref[0], ex) return def add_acls(self, mailbox, mb_acls, original_user, target_user): """Add acls to mailbox.""" mailbox = mailbox.replace("user/" + original_user + "/", "INBOX/") for acl_user in mb_acls: # (in case target user != original user): # - don't overwrite acls eventually set for the current targetuser # - don't set the default owner acls for the new folder if acl_user != target_user and acl_user != original_user: try: self.mail_con.setacl(mailbox, acl_user, mb_acls[acl_user]) logging.debug("Set acls %s for user %s on mailbox %s", mb_acls[acl_user], acl_user, mailbox) except self.mail_con.error as ex: logging.warning("Could not set acls %s for user %s on mailbox %s: %s", mb_acls[acl_user], acl_user, mailbox, ex) return def delete_mailboxes(self, deleted_mailbox): """Delete specified mailbox or empty inbox.""" for mailbox in self.mailboxes: pattern = '^\"?' + deleted_mailbox # if INBOX it cannot be deleted so add delimiter if deleted_mailbox == "INBOX": pattern += mailbox[1] if re.compile(pattern).match(mailbox[2]): result, data = self.mail_con.delete(mailbox[2]) logging.debug("Deleted mailbox %s", mailbox[2]) if result != "OK": logging.warning("Could not delete mailbox %s: %s", mailbox[2], data[0]) return def create_mailbox(self, mailbox): """Create new mailbox to inject messages.""" if mailbox != "INBOX": result, data = self.mail_con.create(mailbox) logging.debug("Creating mailbox %s", mailbox) if result != "OK": logging.warning("Could not create mailbox %s: %s", mailbox, data[0].decode('iso-8859-1')) return def inject_message(self, message, mailbox, internal_date): """Inject a message into a mailbox.""" result, data = self.mail_con.append(mailbox, "\\Seen", internal_date, message.encode()) logging.debug("Appending message to mailbox %s", mailbox) if result != "OK": logging.warning("Could not append an e-mail to the mailbox %s: %s.", mailbox, data[0].decode('iso-8859-1')) #logging.debug("Email content:\n%s", message) return