b217f4a5874b6dedb93905437a7f38f953f78efd
[imap-fix-internaldate] / mail_iterator.py
1 '''
2 mail_iterator.py - The module contains the MailIterator class.
3
4 Copyright (c) 2012 Intra2net AG
5 Author: Plamen Dimitrov
6
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.
11
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.
16
17 Add '-t' argument when running the module for a test mode.
18 For a detailed list of each message with a date conflict change
19 the 'log_level' in the configuration file from '30' to '20'.
20 '''
21
22 import imaplib
23 import re
24 import time
25
26 LIST_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')
27
28 class 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