Basic acl functionality added
authorPlamen Dimitrov <plamen.dimitrov@intra2net.com>
Thu, 5 Jul 2012 15:00:26 +0000 (17:00 +0200)
committerPlamen Dimitrov <plamen.dimitrov@intra2net.com>
Thu, 5 Jul 2012 15:00:26 +0000 (17:00 +0200)
file_iterator.py
mail_iterator.py
restore_mail_inject.py

index 1e464fb..265caf9 100644 (file)
@@ -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
     #
index e8be1e0..0465f11 100644 (file)
@@ -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]))
index fc41f01..de15a1b 100644 (file)
@@ -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()