Commit | Line | Data |
---|---|---|
67e2ec02 PD |
1 | ''' |
2 | restore-mail-inject.py - Tool to inject mails via IMAP | |
3 | ||
4 | Copyright (c) 2012 Intra2net AG | |
5 | ''' | |
6 | ||
5737b10e | 7 | import os |
67e2ec02 | 8 | import re |
f797b0fd | 9 | import logging |
67e2ec02 PD |
10 | |
11 | MAIL_FILENAME = re.compile("^[0-9]+\.$") | |
e42bd6a5 PD |
12 | MBOXFILE_LINE = re.compile("^(.*?)\t(?:\d )?default[\t ](.*)$") |
13 | ACL_STRING = re.compile("^(.*?)[\t ](.*?)\t(.*)$") | |
67e2ec02 PD |
14 | |
15 | class FileIterator: | |
16 | """This class iterates through the e-mail files.""" | |
17 | ||
18 | # class attributes | |
67e2ec02 PD |
19 | # mailboxes created during file traversal |
20 | created_mailboxes = None | |
21 | # mailboxes to update during file traversal | |
22 | acl_mailboxes = None | |
23 | ||
e42bd6a5 | 24 | def __init__(self): |
67e2ec02 | 25 | """Creates a connection and a user session.""" |
0cf4dc33 | 26 | |
e42bd6a5 PD |
27 | self.created_mailboxes = [] |
28 | self.acl_mailboxes = [] | |
67e2ec02 | 29 | |
67e2ec02 PD |
30 | return |
31 | ||
38f15e57 PD |
32 | @classmethod |
33 | def _message_read(cls, filename): | |
67e2ec02 | 34 | """Retrieves a message from the message file.""" |
0cf4dc33 | 35 | |
67e2ec02 PD |
36 | try: |
37 | with open(filename, "r") as msgfile: | |
38 | message = msgfile.read() | |
f797b0fd PD |
39 | except IOError: |
40 | logging.warning("Could not open the e-mail file %s", filename) | |
41 | raise | |
0cf4dc33 | 42 | |
67e2ec02 PD |
43 | return message |
44 | ||
38f15e57 PD |
45 | @classmethod |
46 | def load_mailbox_list(cls, mboxlistfile = ""): | |
0cf4dc33 PD |
47 | """Load the list of mailboxes and acl rights for each from file.""" |
48 | ||
b0169e56 | 49 | mboxdb = {} |
e42bd6a5 PD |
50 | if mboxlistfile != "": |
51 | try: | |
52 | with open(mboxlistfile, 'r') as acl_file: | |
53 | for line in acl_file: | |
b0169e56 | 54 | acls = {} |
79c28101 PD |
55 | try: |
56 | linedata = MBOXFILE_LINE.match(line).groups() | |
57 | except AttributeError: | |
38f15e57 | 58 | logging.warning("Illegal line in mailbox list dump: %s", line) |
f797b0fd | 59 | continue |
b0169e56 | 60 | key = linedata[0] |
e42bd6a5 | 61 | aclstr = linedata[1] |
79c28101 | 62 | |
b0169e56 | 63 | # loop through acl rights string and build dictionary of users and rights |
e42bd6a5 | 64 | while(aclstr != ""): |
79c28101 PD |
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 = "" | |
f797b0fd | 70 | continue |
e42bd6a5 | 71 | aclstr = acldata[2] |
38f15e57 | 72 | acls[acldata[0]] = acldata[1] |
b0169e56 PD |
73 | |
74 | mboxdb[key] = acls | |
e42bd6a5 | 75 | except IOError: |
38f15e57 | 76 | logging.warning("Could not open mboxlist file %s", mboxlistfile) |
0cf4dc33 | 77 | |
e42bd6a5 PD |
78 | return mboxdb |
79 | ||
67e2ec02 PD |
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).""" | |
0cf4dc33 | 84 | |
38f15e57 | 85 | logging.debug("Entered directory %s -> %s", filepath, mailpath) |
67e2ec02 PD |
86 | try: |
87 | filepath = os.path.abspath(filepath) | |
88 | os.chdir(filepath) | |
89 | except OSError: | |
38f15e57 | 90 | logging.warning("Can't open the directory %s", filepath) |
67e2ec02 PD |
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: | |
e42bd6a5 | 96 | if subpath == "." or subpath == "..": |
67e2ec02 PD |
97 | continue |
98 | new_filepath = filepath + "/" + subpath | |
99 | if (os.path.isfile(new_filepath)): | |
e42bd6a5 | 100 | if os.path.getsize(new_filepath) == 0: |
38f15e57 | 101 | logging.info("Skipping empty file %s", subpath) |
67e2ec02 | 102 | else: |
e42bd6a5 | 103 | if MAIL_FILENAME.match(subpath): |
38f15e57 | 104 | logging.info("Injecting file %s", subpath) |
67e2ec02 PD |
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)) | |
f797b0fd | 109 | except IOError: |
38f15e57 | 110 | logging.warning("Could not retrieve mail from the file %s", new_filepath) |
67e2ec02 | 111 | else: |
e42bd6a5 | 112 | if os.path.isdir(new_filepath): |
67e2ec02 PD |
113 | # cyrus ^ vs . storage replacement |
114 | subpath = subpath.replace("^", ".") | |
115 | new_mailpath = mailpath + "/" + subpath | |
38f15e57 | 116 | logging.debug("Inserting mails from directory %s into mailbox %s", new_filepath, new_mailpath) |
67e2ec02 PD |
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 | |
38f15e57 | 122 | logging.debug("Done with directory %s and mailbox %s", new_filepath, new_mailpath) |
67e2ec02 PD |
123 | # mark mailboxes that need acl update |
124 | self.acl_mailboxes.append(mailpath) | |
0cf4dc33 | 125 | |
b2bbd1f5 | 126 | return |