This is all Plamen's work, I just contributed some ideas here and there
[imap-mark-seen] / src / mail_iterator.py
CommitLineData
87758662 1'''
1d595a61 2The module contains the MailIterator class.
87758662
PD
3
4Copyright (c) 2012 Intra2net AG
9bfaa115 5Author: Plamen Dimitrov
6a1e092b
PD
6
7This program is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
87758662
PD
16'''
17
18import sys
19import imaplib, socket
20import re
21import logging
22
23MAILBOX_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')
24
25#imaplib.Debug = 4
26
27class MailIterator:
25ef1a0c 28 """This class communicates with the IMAP server."""
87758662
PD
29
30 # class attributes
31 # IMAP4_SSL for connection with an IMAP server
32 mail_con = None
33 # list of tuples (uidvalidity, mailboxname) for the retrieved mailboxes
34 mailboxes = None
35 # logged in status
36 logged_in = None
37
38 def __init__(self, server, username, password):
39 """Creates a connection and a user session."""
40
41 # connect to server
42 try:
25ef1a0c 43 self.mail_con = imaplib.IMAP4_SSL(server)
87758662
PD
44 logging.info("Connected to %s", server)
45 except socket.error as ex:
46 logging.error("Could not connect to host: %s", ex)
47 sys.exit()
48
49 # log in
50 try:
51 self.mail_con.login(username, password)
52 self.logged_in = True
53 logging.info("Logged in as %s.", username)
54 except self.mail_con.error as ex:
55 self.logged_in = False
56 logging.error("Could not log in as user %s: %s", username, ex)
57 sys.exit()
58
59 # list mailboxes
60 try:
61 _result, mailboxes = self.mail_con.list()
62 except self.mail_con.error as ex:
63 logging.warning("Could not retrieve mailboxes for user %s: %s", username, ex)
64 self.mailboxes = []
65 for mailbox in mailboxes:
66 mailbox = MAILBOX_RESP.match(mailbox.decode('iso-8859-1')).groups()
67 self.mailboxes.append(mailbox)
68 self.mailboxes = sorted(self.mailboxes, key=lambda box: box[2], reverse=True)
69
70 return
71
72 def __del__(self):
73 """Closes the connection and the user session."""
74
75 if(self.logged_in):
76 self.mail_con.close()
77 self.mail_con.logout()
78
79 def __iter__(self):
80 """Iterates through all mailboxes, returns (children,delimiter,name)."""
81
82 for mailbox in self.mailboxes:
83 logging.debug("Checking mailbox %s", mailbox[2])
84 # select mailbox if writable
85 try:
86 self.mail_con.select(mailbox[2])
87 logging.info("Processing mailbox %s", mailbox[2])
88 except self.mail_con.readonly:
9d328275 89 logging.warning("Mailbox %s is not writable and therefore skipped", mailbox[2])
87758662
PD
90 continue
91 yield mailbox
92
93 def fetch_messages(self):
94 """Fetches the messages from the current mailbox, return list of uids."""
95
96 try:
97 # Work around unsolicited server responses in imaplib by clearing them
98 self.mail_con.response('SEARCH')
99 _result, data = self.mail_con.uid('search', None, "ALL")
100 except (self.mail_con.error):
9d328275 101 raise UserWarning("Could not fetch messages")
87758662
PD
102 mailid_list = data[0].split()
103 return mailid_list
104
9d328275
PD
105 def set_seen_messages(self, mid_range):
106 """Sets the \\Seen flag for all messages with the respective mids."""
9bfaa115 107
87758662
PD
108 try:
109 # Work around unsolicited server responses in imaplib by clearing them
110 self.mail_con.response('STORE')
9d328275
PD
111 _result, data = self.mail_con.uid('STORE', mid_range, '+FLAGS', "(\Seen)")
112 logging.info("New flags for messages %s are %s", mid_range, data)
113 except (self.mail_con.error) as ex:
114 raise UserWarning("Could not set the flags for some messages: %s", ex)
87758662 115 self.mail_con.expunge()