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