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