Commit | Line | Data |
---|---|---|
67e2ec02 PD |
1 | ''' |
2 | restore-mail-inject.py - Tool to inject mails via IMAP | |
3 | ||
4 | Copyright (c) 2012 Intra2net AG | |
20760d94 PD |
5 | Author: Plamen Dimitrov and Thomas Jarosch |
6 | ||
7 | This program is free software: you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation, either version 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
67e2ec02 PD |
16 | ''' |
17 | ||
f797b0fd | 18 | import sys |
4a1a0b03 | 19 | import socket, imaplib |
67e2ec02 | 20 | import re |
f797b0fd | 21 | import logging |
67e2ec02 PD |
22 | |
23 | MAILBOX_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)') | |
24 | UIDVAL_RESP = re.compile(r'(?P<name>.*) \(UIDVALIDITY (?P<uidval>.*)\)') | |
4a1a0b03 | 25 | ACLS_RESP = re.compile(b'(?P<user>.*) (?P<acls>.*)') |
67e2ec02 PD |
26 | |
27 | class 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 |