]>
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 def __init__(self
, imapserver
, name
, visiblename
, accountname
, repository
):
40 self
.realdelete
= repository
.getrealdelete(name
)
41 self
.trash_folder
= repository
.gettrashfolder(name
)
42 #: Gmail will really delete messages upon EXPUNGE in these folders
43 self
.real_delete_folders
= [ self
.trash_folder
, repository
.getspamfolder() ]
44 IMAPFolder
.__init
__(self
, imapserver
, name
, visiblename
, \
45 accountname
, repository
)
47 def deletemessages_noconvert(self
, uidlist
):
48 uidlist
= [uid
for uid
in uidlist
if uid
in self
.messagelist
]
52 if self
.realdelete
and not (self
.getname() in self
.real_delete_folders
):
53 # IMAP expunge is just "remove label" in this folder,
54 # so map the request into a "move into Trash"
56 imapobj
= self
.imapserver
.acquireconnection()
58 imapobj
.select(self
.getfullname())
59 result
= imapobj
.uid('copy',
60 imaputil
.listjoin(uidlist
),
62 assert result
[0] == 'OK', \
63 "Bad IMAPlib result: %s" % result
[0]
65 self
.imapserver
.releaseconnection(imapobj
)
67 del self
.messagelist
[uid
]
69 IMAPFolder
.deletemessages_noconvert(self
, uidlist
)
71 def processmessagesflags(self
, operation
, uidlist
, flags
):
72 # XXX: the imapobj.myrights(...) calls dies with an error
73 # report from Gmail server stating that IMAP command
74 # 'MYRIGHTS' is not implemented. So, this
75 # `processmessagesflags` is just a copy from `IMAPFolder`,
76 # with the references to `imapobj.myrights()` deleted This
77 # shouldn't hurt, however, Gmail users always have full
78 # control over all their mailboxes (apparently).
79 if len(uidlist
) > 101:
80 # Hack for those IMAP ervers with a limited line length
81 self
.processmessagesflags(operation
, uidlist
[:100], flags
)
82 self
.processmessagesflags(operation
, uidlist
[100:], flags
)
85 imapobj
= self
.imapserver
.acquireconnection()
87 imapobj
.select(self
.getfullname())
88 r
= imapobj
.uid('store',
89 imaputil
.listjoin(uidlist
),
91 imaputil
.flagsmaildir2imap(flags
))
92 assert r
[0] == 'OK', 'Error with store: ' + '. '.join(r
[1])
95 self
.imapserver
.releaseconnection(imapobj
)
97 needupdate
= copy(uidlist
)
100 # Compensate for servers that don't return anything from
103 attributehash
= imaputil
.flags2hash(imaputil
.imapsplit(result
)[1])
104 if not ('UID' in attributehash
and 'FLAGS' in attributehash
):
105 # Compensate for servers that don't return a UID attribute.
107 flags
= attributehash
['FLAGS']
108 uid
= long(attributehash
['UID'])
109 self
.messagelist
[uid
]['flags'] = imaputil
.flagsimap2maildir(flags
)
111 needupdate
.remove(uid
)
112 except ValueError: # Let it slide if it's not in the list
114 for uid
in needupdate
:
117 if not flag
in self
.messagelist
[uid
]['flags']:
118 self
.messagelist
[uid
]['flags'].append(flag
)
119 self
.messagelist
[uid
]['flags'].sort()
120 elif operation
== '-':
122 if flag
in self
.messagelist
[uid
]['flags']:
123 self
.messagelist
[uid
]['flags'].remove(flag
)