Basic refactoring and Tom's recommendations
[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.
c9da760a
PD
16'''
17
18import sys
19import csv
7a1d4c35 20import logging, configparser
c9da760a
PD
21from date_interpreter import DateInterpreter
22from mail_iterator import MailIterator
8301e589 23from caching_data import CachingData
c9da760a
PD
24
25def main():
26 """Iterates through csv list of users and their mailboxes"""
7a1d4c35
PD
27 if(len(sys.argv) > 1):
28 if(sys.argv[1]=="--h"):
29 print("The default mode of the script is test mode."
30 "Add '--u' argument to exit to modify messages."
31 "For a detailed list of each message with a date conflict change"
32 "change the 'log_level' in the configuration file from '30' to '20'.")
33 return
34 if(sys.argv[1]=="--u"):
35 test_mode = False
c9da760a 36 else:
7a1d4c35 37 test_mode = True
c9da760a
PD
38
39 config = load_configuration()
7a1d4c35 40 logging.basicConfig(filename='fix_imap_internaldate.log',
c9da760a
PD
41 format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
42 level=config.getint('basic_settings', 'log_level'))
43
44 date_interp = DateInterpreter()
7a1d4c35
PD
45 caching_data = CachingData()
46 logging.warning("Cache version %s loaded.\n\n", caching_data.version)
c9da760a
PD
47 user_reader = csv.DictReader(open("userdata.csv", "r"), delimiter=',')
48
49 server = config.get('basic_settings', 'imap_server')
50 tolerance = config.getint('basic_settings', 'tolerance')
c9da760a
PD
51
52 for user in user_reader:
53 try:
54 session = MailIterator(server, user['username'], user['password'])
55 except UserWarning as ex:
56 logging.error(ex)
57 continue
58 for mailbox in session:
59 try:
7a1d4c35 60 box = caching_data.retrieve_cached_mailbox(mailbox[0], mailbox[1], user['username'])
c9da760a 61 mail_ids = session.fetch_messages()
7a1d4c35
PD
62 new_ids = box.synchronize(mail_ids)
63 logging.warning("%s non-cached messages found out of %s in %s.\n", len(new_ids), len(mail_ids), box.name)
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==""):
7a1d4c35
PD
74 logging.info("No received date could be found in message uid: %s - mailbox: %s - user: %s.\n",
75 mid.decode("utf-8"), box.name, box.owner)
76 box.no_received_field += 1
c9da760a
PD
77 continue
78 except UserWarning as ex:
79 logging.error(ex)
80 continue
81 if(date_interp.compare_dates(received_date, internal_date, tolerance)):
82 #print(received_date, internal_date)
83 if(test_mode==0):
84 try:
7a1d4c35 85 session.update_message(mid, box.name, received_date)
c9da760a
PD
86 except UserWarning as ex:
87 logging.error(ex)
88 continue
89 else:
90 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.",
7a1d4c35 91 mid.decode("utf-8"), box.name, box.owner,
c9da760a
PD
92 internal_date.strftime("%d %b %Y %H:%M:%S"),
93 received_date.strftime("%d %b %Y %H:%M:%S"),
94 fetched_received_date[0][1].decode("utf-8").split("Received:")[1])
8301e589 95 # count total emails for every user and mailbox
7a1d4c35 96 box.date_conflicts += 1
8301e589 97 # if all messages were successfully fixed confirm caching
7a1d4c35
PD
98 if(not test_mode):
99 box.confirm_change()
100
8301e589 101 # final report on date conflicts
7a1d4c35 102 caching_data.report_date_conflicts()
c9da760a
PD
103
104def load_configuration():
105 """Loads the script configuration from a file or creates such."""
106 config = configparser.RawConfigParser()
107 try:
108 config.read('confscript.cfg')
109 except IOError:
110 config.add_section('basic_settings')
111 config.set('basic_settings', 'log_level', logging.DEBUG)
112 #config.set('Basic settings', 'bool', 'true')
113 with open('confscript.cfg', 'w') as configfile:
114 config.write(configfile)
115 return config
116
117if(__name__ == "__main__"):
118 main()