Factor out the date guessing/retrieving
savemessage was too long and complex. Factor out the date guessing part of the function and put it into a function of its own. The logic of the date guessing is the same, however, we do not use the imaplib.Time2InternalDate() function as it is buggy (http://bugs.python.org/issue11024) and returns localized patches. So we create INTERNALDATE ourselves and pass it to append() as a string. This commit fixes a bug that international users used to pass an invalid date to the IMAP server, which the server will either ignore or complain about. Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de> Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
parent
419f27418e
commit
d22c762385
@ -26,8 +26,9 @@ Bug Fixes
|
|||||||
|
|
||||||
* Allow SSL connections to send keep-alive messages.
|
* Allow SSL connections to send keep-alive messages.
|
||||||
* Fix regression (UIBase is no more).
|
* Fix regression (UIBase is no more).
|
||||||
|
|
||||||
* Make profiling mode really enforce single-threading
|
* Make profiling mode really enforce single-threading
|
||||||
|
* Do not send localized date strings to the IMAP server as it will
|
||||||
|
either ignore or refuse them.
|
||||||
|
|
||||||
Pending for the next major release
|
Pending for the next major release
|
||||||
==================================
|
==================================
|
||||||
|
@ -299,52 +299,112 @@ class IMAPFolder(BaseFolder):
|
|||||||
matchinguids.sort()
|
matchinguids.sort()
|
||||||
return long(matchinguids[0])
|
return long(matchinguids[0])
|
||||||
|
|
||||||
|
|
||||||
|
def getmessageinternaldate(self, content, rtime=None):
|
||||||
|
"""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
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
TODO: We should probably be returning a bytearray rather than a
|
||||||
|
string here, because the IMAP server will expect plain
|
||||||
|
ASCII. However, imaplib.Time2INternaldate currently returns a
|
||||||
|
string so we go with the same for now.
|
||||||
|
|
||||||
|
:param rtime: epoch timestamp to be used rather than analyzing
|
||||||
|
the email.
|
||||||
|
: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'))
|
||||||
|
|
||||||
|
if datetuple is None:
|
||||||
|
#could not determine the date, use the local time.
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
#rtime is set, use that instead
|
||||||
|
datetuple = time.localtime(rtime)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Check for invalid dates
|
||||||
|
if datetuple[0] < 1981:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
# Check for invalid dates
|
||||||
|
datetuple_check = time.localtime(time.mktime(datetuple))
|
||||||
|
if datetuple[:2] != datetuple_check[:2]:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
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.
|
||||||
|
self.ui.debug("Message with invalid date %s. Server will use local time." % datetuple)
|
||||||
|
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 datetuple.tm_isdst == '1':
|
||||||
|
zone = -time.altzone
|
||||||
|
else:
|
||||||
|
zone = -time.timezone
|
||||||
|
offset_h, offset_m = divmod(zone//60, 60)
|
||||||
|
|
||||||
|
internaldate = '"%02d-%s-%04d %02d:%02d:%02d %+03d%02d"' \
|
||||||
|
% (datetuple.tm_mday, num2mon[datetuple.tm_mon], datetuple.tm_year, \
|
||||||
|
datetuple.tm_hour, datetuple.tm_min, datetuple.tm_sec, offset_h, offset_m)
|
||||||
|
|
||||||
|
return internaldate
|
||||||
|
|
||||||
def savemessage(self, uid, content, flags, rtime):
|
def savemessage(self, uid, content, flags, rtime):
|
||||||
|
"""Save the message on the Server
|
||||||
|
|
||||||
|
This backend always assigns a new uid, so the uid arg is ignored.
|
||||||
|
|
||||||
|
This function will update the self.messagelist dict to contain
|
||||||
|
the new message after sucessfully saving it.
|
||||||
|
|
||||||
|
:param rtime: A timestamp to be
|
||||||
|
:returns: the UID of the new message as assigned by the
|
||||||
|
server. If the folder is read-only it will return 0."""
|
||||||
imapobj = self.imapserver.acquireconnection()
|
imapobj = self.imapserver.acquireconnection()
|
||||||
self.ui.debug('imap', 'savemessage: called')
|
self.ui.debug('imap', 'savemessage: called')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
imapobj.select(self.getfullname()) # Needed for search
|
imapobj.select(self.getfullname()) # Needed for search
|
||||||
except imapobj.readonly:
|
except imapobj.readonly:
|
||||||
self.ui.msgtoreadonly(self, uid, content, flags)
|
self.ui.msgtoreadonly(self, uid, content, flags)
|
||||||
# Return indicating message taken, but no UID assigned.
|
# Return indicating message taken, but no UID assigned.
|
||||||
# Fudge it.
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# This backend always assigns a new uid, so the uid arg is ignored.
|
# get the date of the message file, so we can pass it to the server.
|
||||||
# In order to get the new uid, we need to save off the message ID.
|
date = self.getmessageinternaldate(content, rtime)
|
||||||
|
|
||||||
message = rfc822.Message(StringIO(content))
|
self.ui.debug('imap', 'savemessage: using date %s' % date)
|
||||||
datetuple_msg = rfc822.parsedate(message.getheader('Date'))
|
|
||||||
# Will be None if missing or not in a valid format.
|
|
||||||
|
|
||||||
# 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 ' + str(date))
|
|
||||||
content = re.sub("(?<!\r)\n", "\r\n", content)
|
content = re.sub("(?<!\r)\n", "\r\n", content)
|
||||||
self.ui.debug('imap', 'savemessage: initial content is: ' + repr(content))
|
self.ui.debug('imap', 'savemessage: initial content is: ' + repr(content))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user