Commit | Line | Data |
---|---|---|
c9da760a PD |
1 | ''' |
2 | date_interpreter.py - The module contains the MailIterator class. | |
3 | ||
4 | Copyright (c) 2012 Intra2net AG | |
5 | Author: Plamen Dimitrov | |
6 | ||
7 | This program is free software: you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation, either version 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | Add '-t' argument when running the module for a test mode. | |
18 | For a detailed list of each message with a date conflict change | |
19 | the 'log_level' in the configuration file from '30' to '20'. | |
20 | ''' | |
21 | ||
22 | import datetime | |
23 | import re | |
24 | import time | |
25 | ||
26 | #reg expressions | |
27 | RECEIVED_DATE = re.compile(r'(0?[1-9]|[1-2][0-9]|3[01])\s+([A-Z][a-z][a-z])\s+' | |
28 | 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*' | |
29 | r'(?:([-\+])([0-9]{2})([0-5][0-9]))*') | |
30 | INTERNAL_DATE = re.compile(r'(?P<day>[ 0123][0-9])-(?P<mon>[A-Z][a-z][a-z])-(?P<year>[0-9][0-9][0-9][0-9])' | |
31 | r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])' | |
32 | r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])') | |
33 | CONTROL_SYMBOLS = re.compile(r'[\n\r\t]') | |
34 | ||
35 | class DateInterpreter: | |
36 | """This class extracts dates from imap server responses and compares them.""" | |
37 | ||
38 | def __init__(self): | |
39 | return | |
40 | ||
41 | @classmethod | |
42 | def extract_internal_date(cls, fetchresult): | |
43 | """Extracts the internal date from INTERNALDATE, returns datetime.""" | |
44 | return datetime.datetime.fromtimestamp(time.mktime(fetchresult)) | |
45 | ||
46 | @classmethod | |
47 | def extract_received_date(cls, fetchresult): | |
48 | """Extracts the first date from RECEIVED, returns datetime.""" | |
49 | fetchresult = CONTROL_SYMBOLS.sub('', fetchresult[0][1].decode("utf-8")) | |
50 | received_dates = RECEIVED_DATE.findall(fetchresult) | |
51 | if(len(received_dates)==0): | |
52 | return "" | |
53 | else: received_date = received_dates[0] | |
54 | #print("Retrieved date ", received_date, " from header ", fetchresult) | |
55 | month = datetime.datetime.strptime(received_date[1],'%b').month | |
56 | ||
57 | if(received_date[3]!=""): | |
58 | hours = int(received_date[3]) | |
59 | else: hours = 0 | |
60 | if(received_date[4]!=""): | |
61 | minutes = int(received_date[4]) | |
62 | else: minutes = 0 | |
63 | if(received_date[5]!=""): | |
64 | seconds = int(received_date[5]) | |
65 | else: seconds = 0 | |
66 | ||
67 | if(received_date[6]!=""): | |
68 | zonen = received_date[6] | |
69 | else: zonen = b'+' | |
70 | if(received_date[7]!=""): | |
71 | zoneh = int(received_date[7]) | |
72 | else: zoneh = 0 | |
73 | if(received_date[8]!=""): | |
74 | zonem = int(received_date[8]) | |
75 | else: zonem = 0 | |
76 | # subtract time zone to get unified time | |
77 | zone = (zoneh * 60 + zonem) * 60 | |
78 | if(zonen == b'-'): | |
79 | zone = -zone | |
80 | ||
81 | time_tuple = (int(received_date[2]), month, int(received_date[0]), hours, minutes, seconds, -1, -1, -1) | |
82 | #'mktime' assumes arg in local timezone, so add timezone/altzone | |
83 | utc = time.mktime(time_tuple) | |
84 | #adjust to DST | |
85 | if(time.daylight and time.localtime(utc)[-1]): | |
86 | zone = zone + time.altzone | |
87 | else: | |
88 | zone = zone + time.timezone | |
89 | ||
90 | received_time_tuple = time.localtime(utc - zone) | |
91 | converted_received_date = datetime.datetime.fromtimestamp(time.mktime(received_time_tuple)) | |
92 | return converted_received_date | |
93 | ||
94 | @classmethod | |
95 | def compare_dates(cls, date1, date2, tolerance=1800): | |
96 | """Compares datetime objects for deviation given certain tolerance.""" | |
97 | """Returns 1 if there is a significant difference.""" | |
98 | #print(date1, "<>", date2) | |
99 | timedelta = abs(date1 - date2) | |
100 | if(timedelta.total_seconds()>tolerance): | |
101 | return 1 | |
102 | else: | |
103 | return 0 |