Commit | Line | Data |
---|---|---|
c9da760a PD |
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. | |
c9da760a PD |
16 | ''' |
17 | ||
18 | import sys | |
19 | import csv | |
8fe4e3ff PD |
20 | import argparse, configparser |
21 | import logging | |
c9da760a PD |
22 | from date_interpreter import DateInterpreter |
23 | from mail_iterator import MailIterator | |
8301e589 | 24 | from caching_data import CachingData |
c9da760a PD |
25 | |
26 | def main(): | |
27 | """Iterates through csv list of users and their mailboxes""" | |
8fe4e3ff PD |
28 | |
29 | """parser = argparse.ArgumentParser(description='Fix the INTERNALDATE field on IMAP servers.') | |
30 | parser.add_argument('--h', metavar='N', type=int, nargs='+', | |
31 | help='an integer for the accumulator') | |
32 | parser.add_argument('--u', dest='accumulate', type=bool, | |
33 | const=sum, default=max, | |
34 | help='sum the integers (default: find the max)') | |
35 | args = parser.parse_args() | |
36 | print(args.accumulate(args.integers))""" | |
37 | ||
7a1d4c35 PD |
38 | if(len(sys.argv) > 1): |
39 | if(sys.argv[1]=="--h"): | |
40 | print("The default mode of the script is test mode." | |
41 | "Add '--u' argument to exit to modify messages." | |
42 | "For a detailed list of each message with a date conflict change" | |
43 | "change the 'log_level' in the configuration file from '30' to '20'.") | |
44 | return | |
45 | if(sys.argv[1]=="--u"): | |
46 | test_mode = False | |
c9da760a | 47 | else: |
7a1d4c35 | 48 | test_mode = True |
c9da760a | 49 | |
8fe4e3ff | 50 | # config and logging setup |
c9da760a | 51 | config = load_configuration() |
8fe4e3ff | 52 | prepare_logger(config) |
c9da760a PD |
53 | |
54 | date_interp = DateInterpreter() | |
7a1d4c35 | 55 | caching_data = CachingData() |
8fe4e3ff | 56 | logging.warning("Cache version %s loaded.", caching_data.version) |
c9da760a | 57 | user_reader = csv.DictReader(open("userdata.csv", "r"), delimiter=',') |
8fe4e3ff PD |
58 | |
59 | # server name is stored in the config | |
c9da760a | 60 | server = config.get('basic_settings', 'imap_server') |
8fe4e3ff PD |
61 | # tolerance is now in seconds |
62 | tolerance = config.getint('basic_settings', 'tolerance') * 60 | |
c9da760a PD |
63 | |
64 | for user in user_reader: | |
65 | try: | |
66 | session = MailIterator(server, user['username'], user['password']) | |
67 | except UserWarning as ex: | |
68 | logging.error(ex) | |
69 | continue | |
70 | for mailbox in session: | |
71 | try: | |
7a1d4c35 | 72 | box = caching_data.retrieve_cached_mailbox(mailbox[0], mailbox[1], user['username']) |
c9da760a | 73 | mail_ids = session.fetch_messages() |
7a1d4c35 | 74 | new_ids = box.synchronize(mail_ids) |
8fe4e3ff | 75 | logging.warning("%s non-cached messages found out of %s in %s.", len(new_ids), len(mail_ids), box.name) |
c9da760a PD |
76 | except UserWarning as ex: |
77 | logging.error(ex) | |
78 | continue | |
8301e589 | 79 | for mid in new_ids: |
c9da760a PD |
80 | try: |
81 | fetched_internal_date = session.fetch_internal_date(mid) | |
82 | internal_date = date_interp.extract_internal_date(fetched_internal_date) | |
83 | fetched_received_date = session.fetch_received_date(mid) | |
84 | received_date = date_interp.extract_received_date(fetched_received_date) | |
85 | if(received_date==""): | |
8fe4e3ff PD |
86 | logging.info("No received date could be found in message uid: %s - mailbox: %s - user: %s.", |
87 | mid, box.name, box.owner) | |
7a1d4c35 | 88 | box.no_received_field += 1 |
c9da760a PD |
89 | continue |
90 | except UserWarning as ex: | |
91 | logging.error(ex) | |
92 | continue | |
93 | if(date_interp.compare_dates(received_date, internal_date, tolerance)): | |
94 | #print(received_date, internal_date) | |
95 | if(test_mode==0): | |
96 | try: | |
7a1d4c35 | 97 | session.update_message(mid, box.name, received_date) |
c9da760a PD |
98 | except UserWarning as ex: |
99 | logging.error(ex) | |
100 | continue | |
101 | else: | |
102 | 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.", | |
8fe4e3ff | 103 | mid, box.name, box.owner, |
c9da760a PD |
104 | internal_date.strftime("%d %b %Y %H:%M:%S"), |
105 | received_date.strftime("%d %b %Y %H:%M:%S"), | |
8fe4e3ff | 106 | fetched_received_date[0][1].decide("utf-8").split("Received:")[1]) |
8301e589 | 107 | # count total emails for every user and mailbox |
7a1d4c35 | 108 | box.date_conflicts += 1 |
8301e589 | 109 | # if all messages were successfully fixed confirm caching |
7a1d4c35 PD |
110 | if(not test_mode): |
111 | box.confirm_change() | |
8fe4e3ff | 112 | |
8301e589 | 113 | # final report on date conflicts |
7a1d4c35 | 114 | caching_data.report_date_conflicts() |
c9da760a PD |
115 | |
116 | def load_configuration(): | |
117 | """Loads the script configuration from a file or creates such.""" | |
118 | config = configparser.RawConfigParser() | |
8fe4e3ff PD |
119 | success = config.read('confscript.cfg') |
120 | if(len(success)==0): | |
c9da760a | 121 | config.add_section('basic_settings') |
8fe4e3ff PD |
122 | config.set('basic_settings', 'file_log_level', logging.INFO) |
123 | config.set('basic_settings', 'console_log_level', logging.INFO) | |
124 | config.set('basic_settings', 'imap_server', 'imap.company.com') | |
125 | config.set('basic_settings', 'tolerance', 30) | |
c9da760a PD |
126 | #config.set('Basic settings', 'bool', 'true') |
127 | with open('confscript.cfg', 'w') as configfile: | |
128 | config.write(configfile) | |
129 | return config | |
130 | ||
8fe4e3ff PD |
131 | def prepare_logger(config): |
132 | """Sets up the logging functionality""" | |
133 | ||
134 | # reset the log | |
135 | with open('fix_imap_internaldate.log', 'w'): | |
136 | pass | |
137 | ||
138 | # add basic configuration | |
139 | logging.basicConfig(filename='fix_imap_internaldate.log', | |
140 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | |
141 | level=config.getint('basic_settings', 'file_log_level')) | |
142 | ||
143 | # add a handler for a console output | |
144 | console = logging.StreamHandler() | |
145 | console.setLevel(config.getint('basic_settings', 'console_log_level')) | |
146 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') | |
147 | console.setFormatter(formatter) | |
148 | logging.getLogger('').addHandler(console) | |
149 | return | |
150 | ||
c9da760a PD |
151 | if(__name__ == "__main__"): |
152 | main() |