Clear inbox acls and other fixes based on autotest validation
[imap-restore-mail] / src / mail_iterator.py
CommitLineData
67e2ec02 1'''
23ebcaa4 2The module contains the MailIterator class.
67e2ec02
PD
3
4Copyright (c) 2012 Intra2net AG
20760d94
PD
5Author: Plamen Dimitrov and Thomas Jarosch
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.
67e2ec02
PD
16'''
17
f797b0fd 18import sys
4a1a0b03 19import socket, imaplib
67e2ec02 20import re
f797b0fd 21import logging
67e2ec02
PD
22
23MAILBOX_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')
24UIDVAL_RESP = re.compile(r'(?P<name>.*) \(UIDVALIDITY (?P<uidval>.*)\)')
bb6cdeea
PD
25ACLS_RESP = re.compile(b'(?P<user>\S+) (?P<acls>\S+)')
26
27#imaplib.Debug = 4
67e2ec02
PD
28
29class 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