38a59dc3710c8d45de98a4886198520ecf0dbf26
[imap-restore-mail] / file_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 os
8 import re
9 import logging
10
11 MAIL_FILENAME = re.compile("^[0-9]+\.$")
12 MBOXFILE_LINE = re.compile("^(.*?)\t(?:\d )?default[\t ](.*)$")
13 ACL_STRING = re.compile("^(.*?)[\t ](.*?)\t(.*)$")
14
15 class FileIterator:
16     """This class iterates through the e-mail files."""
17
18     # class attributes
19     # mailboxes created during file traversal
20     created_mailboxes = None
21     # mailboxes to update during file traversal
22     acl_mailboxes = None
23
24     def __init__(self):
25         """Creates a connection and a user session."""
26
27         self.created_mailboxes = []
28         self.acl_mailboxes = []
29
30         return
31
32     @classmethod
33     def _message_read(cls, filename):
34         """Retrieves a message from the message file."""
35
36         try:
37             with open(filename, "r") as msgfile:
38                 message = msgfile.read()
39         except IOError:
40             logging.warning("Could not open the e-mail file %s", filename)
41             raise
42
43         return message
44
45     @classmethod
46     def load_mailbox_list(cls, mboxlistfile = ""):
47         """Load the list of mailboxes and acl rights for each from file."""
48
49         mboxdb = {}
50         if mboxlistfile != "":
51             try:
52                 with open(mboxlistfile, 'r') as acl_file:
53                     for line in acl_file:
54                         acls = {}
55                         try:
56                             linedata = MBOXFILE_LINE.match(line).groups()
57                         except AttributeError:
58                             logging.warning("Illegal line in mailbox list dump: %s", line)
59                             continue
60                         key = linedata[0]
61                         aclstr = linedata[1]
62
63                         # loop through acl rights string and build dictionary of users and rights
64                         while(aclstr != ""):
65                             try:
66                                 acldata = ACL_STRING.match(aclstr).groups()
67                             except AttributeError:
68                                 logging.warning("Illegal acl string in mailbox list dump: %s", line)
69                                 aclstr = ""
70                                 continue
71                             aclstr = acldata[2]
72                             acls[acldata[0]] = acldata[1]
73
74                         mboxdb[key] = acls
75             except IOError:
76                 logging.warning("Could not open mboxlist file %s", mboxlistfile)
77
78         return mboxdb
79
80     def load_mails(self, filepath, mailpath):
81         """Loads all e-mails from file hierarchy.
82         This recursive generator always returns a tuple of
83         the next found (e-mail, mailbox to store, internaldate)."""
84
85         logging.debug("Entered directory %s -> %s", filepath, mailpath)
86         try:
87             filepath = os.path.abspath(filepath)
88             os.chdir(filepath)
89         except OSError:
90             logging.warning("Can't open the directory %s", filepath)
91             return
92         # mark mailboxes that should be created
93         self.created_mailboxes.append(mailpath)
94         subpaths = os.listdir(filepath)
95         for subpath in subpaths:
96             if subpath == "." or subpath == "..":
97                 continue
98             new_filepath = filepath + "/" + subpath
99             if (os.path.isfile(new_filepath)):
100                 if os.path.getsize(new_filepath) == 0:
101                     logging.info("Skipping empty file %s", subpath)
102                 else:
103                     if MAIL_FILENAME.match(subpath):
104                         logging.info("Injecting file %s", subpath)
105                         try:
106                             message = self._message_read(new_filepath)
107                             # suggest file modification date for internaldate
108                             yield (message, mailpath, os.path.getmtime(new_filepath))
109                         except IOError:
110                             logging.warning("Could not retrieve mail from the file %s", new_filepath)
111             else:
112                 if os.path.isdir(new_filepath):
113                     # cyrus ^ vs . storage replacement
114                     subpath = subpath.replace("^", ".")
115                     new_mailpath = mailpath + "/" + subpath
116                     logging.debug("Inserting mails from directory %s into mailbox %s", new_filepath, new_mailpath)
117                     # load_mails($mboxdbref, $origuser, $targetuser)
118                     rcrs_generator = self.load_mails(new_filepath, new_mailpath)
119                     # you enter the generator in the for loop
120                     for rcr in rcrs_generator:
121                         yield rcr
122                     logging.debug("Done with directory %s and mailbox %s", new_filepath, new_mailpath)
123         # mark mailboxes that need acl update
124         self.acl_mailboxes.append(mailpath)
125
126         return