diff --git a/Changelog.rst b/Changelog.rst index 19bc664..db31674 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -21,6 +21,8 @@ WIP (add new stuff for the next release) (Steve Purcell) * Make the list of authentication mechanisms to be configurable. (Andreas Mack) +* Allow to set message access and modification timestamps based + on the "Date" header of the message itself. (Cyril Russo) OfflineIMAP v6.5.5-rc1 (2012-09-05) =================================== diff --git a/offlineimap/emailutil.py b/offlineimap/emailutil.py new file mode 100644 index 0000000..28463f7 --- /dev/null +++ b/offlineimap/emailutil.py @@ -0,0 +1,38 @@ +# Some useful functions to extract data out of emails +# Copyright (C) 2002-2012 John Goerzen & contributors +# +# 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 2 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. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import email +from email.Parser import Parser as MailParser +import time + +def get_message_date(content, header='Date'): + """ + Parses mail and returns resulting timestamp. + + :param header: the header to extract date from; + :returns: timestamp or `None` in the case of failure. + + """ + message = MailParser().parsestr(content, True) + dateheader = message.get(header) + # parsedate_tz returns a 10-tuple that can be passed to mktime_tz + # Will be None if missing or not in a valid format. Note that + # indexes 6, 7, and 8 of the result tuple are not usable. + datetuple = email.utils.parsedate_tz(dateheader) + if datetuple is None: + return None + return email.utils.mktime_tz(datetuple) diff --git a/offlineimap/folder/Base.py b/offlineimap/folder/Base.py index 27c9a2b..755a31a 100644 --- a/offlineimap/folder/Base.py +++ b/offlineimap/folder/Base.py @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -from offlineimap import threadutil +from offlineimap import threadutil, emailutil from offlineimap import globals from offlineimap.ui import getglobalui from offlineimap.error import OfflineImapError @@ -48,6 +48,13 @@ class BaseFolder(object): if self.visiblename == self.getsep(): self.visiblename = '' self.config = repository.getconfig() + utime_from_message_global = \ + self.config.getdefaultboolean("general", + "utime_from_message", False) + repo = "Repository " + repository.name + self._utime_from_message = \ + self.config.getdefaultboolean(repo, + "utime_from_message", utime_from_message_global) def getname(self): """Returns name""" @@ -66,6 +73,10 @@ class BaseFolder(object): """Should this folder be synced or is it e.g. filtered out?""" return self._sync_this + @property + def utime_from_message(self): + return self._utime_from_message + def suggeststhreads(self): """Returns true if this folder suggests using threads for actions; false otherwise. Probably only IMAP will return true.""" @@ -327,6 +338,9 @@ class BaseFolder(object): message = None flags = self.getmessageflags(uid) rtime = self.getmessagetime(uid) + if dstfolder.utime_from_message: + content = self.getmessage(uid) + rtime = emailutil.get_message_date(content, 'Date') if uid > 0 and dstfolder.uidexists(uid): # dst has message with that UID already, only update status diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py index deba1db..cec9eda 100644 --- a/offlineimap/folder/IMAP.py +++ b/offlineimap/folder/IMAP.py @@ -15,14 +15,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import email import random import binascii import re import time from sys import exc_info from .Base import BaseFolder -from offlineimap import imaputil, imaplibutil, OfflineImapError +from offlineimap import imaputil, imaplibutil, emailutil, OfflineImapError from offlineimap import globals from offlineimap.imaplib2 import MonthNames @@ -431,21 +430,12 @@ class IMAPFolder(BaseFolder): :returns: string in the form of "DD-Mmm-YYYY HH:MM:SS +HHMM" (including double quotes) or `None` in case of failure (which is fine as value for append).""" + if rtime is None: - message = email.message_from_string(content) - dateheader = message.get('Date') - # parsedate_tz returns a 10-tuple that can be passed to mktime_tz; - # Will be None if missing or not in a valid format. Note that - # indexes 6, 7, and 8 of the result tuple are not usable. - datetuple = email.utils.parsedate_tz(dateheader) - if datetuple is None: - #could not determine the date, use the local time. + rtime = emailutil.get_message_date(content) + if rtime == None: return None - #make it a real struct_time, so we have named attributes - datetuple = time.localtime(email.utils.mktime_tz(datetuple)) - else: - #rtime is set, use that instead - datetuple = time.localtime(rtime) + datetuple = time.localtime(rtime) try: # Check for invalid dates