e681ec024e129d0c9156a63d91c870ba1b0aa3af
[imap-restore-mail] / src / imap_restore_mail.py
1 '''
2 restore-mail-inject.py - Tool to inject mails via IMAP
3
4 Copyright (c) 2012 Intra2net AG
5 Author: Plamen Dimitrov and Thomas Jarosch
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 '''
17 import logging
18 import argparse, getpass
19 from mail_iterator import MailIterator
20 from file_iterator import FileIterator
21 from warnings_handler import WarningsHandler
22
23 # logging settings
24 LOG_FILENAME = "imap_restore_mail.log"
25 LOG_FILE_LEVEL = logging.DEBUG
26 LOG_SHELL_LEVEL = logging.INFO
27 LOG_UNCLEAN_EXIT_LEVEL = logging.WARNING
28
29 def main():
30     """Main function."""
31
32     # prepare configuration
33     args = configure_args()
34     warnings_handler = prepare_logger()
35     logging.info("The module restore_mail_inject.py started with user %s, destination %s and source %s.",
36           args.user, args.destination, args.source)
37
38     # connect to unix socket or server
39     psw = getpass.getpass()
40     session = MailIterator(args.server, args.user, psw)
41     storage = FileIterator()
42
43     # retrieve mailbox list if a mailbox list file is specified
44     if args.mboxlistfile != "":
45         storage.load_mailbox_list(args.mboxlistfile, args.ouser)
46
47     # delete olf IMAP folders if no append requested
48     if not args.append:
49         session.delete_mailboxes(args.destination)
50         if args.destination == "INBOX":
51             session.clear_inbox_acls(args.user)
52
53     # inject emails
54     path_generator = storage.load_mails(args.source, args.destination)
55     for message, mailbox, date_modified in path_generator:
56         update_mailboxes(storage, session, args)
57         session.inject_message(message, mailbox, date_modified)
58
59     # last iteration for mailboxes in case root mailbox has no e-mails for injection
60     update_mailboxes(storage, session, args)
61
62     logging.info("Finished injecting mails. Exiting with code %s.", warnings_handler.detected_problems)
63     return warnings_handler.detected_problems
64
65 def update_mailboxes(storage, session, args):
66     """Create new mailboxes if necessary and add acls to mailboxes if necessary."""
67     
68     # create new mailboxes if found by the file iterator
69     for new_mailbox in storage.created_mailboxes:
70         session.create_mailbox(new_mailbox)
71
72     # add acls after all subfolders or their acls will be derived from parent folder
73     for acl_mailbox in storage.acl_mailboxes:
74         # find folder acls record and retrieve them
75         try:
76             mb_acls = storage.file_acls[acl_mailbox]
77         except KeyError:
78             # no rights for the mailbox were found and warn if acl file loaded
79             if len(storage.file_acls) > 0:
80                 logging.warning("Could not find the acls for mailbox %s for user %s.", acl_mailbox, args.ouser)
81             mb_acls = {}
82         session.add_acls(acl_mailbox, mb_acls, args.ouser, args.user)
83     storage.created_mailboxes = []
84     storage.acl_mailboxes = []
85     
86 def configure_args():
87     """Configure arguments and return them."""
88
89     # parse arguments
90     parser = argparse.ArgumentParser(description="Tool to inject mails via IMAP.")
91     parser.add_argument('-d', '--directory', dest='source', action='store',
92                         required=True, help='file directory to read mails from')
93     parser.add_argument('-s', '--server', dest='server', action='store',
94                         default="localhost", help='imap server name with default localhost')    
95     parser.add_argument('-u', '--username', dest='user', action='store',
96                         required=True, help='user to store mails to')
97     parser.add_argument('-o', '--ouser', dest='ouser', action='store',
98                         default="", help='name of the original user (=username if not specified)')
99     parser.add_argument('-f', '--foldername', dest='destination', action='store',
100                         default="INBOX", help='folder to store mails to - if not specified we overwrite INBOX')
101     parser.add_argument('-m', '--mboxlistfile', dest='mboxlistfile', action='store',
102                         default="", help='mboxlist file (flat file format) to read the ACLs from')
103     parser.add_argument('-a', '--append', dest='append', action='store_true',
104                         default=False, help="append mails, don't delete anything")
105     args = parser.parse_args()
106
107     # some default post processing of arguments
108     if (args.destination != "INBOX"):
109         args.destination = "INBOX/" + args.destination
110     if (args.ouser == ""):
111         args.ouser = args.user
112
113     return args
114
115 def prepare_logger():
116     """Sets up the logging functionality"""
117
118     # reset the log
119     with open(LOG_FILENAME, 'w'):
120         pass
121
122     # add basic configuration
123     logging.basicConfig(filename=LOG_FILENAME,
124                         format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
125                         level=LOG_FILE_LEVEL)
126
127     # add a handler for a console output
128     default_logger = logging.getLogger('')
129     console = logging.StreamHandler()
130     console.setLevel(LOG_SHELL_LEVEL)
131     formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
132     console.setFormatter(formatter)
133     default_logger.addHandler(console)
134
135     # add a handler for warnings counting
136     warnings_handler = WarningsHandler()
137     warnings_handler.setLevel(LOG_UNCLEAN_EXIT_LEVEL)
138     default_logger.addHandler(warnings_handler)
139
140     return warnings_handler
141
142 if __name__ == "__main__":
143     main()