]>
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
23 from offlineimap
import imaplib2
, imaputil
, imaplibutil
24 from offlineimap
.ui
import UIBase
28 class GmailFolder(IMAPFolder
):
29 """Folder implementation to support features of the Gmail IMAP server.
30 Specifically, deleted messages are moved to folder `Gmail.TRASH_FOLDER`
31 (by default: ``[Gmail]/Trash``) prior to expunging them, since
32 Gmail maps to IMAP ``EXPUNGE`` command to "remove label".
34 For more information on the Gmail IMAP server:
35 http://mail.google.com/support/bin/answer.py?answer=77657&topic=12815
38 def __init__(self
, imapserver
, name
, visiblename
, accountname
, repository
):
39 self
.realdelete
= repository
.getrealdelete(name
)
40 self
.trash_folder
= repository
.gettrashfolder(name
)
41 #: Gmail will really delete messages upon EXPUNGE in these folders
42 self
.real_delete_folders
= [ self
.trash_folder
, repository
.getspamfolder() ]
43 IMAPFolder
.__init
__(self
, imapserver
, name
, visiblename
, \
44 accountname
, repository
)
46 def deletemessages_noconvert(self
, uidlist
):
47 uidlist
= [uid
for uid
in uidlist
if uid
in self
.messagelist
]
51 if self
.realdelete
and not (self
.getname() in self
.real_delete_folders
):
52 # IMAP expunge is just "remove label" in this folder,
53 # so map the request into a "move into Trash"
55 imapobj
= self
.imapserver
.acquireconnection()
57 imapobj
.select(self
.getfullname())
58 result
= imapobj
.uid('copy',
59 imaputil
.listjoin(uidlist
),
61 assert result
[0] == 'OK', \
62 "Bad IMAPlib result: %s" % result
[0]
64 self
.imapserver
.releaseconnection(imapobj
)
66 del self
.messagelist
[uid
]
68 IMAPFolder
.deletemessages_noconvert(self
, uidlist
)
70 def processmessagesflags(self
, operation
, uidlist
, flags
):
71 # XXX: the imapobj.myrights(...) calls dies with an error
72 # report from Gmail server stating that IMAP command
73 # 'MYRIGHTS' is not implemented. So, this
74 # `processmessagesflags` is just a copy from `IMAPFolder`,
75 # with the references to `imapobj.myrights()` deleted This
76 # shouldn't hurt, however, Gmail users always have full
77 # control over all their mailboxes (apparently).
78 if len(uidlist
) > 101:
79 # Hack for those IMAP ervers with a limited line length
80 self
.processmessagesflags(operation
, uidlist
[:100], flags
)
81 self
.processmessagesflags(operation
, uidlist
[100:], flags
)
84 imapobj
= self
.imapserver
.acquireconnection()
86 imapobj
.select(self
.getfullname())
87 r
= imapobj
.uid('store',
88 imaputil
.listjoin(uidlist
),
90 imaputil
.flagsmaildir2imap(flags
))
91 assert r
[0] == 'OK', 'Error with store: ' + '. '.join(r
[1])
94 self
.imapserver
.releaseconnection(imapobj
)
96 needupdate
= copy(uidlist
)
99 # Compensate for servers that don't return anything from
102 attributehash
= imaputil
.flags2hash(imaputil
.imapsplit(result
)[1])
103 if not ('UID' in attributehash
and 'FLAGS' in attributehash
):
104 # Compensate for servers that don't return a UID attribute.
106 flags
= attributehash
['FLAGS']
107 uid
= long(attributehash
['UID'])
108 self
.messagelist
[uid
]['flags'] = imaputil
.flagsimap2maildir(flags
)
110 needupdate
.remove(uid
)
111 except ValueError: # Let it slide if it's not in the list
113 for uid
in needupdate
:
116 if not flag
in self
.messagelist
[uid
]['flags']:
117 self
.messagelist
[uid
]['flags'].append(flag
)
118 self
.messagelist
[uid
]['flags'].sort()
119 elif operation
== '-':
121 if flag
in self
.messagelist
[uid
]['flags']:
122 self
.messagelist
[uid
]['flags'].remove(flag
)