Commit | Line | Data |
---|---|---|
67e2ec02 | 1 | ''' |
23ebcaa4 | 2 | The module contains the MailIterator class. |
67e2ec02 PD |
3 | |
4 | Copyright (c) 2012 Intra2net AG | |
20760d94 PD |
5 | Author: Plamen Dimitrov and Thomas Jarosch |
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. | |
67e2ec02 PD |
16 | ''' |
17 | ||
f797b0fd | 18 | import sys |
4a1a0b03 | 19 | import socket, imaplib |
67e2ec02 | 20 | import re |
f797b0fd | 21 | import logging |
67e2ec02 PD |
22 | |
23 | MAILBOX_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)') | |
24 | UIDVAL_RESP = re.compile(r'(?P<name>.*) \(UIDVALIDITY (?P<uidval>.*)\)') | |
bb6cdeea PD |
25 | ACLS_RESP = re.compile(b'(?P<user>\S+) (?P<acls>\S+)') |
26 | ||
27 | #imaplib.Debug = 4 | |
67e2ec02 PD |
28 | |
29 | class MailIterator: | |
30 | """This class communicates with the e-mail server.""" | |
31 | ||
32 | # class attributes | |
33 | # IMAP4_SSL for connection with an IMAP server | |
34 | mail_con = None | |
35 | # list of tuples (uidvalidity, mailboxname) for the retrieved mailboxes | |
36 | mailboxes = None | |
37 | # logged in status | |
38 | logged_in = None | |
39 | ||
d6b22227 | 40 | def __init__(self, server, username, password): |
67e2ec02 | 41 | """Creates a connection and a user session.""" |
9ce1038d | 42 | |
b2bbd1f5 | 43 | # connect to server |
67e2ec02 | 44 | try: |
d6b22227 | 45 | self.mail_con = imaplib.IMAP4(server) |
f72ba42a PD |
46 | |
47 | # socket functionality (currently unavailable) | |
48 | #imap_socket = socket.socket(socket.AF_UNIX) | |
49 | #imap_socket.connect("/var/imap/socket/imap") | |
50 | #self.mail_con.sock = imap_socket | |
51 | #self.mail_con.file = self.mail_con.sock.makefile('rb') | |
52 | ||
d6b22227 | 53 | logging.info("Connected to %s", server) |
38f15e57 PD |
54 | except (socket.error, self.mail_con.error) as ex: |
55 | logging.error("Could not connect to host: %s", ex) | |
f797b0fd | 56 | sys.exit() |
b2bbd1f5 PD |
57 | |
58 | # log in | |
59 | try: | |
f72ba42a PD |
60 | |
61 | # proxy authentication | |
62 | #self.mail_con.login("cyrus", "geheim") | |
7aad8dab | 63 | #self.mail_con.proxyauth(username) |
f72ba42a PD |
64 | |
65 | self.mail_con.login(username, password) | |
66 | self.logged_in = True | |
38f15e57 | 67 | logging.info("Logged in as %s.", username) |
f797b0fd | 68 | except self.mail_con.error as ex: |
67e2ec02 | 69 | self.logged_in = False |
38f15e57 | 70 | logging.error("Could not log in as user %s: %s", username, ex) |
f797b0fd | 71 | sys.exit() |
9ce1038d | 72 | |
b2bbd1f5 PD |
73 | # list mailboxes |
74 | try: | |
75 | _result, mailboxes = self.mail_con.list() | |
f797b0fd | 76 | except self.mail_con.error as ex: |
38f15e57 | 77 | logging.warning("Could not retrieve mailboxes for user %s: %s", username, ex) |
67e2ec02 PD |
78 | self.mailboxes = [] |
79 | for mailbox in mailboxes: | |
80 | mailbox = MAILBOX_RESP.match(mailbox.decode('iso-8859-1')).groups() | |
81 | self.mailboxes.append(mailbox) | |
82 | self.mailboxes = sorted(self.mailboxes, key=lambda box: box[2], reverse=True) | |
9ce1038d | 83 | |
67e2ec02 PD |
84 | return |
85 | ||
86 | def __del__(self): | |
87 | """Closes the connection and the user session.""" | |
0cf4dc33 PD |
88 | |
89 | if self.logged_in: | |
90 | self.mail_con.logout() | |
67e2ec02 PD |
91 | |
92 | def clear_inbox_acls(self, user): | |
b2bbd1f5 | 93 | """Resets the inbox acls for a given user.""" |
0cf4dc33 | 94 | |
67e2ec02 | 95 | try: |
b2bbd1f5 | 96 | _result, inbox_acls = self.mail_con.getacl("INBOX") |
f797b0fd | 97 | except self.mail_con.error as ex: |
38f15e57 | 98 | logging.warning("Could not get the acls of INBOX: %s", ex) |
f797b0fd | 99 | return |
9ce1038d | 100 | inbox_acls = ACLS_RESP.findall(inbox_acls[0][6:]) |
38f15e57 | 101 | logging.debug("Retrieved acls from INBOX are %s", inbox_acls) |
9ce1038d | 102 | for acl_ref in inbox_acls: |
bb6cdeea | 103 | if acl_ref[0].decode('iso-8859-1') != user: |
67e2ec02 | 104 | try: |
f797b0fd | 105 | self.mail_con.deleteacl("INBOX", acl_ref[0]) |
38f15e57 | 106 | logging.debug("Reset acls on INBOX for user %s", acl_ref[0].decode('iso-8859-1')) |
f797b0fd | 107 | except self.mail_con.error as ex: |
38f15e57 | 108 | logging.warning("Could not reset acls on INBOX for user %s: %s", acl_ref[0], ex) |
0cf4dc33 | 109 | |
67e2ec02 PD |
110 | return |
111 | ||
ed8dc19f | 112 | def add_acls(self, mailbox, mb_acls, original_user, target_user): |
67e2ec02 | 113 | """Add acls to mailbox.""" |
b2bbd1f5 | 114 | |
79666349 | 115 | mailbox = mailbox.replace("user/" + original_user + "/", "INBOX/") |
ed8dc19f | 116 | for acl_user in mb_acls: |
12d5b37a PD |
117 | # (in case target user != original user): |
118 | # - don't overwrite acls eventually set for the current targetuser | |
119 | # - don't set the default owner acls for the new folder | |
b0169e56 PD |
120 | if acl_user != target_user and acl_user != original_user: |
121 | try: | |
ed8dc19f PD |
122 | self.mail_con.setacl(mailbox, acl_user, mb_acls[acl_user]) |
123 | logging.debug("Set acls %s for user %s on mailbox %s", mb_acls[acl_user], acl_user, mailbox) | |
f797b0fd | 124 | except self.mail_con.error as ex: |
ed8dc19f | 125 | logging.warning("Could not set acls %s for user %s on mailbox %s: %s", mb_acls[acl_user], acl_user, mailbox, ex) |
b2bbd1f5 | 126 | |
67e2ec02 PD |
127 | return |
128 | ||
129 | def delete_mailboxes(self, deleted_mailbox): | |
130 | """Delete specified mailbox or empty inbox.""" | |
0cf4dc33 | 131 | |
67e2ec02 PD |
132 | for mailbox in self.mailboxes: |
133 | pattern = '^\"?' + deleted_mailbox | |
134 | # if INBOX it cannot be deleted so add delimiter | |
ed8dc19f | 135 | if deleted_mailbox == "INBOX": |
67e2ec02 | 136 | pattern += mailbox[1] |
e42bd6a5 | 137 | if re.compile(pattern).match(mailbox[2]): |
67e2ec02 | 138 | result, data = self.mail_con.delete(mailbox[2]) |
bb6cdeea PD |
139 | logging.debug("Deleted mailbox %s", mailbox[2]) |
140 | if result != "OK": | |
0cf4dc33 PD |
141 | logging.warning("Could not delete mailbox %s: %s", mailbox[2], data[0]) |
142 | ||
67e2ec02 PD |
143 | return |
144 | ||
145 | def create_mailbox(self, mailbox): | |
146 | """Create new mailbox to inject messages.""" | |
ed8dc19f | 147 | |
e42bd6a5 | 148 | if mailbox != "INBOX": |
67e2ec02 | 149 | result, data = self.mail_con.create(mailbox) |
bb6cdeea PD |
150 | logging.debug("Creating mailbox %s", mailbox) |
151 | if result != "OK": | |
152 | logging.warning("Could not create mailbox %s: %s", mailbox, data[0].decode('iso-8859-1')) | |
0cf4dc33 | 153 | |
67e2ec02 PD |
154 | return |
155 | ||
156 | def inject_message(self, message, mailbox, internal_date): | |
157 | """Inject a message into a mailbox.""" | |
ed8dc19f | 158 | |
7aad8dab | 159 | result, data = self.mail_con.append(mailbox, "\\Seen", internal_date, message.encode()) |
bb6cdeea PD |
160 | logging.debug("Appending message to mailbox %s", mailbox) |
161 | if result != "OK": | |
162 | logging.warning("Could not append an e-mail to the mailbox %s: %s.", mailbox, data[0].decode('iso-8859-1')) | |
163 | #logging.debug("Email content:\n%s", message) | |
0cf4dc33 | 164 | |
67e2ec02 | 165 | return |