File hierarchy added
[imap-fix-internaldate] / src / 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
8fe4e3ff 21import logging
c9da760a 22
8301e589
PD
23MAILBOX_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')
24UIDVAL_RESP = re.compile(r'(?P<name>.*) \(UIDVALIDITY (?P<uidval>.*)\)')
c9da760a
PD
25
26class MailIterator:
27 """This class communicates with the e-mail server."""
c9da760a 28
7a1d4c35
PD
29 # class attributes
30 # IMAP4_SSL for connection with an IMAP server
31 mail_con = None
32 # list of tuples (uidvalidity, mailboxname) for the retrieved mailboxes
33 mailboxes = None
97bd6bea
PD
34 # logged in status
35 logged_in = None
7a1d4c35 36
c9da760a
PD
37 def __init__(self, server, username, password):
38 """Creates a connection and a user session."""
97bd6bea
PD
39 try:
40 self.mail_con = imaplib.IMAP4_SSL(server)
41 self.mail_con.login(username, password)
42 logging.info("Logged in as %s.", username)
43 except:
44 self.logged_in = False
45 raise UserWarning("Could not log in as user " + username + ".")
46 self.logged_in = True
3103ebb0
PD
47
48 try:
49 result, self.mailboxes = self.mail_con.list()
50 except:
c9da760a
PD
51 raise UserWarning("Could not retrieve mailboxes for user " + username + ".")
52
53 def __del__(self):
54 """Closes the connection and the user session."""
97bd6bea 55 if(self.logged_in):
3103ebb0
PD
56 try:
57 self.mail_con.close()
58 self.mail_con.logout()
59 except:
60 pass
c9da760a
PD
61
62 def __iter__(self):
8301e589 63 """Iterates through all mailboxes, returns (uidval,name)."""
c9da760a 64 for mailbox in self.mailboxes:
8fe4e3ff 65 logging.debug("Checking mailbox %s.", mailbox)
8a9d4c89 66 mailbox = MAILBOX_RESP.match(mailbox.decode('iso-8859-1')).groups()
3103ebb0
PD
67 try:
68 result, data = self.mail_con.status(mailbox[2], '(UIDVALIDITY)')
69 except:
8301e589 70 raise UserWarning("Could not retrieve mailbox uidvalidity.")
8a9d4c89 71 uidval = UIDVAL_RESP.match(data[0].decode('iso-8859-1')).groups()
8fe4e3ff 72 logging.debug("Extracted mailbox info is %s %s.", data[0], uidval)
c9da760a 73 self.mail_con.select(mailbox[2])
8301e589 74 yield (mailbox[2], uidval[1])
c9da760a
PD
75
76 def fetch_messages(self):
77 """Fetches the messages from the current mailbox, return list of uids."""
3103ebb0
PD
78 try:
79 result, data = self.mail_con.uid('search', None, "ALL")
80 except:
c9da760a 81 raise UserWarning("Could not fetch messages.")
c9da760a
PD
82 mailid_list = data[0].split()
83 return mailid_list
84
85 def fetch_internal_date(self, mid):
86 """Fetches the internal date of a message, returns a time tuple."""
3103ebb0
PD
87 try:
88 result, data = self.mail_con.uid('fetch', mid, '(INTERNALDATE)')
89 except:
c9da760a
PD
90 raise UserWarning("Could not fetch the internal date of message" + mid + ".")
91 internal_date = imaplib.Internaldate2tuple(data[0])
92 return internal_date
93
94 def fetch_received_date(self, mid):
95 """Fetches the received date of a message, returns bytes reponse."""
3103ebb0
PD
96 try:
97 result, data = self.mail_con.uid('fetch', mid, '(BODY.PEEK[HEADER.FIELDS (RECEIVED)])')
98 except:
c9da760a 99 raise UserWarning("Could not fetch the received header of message" + mid + ".")
8a9d4c89 100 return data[0][1].decode('iso-8859-1')
c9da760a
PD
101
102 def update_message(self, mid, mailbox, internal_date):
103 """Replaces a message with one with correct internal date."""
104 internal_date_seconds = time.mktime(internal_date.timetuple())
105 internal_date_str = imaplib.Time2Internaldate(internal_date_seconds)
3103ebb0
PD
106 try:
107 result, data = self.mail_con.uid('fetch', mid, '(RFC822)')
108 #logging.debug("Entire e-mail is: %s", data[0][1])
c9da760a 109
3103ebb0
PD
110 fetched_flags = self.mail_con.uid('fetch', mid, '(FLAGS)')[1][0]
111 parsed_flags = imaplib.ParseFlags(fetched_flags)
112 flags_str = " ".join(flag.decode('iso-8859-1') for flag in parsed_flags)
113 result, data = self.mail_con.append(mailbox, flags_str,
114 internal_date_str, data[0][1])
115 logging.debug("Adding corrected copy of the message reponse: %s %s", result, data)
116 except:
c9da760a 117 raise UserWarning("Could not replace the e-mail" + mid + ".")
3103ebb0 118 try:
c9da760a 119 result, data = self.mail_con.uid('STORE', mid, '+FLAGS', r'(\Deleted)')
8fe4e3ff 120 logging.debug("Removing old copy of the message reponse: %s %s", result, data)
3103ebb0
PD
121 except:
122 raise UserWarning("Could not delete the e-mail" + mid + ".")
123 self.mail_con.expunge()
c9da760a 124 return