Caching functionality for performance improvement
[imap-fix-internaldate] / fix_imap_internaldate.py
CommitLineData
c9da760a
PD
1'''
2fix_imap_internaldate.py - Fix the INTERNALDATE field on IMAP servers
3
4Copyright (c) 2012 Intra2net AG
5Author: Plamen Dimitrov
6
7This program is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17Add '-t' argument when running the module for a test mode.
18For a detailed list of each message with a date conflict change
19the 'log_level' in the configuration file from '30' to '20'.
20'''
21
22import sys
23import csv
24import logging
25import configparser
26from date_interpreter import DateInterpreter
27from mail_iterator import MailIterator
8301e589 28from caching_data import CachingData
c9da760a
PD
29
30def main():
31 """Iterates through csv list of users and their mailboxes"""
32 if (len(sys.argv) > 1 and sys.argv[1]=="-t"):
33 test_mode = 1
34 else:
35 test_mode = 0
36
37 config = load_configuration()
38 logging.basicConfig(filename='mailscript.log',
39 format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
40 level=config.getint('basic_settings', 'log_level'))
41
42 date_interp = DateInterpreter()
8301e589
PD
43 cashing_data = CachingData()
44 logging.warning(cashing_data)
c9da760a
PD
45 user_reader = csv.DictReader(open("userdata.csv", "r"), delimiter=',')
46
47 server = config.get('basic_settings', 'imap_server')
48 tolerance = config.getint('basic_settings', 'tolerance')
49 total_per_box = {}
50
51 for user in user_reader:
52 try:
53 session = MailIterator(server, user['username'], user['password'])
54 except UserWarning as ex:
55 logging.error(ex)
56 continue
57 for mailbox in session:
58 try:
8301e589
PD
59 #special key to ensure better mailbox uniqueness
60 mailbox_key = mailbox[0].strip('"') + mailbox[1]
c9da760a 61 mail_ids = session.fetch_messages()
8301e589
PD
62 new_ids = cashing_data.sync_cached_mailbox(user['username'], mailbox_key, mail_ids)
63 #print(len(new_ids), "new out of", len(mail_ids), "in", mailbox)
c9da760a
PD
64 except UserWarning as ex:
65 logging.error(ex)
66 continue
8301e589 67 for mid in new_ids:
c9da760a
PD
68 try:
69 fetched_internal_date = session.fetch_internal_date(mid)
70 internal_date = date_interp.extract_internal_date(fetched_internal_date)
71 fetched_received_date = session.fetch_received_date(mid)
72 received_date = date_interp.extract_received_date(fetched_received_date)
73 if(received_date==""):
74 logging.warning("No received date could be found in message uid: %s - mailbox: %s - user: %s.\n",
8301e589 75 mid.decode("utf-8"), mailbox[0], user['username'])
c9da760a
PD
76 continue
77 except UserWarning as ex:
78 logging.error(ex)
79 continue
80 if(date_interp.compare_dates(received_date, internal_date, tolerance)):
81 #print(received_date, internal_date)
82 if(test_mode==0):
83 try:
8301e589 84 session.update_message(mid, mailbox[0], received_date)
c9da760a
PD
85 except UserWarning as ex:
86 logging.error(ex)
87 continue
88 else:
89 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.",
8301e589 90 mid.decode("utf-8"), mailbox[0], user['username'],
c9da760a
PD
91 internal_date.strftime("%d %b %Y %H:%M:%S"),
92 received_date.strftime("%d %b %Y %H:%M:%S"),
93 fetched_received_date[0][1].decode("utf-8").split("Received:")[1])
8301e589
PD
94 # count total emails for every user and mailbox
95 user_key = user['username']+'|'+mailbox[0].strip('"')
96 total_per_box[user_key] = 1 + total_per_box.get(user_key, 0)
97 # if all messages were successfully fixed confirm caching
98 cashing_data.commit_cached_mailbox(user['username'], mailbox_key)
99 # final report on date conflicts
c9da760a
PD
100 total_per_user = 0
101 for warning in total_per_box:
102 total_per_user += total_per_box[warning]
103 logging.warning("Total date conflicts to be corrected in a mailbox %s are %s.",
104 warning.split('|')[1], total_per_box[warning])
105 logging.warning("Total date conflicts to be corrected for user %s are %s.\n",
106 user['username'], total_per_user)
107
108def load_configuration():
109 """Loads the script configuration from a file or creates such."""
110 config = configparser.RawConfigParser()
111 try:
112 config.read('confscript.cfg')
113 except IOError:
114 config.add_section('basic_settings')
115 config.set('basic_settings', 'log_level', logging.DEBUG)
116 #config.set('Basic settings', 'bool', 'true')
117 with open('confscript.cfg', 'w') as configfile:
118 config.write(configfile)
119 return config
120
121if(__name__ == "__main__"):
122 main()