Commit | Line | Data |
---|---|---|
87758662 | 1 | ''' |
1d595a61 | 2 | The module contains the MailIterator class. |
87758662 PD |
3 | |
4 | Copyright (c) 2012 Intra2net AG | |
9bfaa115 | 5 | Author: Plamen Dimitrov |
6a1e092b PD |
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. | |
87758662 PD |
16 | ''' |
17 | ||
18 | import sys | |
19 | import imaplib, socket | |
20 | import re | |
21 | import logging | |
22 | ||
23 | MAILBOX_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)') | |
24 | ||
25 | #imaplib.Debug = 4 | |
26 | ||
27 | class MailIterator: | |
25ef1a0c | 28 | """This class communicates with the IMAP server.""" |
87758662 PD |
29 | |
30 | # class attributes | |
31 | # IMAP4_SSL for connection with an IMAP server | |
32 | mail_con = None | |
33 | # list of tuples (uidvalidity, mailboxname) for the retrieved mailboxes | |
34 | mailboxes = None | |
35 | # logged in status | |
36 | logged_in = None | |
dfc25eb6 PD |
37 | # skip shared folders |
38 | skip_shared_folders = None | |
87758662 | 39 | |
dfc25eb6 | 40 | def __init__(self, server, username, password, skip_shared_folders = False): |
87758662 PD |
41 | """Creates a connection and a user session.""" |
42 | ||
dfc25eb6 PD |
43 | self.skip_shared_folders = skip_shared_folders |
44 | ||
87758662 PD |
45 | # connect to server |
46 | try: | |
25ef1a0c | 47 | self.mail_con = imaplib.IMAP4_SSL(server) |
87758662 PD |
48 | logging.info("Connected to %s", server) |
49 | except socket.error as ex: | |
50 | logging.error("Could not connect to host: %s", ex) | |
51 | sys.exit() | |
52 | ||
53 | # log in | |
54 | try: | |
55 | self.mail_con.login(username, password) | |
56 | self.logged_in = True | |
57 | logging.info("Logged in as %s.", username) | |
58 | except self.mail_con.error as ex: | |
59 | self.logged_in = False | |
60 | logging.error("Could not log in as user %s: %s", username, ex) | |
61 | sys.exit() | |
62 | ||
63 | # list mailboxes | |
64 | try: | |
65 | _result, mailboxes = self.mail_con.list() | |
66 | except self.mail_con.error as ex: | |
0f949559 | 67 | logging.error("Could not retrieve mailboxes for user %s: %s", username, ex) |
87758662 PD |
68 | self.mailboxes = [] |
69 | for mailbox in mailboxes: | |
70 | mailbox = MAILBOX_RESP.match(mailbox.decode('iso-8859-1')).groups() | |
71 | self.mailboxes.append(mailbox) | |
0f949559 | 72 | self.mailboxes = sorted(self.mailboxes, key=lambda box: box[2], reverse=False) |
87758662 PD |
73 | |
74 | return | |
75 | ||
76 | def __del__(self): | |
77 | """Closes the connection and the user session.""" | |
78 | ||
79 | if(self.logged_in): | |
80 | self.mail_con.close() | |
81 | self.mail_con.logout() | |
82 | ||
83 | def __iter__(self): | |
84 | """Iterates through all mailboxes, returns (children,delimiter,name).""" | |
85 | ||
86 | for mailbox in self.mailboxes: | |
87 | logging.debug("Checking mailbox %s", mailbox[2]) | |
dfc25eb6 | 88 | # detect if mailbox is shared and if skip flag is set iterate further |
4478a36d TJ |
89 | imap_delimiter = mailbox[1] |
90 | if(self.skip_shared_folders and mailbox[2].split(imap_delimiter)[0] == '"user'): | |
dfc25eb6 PD |
91 | logging.info("Mailbox %s is shared and therefore skipped.", mailbox[2]) |
92 | continue | |
87758662 PD |
93 | # select mailbox if writable |
94 | try: | |
95 | self.mail_con.select(mailbox[2]) | |
87758662 | 96 | except self.mail_con.readonly: |
9d328275 | 97 | logging.warning("Mailbox %s is not writable and therefore skipped", mailbox[2]) |
87758662 PD |
98 | continue |
99 | yield mailbox | |
100 | ||
101 | def fetch_messages(self): | |
102 | """Fetches the messages from the current mailbox, return list of uids.""" | |
103 | ||
104 | try: | |
105 | # Work around unsolicited server responses in imaplib by clearing them | |
106 | self.mail_con.response('SEARCH') | |
107 | _result, data = self.mail_con.uid('search', None, "ALL") | |
108 | except (self.mail_con.error): | |
9d328275 | 109 | raise UserWarning("Could not fetch messages") |
87758662 PD |
110 | mailid_list = data[0].split() |
111 | return mailid_list | |
112 | ||
fbd3120a | 113 | def set_seen_messages(self, mailbox, mid_range): |
9d328275 | 114 | """Sets the \\Seen flag for all messages with the respective mids.""" |
9bfaa115 | 115 | |
fbd3120a | 116 | logging.info("Processing mailbox %s", mailbox[2]) |
87758662 PD |
117 | try: |
118 | # Work around unsolicited server responses in imaplib by clearing them | |
119 | self.mail_con.response('STORE') | |
d0b8b762 | 120 | _result, data = self.mail_con.uid('STORE', mid_range, '+FLAGS', "(\\Seen)") |
dfc25eb6 | 121 | logging.debug("New flags for messages %s are %s", mid_range, data) |
9d328275 PD |
122 | except (self.mail_con.error) as ex: |
123 | raise UserWarning("Could not set the flags for some messages: %s", ex) | |
87758662 | 124 | self.mail_con.expunge() |