Refactoring of cyrus stored acls formatting - now performed by FileIterator where...
[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, server, username, password):
39         """Creates a connection and a user session."""
40
41         # connect to server
42         try:
43             self.mail_con = imaplib.IMAP4(server)
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
51             logging.info("Connected to %s", server)
52         except (socket.error, self.mail_con.error) as ex:
53             logging.error("Could not connect to host: %s", ex)
54             sys.exit()
55
56         # log in
57         try:
58
59             # proxy authentication
60             #self.mail_con.login("cyrus", "geheim")
61             #self.mail_con.proxyauth(username)
62
63             self.mail_con.login(username, password)
64             self.logged_in = True
65             logging.info("Logged in as %s.", username)
66         except self.mail_con.error as ex:
67             self.logged_in = False
68             logging.error("Could not log in as user %s: %s", username, ex)
69             sys.exit()
70
71         # list mailboxes
72         try:
73             _result, mailboxes = self.mail_con.list()
74         except self.mail_con.error as ex:
75             logging.warning("Could not retrieve mailboxes for user %s: %s", username, ex)
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)
81
82         return
83
84     def __del__(self):
85         """Closes the connection and the user session."""
86
87         if self.logged_in:
88             self.mail_con.logout()
89
90     def clear_inbox_acls(self, user):
91         """Resets the inbox acls for a given user."""
92
93         try:
94             _result, inbox_acls = self.mail_con.getacl("INBOX")
95         except self.mail_con.error as ex:
96             logging.warning("Could not get the acls of INBOX: %s", ex)
97             return
98         inbox_acls = ACLS_RESP.findall(inbox_acls[0][6:])
99         logging.debug("Retrieved acls from INBOX are %s", inbox_acls)
100         for acl_ref in inbox_acls:
101             if acl_ref[0] != user:
102                 try:
103                     self.mail_con.deleteacl("INBOX", acl_ref[0])
104                     logging.debug("Reset acls on INBOX for user %s", acl_ref[0].decode('iso-8859-1'))
105                 except self.mail_con.error as ex:
106                     logging.warning("Could not reset acls on INBOX for user %s: %s", acl_ref[0], ex)
107
108         return
109
110     def add_acls(self, mailbox, mb_acls, original_user, target_user):
111         """Add acls to mailbox."""
112
113         for acl_user in mb_acls:
114             # (in case target user != original user):
115             # - don't overwrite acls eventually set for the current targetuser
116             # - don't set the default owner acls for the new folder
117             if acl_user != target_user and acl_user != original_user:
118                 try:
119                     self.mail_con.setacl(mailbox, acl_user, mb_acls[acl_user])
120                     logging.debug("Set acls %s for user %s on mailbox %s", mb_acls[acl_user], acl_user, mailbox)
121                 except self.mail_con.error as ex:
122                     logging.warning("Could not set acls %s for user %s on mailbox %s: %s", mb_acls[acl_user], acl_user, mailbox, ex)    
123
124         return
125
126     def delete_mailboxes(self, deleted_mailbox):
127         """Delete specified mailbox or empty inbox."""
128
129         for mailbox in self.mailboxes:
130             pattern = '^\"?' + deleted_mailbox
131             # if INBOX it cannot be deleted so add delimiter
132             if deleted_mailbox == "INBOX":
133                 pattern += mailbox[1]
134             if re.compile(pattern).match(mailbox[2]):
135                 result, data = self.mail_con.delete(mailbox[2])
136                 if result == "OK":
137                     logging.debug("Deleted mailbox %s", mailbox[2])
138                 else:
139                     logging.warning("Could not delete mailbox %s: %s", mailbox[2], data[0]) 
140         
141         return
142
143     def create_mailbox(self, mailbox):
144         """Create new mailbox to inject messages."""
145
146         if mailbox != "INBOX":
147             result, data = self.mail_con.create(mailbox)
148             if result == "OK":
149                 logging.debug("Creating mailbox %s", mailbox)
150             else:
151                 logging.warning("Could not create mailbox %s: %s", mailbox, data[0])
152
153         return
154
155     def inject_message(self, message, mailbox, internal_date):
156         """Inject a message into a mailbox."""
157
158         result, data = self.mail_con.append(mailbox, "\\Seen", internal_date, message.encode())
159         if result == "OK":
160             logging.debug("Appending message to mailbox %s", mailbox)
161         else:
162             logging.warning("Could not append the e-mail %s: %s", message, data[0])
163
164         return