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