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