File and console logging added for exception handling and information
[imap-restore-mail] / mail_iterator.py
1 '''
2 restore-mail-inject.py - Tool to inject mails via IMAP
3
4 Copyright (c) 2012 Intra2net AG
5 '''
6
7 import sys
8 import socket, imaplib
9 import re
10 import logging
11
12 MAILBOX_RESP = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')
13 UIDVAL_RESP = re.compile(r'(?P<name>.*) \(UIDVALIDITY (?P<uidval>.*)\)')
14 ACLS_RESP = re.compile(b'(?P<user>.*) (?P<acls>.*)')
15
16 class MailIterator:
17     """This class communicates with the e-mail server."""
18
19     # class attributes
20     # IMAP4_SSL for connection with an IMAP server
21     mail_con = None
22     # list of tuples (uidvalidity, mailboxname) for the retrieved mailboxes
23     mailboxes = None
24     # logged in status
25     logged_in = None
26
27     def __init__(self, username):
28         """Creates a connection and a user session."""
29
30         # connect to server
31         try:
32             self.mail_con = imaplib.IMAP4("intranator.m.i2n")
33             # MODIFIED
34             imap_socket = socket.socket(socket.AF_UNIX)
35             imap_socket.connect("/var/imap/socket/imap")
36             self.mail_con.sock = imap_socket
37             self.mail_con.file = self.mail_con.sock.makefile('rb')
38             logging.info("Connected to mail server.")
39         except (self.mail_con.error, socket.error) as ex:
40             logging.error("Could not connect to host: %s" % (ex))
41             sys.exit()
42
43         # log in
44         try:
45             self.mail_con.login("cyrus", "geheim")
46             self.logged_in = True
47             #self.mail_con.proxyauth(username)
48             logging.info("Logged in as %s." % username)
49         except self.mail_con.error as ex:
50             self.logged_in = False
51             logging.error("Could not log in as user %s: %s" % (username, ex))
52             sys.exit()
53
54         # list mailboxes
55         try:
56             _result, mailboxes = self.mail_con.list()
57         except self.mail_con.error as ex:
58             logging.warning("Could not retrieve mailboxes for user %s: %s" % (username, ex))
59         self.mailboxes = []
60         for mailbox in mailboxes:
61             mailbox = MAILBOX_RESP.match(mailbox.decode('iso-8859-1')).groups()
62             self.mailboxes.append(mailbox)
63         self.mailboxes = sorted(self.mailboxes, key=lambda box: box[2], reverse=True)
64
65         return
66
67     def __del__(self):
68         """Closes the connection and the user session."""
69         #if self.logged_in:
70         #    self.mail_con.close()
71         #    self.mail_con.logout()
72
73     def clear_inbox_acls(self, user):
74         """Resets the inbox acls for a given user."""
75         try:
76             _result, inbox_acls = self.mail_con.getacl("INBOX")
77         except self.mail_con.error as ex:
78             logging.warning("Could not get the acls of INBOX: %s" % ex)
79             return
80         inbox_acls = ACLS_RESP.findall(inbox_acls[0][6:])
81         logging.debug("Retrieved acls from INBOX are %s" % inbox_acls)
82         for acl_ref in inbox_acls:
83             if acl_ref[0] != user:
84                 try:
85                     self.mail_con.deleteacl("INBOX", acl_ref[0])
86                     logging.debug("Reset acls on INBOX for user %s" % acl_ref[0].decode('iso-8859-1'))
87                 except self.mail_con.error as ex:
88                     logging.warning("Could not reset acls on INBOX for user %s: %s" % (acl_ref[0], ex))
89         return
90
91     def add_acls(self, mailbox, mailbox_list, original_user, target_user):
92         """Add acls to mailbox."""
93
94         # change encoding to internal cyrus format and make folder absolute
95         mailbox = mailbox.replace("INBOX/", "user/" + original_user + "/")
96         mailbox = mailbox.replace(".", "^")
97         mailbox = mailbox.replace("/", ".")
98
99         # find folder to set all acls
100         try:
101             mbox_acls = mailbox_list[mailbox]
102         except KeyError:
103             # no rights for the mailbox were found
104             return
105         for acl_user in mbox_acls:
106             if acl_user != target_user and acl_user != original_user:
107                 try:
108                     self.mail_con.setacl(mailbox, acl_user, mbox_acls[acl_user])
109                     logging.debug("Set acls %s for user %s on mailbox %s" % (mbox_acls[acl_user], acl_user, mailbox))
110                 except self.mail_con.error as ex:
111                     logging.warning("Could not set acls %s for user %s on mailbox %s: %s" % (mbox_acls[acl_user], acl_user, mailbox, ex))    
112
113         return
114
115     def delete_mailboxes(self, deleted_mailbox):
116         """Delete specified mailbox or empty inbox."""
117         for mailbox in self.mailboxes:
118             pattern = '^\"?' + deleted_mailbox
119             # if INBOX it cannot be deleted so add delimiter
120             if (deleted_mailbox == "INBOX"):
121                 pattern += mailbox[1]
122             if re.compile(pattern).match(mailbox[2]):
123                 result, data = self.mail_con.delete(mailbox[2])
124                 if result == "OK":
125                     logging.debug("Deleted mailbox %s" % mailbox[2])
126                 else:
127                     logging.warning("Could not delete mailbox %s: %s" % (mailbox[2], data[0]))         
128         return
129
130     def create_mailbox(self, mailbox):
131         """Create new mailbox to inject messages."""
132         if mailbox != "INBOX":
133             result, data = self.mail_con.create(mailbox)
134             if result == "OK":
135                 logging.debug("Creating mailbox %s" % mailbox)
136             else:
137                 logging.warning("Could not create mailbox %s: %s" % (mailbox, data[0]))
138         return
139
140     def inject_message(self, message, mailbox, internal_date):
141         """Inject a message into a mailbox."""
142         result, data = self.mail_con.append(mailbox, "\\Seen", internal_date, message.encode())
143         if result == "OK":
144             logging.debug("Appending message to mailbox %s" % mailbox)
145         else:
146             logging.warning("Could not append the e-mail %s: %s" % (message, data[0]))
147         return