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