Save password passing and normal local connection enabled
[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 26
f72ba42a
PD
27SERVER = "localhost"
28
67e2ec02
PD
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
f72ba42a 40 def __init__(self, username, password):
67e2ec02 41 """Creates a connection and a user session."""
9ce1038d 42
b2bbd1f5 43 # connect to server
67e2ec02 44 try:
f72ba42a
PD
45 self.mail_con = imaplib.IMAP4(SERVER)
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
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
PD
102 for acl_ref in inbox_acls:
103 if acl_ref[0] != 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
e42bd6a5 112 def add_acls(self, mailbox, mailbox_list, original_user, target_user):
67e2ec02 113 """Add acls to mailbox."""
b2bbd1f5 114
e42bd6a5
PD
115 # change encoding to internal cyrus format and make folder absolute
116 mailbox = mailbox.replace("INBOX/", "user/" + original_user + "/")
67e2ec02
PD
117 mailbox = mailbox.replace(".", "^")
118 mailbox = mailbox.replace("/", ".")
b2bbd1f5 119
b0169e56
PD
120 # find folder to set all acls
121 try:
122 mbox_acls = mailbox_list[mailbox]
123 except KeyError:
124 # no rights for the mailbox were found
12d5b37a 125 logging.warning("Could not find the acls for mailbox %s for user %s.", mailbox, original_user)
b0169e56
PD
126 return
127 for acl_user in mbox_acls:
12d5b37a
PD
128 # (in case target user != original user):
129 # - don't overwrite acls eventually set for the current targetuser
130 # - don't set the default owner acls for the new folder
b0169e56
PD
131 if acl_user != target_user and acl_user != original_user:
132 try:
b2bbd1f5 133 self.mail_con.setacl(mailbox, acl_user, mbox_acls[acl_user])
38f15e57 134 logging.debug("Set acls %s for user %s on mailbox %s", mbox_acls[acl_user], acl_user, mailbox)
f797b0fd 135 except self.mail_con.error as ex:
38f15e57 136 logging.warning("Could not set acls %s for user %s on mailbox %s: %s", mbox_acls[acl_user], acl_user, mailbox, ex)
b2bbd1f5 137
67e2ec02
PD
138 return
139
140 def delete_mailboxes(self, deleted_mailbox):
141 """Delete specified mailbox or empty inbox."""
0cf4dc33 142
67e2ec02
PD
143 for mailbox in self.mailboxes:
144 pattern = '^\"?' + deleted_mailbox
145 # if INBOX it cannot be deleted so add delimiter
146 if (deleted_mailbox == "INBOX"):
147 pattern += mailbox[1]
e42bd6a5 148 if re.compile(pattern).match(mailbox[2]):
67e2ec02 149 result, data = self.mail_con.delete(mailbox[2])
e42bd6a5 150 if result == "OK":
38f15e57 151 logging.debug("Deleted mailbox %s", mailbox[2])
67e2ec02 152 else:
0cf4dc33
PD
153 logging.warning("Could not delete mailbox %s: %s", mailbox[2], data[0])
154
67e2ec02
PD
155 return
156
157 def create_mailbox(self, mailbox):
0cf4dc33 158
67e2ec02 159 """Create new mailbox to inject messages."""
e42bd6a5 160 if mailbox != "INBOX":
67e2ec02 161 result, data = self.mail_con.create(mailbox)
e42bd6a5 162 if result == "OK":
38f15e57 163 logging.debug("Creating mailbox %s", mailbox)
67e2ec02 164 else:
38f15e57 165 logging.warning("Could not create mailbox %s: %s", mailbox, data[0])
0cf4dc33 166
67e2ec02
PD
167 return
168
169 def inject_message(self, message, mailbox, internal_date):
0cf4dc33 170
67e2ec02 171 """Inject a message into a mailbox."""
7aad8dab 172 result, data = self.mail_con.append(mailbox, "\\Seen", internal_date, message.encode())
b2bbd1f5 173 if result == "OK":
38f15e57 174 logging.debug("Appending message to mailbox %s", mailbox)
b2bbd1f5 175 else:
38f15e57 176 logging.warning("Could not append the e-mail %s: %s", message, data[0])
0cf4dc33 177
67e2ec02 178 return