From e42bd6a5cccd3ebe6dbc7158b6c898caa3611057 Mon Sep 17 00:00:00 2001 From: Plamen Dimitrov Date: Thu, 5 Jul 2012 17:00:26 +0200 Subject: [PATCH] Basic acl functionality added --- file_iterator.py | 73 ++++++++++++++++++++++++++++++++---------------- mail_iterator.py | 34 ++++++++++++++++------ restore_mail_inject.py | 39 +++++++++++++++++++------ 3 files changed, 103 insertions(+), 43 deletions(-) diff --git a/file_iterator.py b/file_iterator.py index 1e464fb..265caf9 100644 --- a/file_iterator.py +++ b/file_iterator.py @@ -4,10 +4,12 @@ restore-mail-inject.py - Tool to inject mails via IMAP 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.""" @@ -20,28 +22,16 @@ class FileIterator: # 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: @@ -51,13 +41,47 @@ class FileIterator: 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) @@ -70,15 +94,15 @@ class FileIterator: 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 @@ -86,7 +110,7 @@ class FileIterator: 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 @@ -98,6 +122,7 @@ class FileIterator: 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 # diff --git a/mail_iterator.py b/mail_iterator.py index e8be1e0..0465f11 100644 --- a/mail_iterator.py +++ b/mail_iterator.py @@ -45,7 +45,7 @@ class MailIterator: 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() @@ -60,7 +60,7 @@ class MailIterator: 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) @@ -68,13 +68,29 @@ class MailIterator: 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): @@ -84,9 +100,9 @@ class MailIterator: # 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])) @@ -94,9 +110,9 @@ class MailIterator: 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])) diff --git a/restore_mail_inject.py b/restore_mail_inject.py index fc41f01..de15a1b 100644 --- a/restore_mail_inject.py +++ b/restore_mail_inject.py @@ -2,6 +2,11 @@ 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 @@ -20,29 +25,43 @@ def main(): 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(): @@ -55,7 +74,7 @@ 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)') @@ -71,5 +90,5 @@ def configure_args(): return args -if(__name__ == "__main__"): +if __name__ == "__main__": main() -- 1.7.1