]> code.delx.au - offlineimap/blob - head/offlineimap/folder/IMAP.py
b612e2ae75e84b96b88e53d7e4ba89681cfada5e
[offlineimap] / head / offlineimap / folder / IMAP.py
1 # IMAP folder support
2 # Copyright (C) 2002 John Goerzen
3 # <jgoerzen@complete.org>
4 #
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.
9 #
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.
14 #
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 from Base import BaseFolder
20 from offlineimap import imaputil, imaplib
21 import rfc822
22 from StringIO import StringIO
23
24 class IMAPFolder(BaseFolder):
25 def __init__(self, imapserver, name, visiblename):
26 self.name = imaputil.dequote(name)
27 self.root = imapserver.root
28 self.sep = imapserver.delim
29 self.imapserver = imapserver
30 self.messagelist = None
31 self.visiblename = visiblename
32
33 def suggeststhreads(self):
34 return 1
35
36 def waitforthread(self):
37 self.imapserver.connectionwait()
38
39 def getvisiblename(self):
40 return self.visiblename
41
42 def getuidvalidity(self):
43 imapobj = self.imapserver.acquireconnection()
44 try:
45 x = imapobj.status(self.getfullname(), '(UIDVALIDITY)')[1][0]
46 finally:
47 self.imapserver.releaseconnection(imapobj)
48 uidstring = imaputil.imapsplit(x)[1]
49 return long(imaputil.flagsplit(uidstring)[1])
50
51 def cachemessagelist(self):
52 imapobj = self.imapserver.acquireconnection()
53 try:
54 imapobj.select(self.getfullname())
55 self.messagelist = {}
56 response = imapobj.status(self.getfullname(), '(MESSAGES)')[1][0]
57 result = imaputil.imapsplit(response)[1]
58 maxmsgid = long(imaputil.flags2hash(result)['MESSAGES'])
59 if (maxmsgid < 1):
60 # No messages? return.
61 return
62
63 # Now, get the flags and UIDs for these.
64 response = imapobj.fetch('1:%d' % maxmsgid, '(FLAGS UID)')[1]
65 finally:
66 self.imapserver.releaseconnection(imapobj)
67 for messagestr in response:
68 # Discard the message number.
69 messagestr = imaputil.imapsplit(messagestr)[1]
70 options = imaputil.flags2hash(messagestr)
71 uid = long(options['UID'])
72 flags = imaputil.flagsimap2maildir(options['FLAGS'])
73 self.messagelist[uid] = {'uid': uid, 'flags': flags}
74
75 def getmessagelist(self):
76 return self.messagelist
77
78 def getmessage(self, uid):
79 imapobj = self.imapserver.acquireconnection()
80 try:
81 imapobj.select(self.getfullname())
82 return imapobj.uid('fetch', '%d' % uid, '(BODY.PEEK[])')[1][0][1].replace("\r\n", "\n")
83 finally:
84 self.imapserver.releaseconnection(imapobj)
85
86 def getmessageflags(self, uid):
87 return self.messagelist[uid]['flags']
88
89 def savemessage(self, uid, content, flags):
90 imapobj = self.imapserver.acquireconnection()
91 try:
92 # This backend always assigns a new uid, so the uid arg is ignored.
93 # In order to get the new uid, we need to save off the message ID.
94
95 message = rfc822.Message(StringIO(content))
96 mid = imapobj._quote(message.getheader('Message-Id'))
97 date = imaplib.Time2Internaldate(rfc822.parsedate(message.getheader('Date')))
98
99 if content.find("\r\n") == -1: # Convert line endings if not already
100 content = content.replace("\n", "\r\n")
101
102 assert(imapobj.append(self.getfullname(),
103 imaputil.flagsmaildir2imap(flags),
104 date, content)[0] == 'OK')
105 # Checkpoint. Let it write out the messages, etc.
106 assert(imapobj.check()[0] == 'OK')
107 # Now find the UID it got.
108 matchinguids = imapobj.uid('search', None,
109 '(HEADER Message-Id %s)' % mid)[1][0]
110 matchinguids = matchinguids.split(' ')
111 matchinguids.sort()
112 uid = long(matchinguids[-1])
113 self.messagelist[uid] = {'uid': uid, 'flags': flags}
114 return uid
115 finally:
116 self.imapserver.releaseconnection(imapobj)
117
118 def savemessageflags(self, uid, flags):
119 imapobj = self.imapserver.acquireconnection(imapobj)
120 try:
121 imapobj.select(self.getfullname())
122 result = imapobj.uid('store', '%d' % uid, 'FLAGS',
123 imaputil.flagsmaildir2imap(flags))[1][0]
124 finally:
125 self.imapserver.releaseconnection(imapobj)
126 flags = imaputil.flags2hash(imaputil.imapsplit(result)[1])['FLAGS']
127 self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
128
129 def addmessagesflags(self, uidlist, flags):
130 imapobj = self.imapserver.acquireconnection()
131 try:
132 imapobj.select(self.getfullname())
133 r = imapobj.uid('store',
134 ','.join([str(uid) for uid in uidlist]),
135 '+FLAGS',
136 imaputil.flagsmaildir2imap(flags))[1]
137 finally:
138 self.imapserver.releaseconnection(imapobj)
139 resultcount = 0
140 for result in r:
141 resultcount += 1
142 flags = imaputil.flags2hash(imaputil.imapsplit(result)[1])['FLAGS']
143 uid = long(imaputil.flags2hash(imaputil.imapsplit(result)[1])['UID'])
144 self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
145 assert resultcount == len(uidlist), "Got incorrect number of results back"
146
147 def deletemessage(self, uid):
148 self.deletemessages([uid])
149
150 def deletemessages(self, uidlist):
151 # Weed out ones not in self.messagelist
152 uidlist = [uid for uid in uidlist if uid in self.messagelist]
153 if not len(uidlist):
154 return
155
156 self.addmessagesflags(uidlist, ['T'])
157 imapobj = self.imapserver.acquireconnection()
158 try:
159 imapobj.select(self.getfullname())
160 assert(imapobj.expunge()[0] == 'OK')
161 finally:
162 self.imapserver.releaseconnection(imapobj)
163 for uid in uidlist:
164 del(self.messagelist[uid])
165
166