Modules now in 'src' folder, COPYING and README added
[imap-restore-mail] / src / file_iterator.py
CommitLineData
67e2ec02
PD
1'''
2restore-mail-inject.py - Tool to inject mails via IMAP
3
4Copyright (c) 2012 Intra2net AG
20760d94
PD
5Author: Plamen Dimitrov and Thomas Jarosch
6
7This program is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
67e2ec02
PD
16'''
17
5737b10e 18import os
67e2ec02 19import re
f797b0fd 20import logging
67e2ec02
PD
21
22MAIL_FILENAME = re.compile("^[0-9]+\.$")
e42bd6a5
PD
23MBOXFILE_LINE = re.compile("^(.*?)\t(?:\d )?default[\t ](.*)$")
24ACL_STRING = re.compile("^(.*?)[\t ](.*?)\t(.*)$")
67e2ec02
PD
25
26class 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
34
e42bd6a5 35 def __init__(self):
67e2ec02 36 """Creates a connection and a user session."""
0cf4dc33 37
e42bd6a5
PD
38 self.created_mailboxes = []
39 self.acl_mailboxes = []
67e2ec02 40
67e2ec02
PD
41 return
42
38f15e57
PD
43 @classmethod
44 def _message_read(cls, filename):
67e2ec02 45 """Retrieves a message from the message file."""
0cf4dc33 46
67e2ec02
PD
47 try:
48 with open(filename, "r") as msgfile:
49 message = msgfile.read()
f797b0fd
PD
50 except IOError:
51 logging.warning("Could not open the e-mail file %s", filename)
52 raise
0cf4dc33 53
67e2ec02
PD
54 return message
55
38f15e57
PD
56 @classmethod
57 def load_mailbox_list(cls, mboxlistfile = ""):
0cf4dc33
PD
58 """Load the list of mailboxes and acl rights for each from file."""
59
b0169e56 60 mboxdb = {}
e42bd6a5
PD
61 if mboxlistfile != "":
62 try:
63 with open(mboxlistfile, 'r') as acl_file:
64 for line in acl_file:
20760d94 65
b0169e56 66 acls = {}
79c28101
PD
67 try:
68 linedata = MBOXFILE_LINE.match(line).groups()
69 except AttributeError:
38f15e57 70 logging.warning("Illegal line in mailbox list dump: %s", line)
f797b0fd 71 continue
b0169e56 72 key = linedata[0]
e42bd6a5 73 aclstr = linedata[1]
79c28101 74
b0169e56 75 # loop through acl rights string and build dictionary of users and rights
e42bd6a5 76 while(aclstr != ""):
79c28101
PD
77 try:
78 acldata = ACL_STRING.match(aclstr).groups()
79 except AttributeError:
80 logging.warning("Illegal acl string in mailbox list dump: %s", line)
81 aclstr = ""
f797b0fd 82 continue
e42bd6a5 83 aclstr = acldata[2]
38f15e57 84 acls[acldata[0]] = acldata[1]
b0169e56
PD
85
86 mboxdb[key] = acls
e42bd6a5 87 except IOError:
38f15e57 88 logging.warning("Could not open mboxlist file %s", mboxlistfile)
0cf4dc33 89
e42bd6a5
PD
90 return mboxdb
91
67e2ec02
PD
92 def load_mails(self, filepath, mailpath):
93 """Loads all e-mails from file hierarchy.
94 This recursive generator always returns a tuple of
95 the next found (e-mail, mailbox to store, internaldate)."""
0cf4dc33 96
38f15e57 97 logging.debug("Entered directory %s -> %s", filepath, mailpath)
67e2ec02
PD
98 try:
99 filepath = os.path.abspath(filepath)
100 os.chdir(filepath)
101 except OSError:
38f15e57 102 logging.warning("Can't open the directory %s", filepath)
67e2ec02
PD
103 return
104 # mark mailboxes that should be created
105 self.created_mailboxes.append(mailpath)
20760d94 106
67e2ec02
PD
107 subpaths = os.listdir(filepath)
108 for subpath in subpaths:
e42bd6a5 109 if subpath == "." or subpath == "..":
67e2ec02
PD
110 continue
111 new_filepath = filepath + "/" + subpath
20760d94
PD
112
113 # if path is file validate name and inject
67e2ec02 114 if (os.path.isfile(new_filepath)):
e42bd6a5 115 if os.path.getsize(new_filepath) == 0:
38f15e57 116 logging.info("Skipping empty file %s", subpath)
67e2ec02 117 else:
e42bd6a5 118 if MAIL_FILENAME.match(subpath):
38f15e57 119 logging.info("Injecting file %s", subpath)
67e2ec02
PD
120 try:
121 message = self._message_read(new_filepath)
122 # suggest file modification date for internaldate
123 yield (message, mailpath, os.path.getmtime(new_filepath))
f797b0fd 124 except IOError:
38f15e57 125 logging.warning("Could not retrieve mail from the file %s", new_filepath)
67e2ec02 126 else:
20760d94
PD
127
128 # if path is directory do recursive call
e42bd6a5 129 if os.path.isdir(new_filepath):
67e2ec02
PD
130 # cyrus ^ vs . storage replacement
131 subpath = subpath.replace("^", ".")
132 new_mailpath = mailpath + "/" + subpath
38f15e57 133 logging.debug("Inserting mails from directory %s into mailbox %s", new_filepath, new_mailpath)
67e2ec02
PD
134 # load_mails($mboxdbref, $origuser, $targetuser)
135 rcrs_generator = self.load_mails(new_filepath, new_mailpath)
136 # you enter the generator in the for loop
137 for rcr in rcrs_generator:
138 yield rcr
38f15e57 139 logging.debug("Done with directory %s and mailbox %s", new_filepath, new_mailpath)
20760d94 140
67e2ec02
PD
141 # mark mailboxes that need acl update
142 self.acl_mailboxes.append(mailpath)
0cf4dc33 143
b2bbd1f5 144 return