Add mmassive e-mail update for performance by using message set STORE command
[imap-mark-seen] / mail_iterator.py
1 '''
2 imap-mark-seen.py - Tool to mark all e-mails as seen
3
4 Copyright (c) 2012 Intra2net AG
5 Author: Plamen Dimitrov and Thomas Jarosch
6 '''
7
8 import sys
9 import imaplib, socket
10 import re
11 import logging
12
13 MAILBOX_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')
14
15 #imaplib.Debug = 4
16
17 class MailIterator:
18     """This class communicates with the e-mail server."""
19
20     # class attributes
21     # IMAP4_SSL for connection with an IMAP server
22     mail_con = None
23     # list of tuples (uidvalidity, mailboxname) for the retrieved mailboxes
24     mailboxes = None
25     # logged in status
26     logged_in = None
27
28     def __init__(self, server, username, password):
29         """Creates a connection and a user session."""
30
31         # connect to server
32         try:
33             self.mail_con = imaplib.IMAP4(server)
34             logging.info("Connected to %s", server)
35         except socket.error as ex:
36             logging.error("Could not connect to host: %s", ex)
37             sys.exit()
38
39         # log in
40         try:
41             self.mail_con.login(username, password)
42             self.logged_in = True
43             logging.info("Logged in as %s.", username)
44         except self.mail_con.error as ex:
45             self.logged_in = False
46             logging.error("Could not log in as user %s: %s", username, ex)
47             sys.exit()
48
49         # list mailboxes
50         try:
51             _result, mailboxes = self.mail_con.list()
52         except self.mail_con.error as ex:
53             logging.warning("Could not retrieve mailboxes for user %s: %s", username, ex)
54         self.mailboxes = []
55         for mailbox in mailboxes:
56             mailbox = MAILBOX_RESP.match(mailbox.decode('iso-8859-1')).groups()
57             self.mailboxes.append(mailbox)
58         self.mailboxes = sorted(self.mailboxes, key=lambda box: box[2], reverse=True)
59
60         return
61
62     def __del__(self):
63         """Closes the connection and the user session."""
64
65         if(self.logged_in):
66             self.mail_con.close()
67             self.mail_con.logout()
68
69     def __iter__(self):
70         """Iterates through all mailboxes, returns (children,delimiter,name)."""
71
72         for mailbox in self.mailboxes:
73             logging.debug("Checking mailbox %s", mailbox[2])
74             # select mailbox if writable
75             try:
76                 self.mail_con.select(mailbox[2])
77                 logging.info("Processing mailbox %s", mailbox[2])
78             except self.mail_con.readonly:
79                 logging.warning("Mailbox %s is not writable and therefore skipped", mailbox[2])
80                 continue
81             yield mailbox
82
83     def fetch_messages(self):
84         """Fetches the messages from the current mailbox, return list of uids."""
85
86         try:
87             # Work around unsolicited server responses in imaplib by clearing them
88             self.mail_con.response('SEARCH')
89             _result, data = self.mail_con.uid('search', None, "ALL")
90         except (self.mail_con.error):
91             raise UserWarning("Could not fetch messages")
92         mailid_list = data[0].split()
93         return mailid_list
94
95     def set_seen_messages(self, mid_range):
96         """Sets the \\Seen flag for all messages with the respective mids."""
97         
98         try:
99             # Work around unsolicited server responses in imaplib by clearing them
100             self.mail_con.response('STORE')
101             _result, data = self.mail_con.uid('STORE', mid_range, '+FLAGS', "(\Seen)")
102             logging.info("New flags for messages %s are %s", mid_range, data)
103         except (self.mail_con.error) as ex:
104             raise UserWarning("Could not set the flags for some messages: %s", ex)
105         self.mail_con.expunge()