74a9f81ebce43f08453e6412c257c9d12d806c49
[imap-restore-mail] / src / mail_iterator.py
1 '''
2 restore-mail-inject.py - Tool to inject mails via IMAP
3
4 Copyright (c) 2012 Intra2net AG
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.
16 '''
17
18 import sys
19 import socket, imaplib
20 import re
21 import logging
22
23 MAILBOX_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')
24 UIDVAL_RESP = re.compile(r'(?P<name>.*) \(UIDVALIDITY (?P<uidval>.*)\)')
25 ACLS_RESP = re.compile(b'(?P<user>.*) (?P<acls>.*)')
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
38     def __init__(self, username, server = "intranator.m.i2n"):
39         """Creates a connection and a user session."""
40
41         # connect to server
42         try:
43             self.mail_con = imaplib.IMAP4(server)
44             # MODIFIED
45             imap_socket = socket.socket(socket.AF_UNIX)
46             imap_socket.connect("/var/imap/socket/imap")
47             self.mail_con.sock = imap_socket
48             self.mail_con.file = self.mail_con.sock.makefile('rb')
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)
52             sys.exit()
53
54         # log in
55         try:
56             self.mail_con.login("cyrus", "geheim")
57             self.logged_in = True
58             #self.mail_con.proxyauth(username)
59             logging.info("Logged in as %s.", username)
60         except self.mail_con.error as ex:
61             self.logged_in = False
62             logging.error("Could not log in as user %s: %s", username, ex)
63             sys.exit()
64
65         # list mailboxes
66         try:
67             _result, mailboxes = self.mail_con.list()
68         except self.mail_con.error as ex:
69             logging.warning("Could not retrieve mailboxes for user %s: %s", username, ex)
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)
75
76         return
77
78     def __del__(self):
79         """Closes the connection and the user session."""
80
81         if self.logged_in:
82             self.mail_con.logout()
83
84     def clear_inbox_acls(self, user):
85         """Resets the inbox acls for a given user."""
86
87         try:
88             _result, inbox_acls = self.mail_con.getacl("INBOX")
89         except self.mail_con.error as ex:
90             logging.warning("Could not get the acls of INBOX: %s", ex)
91             return
92         inbox_acls = ACLS_RESP.findall(inbox_acls[0][6:])
93         logging.debug("Retrieved acls from INBOX are %s", inbox_acls)
94         for acl_ref in inbox_acls:
95             if acl_ref[0] != user:
96                 try:
97                     self.mail_con.deleteacl("INBOX", acl_ref[0])
98                     logging.debug("Reset acls on INBOX for user %s", acl_ref[0].decode('iso-8859-1'))
99                 except self.mail_con.error as ex:
100                     logging.warning("Could not reset acls on INBOX for user %s: %s", acl_ref[0], ex)
101
102         return
103
104     def add_acls(self, mailbox, mailbox_list, original_user, target_user):
105         """Add acls to mailbox."""
106
107         # change encoding to internal cyrus format and make folder absolute
108         mailbox = mailbox.replace("INBOX/", "user/" + original_user + "/")
109         mailbox = mailbox.replace(".", "^")
110         mailbox = mailbox.replace("/", ".")
111
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:
119             if acl_user != target_user and acl_user != original_user:
120                 try:
121                     self.mail_con.setacl(mailbox, acl_user, mbox_acls[acl_user])
122                     logging.debug("Set acls %s for user %s on mailbox %s", mbox_acls[acl_user], acl_user, mailbox)
123                 except self.mail_con.error as ex:
124                     logging.warning("Could not set acls %s for user %s on mailbox %s: %s", mbox_acls[acl_user], acl_user, mailbox, ex)    
125
126         return
127
128     def delete_mailboxes(self, deleted_mailbox):
129         """Delete specified mailbox or empty inbox."""
130
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]
136             if re.compile(pattern).match(mailbox[2]):
137                 result, data = self.mail_con.delete(mailbox[2])
138                 if result == "OK":
139                     logging.debug("Deleted mailbox %s", mailbox[2])
140                 else:
141                     logging.warning("Could not delete mailbox %s: %s", mailbox[2], data[0]) 
142         
143         return
144
145     def create_mailbox(self, mailbox):
146
147         """Create new mailbox to inject messages."""
148         if mailbox != "INBOX":
149             result, data = self.mail_con.create(mailbox)
150             if result == "OK":
151                 logging.debug("Creating mailbox %s", mailbox)
152             else:
153                 logging.warning("Could not create mailbox %s: %s", mailbox, data[0])
154
155         return
156
157     def inject_message(self, message, mailbox, internal_date):
158
159         """Inject a message into a mailbox."""
160         result, data = self.mail_con.append(mailbox, "\\Seen", internal_date, message.encode())
161         if result == "OK":
162             logging.debug("Appending message to mailbox %s", mailbox)
163         else:
164             logging.warning("Could not append the e-mail %s: %s", message, data[0])
165
166         return