From 3103ebb03398c1b51a531a688a363cb6a962d499 Mon Sep 17 00:00:00 2001 From: Plamen Dimitrov Date: Thu, 28 Jun 2012 16:23:00 +0200 Subject: [PATCH] File hierarchy added --- .gitignore | 8 +- caching_data.py | 113 ----- confscript.cfg | 6 - fix_imap_internaldate.py | 160 ------ mail_date_parser.py | 100 ---- mail_iterator.py | 116 ----- mailbox_state.py | 109 ----- sample_performance_report.txt | 1066 ----------------------------------------- src/caching_data.py | 113 +++++ src/confscript.cfg | 6 + src/fix_imap_internaldate.py | 160 ++++++ src/mail_date_parser.py | 100 ++++ src/mail_iterator.py | 124 +++++ src/mailbox_state.py | 109 +++++ src/unit_tester.py | 109 +++++ src/userdata.csv | 4 + unit_tester.py | 109 ----- userdata.csv | 4 - 18 files changed, 729 insertions(+), 1787 deletions(-) delete mode 100644 caching_data.py delete mode 100644 confscript.cfg delete mode 100644 fix_imap_internaldate.py delete mode 100644 mail_date_parser.py delete mode 100644 mail_iterator.py delete mode 100644 mailbox_state.py delete mode 100644 sample_performance_report.txt create mode 100644 src/caching_data.py create mode 100644 src/confscript.cfg create mode 100644 src/fix_imap_internaldate.py create mode 100644 src/mail_date_parser.py create mode 100644 src/mail_iterator.py create mode 100644 src/mailbox_state.py create mode 100644 src/unit_tester.py create mode 100644 src/userdata.csv delete mode 100644 unit_tester.py delete mode 100644 userdata.csv diff --git a/.gitignore b/.gitignore index 978f1b0..fd368ef 100644 --- a/.gitignore +++ b/.gitignore @@ -11,10 +11,10 @@ build/ bin/ -.metadata/ -.project -.pydevproject -__pycache__/ +src/.metadata/ +src/.project +src/.pydevproject +src/__pycache__/ # kdevelop .kdev4 diff --git a/caching_data.py b/caching_data.py deleted file mode 100644 index c578a3b..0000000 --- a/caching_data.py +++ /dev/null @@ -1,113 +0,0 @@ -''' -caching_data.py - The module contains the CachingData class. - -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 os, tempfile -import pickle -import logging -from mailbox_state import MailboxState - -CACHE_FILENAME = "message_cache.dat" -CACHE_VERSION = 1 - -class CachingData: - """This class is responsible for the caching of data.""" - - # class attributes - # integer for version of the cache - version = None - # dictionary of usernames as keys and dictionaries as values - # the second dictionaries have unique mailbox keys and mailboxes as values - data = None - - def __init__(self): - # open data file or create one and initialize date if not found - try: - cachefile = open(CACHE_FILENAME, 'rb') - self.version, self.data = pickle.load(cachefile) - if(self.version != CACHE_VERSION): - logging.warning("Cache file has version %s and the script version is %s.", - self.version, CACHE_VERSION) - raise IOError - logging.info("Cache version %s", self.version) - logging.debug("%s users found.", len(self.data)) - except IOError: - self.version = CACHE_VERSION - self.data = {} - with open(CACHE_FILENAME, 'wb') as cachefile: - pickle.dump((self.version, self.data), cachefile) - - def __del__(self): - # create temporary file first - location = os.path.dirname(CACHE_FILENAME) - file_descriptor, tmpname = tempfile.mkstemp(dir=location) - try: - cachefile = os.fdopen(file_descriptor, 'wb') - - # prepare data based on a save flag - saved_data = {} - for user in self.data: - saved_data[user] = {} - for box_key in self.data[user]: - if(self.data[user][box_key].needs_save): - saved_data[user][box_key] = self.data[user][box_key] - logging.debug("The mailbox %s will be saved.", saved_data[user][box_key].name) - if(len(saved_data[user])==0): - del saved_data[user] - logging.debug("The user %s will not be saved.", user) - self.data = saved_data - - # avoid test mode or cases where nothing needs saving - if(len(saved_data)==0): - os.unlink(tmpname) - return - - # serialize in file - pickle.dump((self.version, self.data), cachefile) - logging.debug("%s users stored.", len(self.data)) - cachefile.close() - os.rename(tmpname, CACHE_FILENAME) - except: - os.unlink(tmpname) - - def retrieve_cached_mailbox(self, name, uidvalidity, user): - """Retrieve a cached mailbox or create it.""" - box_key = name.strip('"') + uidvalidity - if(user not in self.data): - self.data[user] = {} - logging.debug("New user %s cached.", user) - if(box_key not in self.data[user]): - self.data[user][box_key] = MailboxState(name, uidvalidity, user) - logging.debug("New mailbox %s cached.", box_key) - return self.data[user][box_key] - - def report_conflicts(self): - """Write a date conflicts report in a file.""" - with open("conflict_stats.txt", 'w') as statsfile: - owner_total_conflicts = {} - owner_total_missing = {} - for user in self.data: - owner_total_conflicts[user] = 0 - owner_total_missing[user] = 0 - for box_key in self.data[user]: - owner_total_conflicts[user] += self.data[user][box_key].date_conflicts - owner_total_missing[user] += self.data[user][box_key].no_received_field - statsfile.write("Total date conflicts to be corrected in a mailbox {0} are {1}.\n"\ - .format(self.data[user][box_key].name, self.data[user][box_key].date_conflicts)) - statsfile.write("Total messages without received headers in a mailbox {0} are {1}.\n"\ - .format(self.data[user][box_key].name, self.data[user][box_key].no_received_field)) - statsfile.write("Total date conflicts to be corrected for user {0} are {1}.\n\n"\ - .format(user, owner_total_missing[user])) - return diff --git a/confscript.cfg b/confscript.cfg deleted file mode 100644 index 5e6f10a..0000000 --- a/confscript.cfg +++ /dev/null @@ -1,6 +0,0 @@ -[basic_settings] -# 0 NOTSET, 10 DEBUG, 20 INFO, 30 WARNING, 40 ERROR, 50 CRITICAL -file_log_level = 10 -console_log_level = 20 -imap_server = intranator.m.i2n -tolerance = 120 diff --git a/fix_imap_internaldate.py b/fix_imap_internaldate.py deleted file mode 100644 index cc46e4d..0000000 --- a/fix_imap_internaldate.py +++ /dev/null @@ -1,160 +0,0 @@ -''' -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 -# python version handling -try: - import configparser -except ImportError: - print("This module needs python version 3 or later.") - sys.exit() -import logging -from mail_date_parser import MailDateParser -from mail_iterator import MailIterator -from caching_data import CachingData - -def main(): - """Interprets command arguments and initializes configuration and logger. - Then begins mail synchronization.""" - - # parse arguments - parser = argparse.ArgumentParser(description="Fix the INTERNALDATE field on IMAP servers. " - "Small tool to fix the IMAP internaldate " - "in case it's too much off compared to the last date " - "stored in the received lines.") - parser.add_argument('-u', '--update', dest='test_mode', action='store_false', - default=True, help='update all e-mails and exit test mode') - - # config and logging setup - config = load_configuration() - prepare_logger(config) - args = parser.parse_args() - if(args.test_mode): - logging.info("Testing mode initiated. No message will be modified on the server.") - else: - logging.info("Update mode initiated. Messages will be modified.") - - # proceed to main functionality - try: - synchronize_csv(config, args.test_mode) - except KeyboardInterrupt: - logging.info("Script was interrupted by the user.") - - return - -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) - 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 - -def synchronize_csv(config, test_mode): - """Iterates through csv list of users and synchronizes their messages.""" - - # initialize loop permanent data - caching_data = CachingData() - date_parser = MailDateParser() - server = config.get('basic_settings', 'imap_server') - tolerance = config.getint('basic_settings', 'tolerance') * 60 - - # iterate through the users in the csv data - user_reader = csv.DictReader(open("userdata.csv", "r"), delimiter=',') - 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, tolerance) - 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)): - 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]) - if(test_mode==0): - try: - session.update_message(mid, box.name, received_date) - except UserWarning as ex: - logging.error(ex) - continue - - # 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_conflicts() - return - -if(__name__ == "__main__"): - main() diff --git a/mail_date_parser.py b/mail_date_parser.py deleted file mode 100644 index d957d23..0000000 --- a/mail_date_parser.py +++ /dev/null @@ -1,100 +0,0 @@ -''' -mail_date_parser.py - The module contains the MailDateParser class. - -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 datetime, time -import re -import logging - -#reg expressions -RECEIVED_DATE = re.compile(r'(0?[1-9]|[1-2][0-9]|3[01])\s+([A-Z][a-z][a-z])\s+' - r'(19[0-9]{2}|[2-9][0-9]{3}|[0-9]{2})\s+(2[0-3]|[0-1][0-9]):([0-5][0-9])(?::(60|[0-5][0-9]))?\s*' - r'(?:([-\+])([0-9]{2})([0-5][0-9]))*') -INTERNAL_DATE = re.compile(r'(?P[ 0123][0-9])-(?P[A-Z][a-z][a-z])-(?P[0-9][0-9][0-9][0-9])' - r' (?P[0-9][0-9]):(?P[0-9][0-9]):(?P[0-9][0-9])' - r' (?P[-+])(?P[0-9][0-9])(?P[0-9][0-9])') -CONTROL_SYMBOLS = re.compile(r'[\n\r\t]') - -class MailDateParser: - """This class extracts dates from imap server responses and compares them. - This class contains only static methods.""" - - def __init__(self): - return - - @classmethod - def extract_internal_date(cls, fetchresult): - """Extracts the internal date from INTERNALDATE, returns datetime.""" - return datetime.datetime.fromtimestamp(time.mktime(fetchresult)) - - @classmethod - def extract_received_date(cls, fetchresult): - """Extracts the first date from RECEIVED, returns datetime.""" - fetchresult = CONTROL_SYMBOLS.sub('', fetchresult) - received_dates = RECEIVED_DATE.findall(fetchresult) - if(len(received_dates)==0): - return "" - else: received_date = received_dates[0] - logging.debug("Retrieved date %s from header %s.", received_date, fetchresult) - month = datetime.datetime.strptime(received_date[1],'%b').month - - if(received_date[3]!=""): - hours = int(received_date[3]) - else: hours = 0 - if(received_date[4]!=""): - minutes = int(received_date[4]) - else: minutes = 0 - if(received_date[5]!=""): - seconds = int(received_date[5]) - else: seconds = 0 - - if(received_date[6]!=""): - zonen = received_date[6] - else: zonen = b'+' - if(received_date[7]!=""): - zoneh = int(received_date[7]) - else: zoneh = 0 - if(received_date[8]!=""): - zonem = int(received_date[8]) - else: zonem = 0 - # subtract time zone to get unified time - zone = (zoneh * 60 + zonem) * 60 - if(zonen == b'-'): - zone = -zone - - time_tuple = (int(received_date[2]), month, int(received_date[0]), hours, minutes, seconds, -1, -1, -1) - #'mktime' assumes arg in local timezone, so add timezone/altzone - utc = time.mktime(time_tuple) - #adjust to DST - if(time.daylight and time.localtime(utc)[-1]): - zone = zone + time.altzone - else: - zone = zone + time.timezone - - received_time_tuple = time.localtime(utc - zone) - converted_received_date = datetime.datetime.fromtimestamp(time.mktime(received_time_tuple)) - return converted_received_date - - @classmethod - def compare_dates(cls, date1, date2, tolerance=1800): - """Compares datetime objects for deviation given certain tolerance.""" - """Returns 1 if there is a significant difference.""" - logging.debug("Comparing dates %s <> %s.", date1, date2) - timedelta = abs(date1 - date2) - if(timedelta.total_seconds()>tolerance): - return True - else: - return False diff --git a/mail_iterator.py b/mail_iterator.py deleted file mode 100644 index a17ab6c..0000000 --- a/mail_iterator.py +++ /dev/null @@ -1,116 +0,0 @@ -''' -mail_iterator.py - The module contains the MailIterator class. - -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 imaplib -import re -import time -import logging - -MAILBOX_RESP = re.compile(r'\((?P.*?)\) "(?P.*)" (?P.*)') -UIDVAL_RESP = re.compile(r'(?P.*) \(UIDVALIDITY (?P.*)\)') - -class MailIterator: - """This class communicates with the e-mail server.""" - - # class attributes - # IMAP4_SSL for connection with an IMAP server - mail_con = None - # list of tuples (uidvalidity, mailboxname) for the retrieved mailboxes - mailboxes = None - # logged in status - logged_in = None - - def __init__(self, server, username, password): - """Creates a connection and a user session.""" - try: - self.mail_con = imaplib.IMAP4_SSL(server) - self.mail_con.login(username, password) - logging.info("Logged in as %s.", username) - except: - self.logged_in = False - raise UserWarning("Could not log in as user " + username + ".") - self.logged_in = True - result, self.mailboxes = self.mail_con.list() - if(result!="OK"): - raise UserWarning("Could not retrieve mailboxes for user " + username + ".") - - def __del__(self): - """Closes the connection and the user session.""" - if(self.logged_in): - self.mail_con.close() - self.mail_con.logout() - - def __iter__(self): - """Iterates through all mailboxes, returns (uidval,name).""" - for mailbox in self.mailboxes: - logging.debug("Checking mailbox %s.", mailbox) - mailbox = MAILBOX_RESP.match(mailbox.decode('iso-8859-1')).groups() - result, data = self.mail_con.status(mailbox[2], '(UIDVALIDITY)') - if(result!="OK"): - raise UserWarning("Could not retrieve mailbox uidvalidity.") - uidval = UIDVAL_RESP.match(data[0].decode('iso-8859-1')).groups() - logging.debug("Extracted mailbox info is %s %s.", data[0], uidval) - self.mail_con.select(mailbox[2]) - yield (mailbox[2], uidval[1]) - - def fetch_messages(self): - """Fetches the messages from the current mailbox, return list of uids.""" - result, data = self.mail_con.uid('search', None, "ALL") - if(result!="OK"): - raise UserWarning("Could not fetch messages.") - mailid_list = data[0].split() - return mailid_list - - def fetch_internal_date(self, mid): - """Fetches the internal date of a message, returns a time tuple.""" - result, data = self.mail_con.uid('fetch', mid, '(INTERNALDATE)') - if(result!="OK"): - raise UserWarning("Could not fetch the internal date of message" + mid + ".") - internal_date = imaplib.Internaldate2tuple(data[0]) - return internal_date - - def fetch_received_date(self, mid): - """Fetches the received date of a message, returns bytes reponse.""" - result, data = self.mail_con.uid('fetch', mid, '(BODY.PEEK[HEADER.FIELDS (RECEIVED)])') - if(result!="OK"): - raise UserWarning("Could not fetch the received header of message" + mid + ".") - return data[0][1].decode('iso-8859-1') - - def update_message(self, mid, mailbox, internal_date): - """Replaces a message with one with correct internal date.""" - internal_date_seconds = time.mktime(internal_date.timetuple()) - internal_date_str = imaplib.Time2Internaldate(internal_date_seconds) - result, data = self.mail_con.uid('fetch', mid, '(RFC822)') - if(result!="OK"): - raise UserWarning("Could not retrieve the entire e-mail" + mid + ".") - #logging.debug("Entire e-mail is: %s", data[0][1]) - - fetched_flags = self.mail_con.uid('fetch', mid, '(FLAGS)')[1][0] - parsed_flags = imaplib.ParseFlags(fetched_flags) - flags_str = " ".join(flag.decode('iso-8859-1') for flag in parsed_flags) - result, data = self.mail_con.append(mailbox, flags_str, - internal_date_str, data[0][1]) - logging.debug("Adding corrected copy of the message reponse: %s %s", result, data) - if(result!="OK"): - raise UserWarning("Could not replace the e-mail" + mid + ".") - else: - result, data = self.mail_con.uid('STORE', mid, '+FLAGS', r'(\Deleted)') - logging.debug("Removing old copy of the message reponse: %s %s", result, data) - if(result!="OK"): - raise UserWarning("Could not delete the e-mail" + mid + ".") - else: self.mail_con.expunge() - return diff --git a/mailbox_state.py b/mailbox_state.py deleted file mode 100644 index 2d1e3af..0000000 --- a/mailbox_state.py +++ /dev/null @@ -1,109 +0,0 @@ -''' -mailbox_state.py - The module contains the MailboxState class. - -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. -''' - -class MailboxState: - """This class is responsible for containing and updating a mailbox data.""" - - # class attributes - # string with quotation marks for the mailbox name - name = None - # string for the mailbox uidvalidity - uidvalidity = None - # string for user owning the mailbox - owner = None - # list of bytes for last cached mail uids - uids = None - # tolerance with which the mailbox was synced - tolerance = None - # boolean flag for committing state changes - needs_save = None - # integer for found date conflicts - date_conflicts = None - # integer for found messages with missing received headers - no_received_field = None - # unique key for a mailbox - key = None - - def __init__(self, name, uidvalidity, owner): - self.name = name - self.uidvalidity = uidvalidity - self.owner = owner - - self.uids = [] - self.tolerance = 0 - self.needs_save = False - - self.date_conflicts = 0 - self.no_received_field = 0 - - #special key to ensure better mailbox uniqueness - self.key = self.name.strip('"') + self.uidvalidity - - return - - def __getstate__(self): - """Prepares the MailboxState instance for pickling.""" - changed_dict = self.__dict__.copy() - # remove the following attributes for pickling - del changed_dict['needs_save'] - del changed_dict['date_conflicts'] - del changed_dict['no_received_field'] - return changed_dict - - def __setstate__(self, dict): - """Prepares the MailboxState instance for unpickling.""" - self.name = dict["name"] - self.uidvalidity = dict["uidvalidity"] - self.owner = dict["owner"] - - self.uids = dict["uids"] - self.tolerance = dict["tolerance"] - self.needs_save = False - - self.date_conflicts = 0 - self.no_received_field = 0 - - self.key = dict["key"] - - return - - def __str__(self): - """Makes the class printable.""" - return self.key - - def synchronize(self, list_ids, tolerance): - """Adds new messages to the cache and returns a list of them. - Confirm the changes to a mailbox to finally save it.""" - new_ids = [] - # cache is invalid if mailbox is synced with different tolerance - if(len(self.uids)==0 or tolerance != self.tolerance): - new_ids = list_ids - self.tolerance = tolerance - else: - for uid in list_ids: - try: - self.uids.index(uid) - except ValueError: - new_ids.append(uid) - # update this mailbox potential uids - self.uids = list_ids - return new_ids - - def confirm_change(self): - """Confirm the chages to the cached mailbox and specify used tolerance.""" - self.needs_save = True - return diff --git a/sample_performance_report.txt b/sample_performance_report.txt deleted file mode 100644 index 8b80c4b..0000000 --- a/sample_performance_report.txt +++ /dev/null @@ -1,1066 +0,0 @@ -CACHING PERFORMANCE REPORT: ---------------------------- - -STATISTICS WITHOUT CACHING: - -64349556 function calls (64349003 primitive calls) in 1052.038 seconds - - Ordered by: standard name - - ncalls tottime percall cumtime percall filename:lineno(function) - 1 0.000 0.000 0.000 0.000 __init__.py:1001(_StderrHandler) - 1 0.000 0.000 0.000 0.000 __init__.py:1007(__init__) - 1 0.000 0.000 0.000 0.000 __init__.py:1025(PlaceHolder) - 1 0.000 0.000 0.000 0.000 __init__.py:1069(Manager) - 1 0.000 0.000 0.000 0.000 __init__.py:1074(__init__) - 1 0.000 0.000 0.000 0.000 __init__.py:1177(Logger) - 1 0.000 0.000 0.000 0.000 __init__.py:1192(__init__) - 1 0.000 0.000 0.000 0.000 __init__.py:1204(setLevel) - 135 0.001 0.000 0.042 0.000 __init__.py:1234(warning) - 135 0.002 0.000 0.003 0.000 __init__.py:1298(findCaller) - 135 0.001 0.000 0.009 0.000 __init__.py:1328(makeRecord) - 135 0.001 0.000 0.040 0.000 __init__.py:1343(_log) - 135 0.001 0.000 0.026 0.000 __init__.py:1366(handle) - 1 0.000 0.000 0.000 0.000 __init__.py:1376(addHandler) - 135 0.001 0.000 0.026 0.000 __init__.py:1420(callHandlers) - 135 0.000 0.000 0.000 0.000 __init__.py:1450(getEffectiveLevel) - 135 0.000 0.000 0.001 0.000 __init__.py:1464(isEnabledFor) - 135 0.001 0.000 0.001 0.000 __init__.py:148(getLevelName) - 1 0.000 0.000 0.000 0.000 __init__.py:1491(RootLogger) - 1 0.000 0.000 0.000 0.000 __init__.py:1497(__init__) - 1 0.000 0.000 0.000 0.000 __init__.py:1505(LoggerAdapter) - 1 0.000 0.000 0.001 0.001 __init__.py:1625(basicConfig) - 135 0.001 0.000 0.043 0.000 __init__.py:1735(warning) - 4 0.000 0.000 0.000 0.000 __init__.py:177(_checkLevel) - 1 0.000 0.000 0.000 0.000 __init__.py:1819(NullHandler) - 4 0.000 0.000 0.000 0.000 __init__.py:206(_acquireLock) - 4 0.000 0.000 0.000 0.000 __init__.py:215(_releaseLock) - 1 0.000 0.000 0.000 0.000 __init__.py:226(LogRecord) - 135 0.004 0.000 0.009 0.000 __init__.py:238(__init__) - 1 0.035 0.035 0.037 0.037 __init__.py:24() - 135 0.001 0.000 0.001 0.000 __init__.py:308(getMessage) - 1 0.000 0.000 0.000 0.000 __init__.py:357(PercentStyle) - 2 0.000 0.000 0.000 0.000 __init__.py:363(__init__) - 135 0.000 0.000 0.001 0.000 __init__.py:366(usesTime) - 135 0.001 0.000 0.001 0.000 __init__.py:369(format) - 1 0.000 0.000 0.000 0.000 __init__.py:372(StrFormatStyle) - 1 0.000 0.000 0.000 0.000 __init__.py:381(StringTemplateStyle) - 1 0.000 0.000 0.000 0.000 __init__.py:403(Formatter) - 1 0.000 0.000 0.000 0.000 __init__.py:42(normalize_encoding) - 2 0.000 0.000 0.000 0.000 __init__.py:447(__init__) - 135 0.001 0.000 0.003 0.000 __init__.py:469(formatTime) - 135 0.000 0.000 0.001 0.000 __init__.py:514(usesTime) - 135 0.000 0.000 0.001 0.000 __init__.py:520(formatMessage) - 135 0.001 0.000 0.007 0.000 __init__.py:536(format) - 1 0.000 0.000 0.000 0.000 __init__.py:573(BufferingFormatter) - 1 0.000 0.000 0.000 0.000 __init__.py:615(Filter) - 1 0.000 0.000 0.000 0.000 __init__.py:652(Filterer) - 3 0.000 0.000 0.000 0.000 __init__.py:657(__init__) - 270 0.001 0.000 0.001 0.000 __init__.py:677(filter) - 1 0.000 0.000 0.032 0.032 __init__.py:69(search_function) - 2 0.000 0.000 0.000 0.000 __init__.py:722(_addHandlerRef) - 1 0.000 0.000 0.000 0.000 __init__.py:732(Handler) - 2 0.000 0.000 0.000 0.000 __init__.py:741(__init__) - 135 0.000 0.000 0.001 0.000 __init__.py:76() - 2 0.000 0.000 0.000 0.000 __init__.py:770(createLock) - 135 0.000 0.000 0.000 0.000 __init__.py:779(acquire) - 135 0.000 0.000 0.000 0.000 __init__.py:786(release) - 135 0.000 0.000 0.007 0.000 __init__.py:799(format) - 135 0.001 0.000 0.025 0.000 __init__.py:822(handle) - 1 0.000 0.000 0.000 0.000 __init__.py:840(setFormatter) - 1 0.000 0.000 0.000 0.000 __init__.py:896(StreamHandler) - 1 0.000 0.000 0.000 0.000 __init__.py:905(__init__) - 135 0.000 0.000 0.014 0.000 __init__.py:916(flush) - 135 0.001 0.000 0.022 0.000 __init__.py:923(emit) - 1 0.000 0.000 0.000 0.000 __init__.py:945(FileHandler) - 1 0.000 0.000 0.001 0.001 __init__.py:949(__init__) - 1 0.000 0.000 0.000 0.000 __init__.py:979(_open) - 135 0.000 0.000 0.023 0.000 __init__.py:990(emit) - 3 0.000 0.000 0.000 0.000 _abcoll.py:374(items) - 3 0.000 0.000 0.000 0.000 _abcoll.py:391(__init__) - 7 0.000 0.000 0.000 0.000 _abcoll.py:432(__iter__) - 4 0.000 0.000 0.000 0.000 _abcoll.py:493(update) - 1 0.000 0.000 0.000 0.000 _compat_pickle.py:8() - 52 0.000 0.000 0.000 0.000 _compat_pickle.py:80() - 11 0.000 0.000 0.000 0.000 _compat_pickle.py:81() - 1 0.000 0.000 0.000 0.000 _strptime.py:100() - 1 0.000 0.000 0.000 0.000 _strptime.py:101() - 1 0.000 0.000 0.000 0.000 _strptime.py:105(__calc_am_pm) - 1 0.000 0.000 0.000 0.000 _strptime.py:117(__calc_date_time) - 1 0.013 0.013 0.016 0.016 _strptime.py:12() - 1 0.000 0.000 0.000 0.000 _strptime.py:138() - 1 0.000 0.000 0.000 0.000 _strptime.py:162(__calc_timezone) - 1 0.000 0.000 0.000 0.000 _strptime.py:178(TimeRE) - 1 0.000 0.000 0.003 0.003 _strptime.py:181(__init__) - 5 0.000 0.000 0.000 0.000 _strptime.py:215() - 6 0.000 0.000 0.001 0.000 _strptime.py:224(__seqToRE) - 50 0.000 0.000 0.000 0.000 _strptime.py:239() - 4 0.000 0.000 0.002 0.000 _strptime.py:243(pattern) - 1 0.000 0.000 0.002 0.002 _strptime.py:266(compile) - 301750 0.739 0.000 6.261 0.000 _strptime.py:29(_getlang) - 301748 8.749 0.000 19.785 0.000 _strptime.py:298(_strptime) - 1 0.000 0.000 0.000 0.000 _strptime.py:33(LocaleTime) - 301748 1.708 0.000 21.493 0.000 _strptime.py:485(_strptime_datetime) - 1 0.000 0.000 0.001 0.001 _strptime.py:52(__init__) - 1 0.000 0.000 0.000 0.000 _strptime.py:90(__calc_weekday) - 1 0.000 0.000 0.000 0.000 _strptime.py:93() - 1 0.000 0.000 0.000 0.000 _strptime.py:94() - 1 0.000 0.000 0.000 0.000 _strptime.py:98(__calc_month) - 16 0.000 0.000 0.000 0.000 _weakrefset.py:36(__init__) - 8 0.000 0.000 0.000 0.000 _weakrefset.py:68(__contains__) - 1 0.000 0.000 0.000 0.000 _weakrefset.py:79(add) - 5 0.000 0.000 0.000 0.000 abc.py:116(__new__) - 5 0.000 0.000 0.000 0.000 abc.py:119() - 4 0.000 0.000 0.000 0.000 abc.py:158(__instancecheck__) - 1 0.000 0.000 0.000 0.000 base64.py:148() - 1 0.000 0.000 0.000 0.000 base64.py:149() - 1 0.000 0.000 0.000 0.000 base64.py:3() - 1 0.000 0.000 0.000 0.000 calendar.py:126(Calendar) - 1 0.000 0.000 0.000 0.000 calendar.py:132(__init__) - 1 0.000 0.000 0.000 0.000 calendar.py:138(setfirstweekday) - 1 0.000 0.000 0.000 0.000 calendar.py:21(IllegalMonthError) - 1 0.000 0.000 0.000 0.000 calendar.py:255(TextCalendar) - 1 0.000 0.000 0.000 0.000 calendar.py:28(IllegalWeekdayError) - 1 0.000 0.000 0.000 0.000 calendar.py:372(HTMLCalendar) - 1 0.000 0.000 0.000 0.000 calendar.py:47(_localized_month) - 1 0.000 0.000 0.000 0.000 calendar.py:484(different_locale) - 1 0.000 0.000 0.000 0.000 calendar.py:49() - 1 0.000 0.000 0.000 0.000 calendar.py:496(LocaleTextCalendar) - 2 0.000 0.000 0.000 0.000 calendar.py:50() - 2 0.000 0.000 0.000 0.000 calendar.py:52(__init__) - 1 0.000 0.000 0.000 0.000 calendar.py:527(LocaleHTMLCalendar) - 26 0.000 0.000 0.000 0.000 calendar.py:55(__getitem__) - 1 0.000 0.000 0.000 0.000 calendar.py:6() - 1 0.000 0.000 0.000 0.000 calendar.py:66(_localized_day) - 1 0.000 0.000 0.000 0.000 calendar.py:69() - 2 0.000 0.000 0.000 0.000 calendar.py:71(__init__) - 14 0.000 0.000 0.000 0.000 calendar.py:74(__getitem__) - 1 0.000 0.000 0.000 0.000 cashing_data.py:16() - 1 0.000 0.000 0.000 0.000 cashing_data.py:20(CashingData) - 1 0.001 0.001 0.004 0.004 cashing_data.py:23(__init__) - 1 0.316 0.316 0.412 0.412 cashing_data.py:39(__del__) - 47 0.000 0.000 0.000 0.000 cashing_data.py:56(_cache_new_mailbox) - 47 0.000 0.000 0.000 0.000 cashing_data.py:68(sync_cached_mailbox) - 47 0.000 0.000 0.000 0.000 cashing_data.py:91(commit_cached_mailbox) - 1 0.000 0.000 0.000 0.000 codecs.py:164(__init__) - 1 0.000 0.000 0.000 0.000 codecs.py:192(setstate) - 2 0.000 0.000 0.000 0.000 codecs.py:238(__init__) - 2 0.000 0.000 0.000 0.000 codecs.py:287(__init__) - 4 0.000 0.000 0.000 0.000 codecs.py:297(decode) - 1 0.000 0.000 0.000 0.000 codecs.py:77(__new__) - 101 0.000 0.000 0.000 0.000 collections.py:130(move_to_end) - 4 0.000 0.000 0.000 0.000 collections.py:40(__init__) - 37 0.000 0.000 0.000 0.000 collections.py:57(__setitem__) - 3 0.000 0.000 0.000 0.000 collections.py:695(__init__) - 3 0.000 0.000 0.000 0.000 collections.py:705(__getitem__) - 7 0.000 0.000 0.000 0.000 collections.py:82(__iter__) - 1 0.000 0.000 0.000 0.000 configparser.py:1084(_join_multiline_values) - 3 0.000 0.000 0.000 0.000 configparser.py:1102(_unify_values) - 1 0.000 0.000 0.000 0.000 configparser.py:1151(ConfigParser) - 1 0.000 0.000 0.000 0.000 configparser.py:1170(SafeConfigParser) - 1 0.000 0.000 0.000 0.000 configparser.py:1183(SectionProxy) - 2 0.000 0.000 0.000 0.000 configparser.py:1186(__init__) - 1 0.000 0.000 0.012 0.012 configparser.py:120() - 1 0.000 0.000 0.000 0.000 configparser.py:144(Error) - 1 0.000 0.000 0.000 0.000 configparser.py:174(NoSectionError) - 1 0.000 0.000 0.000 0.000 configparser.py:183(DuplicateSectionError) - 1 0.000 0.000 0.000 0.000 configparser.py:209(DuplicateOptionError) - 1 0.000 0.000 0.000 0.000 configparser.py:236(NoOptionError) - 1 0.000 0.000 0.000 0.000 configparser.py:247(InterpolationError) - 1 0.000 0.000 0.000 0.000 configparser.py:257(InterpolationMissingOptionError) - 1 0.000 0.000 0.000 0.000 configparser.py:272(InterpolationSyntaxError) - 1 0.000 0.000 0.000 0.000 configparser.py:280(InterpolationDepthError) - 1 0.000 0.000 0.000 0.000 configparser.py:293(ParsingError) - 1 0.000 0.000 0.000 0.000 configparser.py:336(MissingSectionHeaderError) - 1 0.000 0.000 0.000 0.000 configparser.py:356(Interpolation) - 3 0.000 0.000 0.000 0.000 configparser.py:359(before_get) - 3 0.000 0.000 0.000 0.000 configparser.py:365(before_read) - 1 0.000 0.000 0.001 0.001 configparser.py:372(BasicInterpolation) - 1 0.000 0.000 0.000 0.000 configparser.py:443(ExtendedInterpolation) - 1 0.000 0.000 0.001 0.001 configparser.py:516(LegacyInterpolation) - 1 0.000 0.000 0.010 0.010 configparser.py:554(RawConfigParser) - 1 0.000 0.000 0.000 0.000 configparser.py:595(__init__) - 1 0.000 0.000 0.001 0.001 configparser.py:671(read) - 3 0.000 0.000 0.000 0.000 configparser.py:755(get) - 2 0.000 0.000 0.000 0.000 configparser.py:792(_get) - 2 0.000 0.000 0.000 0.000 configparser.py:795(getint) - 6 0.000 0.000 0.000 0.000 configparser.py:855(optionxform) - 1 0.000 0.000 0.000 0.000 configparser.py:970(_read) - 2 0.000 0.000 0.001 0.000 csv.py:106(__next__) - 1 0.000 0.000 0.000 0.000 csv.py:129(DictWriter) - 1 0.000 0.000 0.000 0.000 csv.py:167(Sniffer) - 1 0.000 0.000 0.000 0.000 csv.py:22(Dialect) - 1 0.005 0.005 0.006 0.006 csv.py:4() - 1 0.000 0.000 0.000 0.000 csv.py:53(excel) - 1 0.000 0.000 0.000 0.000 csv.py:63(excel_tab) - 1 0.000 0.000 0.000 0.000 csv.py:68(unix_dialect) - 1 0.000 0.000 0.000 0.000 csv.py:79(DictReader) - 1 0.000 0.000 0.000 0.000 csv.py:80(__init__) - 1 0.000 0.000 0.000 0.000 csv.py:89(__iter__) - 3 0.000 0.000 0.000 0.000 csv.py:92(fieldnames) - 1 0.024 0.024 0.049 0.049 date_interpreter.py:20() - 1 0.000 0.000 0.000 0.000 date_interpreter.py:35(DateInterpreter) - 1 0.000 0.000 0.000 0.000 date_interpreter.py:38(__init__) - 301881 0.959 0.000 3.873 0.000 date_interpreter.py:41(extract_internal_date) - 301881 10.289 0.000 123.908 0.000 date_interpreter.py:46(extract_received_date) - 301748 1.135 0.000 1.791 0.000 date_interpreter.py:94(compare_dates) - 2 0.000 0.000 0.000 0.000 datetime.py:1017(__new__) - 1 0.000 0.000 0.000 0.000 datetime.py:1302(datetime) - 2 0.000 0.000 0.000 0.000 datetime.py:1312(__new__) - 1 0.013 0.013 0.014 0.014 datetime.py:17() - 1 0.000 0.000 0.000 0.000 datetime.py:1786(timezone) - 3 0.000 0.000 0.000 0.000 datetime.py:1810(_create) - 4 0.000 0.000 0.000 0.000 datetime.py:267(_check_date_fields) - 4 0.000 0.000 0.000 0.000 datetime.py:278(_check_time_fields) - 4 0.000 0.000 0.000 0.000 datetime.py:290(_check_tzinfo_arg) - 1 0.000 0.000 0.000 0.000 datetime.py:298(timedelta) - 9 0.000 0.000 0.001 0.000 datetime.py:317(__new__) - 1 0.000 0.000 0.000 0.000 datetime.py:500(__neg__) - 3 0.000 0.000 0.000 0.000 datetime.py:51(_days_before_year) - 4 0.000 0.000 0.000 0.000 datetime.py:56(_days_in_month) - 1 0.000 0.000 0.000 0.000 datetime.py:631(date) - 4 0.000 0.000 0.000 0.000 datetime.py:661(__new__) - 1 0.000 0.000 0.000 0.000 datetime.py:924(tzinfo) - 1 0.000 0.000 0.000 0.000 datetime.py:993(time) - 1 0.000 0.000 0.001 0.001 fix_imap_internaldate.py:108(load_configuration) - 1 0.070 0.070 1052.038 1052.038 fix_imap_internaldate.py:20() - 1 4.227 4.227 1050.771 1050.771 fix_imap_internaldate.py:30(main) - 129 0.001 0.000 0.053 0.000 functools.py:170(wrapper) - 135 0.001 0.000 0.001 0.000 genericpath.py:85(_splitext) - 1 0.008 0.008 0.008 0.008 hashlib.py:53() - 6 0.000 0.000 0.000 0.000 hashlib.py:94(__get_openssl_constructor) - 1 0.000 0.000 0.000 0.000 idna.py:146(Codec) - 2 0.000 0.000 0.000 0.000 idna.py:147(encode) - 1 0.000 0.000 0.000 0.000 idna.py:196(IncrementalEncoder) - 1 0.000 0.000 0.000 0.000 idna.py:231(IncrementalDecoder) - 1 0.000 0.000 0.000 0.000 idna.py:270(StreamWriter) - 1 0.000 0.000 0.000 0.000 idna.py:273(StreamReader) - 1 0.000 0.000 0.000 0.000 idna.py:278(getregentry) - 1 0.010 0.010 0.029 0.029 idna.py:3() - 7 0.000 0.000 0.000 0.000 idna.py:62(ToASCII) - 603908 3.232 0.000 833.897 0.001 imaplib.py:1050(_get_tagged_response) - 1 0.000 0.000 0.000 0.000 imaplib.py:107(IMAP4) - 1510072 13.473 0.000 781.901 0.001 imaplib.py:1072(_get_line) - 3926639 8.919 0.000 17.622 0.000 imaplib.py:1091(_match) - 1 0.052 0.052 0.157 0.157 imaplib.py:11() - 603908 3.824 0.000 3.824 0.000 imaplib.py:1103(_new_tag) - 1 0.000 0.000 0.000 0.000 imaplib.py:1111(_quote) - 603908 2.429 0.000 890.173 0.001 imaplib.py:1119(_simple_command) - 603858 1.824 0.000 2.359 0.000 imaplib.py:1124(_untagged_response) - 2113980 8.619 0.000 11.107 0.000 imaplib.py:1153(_log) - 1 0.000 0.000 0.000 0.000 imaplib.py:1176(IMAP4_SSL) - 1 0.000 0.000 0.114 0.114 imaplib.py:1191(__init__) - 1 0.000 0.000 0.073 0.073 imaplib.py:1196(_create_socket) - 1 0.000 0.000 0.073 0.073 imaplib.py:1200(open) - 1 0.000 0.000 0.000 0.000 imaplib.py:1211(IMAP4_stream) - 1 0.000 0.000 0.000 0.000 imaplib.py:1267(_Authenticator) - 301881 8.513 0.000 17.287 0.000 imaplib.py:1314(Internaldate2tuple) - 1 0.000 0.000 0.000 0.000 imaplib.py:1359(Int2AP) - 1 0.000 0.000 0.000 0.000 imaplib.py:153(error) - 1 0.000 0.000 0.000 0.000 imaplib.py:154(abort) - 1 0.000 0.000 0.000 0.000 imaplib.py:155(readonly) - 1 0.000 0.000 0.114 0.114 imaplib.py:157(__init__) - 1 0.000 0.000 0.041 0.041 imaplib.py:182(_connect) - 1 0.000 0.000 0.034 0.034 imaplib.py:235(_create_socket) - 1 0.000 0.000 0.073 0.073 imaplib.py:238(open) - 301881 2.303 0.000 4.942 0.000 imaplib.py:250(read) - 1510072 2.630 0.000 758.640 0.001 imaplib.py:263(readline) - 603908 1.442 0.000 26.567 0.000 imaplib.py:268(send) - 1 0.000 0.000 0.000 0.000 imaplib.py:273(shutdown) - 1 0.000 0.000 0.001 0.001 imaplib.py:381(capability) - 1 0.000 0.000 0.503 0.503 imaplib.py:398(close) - 1 0.000 0.000 0.004 0.004 imaplib.py:511(list) - 1 0.000 0.000 0.001 0.001 imaplib.py:523(login) - 1 0.000 0.000 0.001 0.001 imaplib.py:552(logout) - 47 0.000 0.000 2.953 0.063 imaplib.py:656(select) - 47 0.000 0.000 0.118 0.003 imaplib.py:751(status) - 603809 4.002 0.000 893.499 0.001 imaplib.py:792(uid) - 906449 2.102 0.000 2.383 0.000 imaplib.py:847(_append_untagged) - 1207814 2.410 0.000 3.381 0.000 imaplib.py:861(_check_bye) - 603908 11.404 0.000 46.967 0.000 imaplib.py:867(_command) - 603908 3.500 0.000 840.777 0.001 imaplib.py:944(_command_complete) - 1 0.000 0.000 0.001 0.001 imaplib.py:961(_get_capabilities) - 1208191 19.106 0.000 830.703 0.001 imaplib.py:970(_get_response) - 301750 2.111 0.000 3.297 0.000 locale.py:339(normalize) - 301750 0.674 0.000 3.971 0.000 locale.py:405(_parse_localename) - 301750 0.900 0.000 5.522 0.000 locale.py:508(getlocale) - 9 0.000 0.000 0.000 0.000 locale.py:526(setlocale) - 3 0.000 0.000 0.000 0.000 locale.py:574(getpreferredencoding) - 1 0.018 0.018 0.177 0.177 mail_iterator.py:20() - 1 0.000 0.000 0.000 0.000 mail_iterator.py:29(MailIterator) - 1 0.000 0.000 0.120 0.120 mail_iterator.py:32(__init__) - 1 0.000 0.000 0.504 0.504 mail_iterator.py:42(__del__) - 48 0.001 0.000 3.072 0.064 mail_iterator.py:47(__iter__) - 47 0.000 0.000 0.327 0.007 mail_iterator.py:60(fetch_messages) - 301881 1.842 0.000 369.425 0.001 mail_iterator.py:69(fetch_internal_date) - 301881 1.076 0.000 543.976 0.002 mail_iterator.py:77(fetch_received_date) - 1 0.000 0.000 0.000 0.000 os.py:35(_get_exports_list) - 1 0.000 0.000 0.000 0.000 os.py:39() - 1 0.000 0.000 0.002 0.002 pickle.py:173() - 1 0.000 0.000 0.000 0.000 pickle.py:177(_Pickler) - 1 0.012 0.012 0.014 0.014 pickle.py:24() - 1 0.000 0.000 0.000 0.000 pickle.py:68(PickleError) - 1 0.000 0.000 0.000 0.000 pickle.py:72(PicklingError) - 1 0.000 0.000 0.000 0.000 pickle.py:781(_Unpickler) - 1 0.000 0.000 0.000 0.000 pickle.py:79(UnpicklingError) - 1 0.000 0.000 0.000 0.000 pickle.py:92(_Stop) - 135 0.001 0.000 0.002 0.000 posixpath.py:108(splitext) - 135 0.001 0.000 0.001 0.000 posixpath.py:129(basename) - 1 0.000 0.000 0.000 0.000 posixpath.py:330(normpath) - 1 0.000 0.000 0.000 0.000 posixpath.py:367(abspath) - 137 0.000 0.000 0.000 0.000 posixpath.py:38(_get_sep) - 271 0.001 0.000 0.001 0.000 posixpath.py:49(normcase) - 1 0.000 0.000 0.000 0.000 posixpath.py:61(isabs) - 1 0.000 0.000 0.000 0.000 posixpath.py:71(join) - 1 0.000 0.000 0.000 0.000 random.py:165(randrange) - 1 0.000 0.000 0.000 0.000 random.py:210(randint) - 1 0.000 0.000 0.000 0.000 random.py:216(_randbelow) - 1 0.013 0.013 0.021 0.021 random.py:37() - 1 0.000 0.000 0.000 0.000 random.py:625(SystemRandom) - 1 0.000 0.000 0.000 0.000 random.py:68(Random) - 1 0.000 0.000 0.000 0.000 random.py:84(__init__) - 1 0.000 0.000 0.000 0.000 random.py:93(seed) - 90 0.000 0.000 0.002 0.000 re.py:150(match) - 31 0.000 0.000 0.052 0.002 re.py:204(compile) - 44 0.000 0.000 0.000 0.000 re.py:222(escape) - 121 0.000 0.000 0.053 0.000 re.py:254(_compile) - 26 0.000 0.000 0.052 0.002 re.py:257(_compile_typed) - 2 0.000 0.000 0.000 0.000 re.py:269(_compile_repl) - 8 0.000 0.000 0.000 0.000 re.py:279(_subx) - 1 0.000 0.000 0.000 0.000 socket.py:141(makefile) - 1 0.000 0.000 0.000 0.000 socket.py:184(_decref_socketios) - 2 0.000 0.000 0.000 0.000 socket.py:190(_real_close) - 1 0.000 0.000 0.000 0.000 socket.py:194(close) - 1 0.000 0.000 0.000 0.000 socket.py:233(SocketIO) - 1 0.000 0.000 0.000 0.000 socket.py:250(__init__) - 604301 2.811 0.000 746.842 0.001 socket.py:262(readinto) - 604302 1.458 0.000 1.458 0.000 socket.py:304(readable) - 1 0.000 0.000 0.000 0.000 socket.py:331(close) - 1 0.000 0.000 0.034 0.034 socket.py:370(create_connection) - 1 0.006 0.006 0.006 0.006 socket.py:44() - 1 0.000 0.000 0.000 0.000 socket.py:87(socket) - 2 0.000 0.000 0.000 0.000 socket.py:93(__init__) - 92 0.001 0.000 0.017 0.000 sre_compile.py:178(_compile_charset) - 92 0.004 0.000 0.016 0.000 sre_compile.py:207(_optimize_charset) - 344 0.000 0.000 0.000 0.000 sre_compile.py:24(_identityfunction) - 10 0.001 0.000 0.001 0.000 sre_compile.py:258(_mk_bitmap) - 2 0.009 0.004 0.012 0.006 sre_compile.py:301(_optimize_unicode) - 168/26 0.003 0.000 0.013 0.000 sre_compile.py:32(_compile) - 51 0.000 0.000 0.000 0.000 sre_compile.py:355(_simple) - 26 0.000 0.000 0.011 0.000 sre_compile.py:362(_compile_info) - 52 0.000 0.000 0.000 0.000 sre_compile.py:468(isstring) - 26 0.000 0.000 0.024 0.001 sre_compile.py:471(_code) - 26 0.000 0.000 0.052 0.002 sre_compile.py:486(compile) - 208 0.000 0.000 0.000 0.000 sre_parse.py:127(__len__) - 609 0.001 0.000 0.002 0.000 sre_parse.py:131(__getitem__) - 51 0.000 0.000 0.000 0.000 sre_parse.py:135(__setitem__) - 326 0.001 0.000 0.001 0.000 sre_parse.py:139(append) - 219/77 0.001 0.000 0.002 0.000 sre_parse.py:141(getwidth) - 28 0.000 0.000 0.000 0.000 sre_parse.py:179(__init__) - 2155 0.009 0.000 0.013 0.000 sre_parse.py:183(__next) - 648 0.001 0.000 0.003 0.000 sre_parse.py:202(match) - 1827 0.003 0.000 0.014 0.000 sre_parse.py:208(get) - 5 0.000 0.000 0.000 0.000 sre_parse.py:212(tell) - 1 0.000 0.000 0.000 0.000 sre_parse.py:214(seek) - 184 0.000 0.000 0.000 0.000 sre_parse.py:217(isident) - 1 0.000 0.000 0.000 0.000 sre_parse.py:220(isdigit) - 44 0.000 0.000 0.000 0.000 sre_parse.py:223(isname) - 11 0.000 0.000 0.000 0.000 sre_parse.py:232(_class_escape) - 37 0.000 0.000 0.000 0.000 sre_parse.py:264(_escape) - 90/26 0.001 0.000 0.026 0.001 sre_parse.py:308(_parse_sub) - 112/29 0.005 0.000 0.026 0.001 sre_parse.py:386(_parse) - 26 0.000 0.000 0.000 0.000 sre_parse.py:670(fix_flags) - 26 0.000 0.000 0.000 0.000 sre_parse.py:68(__init__) - 26 0.000 0.000 0.027 0.001 sre_parse.py:682(parse) - 2 0.000 0.000 0.000 0.000 sre_parse.py:711(parse_template) - 3 0.000 0.000 0.000 0.000 sre_parse.py:718(literal) - 61 0.000 0.000 0.000 0.000 sre_parse.py:73(opengroup) - 2 0.000 0.000 0.000 0.000 sre_parse.py:790() - 61 0.000 0.000 0.000 0.000 sre_parse.py:84(closegroup) - 172 0.000 0.000 0.000 0.000 sre_parse.py:91(__init__) - 1 0.000 0.000 0.000 0.000 ssl.py:107(CertificateError) - 1 0.000 0.000 0.000 0.000 ssl.py:166(SSLContext) - 1 0.000 0.000 0.000 0.000 ssl.py:172(__new__) - 1 0.000 0.000 0.000 0.000 ssl.py:178(__init__) - 1 0.000 0.000 0.000 0.000 ssl.py:192(SSLSocket) - 1 0.000 0.000 0.040 0.040 ssl.py:197(__init__) - 2416419 1.509 0.000 1.509 0.000 ssl.py:282(_checkClosed) - 604301 2.160 0.000 735.975 0.001 ssl.py:286(read) - 603908 2.265 0.000 20.399 0.000 ssl.py:329(send) - 603908 3.933 0.000 25.125 0.000 ssl.py:361(sendall) - 604301 2.972 0.000 739.833 0.001 ssl.py:388(recv_into) - 1 0.000 0.000 0.000 0.000 ssl.py:426(shutdown) - 2 0.000 0.000 0.000 0.000 ssl.py:439(_real_close) - 1 0.000 0.000 0.039 0.039 ssl.py:444(do_handshake) - 1 0.000 0.000 0.000 0.000 ssl.py:505(__del__) - 1 0.000 0.000 0.040 0.040 ssl.py:510(wrap_socket) - 1 0.016 0.016 0.017 0.017 ssl.py:55() - 1 0.006 0.006 0.006 0.006 stringprep.py:6() - 1 0.035 0.035 0.049 0.049 subprocess.py:336() - 1 0.000 0.000 0.000 0.000 subprocess.py:351(CalledProcessError) - 1 0.000 0.000 0.000 0.000 subprocess.py:641(Popen) - 1 0.000 0.000 0.001 0.001 threading.py:1() - 135 0.000 0.000 0.000 0.000 threading.py:1016(current_thread) - 2 0.000 0.000 0.000 0.000 threading.py:172(Condition) - 1 0.000 0.000 0.000 0.000 threading.py:175(_Condition) - 2 0.000 0.000 0.000 0.000 threading.py:177(__init__) - 1 0.000 0.000 0.000 0.000 threading.py:217(_is_owned) - 1 0.000 0.000 0.000 0.000 threading.py:284(notify) - 1 0.000 0.000 0.000 0.000 threading.py:302(notify_all) - 1 0.000 0.000 0.000 0.000 threading.py:311(_Semaphore) - 1 0.000 0.000 0.000 0.000 threading.py:369(_BoundedSemaphore) - 1 0.000 0.000 0.000 0.000 threading.py:381(Event) - 1 0.000 0.000 0.000 0.000 threading.py:384(_Event) - 1 0.000 0.000 0.000 0.000 threading.py:388(__init__) - 1 0.000 0.000 0.000 0.000 threading.py:402(set) - 1 0.000 0.000 0.000 0.000 threading.py:43(_Verbose) - 1 0.000 0.000 0.000 0.000 threading.py:439(Barrier) - 4 0.000 0.000 0.000 0.000 threading.py:45(__init__) - 1 0.000 0.000 0.000 0.000 threading.py:50(_note) - 1 0.000 0.000 0.000 0.000 threading.py:595(BrokenBarrierError) - 1 0.000 0.000 0.000 0.000 threading.py:615(Thread) - 1 0.000 0.000 0.000 0.000 threading.py:627(__init__) - 1 0.000 0.000 0.000 0.000 threading.py:719(_set_ident) - 135 0.000 0.000 0.000 0.000 threading.py:872(name) - 3 0.000 0.000 0.000 0.000 threading.py:88(RLock) - 1 0.000 0.000 0.000 0.000 threading.py:923(_Timer) - 1 0.000 0.000 0.000 0.000 threading.py:95(_RLock) - 1 0.000 0.000 0.000 0.000 threading.py:952(_MainThread) - 1 0.000 0.000 0.000 0.000 threading.py:954(__init__) - 1 0.000 0.000 0.000 0.000 threading.py:961(_set_daemon) - 1 0.000 0.000 0.000 0.000 threading.py:992(_DummyThread) - 1 0.000 0.000 0.000 0.000 warnings.py:1() - 1 0.000 0.000 0.000 0.000 warnings.py:269(WarningMessage) - 1 0.000 0.000 0.000 0.000 warnings.py:289(catch_warnings) - 1 0.000 0.000 0.000 0.000 warnings.py:83(_OptionError) - 1 0.000 0.000 0.000 0.000 warnings.py:88(_processoptions) - 1 0.000 0.000 0.000 0.000 weakref.py:200(update) - 1 0.000 0.000 0.000 0.000 weakref.py:45(__init__) - 107/104 0.003 0.000 0.016 0.000 {built-in method __build_class__} - 1 0.002 0.002 0.032 0.032 {built-in method __import__} - 25 0.000 0.000 0.000 0.000 {built-in method __new__ of type object at 0x7feb008d2f20} - 135 0.000 0.000 0.000 0.000 {built-in method _getframe} - 301830 0.224 0.000 0.224 0.000 {built-in method abs} - 4 0.000 0.000 0.000 0.000 {built-in method allocate_lock} - 463 0.000 0.000 0.000 0.000 {built-in method chr} - 26 0.000 0.000 0.000 0.000 {built-in method compile} - 2 0.000 0.000 0.000 0.000 {built-in method dir} - 49 0.000 0.000 0.000 0.000 {built-in method divmod} - 2 0.093 0.046 0.093 0.046 {built-in method dump} - 1 0.000 0.000 1052.038 1052.038 {built-in method exec} - 1 0.000 0.000 0.000 0.000 {built-in method exp} - 1 0.000 0.000 0.000 0.000 {built-in method from_bytes} - 603629 3.208 0.000 3.208 0.000 {built-in method fromtimestamp} - 271 0.000 0.000 0.000 0.000 {built-in method get_ident} - 1 0.001 0.001 0.033 0.033 {built-in method getaddrinfo} - 120 0.000 0.000 0.000 0.000 {built-in method getattr} - 1 0.000 0.000 0.000 0.000 {built-in method getcwd} - 36 0.000 0.000 0.000 0.000 {built-in method getlower} - 135 0.000 0.000 0.000 0.000 {built-in method getpid} - 6 0.000 0.000 0.000 0.000 {built-in method globals} - 548 0.001 0.000 0.001 0.000 {built-in method hasattr} - 2418651 2.307 0.000 2.307 0.000 {built-in method isinstance} -2421386/2421311 1.722 0.000 1.722 0.000 {built-in method len} - 1207393 6.127 0.000 6.127 0.000 {built-in method localtime} - 2 0.000 0.000 0.000 0.000 {built-in method log} - 27 0.000 0.000 0.000 0.000 {built-in method max} - 302246 0.442 0.000 0.442 0.000 {built-in method min} - 1207258 6.263 0.000 6.263 0.000 {built-in method mktime} - 3 0.001 0.000 0.001 0.000 {built-in method next} - 3 0.000 0.000 0.000 0.000 {built-in method nl_langinfo} - 1 0.000 0.000 0.000 0.000 {built-in method openssl_md5} - 1 0.000 0.000 0.000 0.000 {built-in method openssl_sha1} - 1 0.000 0.000 0.000 0.000 {built-in method openssl_sha224} - 1 0.000 0.000 0.000 0.000 {built-in method openssl_sha256} - 1 0.000 0.000 0.000 0.000 {built-in method openssl_sha384} - 1 0.000 0.000 0.000 0.000 {built-in method openssl_sha512} - 6 0.007 0.001 0.007 0.001 {built-in method open} - 309 0.000 0.000 0.000 0.000 {built-in method ord} - 47 0.003 0.000 0.003 0.000 {built-in method print} - 38 0.000 0.000 0.000 0.000 {built-in method proxy} - 1 0.000 0.000 0.000 0.000 {built-in method reader} - 3 0.000 0.000 0.000 0.000 {built-in method register_dialect} - 1 0.000 0.000 0.000 0.000 {built-in method register} - 9 0.000 0.000 0.000 0.000 {built-in method round} - 301759 0.651 0.000 0.651 0.000 {built-in method setlocale} - 7 0.000 0.000 0.000 0.000 {built-in method sorted} - 1 0.000 0.000 0.000 0.000 {built-in method sqrt} - 143 0.001 0.000 0.001 0.000 {built-in method strftime} - 301748 1.559 0.000 23.068 0.000 {built-in method strptime} - 1 0.000 0.000 0.000 0.000 {built-in method sysconf} - 2114116 2.488 0.000 2.488 0.000 {built-in method time} - 1 0.000 0.000 0.000 0.000 {built-in method tzset} - 1 0.000 0.000 0.000 0.000 {built-in method urandom} - 4 0.000 0.000 0.000 0.000 {built-in method utf_8_decode} - 1 0.000 0.000 0.000 0.000 {function seed at 0x108da68} - 1 0.000 0.000 0.000 0.000 {method '__getitem__' of 'dict' objects} - 604301 1.360 0.000 1.360 0.000 {method '_checkClosed' of '_io._IOBase' objects} - 604301 1.379 0.000 2.837 0.000 {method '_checkReadable' of '_io._IOBase' objects} - 1 0.000 0.000 0.000 0.000 {method '_wrap_socket' of '_ssl._SSLContext' objects} - 139 0.000 0.000 0.000 0.000 {method 'acquire' of '_thread.RLock' objects} - 2 0.000 0.000 0.000 0.000 {method 'acquire' of '_thread.lock' objects} - 5 0.000 0.000 0.000 0.000 {method 'add' of 'set' objects} - 606573 0.517 0.000 0.517 0.000 {method 'append' of 'list' objects} - 1 0.000 0.000 0.000 0.000 {method 'bit_length' of 'int' objects} - 1 0.000 0.000 0.000 0.000 {method 'close' of '_io.BufferedReader' objects} - 1 0.001 0.001 0.001 0.001 {method 'connect' of '_socket.socket' objects} - 302108 1.285 0.000 1.285 0.000 {method 'decode' of 'bytes' objects} - 1 0.000 0.000 0.000 0.000 {method 'detach' of '_socket.socket' objects} - 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} - 1 0.039 0.039 0.039 0.039 {method 'do_handshake' of '_ssl._SSLSocket' objects} - 7 0.000 0.000 0.000 0.000 {method 'encode' of 'str' objects} - 301748 0.294 0.000 0.294 0.000 {method 'end' of '_sre.SRE_Match' objects} - 1510072 1.974 0.000 1.974 0.000 {method 'endswith' of 'bytes' objects} - 1 0.000 0.000 0.000 0.000 {method 'endswith' of 'str' objects} - 12 0.000 0.000 0.000 0.000 {method 'extend' of 'bytearray' objects} - 23 0.000 0.000 0.000 0.000 {method 'extend' of 'list' objects} - 1 0.000 0.000 0.000 0.000 {method 'fileno' of '_socket.socket' objects} - 135 0.000 0.000 0.000 0.000 {method 'find' of 'str' objects} - 301881 62.003 0.000 62.003 0.000 {method 'findall' of '_sre.SRE_Pattern' objects} - 135 0.013 0.000 0.013 0.000 {method 'flush' of '_io.TextIOWrapper' objects} - 2 0.000 0.000 0.000 0.000 {method 'format' of 'str' objects} - 2415262 1.855 0.000 1.855 0.000 {method 'get' of 'dict' objects} - 1 0.000 0.000 0.000 0.000 {method 'getpeername' of '_socket.socket' objects} - 1 0.000 0.000 0.000 0.000 {method 'getrandbits' of '_random.Random' objects} - 3 0.000 0.000 0.000 0.000 {method 'gettimeout' of '_socket.socket' objects} - 6643530 7.509 0.000 7.509 0.000 {method 'group' of '_sre.SRE_Match' objects} - 301748 0.749 0.000 0.749 0.000 {method 'groupdict' of '_sre.SRE_Match' objects} - 94 0.000 0.000 0.000 0.000 {method 'groups' of '_sre.SRE_Match' objects} - 301748 0.491 0.000 0.491 0.000 {method 'index' of 'list' objects} - 14 0.000 0.000 0.000 0.000 {method 'index' of 'str' objects} - 1 0.000 0.000 0.000 0.000 {method 'insert' of 'list' objects} - 4 0.000 0.000 0.000 0.000 {method 'isalnum' of 'str' objects} - 39 0.000 0.000 0.000 0.000 {method 'items' of 'dict' objects} - 301881 0.224 0.000 0.224 0.000 {method 'join' of 'bytes' objects} - 55/11 0.000 0.000 0.000 0.000 {method 'join' of 'str' objects} - 301748 0.216 0.000 0.216 0.000 {method 'keys' of 'dict' objects} - 603552 0.538 0.000 0.538 0.000 {method 'lower' of 'str' objects} - 4530459 10.616 0.000 10.616 0.000 {method 'match' of '_sre.SRE_Pattern' objects} - 603858 0.535 0.000 0.535 0.000 {method 'pop' of 'dict' objects} - 301883 1.449 0.000 1.529 0.000 {method 'read' of '_io.BufferedReader' objects} - 604301 733.455 0.001 733.455 0.001 {method 'read' of '_ssl._SSLSocket' objects} - 1510072 9.248 0.000 756.011 0.001 {method 'readline' of '_io.BufferedReader' objects} - 139 0.000 0.000 0.000 0.000 {method 'release' of '_thread.RLock' objects} - 1 0.000 0.000 0.000 0.000 {method 'release' of '_thread.lock' objects} - 61 0.000 0.000 0.000 0.000 {method 'remove' of 'list' objects} - 603570 0.490 0.000 0.490 0.000 {method 'replace' of 'str' objects} - 405 0.000 0.000 0.000 0.000 {method 'rfind' of 'str' objects} - 6 0.000 0.000 0.000 0.000 {method 'rstrip' of 'str' objects} - 4 0.000 0.000 0.000 0.000 {method 'search' of '_sre.SRE_Pattern' objects} - 1 0.000 0.000 0.000 0.000 {method 'set_ciphers' of '_ssl._SSLContext' objects} - 512 0.002 0.000 0.002 0.000 {method 'setdefault' of 'dict' objects} - 4 0.000 0.000 0.000 0.000 {method 'setter' of 'property' objects} - 2 0.000 0.000 0.000 0.000 {method 'settimeout' of '_socket.socket' objects} - 2 0.000 0.000 0.000 0.000 {method 'split' of '_sre.SRE_Pattern' objects} - 47 0.022 0.000 0.022 0.000 {method 'split' of 'bytes' objects} - 2 0.000 0.000 0.000 0.000 {method 'split' of 'str' objects} - 4 0.000 0.000 0.000 0.000 {method 'start' of '_sre.SRE_Match' objects} - 13 0.000 0.000 0.000 0.000 {method 'startswith' of 'str' objects} - 38 0.000 0.000 0.000 0.000 {method 'strftime' of 'datetime.date' objects} - 62 0.000 0.000 0.000 0.000 {method 'strip' of 'str' objects} - 301889 19.339 0.000 19.339 0.000 {method 'sub' of '_sre.SRE_Pattern' objects} - 2 0.000 0.000 0.000 0.000 {method 'tobytes' of 'array.array' objects} - 2 0.000 0.000 0.000 0.000 {method 'tolist' of 'array.array' objects} - 603497 0.424 0.000 0.424 0.000 {method 'toordinal' of 'datetime.date' objects} - 301748 0.432 0.000 0.432 0.000 {method 'total_seconds' of 'datetime.timedelta' objects} - 1 0.000 0.000 0.000 0.000 {method 'union' of 'set' objects} - 603810 0.546 0.000 0.546 0.000 {method 'upper' of 'str' objects} - 301748 0.190 0.000 0.190 0.000 {method 'weekday' of 'datetime.date' objects} - 270 0.001 0.000 0.001 0.000 {method 'write' of '_io.TextIOWrapper' objects} - 603908 17.757 0.000 17.757 0.000 {method 'write' of '_ssl._SSLSocket' objects} - - - - - - -STATISTICS WITH CACHING: - -344848 function calls (344387 primitive calls) in 136.603 seconds - - Ordered by: standard name - - ncalls tottime percall cumtime percall filename:lineno(function) - 1 0.000 0.000 0.000 0.000 __init__.py:1001(_StderrHandler) - 1 0.000 0.000 0.000 0.000 __init__.py:1007(__init__) - 1 0.000 0.000 0.000 0.000 __init__.py:1025(PlaceHolder) - 1 0.000 0.000 0.000 0.000 __init__.py:1069(Manager) - 1 0.000 0.000 0.000 0.000 __init__.py:1074(__init__) - 1 0.000 0.000 0.000 0.000 __init__.py:1177(Logger) - 1 0.000 0.000 0.000 0.000 __init__.py:1192(__init__) - 1 0.000 0.000 0.000 0.000 __init__.py:1204(setLevel) - 2 0.000 0.000 0.001 0.001 __init__.py:1234(warning) - 2 0.000 0.000 0.000 0.000 __init__.py:1298(findCaller) - 2 0.000 0.000 0.000 0.000 __init__.py:1328(makeRecord) - 2 0.000 0.000 0.001 0.001 __init__.py:1343(_log) - 2 0.000 0.000 0.001 0.000 __init__.py:1366(handle) - 1 0.000 0.000 0.000 0.000 __init__.py:1376(addHandler) - 2 0.000 0.000 0.001 0.000 __init__.py:1420(callHandlers) - 2 0.000 0.000 0.000 0.000 __init__.py:1450(getEffectiveLevel) - 2 0.000 0.000 0.000 0.000 __init__.py:1464(isEnabledFor) - 2 0.000 0.000 0.000 0.000 __init__.py:148(getLevelName) - 1 0.000 0.000 0.000 0.000 __init__.py:1491(RootLogger) - 1 0.000 0.000 0.000 0.000 __init__.py:1497(__init__) - 1 0.000 0.000 0.000 0.000 __init__.py:1505(LoggerAdapter) - 1 0.000 0.000 0.001 0.001 __init__.py:1625(basicConfig) - 2 0.000 0.000 0.001 0.001 __init__.py:1735(warning) - 4 0.000 0.000 0.000 0.000 __init__.py:177(_checkLevel) - 1 0.000 0.000 0.000 0.000 __init__.py:1819(NullHandler) - 4 0.000 0.000 0.000 0.000 __init__.py:206(_acquireLock) - 4 0.000 0.000 0.000 0.000 __init__.py:215(_releaseLock) - 1 0.000 0.000 0.000 0.000 __init__.py:226(LogRecord) - 2 0.000 0.000 0.000 0.000 __init__.py:238(__init__) - 1 0.035 0.035 0.037 0.037 __init__.py:24() - 2 0.000 0.000 0.000 0.000 __init__.py:308(getMessage) - 1 0.000 0.000 0.000 0.000 __init__.py:357(PercentStyle) - 2 0.000 0.000 0.000 0.000 __init__.py:363(__init__) - 2 0.000 0.000 0.000 0.000 __init__.py:366(usesTime) - 2 0.000 0.000 0.000 0.000 __init__.py:369(format) - 1 0.000 0.000 0.000 0.000 __init__.py:372(StrFormatStyle) - 1 0.000 0.000 0.000 0.000 __init__.py:381(StringTemplateStyle) - 1 0.000 0.000 0.000 0.000 __init__.py:403(Formatter) - 1 0.000 0.000 0.000 0.000 __init__.py:42(normalize_encoding) - 2 0.000 0.000 0.000 0.000 __init__.py:447(__init__) - 2 0.000 0.000 0.000 0.000 __init__.py:469(formatTime) - 2 0.000 0.000 0.000 0.000 __init__.py:514(usesTime) - 2 0.000 0.000 0.000 0.000 __init__.py:520(formatMessage) - 2 0.000 0.000 0.000 0.000 __init__.py:536(format) - 1 0.000 0.000 0.000 0.000 __init__.py:573(BufferingFormatter) - 1 0.000 0.000 0.000 0.000 __init__.py:615(Filter) - 1 0.000 0.000 0.000 0.000 __init__.py:652(Filterer) - 3 0.000 0.000 0.000 0.000 __init__.py:657(__init__) - 4 0.000 0.000 0.000 0.000 __init__.py:677(filter) - 1 0.000 0.000 0.019 0.019 __init__.py:69(search_function) - 2 0.000 0.000 0.000 0.000 __init__.py:722(_addHandlerRef) - 1 0.000 0.000 0.000 0.000 __init__.py:732(Handler) - 2 0.000 0.000 0.000 0.000 __init__.py:741(__init__) - 2 0.000 0.000 0.000 0.000 __init__.py:76() - 2 0.000 0.000 0.000 0.000 __init__.py:770(createLock) - 2 0.000 0.000 0.000 0.000 __init__.py:779(acquire) - 2 0.000 0.000 0.000 0.000 __init__.py:786(release) - 2 0.000 0.000 0.000 0.000 __init__.py:799(format) - 2 0.000 0.000 0.001 0.000 __init__.py:822(handle) - 1 0.000 0.000 0.000 0.000 __init__.py:840(setFormatter) - 1 0.000 0.000 0.000 0.000 __init__.py:896(StreamHandler) - 1 0.000 0.000 0.000 0.000 __init__.py:905(__init__) - 2 0.000 0.000 0.001 0.000 __init__.py:916(flush) - 2 0.000 0.000 0.001 0.000 __init__.py:923(emit) - 1 0.000 0.000 0.000 0.000 __init__.py:945(FileHandler) - 1 0.000 0.000 0.001 0.001 __init__.py:949(__init__) - 1 0.000 0.000 0.001 0.001 __init__.py:979(_open) - 2 0.000 0.000 0.001 0.000 __init__.py:990(emit) - 3 0.000 0.000 0.000 0.000 _abcoll.py:374(items) - 3 0.000 0.000 0.000 0.000 _abcoll.py:391(__init__) - 7 0.000 0.000 0.000 0.000 _abcoll.py:432(__iter__) - 4 0.000 0.000 0.000 0.000 _abcoll.py:493(update) - 1 0.000 0.000 0.000 0.000 _compat_pickle.py:8() - 52 0.000 0.000 0.000 0.000 _compat_pickle.py:80() - 11 0.000 0.000 0.000 0.000 _compat_pickle.py:81() - 16 0.000 0.000 0.000 0.000 _weakrefset.py:36(__init__) - 8 0.000 0.000 0.000 0.000 _weakrefset.py:68(__contains__) - 1 0.000 0.000 0.000 0.000 _weakrefset.py:79(add) - 5 0.000 0.000 0.001 0.000 abc.py:116(__new__) - 5 0.000 0.000 0.000 0.000 abc.py:119() - 4 0.000 0.000 0.000 0.000 abc.py:158(__instancecheck__) - 1 0.000 0.000 0.000 0.000 base64.py:148() - 1 0.000 0.000 0.000 0.000 base64.py:149() - 1 0.000 0.000 0.000 0.000 base64.py:3() - 1 0.000 0.000 0.000 0.000 cashing_data.py:16() - 1 0.000 0.000 0.000 0.000 cashing_data.py:20(CashingData) - 1 0.000 0.000 0.305 0.305 cashing_data.py:23(__init__) - 1 0.316 0.316 0.406 0.406 cashing_data.py:39(__del__) - 47 0.316 0.007 132.650 2.822 cashing_data.py:68(sync_cached_mailbox) - 47 0.000 0.000 0.000 0.000 cashing_data.py:91(commit_cached_mailbox) - 1 0.000 0.000 0.000 0.000 codecs.py:164(__init__) - 1 0.000 0.000 0.000 0.000 codecs.py:192(setstate) - 2 0.000 0.000 0.000 0.000 codecs.py:238(__init__) - 2 0.000 0.000 0.000 0.000 codecs.py:287(__init__) - 4 0.000 0.000 0.000 0.000 codecs.py:297(decode) - 1 0.000 0.000 0.000 0.000 codecs.py:77(__new__) - 89 0.000 0.000 0.000 0.000 collections.py:130(move_to_end) - 4 0.000 0.000 0.000 0.000 collections.py:40(__init__) - 32 0.000 0.000 0.000 0.000 collections.py:57(__setitem__) - 3 0.000 0.000 0.000 0.000 collections.py:695(__init__) - 3 0.000 0.000 0.000 0.000 collections.py:705(__getitem__) - 7 0.000 0.000 0.000 0.000 collections.py:82(__iter__) - 1 0.000 0.000 0.000 0.000 configparser.py:1084(_join_multiline_values) - 3 0.000 0.000 0.000 0.000 configparser.py:1102(_unify_values) - 1 0.000 0.000 0.000 0.000 configparser.py:1151(ConfigParser) - 1 0.000 0.000 0.000 0.000 configparser.py:1170(SafeConfigParser) - 1 0.000 0.000 0.000 0.000 configparser.py:1183(SectionProxy) - 2 0.000 0.000 0.000 0.000 configparser.py:1186(__init__) - 1 0.000 0.000 0.013 0.013 configparser.py:120() - 1 0.000 0.000 0.000 0.000 configparser.py:144(Error) - 1 0.000 0.000 0.000 0.000 configparser.py:174(NoSectionError) - 1 0.000 0.000 0.000 0.000 configparser.py:183(DuplicateSectionError) - 1 0.000 0.000 0.000 0.000 configparser.py:209(DuplicateOptionError) - 1 0.000 0.000 0.000 0.000 configparser.py:236(NoOptionError) - 1 0.000 0.000 0.000 0.000 configparser.py:247(InterpolationError) - 1 0.000 0.000 0.000 0.000 configparser.py:257(InterpolationMissingOptionError) - 1 0.000 0.000 0.000 0.000 configparser.py:272(InterpolationSyntaxError) - 1 0.000 0.000 0.000 0.000 configparser.py:280(InterpolationDepthError) - 1 0.000 0.000 0.000 0.000 configparser.py:293(ParsingError) - 1 0.000 0.000 0.000 0.000 configparser.py:336(MissingSectionHeaderError) - 1 0.000 0.000 0.000 0.000 configparser.py:356(Interpolation) - 3 0.000 0.000 0.000 0.000 configparser.py:359(before_get) - 3 0.000 0.000 0.000 0.000 configparser.py:365(before_read) - 1 0.000 0.000 0.001 0.001 configparser.py:372(BasicInterpolation) - 1 0.000 0.000 0.000 0.000 configparser.py:443(ExtendedInterpolation) - 1 0.000 0.000 0.001 0.001 configparser.py:516(LegacyInterpolation) - 1 0.000 0.000 0.011 0.011 configparser.py:554(RawConfigParser) - 1 0.000 0.000 0.000 0.000 configparser.py:595(__init__) - 1 0.001 0.001 0.001 0.001 configparser.py:671(read) - 3 0.000 0.000 0.000 0.000 configparser.py:755(get) - 2 0.000 0.000 0.000 0.000 configparser.py:792(_get) - 2 0.000 0.000 0.000 0.000 configparser.py:795(getint) - 6 0.000 0.000 0.000 0.000 configparser.py:855(optionxform) - 1 0.000 0.000 0.000 0.000 configparser.py:970(_read) - 2 0.000 0.000 0.001 0.000 csv.py:106(__next__) - 1 0.000 0.000 0.000 0.000 csv.py:129(DictWriter) - 1 0.000 0.000 0.000 0.000 csv.py:167(Sniffer) - 1 0.000 0.000 0.000 0.000 csv.py:22(Dialect) - 1 0.007 0.007 0.007 0.007 csv.py:4() - 1 0.000 0.000 0.000 0.000 csv.py:53(excel) - 1 0.000 0.000 0.000 0.000 csv.py:63(excel_tab) - 1 0.000 0.000 0.000 0.000 csv.py:68(unix_dialect) - 1 0.000 0.000 0.000 0.000 csv.py:79(DictReader) - 1 0.000 0.000 0.000 0.000 csv.py:80(__init__) - 1 0.000 0.000 0.000 0.000 csv.py:89(__iter__) - 3 0.000 0.000 0.000 0.000 csv.py:92(fieldnames) - 1 0.025 0.025 0.049 0.049 date_interpreter.py:20() - 1 0.000 0.000 0.000 0.000 date_interpreter.py:35(DateInterpreter) - 1 0.000 0.000 0.000 0.000 date_interpreter.py:38(__init__) - 2 0.000 0.000 0.000 0.000 datetime.py:1017(__new__) - 1 0.000 0.000 0.000 0.000 datetime.py:1302(datetime) - 2 0.000 0.000 0.000 0.000 datetime.py:1312(__new__) - 1 0.012 0.012 0.013 0.013 datetime.py:17() - 1 0.000 0.000 0.000 0.000 datetime.py:1786(timezone) - 3 0.000 0.000 0.000 0.000 datetime.py:1810(_create) - 4 0.000 0.000 0.000 0.000 datetime.py:267(_check_date_fields) - 4 0.000 0.000 0.000 0.000 datetime.py:278(_check_time_fields) - 4 0.000 0.000 0.000 0.000 datetime.py:290(_check_tzinfo_arg) - 1 0.000 0.000 0.000 0.000 datetime.py:298(timedelta) - 9 0.000 0.000 0.000 0.000 datetime.py:317(__new__) - 1 0.000 0.000 0.000 0.000 datetime.py:500(__neg__) - 3 0.000 0.000 0.000 0.000 datetime.py:51(_days_before_year) - 4 0.000 0.000 0.000 0.000 datetime.py:56(_days_in_month) - 1 0.000 0.000 0.000 0.000 datetime.py:631(date) - 4 0.000 0.000 0.000 0.000 datetime.py:661(__new__) - 1 0.000 0.000 0.000 0.000 datetime.py:924(tzinfo) - 1 0.000 0.000 0.000 0.000 datetime.py:993(time) - 1 0.000 0.000 0.002 0.002 fix_imap_internaldate.py:108(load_configuration) - 1 0.074 0.074 136.603 136.603 fix_imap_internaldate.py:20() - 1 0.002 0.002 135.351 135.351 fix_imap_internaldate.py:30(main) - 112 0.001 0.000 0.044 0.000 functools.py:170(wrapper) - 2 0.000 0.000 0.000 0.000 genericpath.py:85(_splitext) - 1 0.008 0.008 0.008 0.008 hashlib.py:53() - 6 0.000 0.000 0.000 0.000 hashlib.py:94(__get_openssl_constructor) - 1 0.000 0.000 0.000 0.000 idna.py:146(Codec) - 2 0.000 0.000 0.000 0.000 idna.py:147(encode) - 1 0.000 0.000 0.000 0.000 idna.py:196(IncrementalEncoder) - 1 0.000 0.000 0.000 0.000 idna.py:231(IncrementalDecoder) - 1 0.000 0.000 0.000 0.000 idna.py:270(StreamWriter) - 1 0.000 0.000 0.000 0.000 idna.py:273(StreamReader) - 1 0.000 0.000 0.000 0.000 idna.py:278(getregentry) - 1 0.011 0.011 0.017 0.017 idna.py:3() - 7 0.000 0.000 0.000 0.000 idna.py:62(ToASCII) - 146 0.001 0.000 2.683 0.018 imaplib.py:1050(_get_tagged_response) - 1 0.000 0.000 0.000 0.000 imaplib.py:107(IMAP4) - 667 0.019 0.000 2.689 0.004 imaplib.py:1072(_get_line) - 2186 0.005 0.000 0.017 0.000 imaplib.py:1091(_match) - 1 0.054 0.054 0.161 0.161 imaplib.py:11() - 146 0.001 0.000 0.001 0.000 imaplib.py:1103(_new_tag) - 1 0.000 0.000 0.000 0.000 imaplib.py:1111(_quote) - 146 0.001 0.000 2.696 0.018 imaplib.py:1119(_simple_command) - 96 0.000 0.000 0.000 0.000 imaplib.py:1124(_untagged_response) - 813 0.003 0.000 0.004 0.000 imaplib.py:1153(_log) - 1 0.000 0.000 0.000 0.000 imaplib.py:1176(IMAP4_SSL) - 1 0.000 0.000 0.155 0.155 imaplib.py:1191(__init__) - 1 0.000 0.000 0.114 0.114 imaplib.py:1196(_create_socket) - 1 0.000 0.000 0.114 0.114 imaplib.py:1200(open) - 1 0.000 0.000 0.000 0.000 imaplib.py:1211(IMAP4_stream) - 1 0.000 0.000 0.000 0.000 imaplib.py:1267(_Authenticator) - 1 0.000 0.000 0.000 0.000 imaplib.py:1359(Int2AP) - 1 0.000 0.000 0.000 0.000 imaplib.py:153(error) - 1 0.000 0.000 0.000 0.000 imaplib.py:154(abort) - 1 0.000 0.000 0.000 0.000 imaplib.py:155(readonly) - 1 0.000 0.000 0.155 0.155 imaplib.py:157(__init__) - 1 0.000 0.000 0.041 0.041 imaplib.py:182(_connect) - 1 0.000 0.000 0.021 0.021 imaplib.py:235(_create_socket) - 1 0.000 0.000 0.114 0.114 imaplib.py:238(open) - 667 0.001 0.000 2.666 0.004 imaplib.py:263(readline) - 146 0.000 0.000 0.006 0.000 imaplib.py:268(send) - 1 0.000 0.000 0.000 0.000 imaplib.py:273(shutdown) - 1 0.000 0.000 0.001 0.001 imaplib.py:381(capability) - 1 0.000 0.000 0.484 0.484 imaplib.py:398(close) - 1 0.000 0.000 0.004 0.004 imaplib.py:511(list) - 1 0.000 0.000 0.001 0.001 imaplib.py:523(login) - 1 0.000 0.000 0.001 0.001 imaplib.py:552(logout) - 47 0.000 0.000 1.852 0.039 imaplib.py:656(select) - 47 0.000 0.000 0.070 0.001 imaplib.py:751(status) - 47 0.000 0.000 0.285 0.006 imaplib.py:792(uid) - 806 0.002 0.000 0.002 0.000 imaplib.py:847(_append_untagged) - 290 0.001 0.000 0.001 0.000 imaplib.py:861(_check_bye) - 146 0.002 0.000 0.011 0.000 imaplib.py:867(_command) - 146 0.001 0.000 2.685 0.018 imaplib.py:944(_command_complete) - 1 0.000 0.000 0.001 0.001 imaplib.py:961(_get_capabilities) - 667 0.009 0.000 2.720 0.004 imaplib.py:970(_get_response) - 9 0.000 0.000 0.000 0.000 locale.py:526(setlocale) - 3 0.000 0.000 0.000 0.000 locale.py:574(getpreferredencoding) - 1 0.019 0.019 0.182 0.182 mail_iterator.py:20() - 1 0.000 0.000 0.000 0.000 mail_iterator.py:29(MailIterator) - 1 0.000 0.000 0.160 0.160 mail_iterator.py:32(__init__) - 1 0.000 0.000 0.485 0.485 mail_iterator.py:42(__del__) - 48 0.001 0.000 1.923 0.040 mail_iterator.py:47(__iter__) - 47 0.000 0.000 0.305 0.006 mail_iterator.py:60(fetch_messages) - 1 0.000 0.000 0.000 0.000 os.py:35(_get_exports_list) - 1 0.000 0.000 0.000 0.000 os.py:39() - 1 0.000 0.000 0.002 0.002 pickle.py:173() - 1 0.000 0.000 0.000 0.000 pickle.py:177(_Pickler) - 1 0.013 0.013 0.015 0.015 pickle.py:24() - 1 0.000 0.000 0.000 0.000 pickle.py:68(PickleError) - 1 0.000 0.000 0.000 0.000 pickle.py:72(PicklingError) - 1 0.000 0.000 0.000 0.000 pickle.py:781(_Unpickler) - 1 0.000 0.000 0.000 0.000 pickle.py:79(UnpicklingError) - 1 0.000 0.000 0.000 0.000 pickle.py:92(_Stop) - 2 0.000 0.000 0.000 0.000 posixpath.py:108(splitext) - 2 0.000 0.000 0.000 0.000 posixpath.py:129(basename) - 1 0.000 0.000 0.000 0.000 posixpath.py:330(normpath) - 1 0.000 0.000 0.000 0.000 posixpath.py:367(abspath) - 4 0.000 0.000 0.000 0.000 posixpath.py:38(_get_sep) - 5 0.000 0.000 0.000 0.000 posixpath.py:49(normcase) - 1 0.000 0.000 0.000 0.000 posixpath.py:61(isabs) - 1 0.000 0.000 0.000 0.000 posixpath.py:71(join) - 1 0.000 0.000 0.000 0.000 random.py:165(randrange) - 1 0.000 0.000 0.000 0.000 random.py:210(randint) - 1 0.000 0.000 0.000 0.000 random.py:216(_randbelow) - 1 0.012 0.012 0.020 0.020 random.py:37() - 1 0.000 0.000 0.000 0.000 random.py:625(SystemRandom) - 1 0.000 0.000 0.000 0.000 random.py:68(Random) - 1 0.000 0.000 0.000 0.000 random.py:84(__init__) - 1 0.000 0.000 0.000 0.000 random.py:93(seed) - 90 0.000 0.000 0.002 0.000 re.py:150(match) - 22 0.000 0.000 0.043 0.002 re.py:204(compile) - 112 0.000 0.000 0.045 0.000 re.py:254(_compile) - 23 0.000 0.000 0.043 0.002 re.py:257(_compile_typed) - 1 0.000 0.000 0.000 0.000 socket.py:141(makefile) - 1 0.000 0.000 0.000 0.000 socket.py:184(_decref_socketios) - 2 0.000 0.000 0.000 0.000 socket.py:190(_real_close) - 1 0.000 0.000 0.000 0.000 socket.py:194(close) - 1 0.000 0.000 0.000 0.000 socket.py:233(SocketIO) - 1 0.000 0.000 0.000 0.000 socket.py:250(__init__) - 529 0.003 0.000 2.657 0.005 socket.py:262(readinto) - 530 0.001 0.000 0.001 0.000 socket.py:304(readable) - 1 0.000 0.000 0.000 0.000 socket.py:331(close) - 1 0.000 0.000 0.021 0.021 socket.py:370(create_connection) - 1 0.006 0.006 0.007 0.007 socket.py:44() - 1 0.000 0.000 0.000 0.000 socket.py:87(socket) - 2 0.000 0.000 0.000 0.000 socket.py:93(__init__) - 90 0.001 0.000 0.010 0.000 sre_compile.py:178(_compile_charset) - 90 0.004 0.000 0.009 0.000 sre_compile.py:207(_optimize_charset) - 330 0.000 0.000 0.000 0.000 sre_compile.py:24(_identityfunction) - 9 0.000 0.000 0.000 0.000 sre_compile.py:258(_mk_bitmap) - 2 0.003 0.001 0.005 0.002 sre_compile.py:301(_optimize_unicode) - 150/23 0.002 0.000 0.011 0.000 sre_compile.py:32(_compile) - 50 0.000 0.000 0.000 0.000 sre_compile.py:355(_simple) - 23 0.000 0.000 0.005 0.000 sre_compile.py:362(_compile_info) - 46 0.000 0.000 0.000 0.000 sre_compile.py:468(isstring) - 23 0.000 0.000 0.016 0.001 sre_compile.py:471(_code) - 23 0.000 0.000 0.043 0.002 sre_compile.py:486(compile) - 200 0.000 0.000 0.000 0.000 sre_parse.py:127(__len__) - 547 0.001 0.000 0.002 0.000 sre_parse.py:131(__getitem__) - 50 0.000 0.000 0.000 0.000 sre_parse.py:135(__setitem__) - 285 0.000 0.000 0.001 0.000 sre_parse.py:139(append) - 200/73 0.001 0.000 0.001 0.000 sre_parse.py:141(getwidth) - 23 0.000 0.000 0.000 0.000 sre_parse.py:179(__init__) - 2064 0.009 0.000 0.013 0.000 sre_parse.py:183(__next) - 608 0.001 0.000 0.003 0.000 sre_parse.py:202(match) - 1757 0.003 0.000 0.013 0.000 sre_parse.py:208(get) - 5 0.000 0.000 0.000 0.000 sre_parse.py:212(tell) - 1 0.000 0.000 0.000 0.000 sre_parse.py:214(seek) - 183 0.000 0.000 0.000 0.000 sre_parse.py:217(isident) - 1 0.000 0.000 0.000 0.000 sre_parse.py:220(isdigit) - 43 0.000 0.000 0.000 0.000 sre_parse.py:223(isname) - 6 0.000 0.000 0.000 0.000 sre_parse.py:232(_class_escape) - 36 0.000 0.000 0.000 0.000 sre_parse.py:264(_escape) - 85/23 0.001 0.000 0.025 0.001 sre_parse.py:308(_parse_sub) - 96/26 0.005 0.000 0.025 0.001 sre_parse.py:386(_parse) - 23 0.000 0.000 0.000 0.000 sre_parse.py:670(fix_flags) - 23 0.000 0.000 0.000 0.000 sre_parse.py:68(__init__) - 23 0.000 0.000 0.026 0.001 sre_parse.py:682(parse) - 59 0.000 0.000 0.000 0.000 sre_parse.py:73(opengroup) - 59 0.000 0.000 0.000 0.000 sre_parse.py:84(closegroup) - 154 0.000 0.000 0.000 0.000 sre_parse.py:91(__init__) - 1 0.000 0.000 0.000 0.000 ssl.py:107(CertificateError) - 1 0.000 0.000 0.000 0.000 ssl.py:166(SSLContext) - 1 0.000 0.000 0.000 0.000 ssl.py:172(__new__) - 1 0.000 0.000 0.000 0.000 ssl.py:178(__init__) - 1 0.000 0.000 0.000 0.000 ssl.py:192(SSLSocket) - 1 0.000 0.000 0.093 0.093 ssl.py:197(__init__) - 1351 0.001 0.000 0.001 0.000 ssl.py:282(_checkClosed) - 529 0.002 0.000 2.647 0.005 ssl.py:286(read) - 146 0.001 0.000 0.005 0.000 ssl.py:329(send) - 146 0.001 0.000 0.006 0.000 ssl.py:361(sendall) - 529 0.003 0.000 2.651 0.005 ssl.py:388(recv_into) - 1 0.000 0.000 0.000 0.000 ssl.py:426(shutdown) - 2 0.000 0.000 0.000 0.000 ssl.py:439(_real_close) - 1 0.000 0.000 0.093 0.093 ssl.py:444(do_handshake) - 1 0.000 0.000 0.000 0.000 ssl.py:505(__del__) - 1 0.000 0.000 0.093 0.093 ssl.py:510(wrap_socket) - 1 0.017 0.017 0.018 0.018 ssl.py:55() - 1 0.000 0.000 0.000 0.000 stringprep.py:6() - 1 0.035 0.035 0.051 0.051 subprocess.py:336() - 1 0.000 0.000 0.000 0.000 subprocess.py:351(CalledProcessError) - 1 0.000 0.000 0.000 0.000 subprocess.py:641(Popen) - 1 0.000 0.000 0.001 0.001 threading.py:1() - 2 0.000 0.000 0.000 0.000 threading.py:1016(current_thread) - 2 0.000 0.000 0.000 0.000 threading.py:172(Condition) - 1 0.000 0.000 0.000 0.000 threading.py:175(_Condition) - 2 0.000 0.000 0.000 0.000 threading.py:177(__init__) - 1 0.000 0.000 0.000 0.000 threading.py:217(_is_owned) - 1 0.000 0.000 0.000 0.000 threading.py:284(notify) - 1 0.000 0.000 0.000 0.000 threading.py:302(notify_all) - 1 0.000 0.000 0.000 0.000 threading.py:311(_Semaphore) - 1 0.000 0.000 0.000 0.000 threading.py:369(_BoundedSemaphore) - 1 0.000 0.000 0.000 0.000 threading.py:381(Event) - 1 0.000 0.000 0.000 0.000 threading.py:384(_Event) - 1 0.000 0.000 0.000 0.000 threading.py:388(__init__) - 1 0.000 0.000 0.000 0.000 threading.py:402(set) - 1 0.000 0.000 0.000 0.000 threading.py:43(_Verbose) - 1 0.000 0.000 0.000 0.000 threading.py:439(Barrier) - 4 0.000 0.000 0.000 0.000 threading.py:45(__init__) - 1 0.000 0.000 0.000 0.000 threading.py:50(_note) - 1 0.000 0.000 0.000 0.000 threading.py:595(BrokenBarrierError) - 1 0.000 0.000 0.000 0.000 threading.py:615(Thread) - 1 0.000 0.000 0.000 0.000 threading.py:627(__init__) - 1 0.000 0.000 0.000 0.000 threading.py:719(_set_ident) - 2 0.000 0.000 0.000 0.000 threading.py:872(name) - 3 0.000 0.000 0.000 0.000 threading.py:88(RLock) - 1 0.000 0.000 0.000 0.000 threading.py:923(_Timer) - 1 0.000 0.000 0.000 0.000 threading.py:95(_RLock) - 1 0.000 0.000 0.000 0.000 threading.py:952(_MainThread) - 1 0.000 0.000 0.000 0.000 threading.py:954(__init__) - 1 0.000 0.000 0.000 0.000 threading.py:961(_set_daemon) - 1 0.000 0.000 0.000 0.000 threading.py:992(_DummyThread) - 1 0.000 0.000 0.000 0.000 warnings.py:1() - 1 0.000 0.000 0.000 0.000 warnings.py:269(WarningMessage) - 1 0.000 0.000 0.000 0.000 warnings.py:289(catch_warnings) - 1 0.000 0.000 0.000 0.000 warnings.py:83(_OptionError) - 1 0.000 0.000 0.000 0.000 warnings.py:88(_processoptions) - 1 0.000 0.000 0.000 0.000 weakref.py:200(update) - 1 0.000 0.000 0.000 0.000 weakref.py:45(__init__) - 95/92 0.003 0.000 0.016 0.000 {built-in method __build_class__} - 1 0.002 0.002 0.019 0.019 {built-in method __import__} - 25 0.000 0.000 0.000 0.000 {built-in method __new__ of type object at 0x7f0f161d7f20} - 2 0.000 0.000 0.000 0.000 {built-in method _getframe} - 82 0.000 0.000 0.000 0.000 {built-in method abs} - 3 0.000 0.000 0.000 0.000 {built-in method allocate_lock} - 462 0.000 0.000 0.000 0.000 {built-in method chr} - 23 0.000 0.000 0.000 0.000 {built-in method compile} - 2 0.000 0.000 0.000 0.000 {built-in method dir} - 49 0.000 0.000 0.000 0.000 {built-in method divmod} - 1 0.086 0.086 0.086 0.086 {built-in method dump} - 1 0.000 0.000 136.603 136.603 {built-in method exec} - 1 0.000 0.000 0.000 0.000 {built-in method exp} - 1 0.000 0.000 0.000 0.000 {built-in method from_bytes} - 5 0.000 0.000 0.000 0.000 {built-in method get_ident} - 1 0.001 0.001 0.020 0.020 {built-in method getaddrinfo} - 120 0.000 0.000 0.000 0.000 {built-in method getattr} - 1 0.000 0.000 0.000 0.000 {built-in method getcwd} - 2 0.000 0.000 0.000 0.000 {built-in method getpid} - 6 0.000 0.000 0.000 0.000 {built-in method globals} - 16 0.000 0.000 0.000 0.000 {built-in method hasattr} - 3088 0.003 0.000 0.003 0.000 {built-in method isinstance} -6063/5991 0.003 0.000 0.004 0.000 {built-in method len} - 1 0.304 0.304 0.304 0.304 {built-in method load} - 2 0.000 0.000 0.000 0.000 {built-in method localtime} - 2 0.000 0.000 0.000 0.000 {built-in method log} - 15 0.000 0.000 0.000 0.000 {built-in method max} - 315 0.000 0.000 0.000 0.000 {built-in method min} - 3 0.000 0.000 0.000 0.000 {built-in method next} - 3 0.000 0.000 0.000 0.000 {built-in method nl_langinfo} - 1 0.000 0.000 0.000 0.000 {built-in method openssl_md5} - 1 0.000 0.000 0.000 0.000 {built-in method openssl_sha1} - 1 0.000 0.000 0.000 0.000 {built-in method openssl_sha224} - 1 0.000 0.000 0.000 0.000 {built-in method openssl_sha256} - 1 0.000 0.000 0.000 0.000 {built-in method openssl_sha384} - 1 0.000 0.000 0.000 0.000 {built-in method openssl_sha512} - 5 0.007 0.001 0.007 0.001 {built-in method open} - 260 0.000 0.000 0.000 0.000 {built-in method ord} - 47 0.002 0.000 0.002 0.000 {built-in method print} - 33 0.000 0.000 0.000 0.000 {built-in method proxy} - 1 0.000 0.000 0.000 0.000 {built-in method reader} - 3 0.000 0.000 0.000 0.000 {built-in method register_dialect} - 1 0.000 0.000 0.000 0.000 {built-in method register} - 9 0.000 0.000 0.000 0.000 {built-in method round} - 9 0.000 0.000 0.000 0.000 {built-in method setlocale} - 1 0.000 0.000 0.000 0.000 {built-in method sorted} - 1 0.000 0.000 0.000 0.000 {built-in method sqrt} - 2 0.000 0.000 0.000 0.000 {built-in method strftime} - 1 0.000 0.000 0.000 0.000 {built-in method sysconf} - 816 0.001 0.000 0.001 0.000 {built-in method time} - 1 0.000 0.000 0.000 0.000 {built-in method urandom} - 4 0.000 0.000 0.000 0.000 {built-in method utf_8_decode} - 1 0.000 0.000 0.000 0.000 {function seed at 0x1502a68} - 529 0.002 0.000 0.002 0.000 {method '_checkClosed' of '_io._IOBase' objects} - 529 0.001 0.000 0.002 0.000 {method '_checkReadable' of '_io._IOBase' objects} - 1 0.000 0.000 0.000 0.000 {method '_wrap_socket' of '_ssl._SSLContext' objects} - 6 0.000 0.000 0.000 0.000 {method 'acquire' of '_thread.RLock' objects} - 2 0.000 0.000 0.000 0.000 {method 'acquire' of '_thread.lock' objects} - 5 0.000 0.000 0.000 0.000 {method 'add' of 'set' objects} - 2553 0.002 0.000 0.002 0.000 {method 'append' of 'list' objects} - 1 0.000 0.000 0.000 0.000 {method 'bit_length' of 'int' objects} - 1 0.000 0.000 0.000 0.000 {method 'close' of '_io.BufferedReader' objects} - 1 0.001 0.001 0.001 0.001 {method 'connect' of '_socket.socket' objects} - 94 0.000 0.000 0.000 0.000 {method 'decode' of 'bytes' objects} - 1 0.000 0.000 0.000 0.000 {method 'detach' of '_socket.socket' objects} - 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} - 1 0.093 0.093 0.093 0.093 {method 'do_handshake' of '_ssl._SSLSocket' objects} - 7 0.000 0.000 0.000 0.000 {method 'encode' of 'str' objects} - 667 0.001 0.000 0.001 0.000 {method 'endswith' of 'bytes' objects} - 1 0.000 0.000 0.000 0.000 {method 'endswith' of 'str' objects} - 12 0.000 0.000 0.000 0.000 {method 'extend' of 'bytearray' objects} - 21 0.000 0.000 0.000 0.000 {method 'extend' of 'list' objects} - 1 0.000 0.000 0.000 0.000 {method 'fileno' of '_socket.socket' objects} - 2 0.000 0.000 0.000 0.000 {method 'find' of 'str' objects} - 2 0.001 0.000 0.001 0.000 {method 'flush' of '_io.TextIOWrapper' objects} - 2 0.000 0.000 0.000 0.000 {method 'format' of 'str' objects} - 465 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects} - 1 0.000 0.000 0.000 0.000 {method 'getpeername' of '_socket.socket' objects} - 1 0.000 0.000 0.000 0.000 {method 'getrandbits' of '_random.Random' objects} - 3 0.000 0.000 0.000 0.000 {method 'gettimeout' of '_socket.socket' objects} - 2148 0.003 0.000 0.003 0.000 {method 'group' of '_sre.SRE_Match' objects} - 94 0.000 0.000 0.000 0.000 {method 'groups' of '_sre.SRE_Match' objects} - 301881 132.333 0.000 132.333 0.000 {method 'index' of 'list' objects} - 4 0.000 0.000 0.000 0.000 {method 'isalnum' of 'str' objects} - 36 0.000 0.000 0.000 0.000 {method 'items' of 'dict' objects} - 5 0.000 0.000 0.000 0.000 {method 'join' of 'str' objects} - 7 0.000 0.000 0.000 0.000 {method 'lower' of 'str' objects} - 2377 0.013 0.000 0.013 0.000 {method 'match' of '_sre.SRE_Pattern' objects} - 96 0.000 0.000 0.000 0.000 {method 'pop' of 'dict' objects} - 529 2.645 0.005 2.645 0.005 {method 'read' of '_ssl._SSLSocket' objects} - 667 0.008 0.000 2.665 0.004 {method 'readline' of '_io.BufferedReader' objects} - 6 0.000 0.000 0.000 0.000 {method 'release' of '_thread.RLock' objects} - 1 0.000 0.000 0.000 0.000 {method 'release' of '_thread.lock' objects} - 59 0.000 0.000 0.000 0.000 {method 'remove' of 'list' objects} - 3 0.000 0.000 0.000 0.000 {method 'replace' of 'str' objects} - 6 0.000 0.000 0.000 0.000 {method 'rfind' of 'str' objects} - 6 0.000 0.000 0.000 0.000 {method 'rstrip' of 'str' objects} - 4 0.000 0.000 0.000 0.000 {method 'search' of '_sre.SRE_Pattern' objects} - 1 0.000 0.000 0.000 0.000 {method 'set_ciphers' of '_ssl._SSLContext' objects} - 512 0.002 0.000 0.002 0.000 {method 'setdefault' of 'dict' objects} - 4 0.000 0.000 0.000 0.000 {method 'setter' of 'property' objects} - 2 0.000 0.000 0.000 0.000 {method 'settimeout' of '_socket.socket' objects} - 2 0.000 0.000 0.000 0.000 {method 'split' of '_sre.SRE_Pattern' objects} - 47 0.019 0.000 0.019 0.000 {method 'split' of 'bytes' objects} - 2 0.000 0.000 0.000 0.000 {method 'split' of 'str' objects} - 4 0.000 0.000 0.000 0.000 {method 'start' of '_sre.SRE_Match' objects} - 13 0.000 0.000 0.000 0.000 {method 'startswith' of 'str' objects} - 62 0.000 0.000 0.000 0.000 {method 'strip' of 'str' objects} - 2 0.000 0.000 0.000 0.000 {method 'tobytes' of 'array.array' objects} - 2 0.000 0.000 0.000 0.000 {method 'tolist' of 'array.array' objects} - 1 0.000 0.000 0.000 0.000 {method 'union' of 'set' objects} - 48 0.000 0.000 0.000 0.000 {method 'upper' of 'str' objects} - 4 0.000 0.000 0.000 0.000 {method 'write' of '_io.TextIOWrapper' objects} - 146 0.004 0.000 0.004 0.000 {method 'write' of '_ssl._SSLSocket' objects} - diff --git a/src/caching_data.py b/src/caching_data.py new file mode 100644 index 0000000..c578a3b --- /dev/null +++ b/src/caching_data.py @@ -0,0 +1,113 @@ +''' +caching_data.py - The module contains the CachingData class. + +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 os, tempfile +import pickle +import logging +from mailbox_state import MailboxState + +CACHE_FILENAME = "message_cache.dat" +CACHE_VERSION = 1 + +class CachingData: + """This class is responsible for the caching of data.""" + + # class attributes + # integer for version of the cache + version = None + # dictionary of usernames as keys and dictionaries as values + # the second dictionaries have unique mailbox keys and mailboxes as values + data = None + + def __init__(self): + # open data file or create one and initialize date if not found + try: + cachefile = open(CACHE_FILENAME, 'rb') + self.version, self.data = pickle.load(cachefile) + if(self.version != CACHE_VERSION): + logging.warning("Cache file has version %s and the script version is %s.", + self.version, CACHE_VERSION) + raise IOError + logging.info("Cache version %s", self.version) + logging.debug("%s users found.", len(self.data)) + except IOError: + self.version = CACHE_VERSION + self.data = {} + with open(CACHE_FILENAME, 'wb') as cachefile: + pickle.dump((self.version, self.data), cachefile) + + def __del__(self): + # create temporary file first + location = os.path.dirname(CACHE_FILENAME) + file_descriptor, tmpname = tempfile.mkstemp(dir=location) + try: + cachefile = os.fdopen(file_descriptor, 'wb') + + # prepare data based on a save flag + saved_data = {} + for user in self.data: + saved_data[user] = {} + for box_key in self.data[user]: + if(self.data[user][box_key].needs_save): + saved_data[user][box_key] = self.data[user][box_key] + logging.debug("The mailbox %s will be saved.", saved_data[user][box_key].name) + if(len(saved_data[user])==0): + del saved_data[user] + logging.debug("The user %s will not be saved.", user) + self.data = saved_data + + # avoid test mode or cases where nothing needs saving + if(len(saved_data)==0): + os.unlink(tmpname) + return + + # serialize in file + pickle.dump((self.version, self.data), cachefile) + logging.debug("%s users stored.", len(self.data)) + cachefile.close() + os.rename(tmpname, CACHE_FILENAME) + except: + os.unlink(tmpname) + + def retrieve_cached_mailbox(self, name, uidvalidity, user): + """Retrieve a cached mailbox or create it.""" + box_key = name.strip('"') + uidvalidity + if(user not in self.data): + self.data[user] = {} + logging.debug("New user %s cached.", user) + if(box_key not in self.data[user]): + self.data[user][box_key] = MailboxState(name, uidvalidity, user) + logging.debug("New mailbox %s cached.", box_key) + return self.data[user][box_key] + + def report_conflicts(self): + """Write a date conflicts report in a file.""" + with open("conflict_stats.txt", 'w') as statsfile: + owner_total_conflicts = {} + owner_total_missing = {} + for user in self.data: + owner_total_conflicts[user] = 0 + owner_total_missing[user] = 0 + for box_key in self.data[user]: + owner_total_conflicts[user] += self.data[user][box_key].date_conflicts + owner_total_missing[user] += self.data[user][box_key].no_received_field + statsfile.write("Total date conflicts to be corrected in a mailbox {0} are {1}.\n"\ + .format(self.data[user][box_key].name, self.data[user][box_key].date_conflicts)) + statsfile.write("Total messages without received headers in a mailbox {0} are {1}.\n"\ + .format(self.data[user][box_key].name, self.data[user][box_key].no_received_field)) + statsfile.write("Total date conflicts to be corrected for user {0} are {1}.\n\n"\ + .format(user, owner_total_missing[user])) + return diff --git a/src/confscript.cfg b/src/confscript.cfg new file mode 100644 index 0000000..5e6f10a --- /dev/null +++ b/src/confscript.cfg @@ -0,0 +1,6 @@ +[basic_settings] +# 0 NOTSET, 10 DEBUG, 20 INFO, 30 WARNING, 40 ERROR, 50 CRITICAL +file_log_level = 10 +console_log_level = 20 +imap_server = intranator.m.i2n +tolerance = 120 diff --git a/src/fix_imap_internaldate.py b/src/fix_imap_internaldate.py new file mode 100644 index 0000000..cc46e4d --- /dev/null +++ b/src/fix_imap_internaldate.py @@ -0,0 +1,160 @@ +''' +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 +# python version handling +try: + import configparser +except ImportError: + print("This module needs python version 3 or later.") + sys.exit() +import logging +from mail_date_parser import MailDateParser +from mail_iterator import MailIterator +from caching_data import CachingData + +def main(): + """Interprets command arguments and initializes configuration and logger. + Then begins mail synchronization.""" + + # parse arguments + parser = argparse.ArgumentParser(description="Fix the INTERNALDATE field on IMAP servers. " + "Small tool to fix the IMAP internaldate " + "in case it's too much off compared to the last date " + "stored in the received lines.") + parser.add_argument('-u', '--update', dest='test_mode', action='store_false', + default=True, help='update all e-mails and exit test mode') + + # config and logging setup + config = load_configuration() + prepare_logger(config) + args = parser.parse_args() + if(args.test_mode): + logging.info("Testing mode initiated. No message will be modified on the server.") + else: + logging.info("Update mode initiated. Messages will be modified.") + + # proceed to main functionality + try: + synchronize_csv(config, args.test_mode) + except KeyboardInterrupt: + logging.info("Script was interrupted by the user.") + + return + +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) + 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 + +def synchronize_csv(config, test_mode): + """Iterates through csv list of users and synchronizes their messages.""" + + # initialize loop permanent data + caching_data = CachingData() + date_parser = MailDateParser() + server = config.get('basic_settings', 'imap_server') + tolerance = config.getint('basic_settings', 'tolerance') * 60 + + # iterate through the users in the csv data + user_reader = csv.DictReader(open("userdata.csv", "r"), delimiter=',') + 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, tolerance) + 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)): + 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]) + if(test_mode==0): + try: + session.update_message(mid, box.name, received_date) + except UserWarning as ex: + logging.error(ex) + continue + + # 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_conflicts() + return + +if(__name__ == "__main__"): + main() diff --git a/src/mail_date_parser.py b/src/mail_date_parser.py new file mode 100644 index 0000000..d957d23 --- /dev/null +++ b/src/mail_date_parser.py @@ -0,0 +1,100 @@ +''' +mail_date_parser.py - The module contains the MailDateParser class. + +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 datetime, time +import re +import logging + +#reg expressions +RECEIVED_DATE = re.compile(r'(0?[1-9]|[1-2][0-9]|3[01])\s+([A-Z][a-z][a-z])\s+' + r'(19[0-9]{2}|[2-9][0-9]{3}|[0-9]{2})\s+(2[0-3]|[0-1][0-9]):([0-5][0-9])(?::(60|[0-5][0-9]))?\s*' + r'(?:([-\+])([0-9]{2})([0-5][0-9]))*') +INTERNAL_DATE = re.compile(r'(?P[ 0123][0-9])-(?P[A-Z][a-z][a-z])-(?P[0-9][0-9][0-9][0-9])' + r' (?P[0-9][0-9]):(?P[0-9][0-9]):(?P[0-9][0-9])' + r' (?P[-+])(?P[0-9][0-9])(?P[0-9][0-9])') +CONTROL_SYMBOLS = re.compile(r'[\n\r\t]') + +class MailDateParser: + """This class extracts dates from imap server responses and compares them. + This class contains only static methods.""" + + def __init__(self): + return + + @classmethod + def extract_internal_date(cls, fetchresult): + """Extracts the internal date from INTERNALDATE, returns datetime.""" + return datetime.datetime.fromtimestamp(time.mktime(fetchresult)) + + @classmethod + def extract_received_date(cls, fetchresult): + """Extracts the first date from RECEIVED, returns datetime.""" + fetchresult = CONTROL_SYMBOLS.sub('', fetchresult) + received_dates = RECEIVED_DATE.findall(fetchresult) + if(len(received_dates)==0): + return "" + else: received_date = received_dates[0] + logging.debug("Retrieved date %s from header %s.", received_date, fetchresult) + month = datetime.datetime.strptime(received_date[1],'%b').month + + if(received_date[3]!=""): + hours = int(received_date[3]) + else: hours = 0 + if(received_date[4]!=""): + minutes = int(received_date[4]) + else: minutes = 0 + if(received_date[5]!=""): + seconds = int(received_date[5]) + else: seconds = 0 + + if(received_date[6]!=""): + zonen = received_date[6] + else: zonen = b'+' + if(received_date[7]!=""): + zoneh = int(received_date[7]) + else: zoneh = 0 + if(received_date[8]!=""): + zonem = int(received_date[8]) + else: zonem = 0 + # subtract time zone to get unified time + zone = (zoneh * 60 + zonem) * 60 + if(zonen == b'-'): + zone = -zone + + time_tuple = (int(received_date[2]), month, int(received_date[0]), hours, minutes, seconds, -1, -1, -1) + #'mktime' assumes arg in local timezone, so add timezone/altzone + utc = time.mktime(time_tuple) + #adjust to DST + if(time.daylight and time.localtime(utc)[-1]): + zone = zone + time.altzone + else: + zone = zone + time.timezone + + received_time_tuple = time.localtime(utc - zone) + converted_received_date = datetime.datetime.fromtimestamp(time.mktime(received_time_tuple)) + return converted_received_date + + @classmethod + def compare_dates(cls, date1, date2, tolerance=1800): + """Compares datetime objects for deviation given certain tolerance.""" + """Returns 1 if there is a significant difference.""" + logging.debug("Comparing dates %s <> %s.", date1, date2) + timedelta = abs(date1 - date2) + if(timedelta.total_seconds()>tolerance): + return True + else: + return False diff --git a/src/mail_iterator.py b/src/mail_iterator.py new file mode 100644 index 0000000..a30af21 --- /dev/null +++ b/src/mail_iterator.py @@ -0,0 +1,124 @@ +''' +mail_iterator.py - The module contains the MailIterator class. + +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 imaplib +import re +import time +import logging + +MAILBOX_RESP = re.compile(r'\((?P.*?)\) "(?P.*)" (?P.*)') +UIDVAL_RESP = re.compile(r'(?P.*) \(UIDVALIDITY (?P.*)\)') + +class MailIterator: + """This class communicates with the e-mail server.""" + + # class attributes + # IMAP4_SSL for connection with an IMAP server + mail_con = None + # list of tuples (uidvalidity, mailboxname) for the retrieved mailboxes + mailboxes = None + # logged in status + logged_in = None + + def __init__(self, server, username, password): + """Creates a connection and a user session.""" + try: + self.mail_con = imaplib.IMAP4_SSL(server) + self.mail_con.login(username, password) + logging.info("Logged in as %s.", username) + except: + self.logged_in = False + raise UserWarning("Could not log in as user " + username + ".") + self.logged_in = True + + try: + result, self.mailboxes = self.mail_con.list() + except: + raise UserWarning("Could not retrieve mailboxes for user " + username + ".") + + def __del__(self): + """Closes the connection and the user session.""" + if(self.logged_in): + try: + self.mail_con.close() + self.mail_con.logout() + except: + pass + + def __iter__(self): + """Iterates through all mailboxes, returns (uidval,name).""" + for mailbox in self.mailboxes: + logging.debug("Checking mailbox %s.", mailbox) + mailbox = MAILBOX_RESP.match(mailbox.decode('iso-8859-1')).groups() + try: + result, data = self.mail_con.status(mailbox[2], '(UIDVALIDITY)') + except: + raise UserWarning("Could not retrieve mailbox uidvalidity.") + uidval = UIDVAL_RESP.match(data[0].decode('iso-8859-1')).groups() + logging.debug("Extracted mailbox info is %s %s.", data[0], uidval) + self.mail_con.select(mailbox[2]) + yield (mailbox[2], uidval[1]) + + def fetch_messages(self): + """Fetches the messages from the current mailbox, return list of uids.""" + try: + result, data = self.mail_con.uid('search', None, "ALL") + except: + raise UserWarning("Could not fetch messages.") + mailid_list = data[0].split() + return mailid_list + + def fetch_internal_date(self, mid): + """Fetches the internal date of a message, returns a time tuple.""" + try: + result, data = self.mail_con.uid('fetch', mid, '(INTERNALDATE)') + except: + raise UserWarning("Could not fetch the internal date of message" + mid + ".") + internal_date = imaplib.Internaldate2tuple(data[0]) + return internal_date + + def fetch_received_date(self, mid): + """Fetches the received date of a message, returns bytes reponse.""" + try: + result, data = self.mail_con.uid('fetch', mid, '(BODY.PEEK[HEADER.FIELDS (RECEIVED)])') + except: + raise UserWarning("Could not fetch the received header of message" + mid + ".") + return data[0][1].decode('iso-8859-1') + + def update_message(self, mid, mailbox, internal_date): + """Replaces a message with one with correct internal date.""" + internal_date_seconds = time.mktime(internal_date.timetuple()) + internal_date_str = imaplib.Time2Internaldate(internal_date_seconds) + try: + result, data = self.mail_con.uid('fetch', mid, '(RFC822)') + #logging.debug("Entire e-mail is: %s", data[0][1]) + + fetched_flags = self.mail_con.uid('fetch', mid, '(FLAGS)')[1][0] + parsed_flags = imaplib.ParseFlags(fetched_flags) + flags_str = " ".join(flag.decode('iso-8859-1') for flag in parsed_flags) + result, data = self.mail_con.append(mailbox, flags_str, + internal_date_str, data[0][1]) + logging.debug("Adding corrected copy of the message reponse: %s %s", result, data) + except: + raise UserWarning("Could not replace the e-mail" + mid + ".") + try: + result, data = self.mail_con.uid('STORE', mid, '+FLAGS', r'(\Deleted)') + logging.debug("Removing old copy of the message reponse: %s %s", result, data) + except: + raise UserWarning("Could not delete the e-mail" + mid + ".") + self.mail_con.expunge() + return diff --git a/src/mailbox_state.py b/src/mailbox_state.py new file mode 100644 index 0000000..2d1e3af --- /dev/null +++ b/src/mailbox_state.py @@ -0,0 +1,109 @@ +''' +mailbox_state.py - The module contains the MailboxState class. + +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. +''' + +class MailboxState: + """This class is responsible for containing and updating a mailbox data.""" + + # class attributes + # string with quotation marks for the mailbox name + name = None + # string for the mailbox uidvalidity + uidvalidity = None + # string for user owning the mailbox + owner = None + # list of bytes for last cached mail uids + uids = None + # tolerance with which the mailbox was synced + tolerance = None + # boolean flag for committing state changes + needs_save = None + # integer for found date conflicts + date_conflicts = None + # integer for found messages with missing received headers + no_received_field = None + # unique key for a mailbox + key = None + + def __init__(self, name, uidvalidity, owner): + self.name = name + self.uidvalidity = uidvalidity + self.owner = owner + + self.uids = [] + self.tolerance = 0 + self.needs_save = False + + self.date_conflicts = 0 + self.no_received_field = 0 + + #special key to ensure better mailbox uniqueness + self.key = self.name.strip('"') + self.uidvalidity + + return + + def __getstate__(self): + """Prepares the MailboxState instance for pickling.""" + changed_dict = self.__dict__.copy() + # remove the following attributes for pickling + del changed_dict['needs_save'] + del changed_dict['date_conflicts'] + del changed_dict['no_received_field'] + return changed_dict + + def __setstate__(self, dict): + """Prepares the MailboxState instance for unpickling.""" + self.name = dict["name"] + self.uidvalidity = dict["uidvalidity"] + self.owner = dict["owner"] + + self.uids = dict["uids"] + self.tolerance = dict["tolerance"] + self.needs_save = False + + self.date_conflicts = 0 + self.no_received_field = 0 + + self.key = dict["key"] + + return + + def __str__(self): + """Makes the class printable.""" + return self.key + + def synchronize(self, list_ids, tolerance): + """Adds new messages to the cache and returns a list of them. + Confirm the changes to a mailbox to finally save it.""" + new_ids = [] + # cache is invalid if mailbox is synced with different tolerance + if(len(self.uids)==0 or tolerance != self.tolerance): + new_ids = list_ids + self.tolerance = tolerance + else: + for uid in list_ids: + try: + self.uids.index(uid) + except ValueError: + new_ids.append(uid) + # update this mailbox potential uids + self.uids = list_ids + return new_ids + + def confirm_change(self): + """Confirm the chages to the cached mailbox and specify used tolerance.""" + self.needs_save = True + return diff --git a/src/unit_tester.py b/src/unit_tester.py new file mode 100644 index 0000000..13da94b --- /dev/null +++ b/src/unit_tester.py @@ -0,0 +1,109 @@ +''' +unit_tester.py - The module contains the MailScriptTester class. + +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 unittest +import datetime, date_interpreter + +class MailScriptTester(unittest.TestCase): + + # class attributes + # DateInterpreter instance testing the DateInterpreter methods + date_interp = None + # datetime for comparison with extracted datetimes and assertions + true_date = None + + def setUp(self): + self.date_interp = date_interpreter.DateInterpreter() + self.true_date = datetime.datetime(2007, 12, 11, 18, 24, 35) + + def test_received_date_extraction1(self): + """Tests the date extraction method.""" + date = [[0, b"Tue, 11 Dec 2007 18:24:35 +0100"]] + extracted_date = self.date_interp.extract_received_date(date) + self.assertEqual(extracted_date, self.true_date, "Failed date format 1") + + def test_received_date_extraction2(self): + """Tests the date extraction method.""" + date = [[0, b"11 Dec 2007 \r\n18:24:35 +0100"]] + extracted_date = self.date_interp.extract_received_date(date) + self.assertEqual(extracted_date, self.true_date, "Failed date format 2") + return + + def test_received_date_extraction3(self): + """Tests the date extraction method.""" + date = [[0, b"11 Dec 2007 18:24:35 +0100"]] + extracted_date = self.date_interp.extract_received_date(date) + self.assertEqual(extracted_date, self.true_date, "Failed date format 3") + + def test_received_date_extraction4(self): + """Tests the date extraction method.""" + date = [[0, b"11 Dec 2007 18:24:35"]] + extracted_date = self.date_interp.extract_received_date(date) + #should not be equal because of time zone assumption + self.assertNotEqual(extracted_date, self.true_date, "Failed date format 4") + + def test_received_date_extraction5(self): + """Tests the received date extraction method.""" + date = [[0, b"11 Dec 2007 18:24:35 GMT"]] + extracted_date = self.date_interp.extract_received_date(date) + #should not be equal because of time zone assumption + self.assertNotEqual(extracted_date, self.true_date, "Failed date format 5") + + def test_received_date_extraction6(self): + """Tests the received date extraction method.""" + date = [[0, b'Received: from intranator.m.i2n ([unix socket])' + b'by intranator.m.i2n with LMTPA; Tue, 11 Dec 2007 18:24:35' + b'+0100Received: from localhost (intranator.m.i2n [127.0.0.1])' + b'by localhost (Postfix) with ESMTP id 895812AC54for ;' + b'Sun, 13 Mar 2011 18:47:18 +0100 (CET)Received: from re04.intra2net.com ' + b'(re04.intra2net.com [82.165.46.26])(using TLSv1 with cipher ADH-AES256-SHA ' + b'(256/256 bits))(No client certificate requested)by intranator.m.i2n (Postfix) with ' + b'ESMTPS id 28DB92AC53for ; Sun, 13 Mar 2011 18:47:15 +0100 ' + b'(CET)Received: from postfix.charite.de (postfix.charite.de [141.42.206.35])(using TLSv1 ' + b'with cipher ADH-AES256-SHA (256/256 bits))(No client certificate requested)by ' + b're04.intra2net.com (Postfix) with ESMTP id C054A3010Afor ; ' + b'Sun, 13 Mar 2011 18:47:14 +0100 (CET)Received: from localhost (localhost [127.0.0.1])by ' + b'de.postfix.org (Postfix) with ESMTP id 7FCCFF7879for ; ' + b'Sun, 13 Mar 2011 18:47:14 +0100 (CET)Received: from de.postfix.org ([127.0.0.1])by ' + b'localhost (de.postfix.org [127.0.0.1]) (amavisd-new, port 10026)with LMTP id ' + b'YSXF-vf3+6E1 for ;Sun, 13 Mar 2011 18:47:14 +0100 (CET)' + b'Received: from de.postfix.org (localhost [127.0.0.1])by de.postfix.org (Postfix) with ' + b'ESMTP id 3C3123DF1Efor ; Sun, 13 Mar 2011 18:46:33 +0100 ' + b'(CET)Received: from localhost (localhost [127.0.0.1])by de.postfix.org (Postfix) with ' + b'ESMTP id AB6CE3DBD2for ; Sun, 13 Mar 2011 18:45:57 +0100 (CET)' + b'Received: from de.postfix.org ([127.0.0.1])by localhost (de.postfix.org [127.0.0.1]) ' + b'(amavisd-new, port 10024)with ESMTP id mBYiZO8wREeS for ;Sun, ' + b'13 Mar 2011 18:45:56 +0100 (CET)Received: from mail.inetmsg.com (mail.inetmsg.com ' + b'[173.10.94.185])by de.postfix.org (Postfix) with ESMTPSfor ; ' + b'Sun, 13 Mar 2011 18:45:55 +0100 (CET)Received: from [192.168.1.107] (fw1.inetmsg.com ' + b'[10.20.30.253])(using TLSv1 with cipher DHE-RSA-CAMELLIA256-SHA (256/256 bits))' + b'(No client certificate requested)by mail.inetmsg.com (INetMsg Mail Service) with ESMTPSA ' + b'id 0B95326CD1for ; Sun, 13 Mar 2011 10:45:41 -0700 (PDT)"]]']] + extracted_date = self.date_interp.extract_received_date(date) + #should not be equal because of time zone assumption + self.assertEqual(extracted_date, self.true_date, "Failed date format 6") + + def test_compare_dates(self): + """Tests the date comparison method.""" + self.true_date2 = datetime.datetime(2007, 12, 11, 18, 34, 35) + #is difference of 10 mins significant if tolerance is 9 mins + self.assertTrue(bool(self.date_interp.compare_dates(self.true_date, self.true_date2, 9*60)), "Failed at comparison test") + #is difference of 10 mins significant if tolerance is 11 mins + self.assertFalse(bool(self.date_interp.compare_dates(self.true_date, self.true_date2, 11*60)), "Failed at comparison test") + +if __name__ == '__main__': + unittest.main() diff --git a/src/userdata.csv b/src/userdata.csv new file mode 100644 index 0000000..51652bf --- /dev/null +++ b/src/userdata.csv @@ -0,0 +1,4 @@ +username,password +usr1,psw1 +usr2,psw2 +usr3,psw3 diff --git a/unit_tester.py b/unit_tester.py deleted file mode 100644 index 13da94b..0000000 --- a/unit_tester.py +++ /dev/null @@ -1,109 +0,0 @@ -''' -unit_tester.py - The module contains the MailScriptTester class. - -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 unittest -import datetime, date_interpreter - -class MailScriptTester(unittest.TestCase): - - # class attributes - # DateInterpreter instance testing the DateInterpreter methods - date_interp = None - # datetime for comparison with extracted datetimes and assertions - true_date = None - - def setUp(self): - self.date_interp = date_interpreter.DateInterpreter() - self.true_date = datetime.datetime(2007, 12, 11, 18, 24, 35) - - def test_received_date_extraction1(self): - """Tests the date extraction method.""" - date = [[0, b"Tue, 11 Dec 2007 18:24:35 +0100"]] - extracted_date = self.date_interp.extract_received_date(date) - self.assertEqual(extracted_date, self.true_date, "Failed date format 1") - - def test_received_date_extraction2(self): - """Tests the date extraction method.""" - date = [[0, b"11 Dec 2007 \r\n18:24:35 +0100"]] - extracted_date = self.date_interp.extract_received_date(date) - self.assertEqual(extracted_date, self.true_date, "Failed date format 2") - return - - def test_received_date_extraction3(self): - """Tests the date extraction method.""" - date = [[0, b"11 Dec 2007 18:24:35 +0100"]] - extracted_date = self.date_interp.extract_received_date(date) - self.assertEqual(extracted_date, self.true_date, "Failed date format 3") - - def test_received_date_extraction4(self): - """Tests the date extraction method.""" - date = [[0, b"11 Dec 2007 18:24:35"]] - extracted_date = self.date_interp.extract_received_date(date) - #should not be equal because of time zone assumption - self.assertNotEqual(extracted_date, self.true_date, "Failed date format 4") - - def test_received_date_extraction5(self): - """Tests the received date extraction method.""" - date = [[0, b"11 Dec 2007 18:24:35 GMT"]] - extracted_date = self.date_interp.extract_received_date(date) - #should not be equal because of time zone assumption - self.assertNotEqual(extracted_date, self.true_date, "Failed date format 5") - - def test_received_date_extraction6(self): - """Tests the received date extraction method.""" - date = [[0, b'Received: from intranator.m.i2n ([unix socket])' - b'by intranator.m.i2n with LMTPA; Tue, 11 Dec 2007 18:24:35' - b'+0100Received: from localhost (intranator.m.i2n [127.0.0.1])' - b'by localhost (Postfix) with ESMTP id 895812AC54for ;' - b'Sun, 13 Mar 2011 18:47:18 +0100 (CET)Received: from re04.intra2net.com ' - b'(re04.intra2net.com [82.165.46.26])(using TLSv1 with cipher ADH-AES256-SHA ' - b'(256/256 bits))(No client certificate requested)by intranator.m.i2n (Postfix) with ' - b'ESMTPS id 28DB92AC53for ; Sun, 13 Mar 2011 18:47:15 +0100 ' - b'(CET)Received: from postfix.charite.de (postfix.charite.de [141.42.206.35])(using TLSv1 ' - b'with cipher ADH-AES256-SHA (256/256 bits))(No client certificate requested)by ' - b're04.intra2net.com (Postfix) with ESMTP id C054A3010Afor ; ' - b'Sun, 13 Mar 2011 18:47:14 +0100 (CET)Received: from localhost (localhost [127.0.0.1])by ' - b'de.postfix.org (Postfix) with ESMTP id 7FCCFF7879for ; ' - b'Sun, 13 Mar 2011 18:47:14 +0100 (CET)Received: from de.postfix.org ([127.0.0.1])by ' - b'localhost (de.postfix.org [127.0.0.1]) (amavisd-new, port 10026)with LMTP id ' - b'YSXF-vf3+6E1 for ;Sun, 13 Mar 2011 18:47:14 +0100 (CET)' - b'Received: from de.postfix.org (localhost [127.0.0.1])by de.postfix.org (Postfix) with ' - b'ESMTP id 3C3123DF1Efor ; Sun, 13 Mar 2011 18:46:33 +0100 ' - b'(CET)Received: from localhost (localhost [127.0.0.1])by de.postfix.org (Postfix) with ' - b'ESMTP id AB6CE3DBD2for ; Sun, 13 Mar 2011 18:45:57 +0100 (CET)' - b'Received: from de.postfix.org ([127.0.0.1])by localhost (de.postfix.org [127.0.0.1]) ' - b'(amavisd-new, port 10024)with ESMTP id mBYiZO8wREeS for ;Sun, ' - b'13 Mar 2011 18:45:56 +0100 (CET)Received: from mail.inetmsg.com (mail.inetmsg.com ' - b'[173.10.94.185])by de.postfix.org (Postfix) with ESMTPSfor ; ' - b'Sun, 13 Mar 2011 18:45:55 +0100 (CET)Received: from [192.168.1.107] (fw1.inetmsg.com ' - b'[10.20.30.253])(using TLSv1 with cipher DHE-RSA-CAMELLIA256-SHA (256/256 bits))' - b'(No client certificate requested)by mail.inetmsg.com (INetMsg Mail Service) with ESMTPSA ' - b'id 0B95326CD1for ; Sun, 13 Mar 2011 10:45:41 -0700 (PDT)"]]']] - extracted_date = self.date_interp.extract_received_date(date) - #should not be equal because of time zone assumption - self.assertEqual(extracted_date, self.true_date, "Failed date format 6") - - def test_compare_dates(self): - """Tests the date comparison method.""" - self.true_date2 = datetime.datetime(2007, 12, 11, 18, 34, 35) - #is difference of 10 mins significant if tolerance is 9 mins - self.assertTrue(bool(self.date_interp.compare_dates(self.true_date, self.true_date2, 9*60)), "Failed at comparison test") - #is difference of 10 mins significant if tolerance is 11 mins - self.assertFalse(bool(self.date_interp.compare_dates(self.true_date, self.true_date2, 11*60)), "Failed at comparison test") - -if __name__ == '__main__': - unittest.main() diff --git a/userdata.csv b/userdata.csv deleted file mode 100644 index 51652bf..0000000 --- a/userdata.csv +++ /dev/null @@ -1,4 +0,0 @@ -username,password -usr1,psw1 -usr2,psw2 -usr3,psw3 -- 1.7.1