Commit | Line | Data |
---|---|---|
67e2ec02 | 1 | ''' |
23ebcaa4 | 2 | The module contains the FileIterator class. |
67e2ec02 PD |
3 | |
4 | Copyright (c) 2012 Intra2net AG | |
20760d94 PD |
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. | |
67e2ec02 PD |
16 | ''' |
17 | ||
5737b10e | 18 | import os |
67e2ec02 | 19 | import re |
f797b0fd | 20 | import logging |
67e2ec02 PD |
21 | |
22 | MAIL_FILENAME = re.compile("^[0-9]+\.$") | |
e42bd6a5 PD |
23 | MBOXFILE_LINE = re.compile("^(.*?)\t(?:\d )?default[\t ](.*)$") |
24 | ACL_STRING = re.compile("^(.*?)[\t ](.*?)\t(.*)$") | |
67e2ec02 PD |
25 | |
26 | class FileIterator: | |
27 | """This class iterates through the e-mail files.""" | |
28 | ||
29 | # class attributes | |
67e2ec02 PD |
30 | # mailboxes created during file traversal |
31 | created_mailboxes = None | |
32 | # mailboxes to update during file traversal | |
33 | acl_mailboxes = None | |
ed8dc19f | 34 | # acls retrieved from a file |
79666349 | 35 | _file_acls = None |
67e2ec02 | 36 | |
e42bd6a5 | 37 | def __init__(self): |
67e2ec02 | 38 | """Creates a connection and a user session.""" |
0cf4dc33 | 39 | |
e42bd6a5 PD |
40 | self.created_mailboxes = [] |
41 | self.acl_mailboxes = [] | |
79666349 | 42 | self._file_acls = {} |
67e2ec02 | 43 | |
38f15e57 PD |
44 | @classmethod |
45 | def _message_read(cls, filename): | |
67e2ec02 | 46 | """Retrieves a message from the message file.""" |
0cf4dc33 | 47 | |
67e2ec02 PD |
48 | try: |
49 | with open(filename, "r") as msgfile: | |
50 | message = msgfile.read() | |
f797b0fd PD |
51 | except IOError: |
52 | logging.warning("Could not open the e-mail file %s", filename) | |
53 | raise | |
0cf4dc33 | 54 | |
67e2ec02 PD |
55 | return message |
56 | ||
79666349 PD |
57 | def get_mailbox_acls(self, mailbox, original_user): |
58 | """Get the acls loaded from a file for a given mailbox. | |
59 | Should be used for access of mailbox acls instead of attribute""" | |
60 | ||
61 | # translate the mailbox to internal stored format | |
bb6cdeea | 62 | internal_mailbox = mailbox.replace("INBOX", "user/" + original_user) |
79666349 PD |
63 | internal_mailbox = internal_mailbox.replace(".", "^") |
64 | internal_mailbox = internal_mailbox.replace("/", ".") | |
65 | logging.debug("Get acls for mailbox %s translated as %s", mailbox, internal_mailbox) | |
66 | ||
67 | # retrieve from internal dicitonary attribute of file acls | |
68 | try: | |
69 | mb_acls = self._file_acls[internal_mailbox] | |
70 | except KeyError: | |
71 | # no rights for the mailbox were found and warn if acl file loaded | |
72 | if len(self._file_acls) > 0: | |
bb6cdeea | 73 | logging.warning("Could not find the acls for mailbox %s", mailbox) |
79666349 PD |
74 | mb_acls = {} |
75 | ||
76 | return mb_acls | |
77 | ||
78 | def load_mailbox_list(self, mboxlistfile): | |
0cf4dc33 PD |
79 | """Load the list of mailboxes and acl rights for each from file.""" |
80 | ||
ed8dc19f PD |
81 | try: |
82 | with open(mboxlistfile, 'r') as acl_file: | |
83 | for line in acl_file: | |
84 | ||
85 | # read a line using regex | |
86 | acls = {} | |
87 | try: | |
88 | linedata = MBOXFILE_LINE.match(line).groups() | |
89 | except AttributeError: | |
90 | logging.warning("Illegal line in mailbox list dump: %s", line) | |
91 | continue | |
92 | aclstr = linedata[1] | |
93 | ||
94 | # changes acls key encoding to internal cyrus format and makes it absolute (as folder). | |
79666349 PD |
95 | key = linedata[0] |
96 | ||
ed8dc19f PD |
97 | # loop through acl rights string and build dictionary of users and rights |
98 | while(aclstr != ""): | |
79c28101 | 99 | try: |
ed8dc19f | 100 | acldata = ACL_STRING.match(aclstr).groups() |
79c28101 | 101 | except AttributeError: |
ed8dc19f PD |
102 | logging.warning("Illegal acl string in mailbox list dump: %s", line) |
103 | aclstr = "" | |
f797b0fd | 104 | continue |
ed8dc19f | 105 | acls[acldata[0]] = acldata[1] |
79666349 | 106 | aclstr = acldata[2] |
ed8dc19f | 107 | |
79666349 | 108 | self._file_acls[key] = acls |
ed8dc19f PD |
109 | except IOError: |
110 | logging.warning("Could not open mboxlist file %s", mboxlistfile) | |
e42bd6a5 | 111 | |
67e2ec02 PD |
112 | def load_mails(self, filepath, mailpath): |
113 | """Loads all e-mails from file hierarchy. | |
114 | This recursive generator always returns a tuple of | |
115 | the next found (e-mail, mailbox to store, internaldate).""" | |
0cf4dc33 | 116 | |
38f15e57 | 117 | logging.debug("Entered directory %s -> %s", filepath, mailpath) |
67e2ec02 PD |
118 | try: |
119 | filepath = os.path.abspath(filepath) | |
120 | os.chdir(filepath) | |
121 | except OSError: | |
38f15e57 | 122 | logging.warning("Can't open the directory %s", filepath) |
67e2ec02 PD |
123 | return |
124 | # mark mailboxes that should be created | |
125 | self.created_mailboxes.append(mailpath) | |
20760d94 | 126 | |
67e2ec02 PD |
127 | subpaths = os.listdir(filepath) |
128 | for subpath in subpaths: | |
e42bd6a5 | 129 | if subpath == "." or subpath == "..": |
67e2ec02 PD |
130 | continue |
131 | new_filepath = filepath + "/" + subpath | |
20760d94 PD |
132 | |
133 | # if path is file validate name and inject | |
67e2ec02 | 134 | if (os.path.isfile(new_filepath)): |
e42bd6a5 | 135 | if os.path.getsize(new_filepath) == 0: |
38f15e57 | 136 | logging.info("Skipping empty file %s", subpath) |
67e2ec02 | 137 | else: |
e42bd6a5 | 138 | if MAIL_FILENAME.match(subpath): |
38f15e57 | 139 | logging.info("Injecting file %s", subpath) |
67e2ec02 PD |
140 | try: |
141 | message = self._message_read(new_filepath) | |
142 | # suggest file modification date for internaldate | |
143 | yield (message, mailpath, os.path.getmtime(new_filepath)) | |
f797b0fd | 144 | except IOError: |
38f15e57 | 145 | logging.warning("Could not retrieve mail from the file %s", new_filepath) |
67e2ec02 | 146 | else: |
20760d94 PD |
147 | |
148 | # if path is directory do recursive call | |
e42bd6a5 | 149 | if os.path.isdir(new_filepath): |
67e2ec02 PD |
150 | # cyrus ^ vs . storage replacement |
151 | subpath = subpath.replace("^", ".") | |
152 | new_mailpath = mailpath + "/" + subpath | |
38f15e57 | 153 | logging.debug("Inserting mails from directory %s into mailbox %s", new_filepath, new_mailpath) |
67e2ec02 PD |
154 | # load_mails($mboxdbref, $origuser, $targetuser) |
155 | rcrs_generator = self.load_mails(new_filepath, new_mailpath) | |
156 | # you enter the generator in the for loop | |
157 | for rcr in rcrs_generator: | |
158 | yield rcr | |
38f15e57 | 159 | logging.debug("Done with directory %s and mailbox %s", new_filepath, new_mailpath) |
20760d94 | 160 | |
67e2ec02 PD |
161 | # mark mailboxes that need acl update |
162 | self.acl_mailboxes.append(mailpath) |