00e0f1552655b3172714767c3285fdd954b1c073
[imap-fix-internaldate] / fix_imap_internaldate.py
1 '''
2 fix_imap_internaldate.py - Fix the INTERNALDATE field on IMAP servers
3
4 Copyright (c) 2012 Intra2net AG
5 Author: Plamen Dimitrov
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 Add '-t' argument when running the module for a test mode.
18 For a detailed list of each message with a date conflict change
19 the 'log_level' in the configuration file from '30' to '20'.
20 '''
21
22 import sys
23 import csv
24 import logging
25 import configparser
26 from date_interpreter import DateInterpreter
27 from mail_iterator import MailIterator
28
29 def main():
30     """Iterates through csv list of users and their mailboxes"""
31     if (len(sys.argv) > 1 and sys.argv[1]=="-t"):
32         test_mode = 1
33     else:
34         test_mode = 0
35
36     config = load_configuration()
37     logging.basicConfig(filename='mailscript.log',
38                         format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
39                         level=config.getint('basic_settings', 'log_level'))
40
41     date_interp = DateInterpreter()
42     user_reader = csv.DictReader(open("userdata.csv", "r"), delimiter=',')
43
44     server = config.get('basic_settings', 'imap_server')
45     tolerance = config.getint('basic_settings', 'tolerance')
46     total_per_box = {}
47
48     for user in user_reader:
49         try:
50             session = MailIterator(server, user['username'], user['password'])
51         except UserWarning as ex:
52             logging.error(ex)
53             continue
54         for mailbox in session:
55             try:
56                 #print(".")
57                 mail_ids = session.fetch_messages()
58             except UserWarning as ex:
59                 logging.error(ex)
60                 continue
61             for mid in mail_ids:
62                 try:
63                     fetched_internal_date = session.fetch_internal_date(mid)
64                     internal_date = date_interp.extract_internal_date(fetched_internal_date)
65                     fetched_received_date = session.fetch_received_date(mid)
66                     received_date = date_interp.extract_received_date(fetched_received_date)
67                     if(received_date==""):
68                         logging.warning("No received date could be found in message uid: %s - mailbox: %s - user: %s.\n",
69                                         mid.decode("utf-8"), mailbox.strip('"'), user['username'])
70                         continue
71                 except UserWarning as ex:
72                     logging.error(ex)
73                     continue
74                 if(date_interp.compare_dates(received_date, internal_date, tolerance)):
75                     #print(received_date, internal_date)
76                     if(test_mode==0):
77                         try:
78                             session.update_message(mid, mailbox, received_date)
79                         except UserWarning as ex:
80                             logging.error(ex)
81                             continue
82                     else:
83                         logging.info("Date conflict found in message uid: %s - mailbox: %s - user: %s.\nInternal date %s is different from received date %s from RECEIVED header:\n%s.",
84                                         mid.decode("utf-8"), mailbox.strip('"'), user['username'],
85                                         internal_date.strftime("%d %b %Y %H:%M:%S"),
86                                         received_date.strftime("%d %b %Y %H:%M:%S"),
87                                         fetched_received_date[0][1].decode("utf-8").split("Received:")[1])
88                     #count total emails for every user and mailbox
89                     mixed_key = user['username']+'|'+mailbox.strip('"')
90                     total_per_box[mixed_key] = 1 + total_per_box.get(mixed_key, 0)
91         total_per_user = 0
92         for warning in total_per_box:
93             total_per_user += total_per_box[warning]
94             logging.warning("Total date conflicts to be corrected in a mailbox %s are %s.",
95                          warning.split('|')[1], total_per_box[warning])
96         logging.warning("Total date conflicts to be corrected for user %s are %s.\n",
97                      user['username'], total_per_user)
98
99 def load_configuration():
100     """Loads the script configuration from a file or creates such."""
101     config = configparser.RawConfigParser()    
102     try:
103         config.read('confscript.cfg')
104     except IOError:
105         config.add_section('basic_settings')
106         config.set('basic_settings', 'log_level', logging.DEBUG)
107         #config.set('Basic settings', 'bool', 'true')
108         with open('confscript.cfg', 'w') as configfile:
109             config.write(configfile)
110     return config
111
112 if(__name__ == "__main__"):
113     main()