Modules now in 'src' folder, COPYING and README added
[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
117 return
118 for acl_user in mbox_acls:
b0169e56
PD
119 if acl_user != target_user and acl_user != original_user:
120 try:
b2bbd1f5 121 self.mail_con.setacl(mailbox, acl_user, mbox_acls[acl_user])
38f15e57 122 logging.debug("Set acls %s for user %s on mailbox %s", mbox_acls[acl_user], acl_user, mailbox)
f797b0fd 123 except self.mail_con.error as ex:
38f15e57 124 logging.warning("Could not set acls %s for user %s on mailbox %s: %s", mbox_acls[acl_user], acl_user, mailbox, ex)
b2bbd1f5 125
67e2ec02
PD
126 return
127
128 def delete_mailboxes(self, deleted_mailbox):
129 """Delete specified mailbox or empty inbox."""
0cf4dc33 130
67e2ec02
PD
131 for mailbox in self.mailboxes:
132 pattern = '^\"?' + deleted_mailbox
133 # if INBOX it cannot be deleted so add delimiter
134 if (deleted_mailbox == "INBOX"):
135 pattern += mailbox[1]
e42bd6a5 136 if re.compile(pattern).match(mailbox[2]):
67e2ec02 137 result, data = self.mail_con.delete(mailbox[2])
e42bd6a5 138 if result == "OK":
38f15e57 139 logging.debug("Deleted mailbox %s", mailbox[2])
67e2ec02 140 else:
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):
0cf4dc33 146
67e2ec02 147 """Create new mailbox to inject messages."""
e42bd6a5 148 if mailbox != "INBOX":
67e2ec02 149 result, data = self.mail_con.create(mailbox)
e42bd6a5 150 if result == "OK":
38f15e57 151 logging.debug("Creating mailbox %s", mailbox)
67e2ec02 152 else:
38f15e57 153 logging.warning("Could not create mailbox %s: %s", mailbox, data[0])
0cf4dc33 154
67e2ec02
PD
155 return
156
157 def inject_message(self, message, mailbox, internal_date):
0cf4dc33 158
67e2ec02 159 """Inject a message into a mailbox."""
7aad8dab 160 result, data = self.mail_con.append(mailbox, "\\Seen", internal_date, message.encode())
b2bbd1f5 161 if result == "OK":
38f15e57 162 logging.debug("Appending message to mailbox %s", mailbox)
b2bbd1f5 163 else:
38f15e57 164 logging.warning("Could not append the e-mail %s: %s", message, data[0])
0cf4dc33 165
67e2ec02 166 return