2 restore-mail-inject.py - Tool to inject mails via IMAP
4 Copyright (c) 2012 Intra2net AG
5 Author: Plamen Dimitrov and Thomas Jarosch
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.
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.
22 MAIL_FILENAME = re.compile("^[0-9]+\.$")
23 MBOXFILE_LINE = re.compile("^(.*?)\t(?:\d )?default[\t ](.*)$")
24 ACL_STRING = re.compile("^(.*?)[\t ](.*?)\t(.*)$")
27 """This class iterates through the e-mail files."""
30 # mailboxes created during file traversal
31 created_mailboxes = None
32 # mailboxes to update during file traversal
34 # acls retrieved from a file
38 """Creates a connection and a user session."""
40 self.created_mailboxes = []
41 self.acl_mailboxes = []
45 def _message_read(cls, filename):
46 """Retrieves a message from the message file."""
49 with open(filename, "r") as msgfile:
50 message = msgfile.read()
52 logging.warning("Could not open the e-mail file %s", filename)
57 def load_mailbox_list(self, mboxlistfile, original_user):
58 """Load the list of mailboxes and acl rights for each from file."""
61 with open(mboxlistfile, 'r') as acl_file:
64 # read a line using regex
67 linedata = MBOXFILE_LINE.match(line).groups()
68 except AttributeError:
69 logging.warning("Illegal line in mailbox list dump: %s", line)
73 # changes acls key encoding to internal cyrus format and makes it absolute (as folder).
74 key = linedata[0].replace("INBOX/", "user/" + original_user + "/")
75 key = key.replace(".", "^")
76 key = key.replace("/", ".")
78 # loop through acl rights string and build dictionary of users and rights
81 acldata = ACL_STRING.match(aclstr).groups()
82 except AttributeError:
83 logging.warning("Illegal acl string in mailbox list dump: %s", line)
87 acls[acldata[0]] = acldata[1]
89 self.acl_mailboxes[key] = acls
91 logging.warning("Could not open mboxlist file %s", mboxlistfile)
93 def load_mails(self, filepath, mailpath):
94 """Loads all e-mails from file hierarchy.
95 This recursive generator always returns a tuple of
96 the next found (e-mail, mailbox to store, internaldate)."""
98 logging.debug("Entered directory %s -> %s", filepath, mailpath)
100 filepath = os.path.abspath(filepath)
103 logging.warning("Can't open the directory %s", filepath)
105 # mark mailboxes that should be created
106 self.created_mailboxes.append(mailpath)
108 subpaths = os.listdir(filepath)
109 for subpath in subpaths:
110 if subpath == "." or subpath == "..":
112 new_filepath = filepath + "/" + subpath
114 # if path is file validate name and inject
115 if (os.path.isfile(new_filepath)):
116 if os.path.getsize(new_filepath) == 0:
117 logging.info("Skipping empty file %s", subpath)
119 if MAIL_FILENAME.match(subpath):
120 logging.info("Injecting file %s", subpath)
122 message = self._message_read(new_filepath)
123 # suggest file modification date for internaldate
124 yield (message, mailpath, os.path.getmtime(new_filepath))
126 logging.warning("Could not retrieve mail from the file %s", new_filepath)
129 # if path is directory do recursive call
130 if os.path.isdir(new_filepath):
131 # cyrus ^ vs . storage replacement
132 subpath = subpath.replace("^", ".")
133 new_mailpath = mailpath + "/" + subpath
134 logging.debug("Inserting mails from directory %s into mailbox %s", new_filepath, new_mailpath)
135 # load_mails($mboxdbref, $origuser, $targetuser)
136 rcrs_generator = self.load_mails(new_filepath, new_mailpath)
137 # you enter the generator in the for loop
138 for rcr in rcrs_generator:
140 logging.debug("Done with directory %s and mailbox %s", new_filepath, new_mailpath)
142 # mark mailboxes that need acl update
143 self.acl_mailboxes.append(mailpath)