Copyright (c) 2012 Intra2net AG
'''
-import os
+import sys, os
import re
MAIL_FILENAME = re.compile("^[0-9]+\.$")
+MBOXFILE_LINE = re.compile("^(.*?)\t(?:\d )?default[\t ](.*)$")
+ACL_STRING = re.compile("^(.*?)[\t ](.*?)\t(.*)$")
class FileIterator:
"""This class iterates through the e-mail files."""
# mailboxes to update during file traversal
acl_mailboxes = None
- def __init__(self, mboxlist = ""):
+ def __init__(self):
"""Creates a connection and a user session."""
- self.mboxdb = []
- if(mboxlist != ""):
- try:
- acl_file = open(mboxlist, 'r')
- self.mboxdb = self._mbox_read(acl_file)
- acl_file.close()
- except IOError:
- print("Could not open mboxlist file %s." % mboxlist)
- mboxlist = ""
+ self.created_mailboxes = []
+ self.acl_mailboxes = []
return
def __del__(self):
"""Closes the connection and the user session."""
return
- def _mbox_read(self, acl_file):
- "Reads something."
- #
- return
-
def _message_read(self, filename):
"""Retrieves a message from the message file."""
try:
print("Could not open the e-mail file %s.", filename)
return message
+ def load_mailbox_list(self, mboxlistfile = ""):
+ mboxdb = []
+ if mboxlistfile != "":
+ try:
+ with open(mboxlistfile, 'r') as acl_file:
+ for line in acl_file:
+ lineparts = []
+ acls = []
+
+ linedata = MBOXFILE_LINE.match(line).groups()
+ #print(linedata)
+ #check this condition
+ if len(linedata) == 0:
+ print("Illegal line in mailbox list dump: %s" % line)
+ sys.exit()
+
+ lineparts.append(linedata[0])
+ aclstr = linedata[1]
+ while(aclstr != ""):
+ this_acl = []
+ acldata = ACL_STRING.match(aclstr).groups()
+ if len(acldata) == 0:
+ print("Illegal line in mailbox list dump: %s" % line)
+ sys.exit()
+ aclstr = acldata[2]
+ this_acl.append(acldata[0])
+ this_acl.append(acldata[1])
+ acls.append(this_acl)
+
+ lineparts.append(acls)
+ mboxdb.append(lineparts)
+
+ #print(mboxdb)
+ except IOError:
+ print("Could not open mboxlist file %s." % mboxlistfile)
+ return mboxdb
+
def load_mails(self, filepath, mailpath):
"""Loads all e-mails from file hierarchy.
This recursive generator always returns a tuple of
the next found (e-mail, mailbox to store, internaldate)."""
- # reset created mailboxes and mailboxes for acl update
- self.created_mailboxes = []
- self.acl_mailboxes = []
#print("Entered directory %s -> %s." % (filepath, mailpath))
try:
filepath = os.path.abspath(filepath)
subpaths = os.listdir(filepath)
for subpath in subpaths:
#print("Now checking subpath %s in %s" % (subpath, filepath))
- if(subpath == "." or subpath == ".."):
+ if subpath == "." or subpath == "..":
continue
new_filepath = filepath + "/" + subpath
if (os.path.isfile(new_filepath)):
- if(os.path.getsize(new_filepath) == 0):
+ if os.path.getsize(new_filepath) == 0:
print("Skipping empty file %s." % subpath)
else:
- if(MAIL_FILENAME.match(subpath)):
- print("Injecting file %s." % subpath)
+ if MAIL_FILENAME.match(subpath):
+ #print("Injecting file %s." % subpath)
try:
message = self._message_read(new_filepath)
# suggest file modification date for internaldate
except:
print("Could not retrieve mail from file: %s", new_filepath)
else:
- if(os.path.isdir(new_filepath)):
+ if os.path.isdir(new_filepath):
# cyrus ^ vs . storage replacement
subpath = subpath.replace("^", ".")
new_mailpath = mailpath + "/" + subpath
yield rcr
#print("Done with directory %s and mailbox %s." % (new_filepath, new_mailpath))
# mark mailboxes that need acl update
+ print("acl %s" % mailpath)
self.acl_mailboxes.append(mailpath)
return
#
def __del__(self):
"""Closes the connection and the user session."""
- if(self.logged_in):
+ if self.logged_in:
try:
self.mail_con.close()
self.mail_con.logout()
print("Could not get the acls of INBOX.")
print(hashref)
for acluser in hashref:
- if(acluser != user):
+ if acluser != user:
try:
#print(self.mail_con.setacl("INBOX", acluser, ""))
print("Reset acls on INBOX for user %s" % acluser)
print("Could not reset acls on INBOX for user %s" % acluser)
return
- def add_acls(self, mailbox, config):
+ def add_acls(self, mailbox, mailbox_list, original_user, target_user):
"""Add acls to mailbox."""
- # change encoding to internal cyrus format
+ # change encoding to internal cyrus format and make folder absolute
+ mailbox = mailbox.replace("INBOX/", "user/" + original_user + "/")
mailbox = mailbox.replace(".", "^")
mailbox = mailbox.replace("/", ".")
-
+
+ lookfor = mailbox
+ for current_folder in mailbox_list:
+ # find folder to set all acls
+ if current_folder[0] == lookfor:
+ acl_listref = current_folder[1]
+ for aclref in acl_listref:
+ print(aclref[0], target_user, original_user)
+ if aclref[0] != target_user and aclref[0] != original_user:
+ try:
+ #self.mail_con.setacl(mailbox, thisaclref[0], thisaclref[1])
+ print("Set acls %s for user %s on mailbox %s." % (aclref[1], aclref[0], mailbox))
+ except:
+ print("Could not set acls %s for user %s on mailbox %s." % (aclref[1], aclref[0], mailbox))
+ break
+
return
def delete_mailboxes(self, deleted_mailbox):
# if INBOX it cannot be deleted so add delimiter
if (deleted_mailbox == "INBOX"):
pattern += mailbox[1]
- if(re.compile(pattern).match(mailbox[2])):
+ if re.compile(pattern).match(mailbox[2]):
result, data = self.mail_con.delete(mailbox[2])
- if(result == "OK"):
+ if result == "OK":
print("Deleted mailbox %s" % mailbox[2])
else:
print("Could not delete folder %s: %s" % (mailbox[2], data[0]))
def create_mailbox(self, mailbox):
"""Create new mailbox to inject messages."""
- if(mailbox != "INBOX"):
+ if mailbox != "INBOX":
result, data = self.mail_con.create(mailbox)
- if(result == "OK"):
+ if result == "OK":
print("Creating mailbox %s" % mailbox)
else:
print("Could not create mailbox %s: %s" % (mailbox, data[0]))
restore-mail-inject.py - Tool to inject mails via IMAP
Copyright (c) 2012 Intra2net AG
+
+This program relies on the following assumptions:
+- IMAP hierarchy separator is / (unixhierarysep=yes)
+- INBOX maps to user/[username]
+- internal encoding of . etc. as of cyrus 2.2
'''
import argparse
try:
session = MailIterator("gwt-intranator.m.i2n", "mit.punkt", "mit.punkt")
#session = MailIterator("/var/imap/socket/imap", "cyrus", "geheim")
- storage = FileIterator(args.mboxlist)
+ storage = FileIterator()
except UserWarning:
print("Couldn't connect to IMAP server.")
return
+ # retrieve mailbox list from the mailbox list file
+ mailbox_list = storage.load_mailbox_list(args.mboxlistfile)
+
# delete olf IMAP folders if no append requested
- if(not args.append):
+ if not args.append:
session.delete_mailboxes(args.folder)
- if(args.folder == "INBOX"):
+ if args.folder == "INBOX":
session.clear_inbox_acls(args.user)
# inject emails
path_generator = storage.load_mails(args.srcdir, args.folder)
for message, mailbox, date_modified in path_generator:
+
+ # mailboxes marked for creating and acl update
+ # add acls after all subfolders or their acls will be derived from parent folder
+ print(storage.created_mailboxes, storage.acl_mailboxes)
for new_mailbox in storage.created_mailboxes:
session.create_mailbox(new_mailbox)
for acl_mailbox in storage.acl_mailboxes:
- session.add_acls(acl_mailbox, args)
+ session.add_acls(acl_mailbox, mailbox_list, args.ouser, args.user)
+ storage.created_mailboxes = []
+ storage.acl_mailboxes = []
+
session.inject_message(message, mailbox, date_modified)
- # add acls after all subfolders or their acls will be derived from parent folder
-
- print("Finished injecting mails.")
-
+
+ # last iteration mailboxes in case root mailbox has no e-mails for injection
+ for new_mailbox in storage.created_mailboxes:
+ session.create_mailbox(new_mailbox)
+ for acl_mailbox in storage.acl_mailboxes:
+ session.add_acls(acl_mailbox, mailbox_list, args.ouser, args.user)
+
+ print("Finished injecting mails. Exiting.")
return
def configure_args():
default="INBOX", help='folder to store mails to - if not specified we overwrite INBOX')
parser.add_argument('-s', '--sourcedir', dest='srcdir', action='store',
required=True, help='folder to read mail from')
- parser.add_argument('-m', '--mboxlist', dest='mboxlist', action='store',
+ parser.add_argument('-m', '--mboxlistfile', dest='mboxlistfile', action='store',
default="", help='mboxlist file (flat file format) to read the ACLs from')
parser.add_argument('-o', '--ouser', dest='ouser', action='store',
default="", help='name of the original user (=username if not specified)')
return args
-if(__name__ == "__main__"):
+if __name__ == "__main__":
main()