]>
code.delx.au - offlineimap/blob - offlineimap/folder/Gmail.py
1 # Gmail IMAP folder support
2 # Copyright (C) 2008 Riccardo Murri <riccardo.murri@gmail.com>
3 # Copyright (C) 2002-2007 John Goerzen <jgoerzen@complete.org>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 """Folder implementation to support features of the Gmail IMAP server.
22 from IMAP
import IMAPFolder
24 from offlineimap
import imaputil
, imaplibutil
25 from offlineimap
.ui
import UIBase
29 class GmailFolder(IMAPFolder
):
30 """Folder implementation to support features of the Gmail IMAP server.
31 Specifically, deleted messages are moved to folder `Gmail.TRASH_FOLDER`
32 (by default: ``[Gmail]/Trash``) prior to expunging them, since
33 Gmail maps to IMAP ``EXPUNGE`` command to "remove label".
35 For more information on the Gmail IMAP server:
36 http://mail.google.com/support/bin/answer.py?answer=77657&topic=12815
39 #: Where deleted mail should be moved
40 TRASH_FOLDER
='[Gmail]/Trash'
42 #: Gmail will really delete messages upon EXPUNGE in these folders
43 REAL_DELETE_FOLDERS
= [ TRASH_FOLDER
, '[Gmail]/Spam' ]
45 def __init__(self
, imapserver
, name
, visiblename
, accountname
, repository
):
46 self
.realdelete
= repository
.getrealdelete(name
)
47 IMAPFolder
.__init
__(self
, imapserver
, name
, visiblename
, \
48 accountname
, repository
)
50 def deletemessages_noconvert(self
, uidlist
):
51 uidlist
= [uid
for uid
in uidlist
if uid
in self
.messagelist
]
55 if self
.realdelete
and not (self
.getname() in self
.REAL_DELETE_FOLDERS
):
56 # IMAP expunge is just "remove label" in this folder,
57 # so map the request into a "move into Trash"
59 imapobj
= self
.imapserver
.acquireconnection()
61 imapobj
.select(self
.getfullname())
62 result
= imapobj
.uid('copy',
63 imaputil
.listjoin(uidlist
),
65 assert result
[0] == 'OK', \
66 "Bad IMAPlib result: %s" % result
[0]
68 self
.imapserver
.releaseconnection(imapobj
)
70 del self
.messagelist
[uid
]
72 IMAPFolder
.deletemessages_noconvert(self
, uidlist
)
74 def processmessagesflags(self
, operation
, uidlist
, flags
):
75 # XXX: the imapobj.myrights(...) calls dies with an error
76 # report from Gmail server stating that IMAP command
77 # 'MYRIGHTS' is not implemented. So, this
78 # `processmessagesflags` is just a copy from `IMAPFolder`,
79 # with the references to `imapobj.myrights()` deleted This
80 # shouldn't hurt, however, Gmail users always have full
81 # control over all their mailboxes (apparently).
82 if len(uidlist
) > 101:
83 # Hack for those IMAP ervers with a limited line length
84 self
.processmessagesflags(operation
, uidlist
[:100], flags
)
85 self
.processmessagesflags(operation
, uidlist
[100:], flags
)
88 imapobj
= self
.imapserver
.acquireconnection()
90 imapobj
.select(self
.getfullname())
91 r
= imapobj
.uid('store',
92 imaputil
.listjoin(uidlist
),
94 imaputil
.flagsmaildir2imap(flags
))
95 assert r
[0] == 'OK', 'Error with store: ' + '. '.join(r
[1])
98 self
.imapserver
.releaseconnection(imapobj
)
100 needupdate
= copy(uidlist
)
103 # Compensate for servers that don't return anything from
106 attributehash
= imaputil
.flags2hash(imaputil
.imapsplit(result
)[1])
107 if not ('UID' in attributehash
and 'FLAGS' in attributehash
):
108 # Compensate for servers that don't return a UID attribute.
110 flags
= attributehash
['FLAGS']
111 uid
= long(attributehash
['UID'])
112 self
.messagelist
[uid
]['flags'] = imaputil
.flagsimap2maildir(flags
)
114 needupdate
.remove(uid
)
115 except ValueError: # Let it slide if it's not in the list
117 for uid
in needupdate
:
120 if not flag
in self
.messagelist
[uid
]['flags']:
121 self
.messagelist
[uid
]['flags'].append(flag
)
122 self
.messagelist
[uid
]['flags'].sort()
123 elif operation
== '-':
125 if flag
in self
.messagelist
[uid
]['flags']:
126 self
.messagelist
[uid
]['flags'].remove(flag
)