2 The module contains the MailIterator class.
4 Copyright (c) 2012 Intra2net AG
5 Author: Plamen Dimitrov
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
19 import imaplib, socket
23 MAILBOX_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')
28 """This class communicates with the IMAP server."""
31 # IMAP4_SSL for connection with an IMAP server
33 # list of tuples (uidvalidity, mailboxname) for the retrieved mailboxes
38 skip_shared_folders = None
40 def __init__(self, server, username, password, skip_shared_folders = False):
41 """Creates a connection and a user session."""
43 self.skip_shared_folders = skip_shared_folders
47 self.mail_con = imaplib.IMAP4_SSL(server)
48 logging.info("Connected to %s", server)
49 except socket.error as ex:
50 logging.error("Could not connect to host: %s", ex)
55 self.mail_con.login(username, password)
57 logging.info("Logged in as %s.", username)
58 except self.mail_con.error as ex:
59 self.logged_in = False
60 logging.error("Could not log in as user %s: %s", username, ex)
65 _result, mailboxes = self.mail_con.list()
66 except self.mail_con.error as ex:
67 logging.warning("Could not retrieve mailboxes for user %s: %s", username, ex)
69 for mailbox in mailboxes:
70 mailbox = MAILBOX_RESP.match(mailbox.decode('iso-8859-1')).groups()
71 self.mailboxes.append(mailbox)
72 self.mailboxes = sorted(self.mailboxes, key=lambda box: box[2], reverse=True)
77 """Closes the connection and the user session."""
81 self.mail_con.logout()
84 """Iterates through all mailboxes, returns (children,delimiter,name)."""
86 for mailbox in self.mailboxes:
87 logging.debug("Checking mailbox %s", mailbox[2])
88 # detect if mailbox is shared and if skip flag is set iterate further
89 imap_delimiter = mailbox[1]
90 if(self.skip_shared_folders and mailbox[2].split(imap_delimiter)[0] == '"user'):
91 logging.info("Mailbox %s is shared and therefore skipped.", mailbox[2])
93 # select mailbox if writable
95 self.mail_con.select(mailbox[2])
96 logging.info("Processing mailbox %s", mailbox[2])
97 except self.mail_con.readonly:
98 logging.warning("Mailbox %s is not writable and therefore skipped", mailbox[2])
102 def fetch_messages(self):
103 """Fetches the messages from the current mailbox, return list of uids."""
106 # Work around unsolicited server responses in imaplib by clearing them
107 self.mail_con.response('SEARCH')
108 _result, data = self.mail_con.uid('search', None, "ALL")
109 except (self.mail_con.error):
110 raise UserWarning("Could not fetch messages")
111 mailid_list = data[0].split()
114 def set_seen_messages(self, mid_range):
115 """Sets the \\Seen flag for all messages with the respective mids."""
118 # Work around unsolicited server responses in imaplib by clearing them
119 self.mail_con.response('STORE')
120 _result, data = self.mail_con.uid('STORE', mid_range, '+FLAGS', "(\Seen)")
121 logging.debug("New flags for messages %s are %s", mid_range, data)
122 except (self.mail_con.error) as ex:
123 raise UserWarning("Could not set the flags for some messages: %s", ex)
124 self.mail_con.expunge()