Initial submission of working tool
[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.
16
17Add '-t' argument when running the module for a test mode.
18For a detailed list of each message with a date conflict change
19the 'log_level' in the configuration file from '30' to '20'.
20'''
21
22import imaplib
23import re
24import time
25
26LIST_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')
27
28class MailIterator:
29 """This class communicates with the e-mail server."""
30 mailboxes = []
31
32 def __init__(self, server, username, password):
33 """Creates a connection and a user session."""
34 self.mail_con = imaplib.IMAP4_SSL(server)
35 result, data = self.mail_con.login(username, password)
36 if(result!="OK"):
37 raise UserWarning("Could not log in as user " + username + ". " + data)
38 result, self.mailboxes = self.mail_con.list()
39 if(result!="OK"):
40 raise UserWarning("Could not retrieve mailboxes for user " + username + ".")
41
42 def __del__(self):
43 """Closes the connection and the user session."""
44 self.mail_con.close()
45 self.mail_con.logout()
46
47 def __iter__(self):
48 """Iterates through the retrieved mailboxes."""
49 for mailbox in self.mailboxes:
50 mailbox = LIST_RESP.match(mailbox.decode("utf-8")).groups()
51 #print("Checking mailbox ", mailbox[2])
52 self.mail_con.select(mailbox[2])
53 yield mailbox[2]
54
55 def fetch_messages(self):
56 """Fetches the messages from the current mailbox, return list of uids."""
57 result, data = self.mail_con.uid('search', None, "ALL")
58 if(result!="OK"):
59 raise UserWarning("Could not fetch messages.")
60 #print("E-mail list for user ", row['username'], " is ", data[0])
61 mailid_list = data[0].split()
62 return mailid_list
63
64 def fetch_internal_date(self, mid):
65 """Fetches the internal date of a message, returns a time tuple."""
66 result, data = self.mail_con.uid('fetch', mid, '(INTERNALDATE)')
67 if(result!="OK"):
68 raise UserWarning("Could not fetch the internal date of message" + mid + ".")
69 internal_date = imaplib.Internaldate2tuple(data[0])
70 return internal_date
71
72 def fetch_received_date(self, mid):
73 """Fetches the received date of a message, returns bytes reponse."""
74 result, data = self.mail_con.uid('fetch', mid, '(BODY.PEEK[HEADER.FIELDS (RECEIVED)])')
75 if(result!="OK"):
76 raise UserWarning("Could not fetch the received header of message" + mid + ".")
77 return data
78
79 def update_message(self, mid, mailbox, internal_date):
80 """Replaces a message with one with correct internal date."""
81 internal_date_seconds = time.mktime(internal_date.timetuple())
82 internal_date_str = imaplib.Time2Internaldate(internal_date_seconds)
83 result, data = self.mail_con.uid('fetch', mid, '(RFC822)')
84 if(result!="OK"):
85 raise UserWarning("Could not retrieve the entire e-mail" + mid + ".")
86 #print("Entire e-mail is: ", data[0][1])
87
88 fetched_flags = self.mail_con.uid('fetch', mid, '(FLAGS)')[1][0]
89 parsed_flags = imaplib.ParseFlags(fetched_flags)
90 flags_str = " ".join(flag.decode("utf-8") for flag in parsed_flags)
91 result, data = self.mail_con.append(mailbox, flags_str,
92 internal_date_str, data[0][1])
93 #print(result, data)
94 if(result!="OK"):
95 raise UserWarning("Could not replace the e-mail" + mid + ".")
96 else:
97 result, data = self.mail_con.uid('STORE', mid, '+FLAGS', r'(\Deleted)')
98 if(result!="OK"):
99 raise UserWarning("Could not delete the e-mail" + mid + ".")
100 else: self.mail_con.expunge()
101 return