Problem with copying message to the server
Nicolas Sebrecht
nicolas.s-dev at laposte.net
Thu Jan 27 18:23:52 GMT 2011
On Thu, Jan 27, 2011 at 06:15:38PM +0100, Sebastian Spaeth wrote:
> The bug has been confirmed to be a python issue. I reworked my date
> refactoring patch to not use the buggy Time2Internaldate() and parse it
> into an INTERNALDATE string outselves.
Thanks.
> Please review the 2 patches in reply to this one.
This is a typical use case where inter-diffs are so usefull.
But patch 4/4 doesn't apply cleanly anymore, so it is only a _partial_
inter-diff.
I'm in favor of "datetuple" rather than "dtuple" as it's more explicit.
Other than that, it looks good to me, for now.
-- >8 --
diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py
index f5fd490..525c3b3 100644
--- a/offlineimap/folder/IMAP.py
+++ b/offlineimap/folder/IMAP.py
@@ -302,39 +302,52 @@ class IMAPFolder(BaseFolder):
def getmessagedate(self, content, rtime=None):
- """Parses mail and returns an 9-datetuple
+ """Parses mail and returns an INTERNALDATE string
It will use information in the following order, falling back as an attempt fails:
- rtime parameter
- Date header of email
- - Local system time
+
+ We return None, if we couldn't find a valid date. In this case
+ the IMAP server will use the server local time when appening
+ (per RFC).
+
+ Note, that imaplib's Time2Internaldate is inherently broken as
+ it returns localized date strings which are invalid for IMAP
+ servers. However, that function is called for *every* append()
+ internally. So we need to either pass in `None` or the correct
+ string (in which case Time2Internaldate() will do nothing) to
+ append(). The output of this function is designed to work as
+ input to the imapobj.append() function.
+
:param rtime: epoch timestamp to be used rather than analyzing
the email.
- :returns: 9-tuple that can be passed directly to time.mktime()
- or None in case of failure. Indexes 6, 7, and 8 are not usable."""
+ :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 = rfc822.Message(StringIO(content))
# parsedate returns a 9-tuple that can be passed directly to
# time.mktime(); 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 = rfc822.parsedate(message.getheader('Date'))
+ dtuple = rfc822.parsedate(message.getheader('Date'))
- if datetuple is None:
+ if dtuple is None:
#could not determine the date, use the local time.
- datetuple = time.localtime()
+ return None
else:
#rtime is set, use that instead
- datetuple = time.localtime(rtime)
+ dtuple = time.localtime(rtime)
try:
# Check for invalid dates
- if datetuple[0] < 1981:
+ if dtuple[0] < 1981:
raise ValueError
# Check for invalid dates
- datetuple_check = time.localtime(time.mktime(datetuple))
- if datetuple[:2] != datetuple_check[:2]:
+ datetuple_check = time.localtime(time.mktime(dtuple))
+ if dtuple[:2] != datetuple_check[:2]:
raise ValueError
except (ValueError, OverflowError):
@@ -342,10 +355,23 @@ class IMAPFolder(BaseFolder):
# or something. Argh. It seems that Time2Internaldate
# will rause a ValueError if the year is 0102 but not 1902,
# but some IMAP servers nonetheless choke on 1902.
- self.ui.debug("Message with invalid date %s. Using local time." % datetuple)
- datetuple = time.localtime()
+ self.ui.debug("Message with invalid date %s. Server will use local time." % dtuple)
+ return None
+
+ #produce a string representation of datetuple that works as
+ #INTERNALDATE
+ num2mon = {1:'Jan', 2:'Feb', 3:'Mar', 4:'Apr', 5:'May', 6:'Jun',
+ 7:'Jul', 8:'Aug', 9:'Sep', 10:'Oct', 11:'Nov', 12:'Dec'}
+
+ if dtuple.tm_isdst == '1':
+ zone = -time.altzone
+ else:
+ zone = -time.timezone
- return datetuple
+ internaldate = '"%02d-%s-%04d %02d:%02d:%02d %+03d%02d"' % (dtuple.tm_mday, num2mon[dtuple.tm_mon], dtuple.tm_year, \
+ dtuple.tm_hour, dtuple.tm_min, dtuple.tm_sec, divmod(zone//60, 60))
+
+ return internaldate
def savemessage(self, uid, content, flags, rtime):
"""Save the message on the Server
@@ -372,32 +398,6 @@ class IMAPFolder(BaseFolder):
# get the date of the message file, so we can pass it to the server.
date = self.getmessagedate(content, rtime)
- # If time isn't known
- if rtime == None and datetuple_msg == None:
- datetuple = time.localtime()
- elif rtime == None:
- datetuple = datetuple_msg
- else:
- datetuple = time.localtime(rtime)
-
- try:
- if datetuple[0] < 1981:
- raise ValueError
-
- # Check for invalid date
- datetuple_check = time.localtime(time.mktime(datetuple))
- if datetuple[:2] != datetuple_check[:2]:
- raise ValueError
-
- # This could raise a value error if it's not a valid format.
- date = imaplib.Time2Internaldate(datetuple)
- except (ValueError, OverflowError):
- # Argh, sometimes it's a valid format but year is 0102
- # or something. Argh. It seems that Time2Internaldate
- # will rause a ValueError if the year is 0102 but not 1902,
- # but some IMAP servers nonetheless choke on 1902.
- date = imaplib.Time2Internaldate(time.localtime())
-
self.ui.debug('imap', 'savemessage: using date %s' % date)
content = re.sub("(?<!\r)\n", "\r\n", content)
self.ui.debug('imap', 'savemessage: initial content is: ' + repr(content))
--
Nicolas Sebrecht
More information about the OfflineIMAP-project
mailing list