Basic refactoring and Tom's recommendations
[imap-fix-internaldate] / mail_iterator.py
CommitLineData
c9da760a
PD
1'''
2mail_iterator.py - The module contains the MailIterator class.
3
4Copyright (c) 2012 Intra2net AG
5Author: Plamen Dimitrov
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.
c9da760a
PD
16'''
17
18import imaplib
19import re
20import time
21
8301e589
PD
22MAILBOX_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')
23UIDVAL_RESP = re.compile(r'(?P<name>.*) \(UIDVALIDITY (?P<uidval>.*)\)')
c9da760a
PD
24
25class MailIterator:
26 """This class communicates with the e-mail server."""
c9da760a 27
7a1d4c35
PD
28 # class attributes
29 # IMAP4_SSL for connection with an IMAP server
30 mail_con = None
31 # list of tuples (uidvalidity, mailboxname) for the retrieved mailboxes
32 mailboxes = None
33
c9da760a
PD
34 def __init__(self, server, username, password):
35 """Creates a connection and a user session."""
36 self.mail_con = imaplib.IMAP4_SSL(server)
37 result, data = self.mail_con.login(username, password)
38 if(result!="OK"):
39 raise UserWarning("Could not log in as user " + username + ". " + data)
40 result, self.mailboxes = self.mail_con.list()
41 if(result!="OK"):
42 raise UserWarning("Could not retrieve mailboxes for user " + username + ".")
43
44 def __del__(self):
45 """Closes the connection and the user session."""
46 self.mail_con.close()
47 self.mail_con.logout()
48
49 def __iter__(self):
8301e589 50 """Iterates through all mailboxes, returns (uidval,name)."""
c9da760a 51 for mailbox in self.mailboxes:
8301e589
PD
52 #print("Checking mailbox ", mailbox)
53 mailbox = MAILBOX_RESP.match(mailbox.decode("utf-8")).groups()
54 result, data = self.mail_con.status(mailbox[2], '(UIDVALIDITY)')
55 if(result!="OK"):
56 raise UserWarning("Could not retrieve mailbox uidvalidity.")
57 uidval = UIDVAL_RESP.match(data[0].decode("utf-8")).groups()
58 #print(data[0], uidval)
c9da760a 59 self.mail_con.select(mailbox[2])
8301e589 60 yield (mailbox[2], uidval[1])
c9da760a
PD
61
62 def fetch_messages(self):
63 """Fetches the messages from the current mailbox, return list of uids."""
64 result, data = self.mail_con.uid('search', None, "ALL")
65 if(result!="OK"):
66 raise UserWarning("Could not fetch messages.")
67 #print("E-mail list for user ", row['username'], " is ", data[0])
68 mailid_list = data[0].split()
69 return mailid_list
70
71 def fetch_internal_date(self, mid):
72 """Fetches the internal date of a message, returns a time tuple."""
73 result, data = self.mail_con.uid('fetch', mid, '(INTERNALDATE)')
74 if(result!="OK"):
75 raise UserWarning("Could not fetch the internal date of message" + mid + ".")
76 internal_date = imaplib.Internaldate2tuple(data[0])
77 return internal_date
78
79 def fetch_received_date(self, mid):
80 """Fetches the received date of a message, returns bytes reponse."""
81 result, data = self.mail_con.uid('fetch', mid, '(BODY.PEEK[HEADER.FIELDS (RECEIVED)])')
82 if(result!="OK"):
83 raise UserWarning("Could not fetch the received header of message" + mid + ".")
84 return data
85
86 def update_message(self, mid, mailbox, internal_date):
87 """Replaces a message with one with correct internal date."""
88 internal_date_seconds = time.mktime(internal_date.timetuple())
89 internal_date_str = imaplib.Time2Internaldate(internal_date_seconds)
90 result, data = self.mail_con.uid('fetch', mid, '(RFC822)')
91 if(result!="OK"):
92 raise UserWarning("Could not retrieve the entire e-mail" + mid + ".")
93 #print("Entire e-mail is: ", data[0][1])
94
95 fetched_flags = self.mail_con.uid('fetch', mid, '(FLAGS)')[1][0]
96 parsed_flags = imaplib.ParseFlags(fetched_flags)
97 flags_str = " ".join(flag.decode("utf-8") for flag in parsed_flags)
98 result, data = self.mail_con.append(mailbox, flags_str,
99 internal_date_str, data[0][1])
100 #print(result, data)
101 if(result!="OK"):
102 raise UserWarning("Could not replace the e-mail" + mid + ".")
103 else:
104 result, data = self.mail_con.uid('STORE', mid, '+FLAGS', r'(\Deleted)')
105 if(result!="OK"):
106 raise UserWarning("Could not delete the e-mail" + mid + ".")
107 else: self.mail_con.expunge()
108 return