''' fix_imap_internaldate.py - Fix the INTERNALDATE field on IMAP servers Copyright (c) 2012 Intra2net AG Author: Plamen Dimitrov This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ''' import sys import csv import argparse, configparser import logging from mail_date_parser import MailDateParser from mail_iterator import MailIterator from caching_data import CachingData def main(): """Iterates through csv list of users and their mailboxes""" """parser = argparse.ArgumentParser(description='Fix the INTERNALDATE field on IMAP servers.') parser.add_argument('--h', metavar='N', type=int, nargs='+', help='an integer for the accumulator') parser.add_argument('--u', dest='accumulate', type=bool, const=sum, default=max, help='sum the integers (default: find the max)') args = parser.parse_args() print(args.accumulate(args.integers))""" if(len(sys.argv) > 1): if(sys.argv[1]=="--h"): print("The default mode of the script is test mode." "Add '--u' argument to exit to modify messages." "For a detailed list of each message with a date conflict change" "change the 'log_level' in the configuration file from '30' to '20'.") return if(sys.argv[1]=="--u"): test_mode = False else: test_mode = True # config and logging setup config = load_configuration() prepare_logger(config) date_parser = MailDateParser() caching_data = CachingData() logging.warning("Cache version %s loaded.", caching_data.version) user_reader = csv.DictReader(open("userdata.csv", "r"), delimiter=',') # server name is stored in the config server = config.get('basic_settings', 'imap_server') # tolerance is now in seconds tolerance = config.getint('basic_settings', 'tolerance') * 60 for user in user_reader: try: session = MailIterator(server, user['username'], user['password']) except UserWarning as ex: logging.error(ex) continue for mailbox in session: try: box = caching_data.retrieve_cached_mailbox(mailbox[0], mailbox[1], user['username']) mail_ids = session.fetch_messages() new_ids = box.synchronize(mail_ids) logging.info("%s new messages out of %s found in %s.", len(new_ids), len(mail_ids), box.name) except UserWarning as ex: logging.error(ex) continue for mid in new_ids: try: fetched_internal_date = session.fetch_internal_date(mid) internal_date = date_parser.extract_internal_date(fetched_internal_date) fetched_received_date = session.fetch_received_date(mid) received_date = date_parser.extract_received_date(fetched_received_date) if(received_date==""): logging.debug("No received date could be found in message uid: %s - mailbox: %s - user: %s.", mid.decode('iso-8859-1'), box.name, box.owner) box.no_received_field += 1 continue except UserWarning as ex: logging.error(ex) continue if(date_parser.compare_dates(received_date, internal_date, tolerance)): #print(received_date, internal_date) if(test_mode==0): try: session.update_message(mid, box.name, received_date) except UserWarning as ex: logging.error(ex) continue else: logging.warning("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.", mid.decode('iso-8859-1'), box.name, box.owner, internal_date.strftime("%d %b %Y %H:%M:%S"), received_date.strftime("%d %b %Y %H:%M:%S"), fetched_received_date.split("Received:")[1]) # count total emails for every user and mailbox box.date_conflicts += 1 # if all messages were successfully fixed confirm caching if(not test_mode): box.confirm_change() # final report on date conflicts caching_data.report_date_conflicts() def load_configuration(): """Loads the script configuration from a file or creates such.""" config = configparser.RawConfigParser() success = config.read('confscript.cfg') if(len(success)==0): config.add_section('basic_settings') config.set('basic_settings', 'file_log_level', logging.INFO) config.set('basic_settings', 'console_log_level', logging.INFO) config.set('basic_settings', 'imap_server', 'imap.company.com') config.set('basic_settings', 'tolerance', 30) #config.set('Basic settings', 'bool', 'true') with open('confscript.cfg', 'w') as configfile: config.write(configfile) return config def prepare_logger(config): """Sets up the logging functionality""" # reset the log with open('fix_imap_internaldate.log', 'w'): pass # add basic configuration logging.basicConfig(filename='fix_imap_internaldate.log', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=config.getint('basic_settings', 'file_log_level')) # add a handler for a console output console = logging.StreamHandler() console.setLevel(config.getint('basic_settings', 'console_log_level')) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') console.setFormatter(formatter) logging.getLogger('').addHandler(console) return if(__name__ == "__main__"): main()