Add unit testing for the mailbox acls parsing functionality
[imap-restore-mail] / file_iterator.py
CommitLineData
67e2ec02
PD
1'''
2restore-mail-inject.py - Tool to inject mails via IMAP
3
4Copyright (c) 2012 Intra2net AG
5'''
6
5737b10e 7import os
67e2ec02 8import re
f797b0fd 9import logging
67e2ec02
PD
10
11MAIL_FILENAME = re.compile("^[0-9]+\.$")
e42bd6a5
PD
12MBOXFILE_LINE = re.compile("^(.*?)\t(?:\d )?default[\t ](.*)$")
13ACL_STRING = re.compile("^(.*?)[\t ](.*?)\t(.*)$")
67e2ec02
PD
14
15class 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