db805043f2
refs deb#469598 Bug report received in Debian. User is having occasional trouble with Gmail driver crashing, indicates problem may have started after switching to Gmail. Gmail driver merged to tree in81b86fb74
on 2008-01-03 and recently released in beta tarballs. BT shows: File "/var/lib/python-support/python2.4/offlineimap/folder/Gmail.py", line 102, in processmessagesflags attributehash = imaputil.flags2hash(imaputil.imapsplit(result)[1]) File "/var/lib/python-support/python2.4/offlineimap/imaputil.py", line 88, in imapsplit for i in range(len(imapstring)): TypeError: len() of unsized object imap debug log shows: imap: 29:58.16 > BEAL75 UID STORE 4887 +FLAGS (\Deleted) imap: 29:58.47 < BEAL75 OK Success imap: 29:58.47 matched r'(?P<tag>BEAL\d+) (?P<type>[A-Z]+) (?P<data>.*)' => ('BEAL75', 'OK', 'Success') imap: imapsplit() called with input: None imap: imapsplit() got a non-string input; working around. looking at code for Gmail.py processmessagesflags, comments from Ricardo indicate it was copied from the same function in folder/IMAP.py. However, folder/IMAP.py has checks for this, added 2002-07-12 in5342dacc
&817a10ce
. Suspect that Gmail author believed those checks superfluous for Gmail. Copied checks from folder/IMAP.py to folder/Gmail.py.
127 lines
5.5 KiB
Python
127 lines
5.5 KiB
Python
# Gmail IMAP folder support
|
|
# Copyright (C) 2008 Riccardo Murri <riccardo.murri@gmail.com>
|
|
# Copyright (C) 2002-2007 John Goerzen <jgoerzen@complete.org>
|
|
#
|
|
# 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
|
|
|
|
"""Folder implementation to support features of the Gmail IMAP server.
|
|
"""
|
|
|
|
from IMAP import IMAPFolder
|
|
import imaplib
|
|
from offlineimap import imaputil, imaplibutil
|
|
from offlineimap.ui import UIBase
|
|
from copy import copy
|
|
|
|
|
|
class GmailFolder(IMAPFolder):
|
|
"""Folder implementation to support features of the Gmail IMAP server.
|
|
Specifically, deleted messages are moved to folder `Gmail.TRASH_FOLDER`
|
|
(by default: ``[Gmail]/Trash``) prior to expunging them, since
|
|
Gmail maps to IMAP ``EXPUNGE`` command to "remove label".
|
|
|
|
For more information on the Gmail IMAP server:
|
|
http://mail.google.com/support/bin/answer.py?answer=77657&topic=12815
|
|
"""
|
|
|
|
#: Where deleted mail should be moved
|
|
TRASH_FOLDER='[Gmail]/Trash'
|
|
|
|
#: Gmail will really delete messages upon EXPUNGE in these folders
|
|
REAL_DELETE_FOLDERS = [ TRASH_FOLDER, '[Gmail]/Spam' ]
|
|
|
|
def __init__(self, imapserver, name, visiblename, accountname, repository):
|
|
self.realdelete = repository.getrealdelete(name)
|
|
IMAPFolder.__init__(self, imapserver, name, visiblename, \
|
|
accountname, repository)
|
|
|
|
def deletemessages_noconvert(self, uidlist):
|
|
uidlist = [uid for uid in uidlist if uid in self.messagelist]
|
|
if not len(uidlist):
|
|
return
|
|
|
|
if self.realdelete and not (self.getname() in self.REAL_DELETE_FOLDERS):
|
|
# IMAP expunge is just "remove label" in this folder,
|
|
# so map the request into a "move into Trash"
|
|
|
|
imapobj = self.imapserver.acquireconnection()
|
|
try:
|
|
imapobj.select(self.getfullname())
|
|
result = imapobj.uid('copy',
|
|
imaputil.listjoin(uidlist),
|
|
self.TRASH_FOLDER)
|
|
assert result[0] == 'OK', \
|
|
"Bad IMAPlib result: %s" % result[0]
|
|
finally:
|
|
self.imapserver.releaseconnection(imapobj)
|
|
for uid in uidlist:
|
|
del self.messagelist[uid]
|
|
else:
|
|
IMAPFolder.deletemessages_noconvert(self, uidlist)
|
|
|
|
def processmessagesflags(self, operation, uidlist, flags):
|
|
# XXX: the imapobj.myrights(...) calls dies with an error
|
|
# report from Gmail server stating that IMAP command
|
|
# 'MYRIGHTS' is not implemented. So, this
|
|
# `processmessagesflags` is just a copy from `IMAPFolder`,
|
|
# with the references to `imapobj.myrights()` deleted This
|
|
# shouldn't hurt, however, Gmail users always have full
|
|
# control over all their mailboxes (apparently).
|
|
if len(uidlist) > 101:
|
|
# Hack for those IMAP ervers with a limited line length
|
|
self.processmessagesflags(operation, uidlist[:100], flags)
|
|
self.processmessagesflags(operation, uidlist[100:], flags)
|
|
return
|
|
|
|
imapobj = self.imapserver.acquireconnection()
|
|
try:
|
|
imapobj.select(self.getfullname())
|
|
r = imapobj.uid('store',
|
|
imaputil.listjoin(uidlist),
|
|
operation + 'FLAGS',
|
|
imaputil.flagsmaildir2imap(flags))
|
|
assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
|
|
r = r[1]
|
|
finally:
|
|
self.imapserver.releaseconnection(imapobj)
|
|
|
|
needupdate = copy(uidlist)
|
|
for result in r:
|
|
if result == None:
|
|
# Compensate for servers that don't return anything from
|
|
# STORE.
|
|
continue
|
|
attributehash = imaputil.flags2hash(imaputil.imapsplit(result)[1])
|
|
if not ('UID' in attributehash and 'FLAGS' in attributehash):
|
|
# Compensate for servers that don't return a UID attribute.
|
|
continue
|
|
flags = attributehash['FLAGS']
|
|
uid = long(attributehash['UID'])
|
|
self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
|
|
try:
|
|
needupdate.remove(uid)
|
|
except ValueError: # Let it slide if it's not in the list
|
|
pass
|
|
for uid in needupdate:
|
|
if operation == '+':
|
|
for flag in flags:
|
|
if not flag in self.messagelist[uid]['flags']:
|
|
self.messagelist[uid]['flags'].append(flag)
|
|
self.messagelist[uid]['flags'].sort()
|
|
elif operation == '-':
|
|
for flag in flags:
|
|
if flag in self.messagelist[uid]['flags']:
|
|
self.messagelist[uid]['flags'].remove(flag)
|