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