]>
code.delx.au - offlineimap/blob - head/offlineimap/folder/Base.py
2 # Copyright (C) 2002 John Goerzen
3 # <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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 from threading
import *
21 from offlineimap
import threadutil
22 from offlineimap
.threadutil
import InstanceLimitedThread
29 def suggeststhreads(self
):
30 """Returns true if this folder suggests using threads for actions;
31 false otherwise. Probably only IMAP will return true."""
34 def waitforthread(self
):
35 """For threading folders, waits until there is a resource available
36 before firing off a thread. For all others, returns immediately."""
39 def getcopyinstancelimit(self
):
40 """For threading folders, returns the instancelimitname for
41 InstanceLimitedThreads."""
42 raise NotImplementedException
44 def getvisiblename(self
):
48 """Returns the root of the folder, in a folder-specific fashion."""
52 """Returns the separator for this folder type."""
55 def getfullname(self
):
57 return self
.getroot() + self
.getsep() + self
.getname()
61 def isuidvalidityok(self
, remotefolder
):
62 raise NotImplementedException
64 def getuidvalidity(self
):
65 raise NotImplementedException
67 def saveuidvalidity(self
, newval
):
68 raise NotImplementedException
70 def cachemessagelist(self
):
71 """Reads the message list from disk or network and stores it in
72 memory for later use. This list will not be re-read from disk or
73 memory unless this function is called again."""
74 raise NotImplementedException
76 def getmessagelist(self
):
77 """Gets the current message list.
78 You must call cachemessagelist() before calling this function!"""
79 raise NotImplementedException
81 def getmessage(self
, uid
):
82 """Returns the content of the specified message."""
83 raise NotImplementedException
85 def savemessage(self
, uid
, content
, flags
):
86 """Writes a new message, with the specified uid.
87 If the uid is < 0, the backend should assign a new uid and return it.
89 If the backend cannot assign a new uid, it returns the uid passed in
90 WITHOUT saving the message.
92 IMAP backend should be the only one that can assign a new uid.
94 If the uid is > 0, the backend should set the uid to this, if it can.
95 If it cannot set the uid to that, it will save it anyway.
96 It will return the uid assigned in any case.
98 raise NotImplementedException
100 def getmessageflags(self
, uid
):
101 """Returns the flags for the specified message."""
102 raise NotImplementedException
104 def savemessageflags(self
, uid
, flags
):
105 """Sets the specified message's flags to the given set."""
106 raise NotImplementedException
108 def addmessageflags(self
, uid
, flags
):
109 """Adds the specified flags to the message's flag set. If a given
110 flag is already present, it will not be duplicated."""
111 newflags
= self
.getmessageflags(uid
)
113 if not flag
in newflags
:
114 newflags
.append(flag
)
116 self
.savemessageflags(uid
, newflags
)
118 def addmessagesflags(self
, uidlist
, flags
):
120 self
.addmessageflags(uid
)
122 def deletemessageflags(self
, uid
, flags
):
123 """Removes each flag given from the message's flag set. If a given
124 flag is already removed, no action will be taken for that flag."""
125 newflags
= self
.getmessageflags(uid
)
128 newflags
.remove(flag
)
130 self
.savemessageflags(uid
, newflags
)
132 def deletemessage(self
, uid
):
133 raise NotImplementedException
135 def deletemessages(self
, uidlist
):
137 self
.deletemessage(uid
)
139 def syncmessagesto_neguid(self
, dest
, applyto
):
140 """Pass 1 of folder synchronization.
142 Look for messages in self with a negative uid. These are messages in
143 Maildirs that were not added by us. Try to add them to the dests,
144 and once that succeeds, get the UID, add it to the others for real,
145 add it to local for real, and delete the fake one."""
147 for uid
in self
.getmessagelist().keys():
150 __main__
.ui
.copyingmessage(uid
, self
, applyto
)
153 message
= self
.getmessage(uid
)
154 flags
= self
.getmessageflags(uid
)
155 for tryappend
in applyto
:
156 successuid
= tryappend
.savemessage(uid
, message
, flags
)
158 successobject
= tryappend
161 if successobject
!= None:
162 # Copy the message to the other remote servers.
163 for appendserver
in [x
for x
in applyto
if x
!= successobject
]:
164 appendserver
.savemessage(successuid
, message
, flags
)
165 # Copy it to its new name on the local server and delete
166 # the one without a UID.
167 self
.savemessage(successuid
, message
, flags
)
168 self
.deletemessage(uid
)
170 # Did not find any server to take this message. Ignore.
173 def copymessageto(self
, uid
, applyto
):
174 __main__
.ui
.copyingmessage(uid
, self
, applyto
)
175 message
= self
.getmessage(uid
)
176 flags
= self
.getmessageflags(uid
)
177 for object in applyto
:
178 newuid
= object.savemessage(uid
, message
, flags
)
179 if newuid
> 0 and newuid
!= uid
:
180 # Change the local uid.
181 self
.savemessage(newuid
, message
, flags
)
182 self
.deletemessage(uid
)
186 def syncmessagesto_copy(self
, dest
, applyto
):
187 """Pass 2 of folder synchronization.
189 Look for messages present in self but not in dest. If any, add
193 for uid
in self
.getmessagelist().keys():
194 if uid
< 0: # Ignore messages that pass 1 missed.
196 if not uid
in dest
.getmessagelist():
197 if self
.suggeststhreads():
199 thread
= InstanceLimitedThread(\
200 self
.getcopyinstancelimit(),
201 target
= self
.copymessageto
,
202 args
= (uid
, applyto
))
204 threads
.append(thread
)
206 self
.copymessageto(uid
, applyto
)
207 for thread
in threads
:
210 def syncmessagesto_delete(self
, dest
, applyto
):
211 """Pass 3 of folder synchronization.
213 Look for message present in dest but not in self.
214 If any, delete them."""
216 for uid
in dest
.getmessagelist().keys():
219 if not uid
in self
.getmessagelist():
220 deletelist
.append(uid
)
222 __main__
.ui
.deletingmessages(deletelist
, applyto
)
223 for object in applyto
:
224 object.deletemessages(deletelist
)
226 def syncmessagesto_flags(self
, dest
, applyto
):
227 """Pass 4 of folder synchronization.
229 Look for any flag matching issues -- set dest message to have the
230 same flags that we have."""
231 for uid
in self
.getmessagelist().keys():
232 if uid
< 0: # Ignore messages missed by pass 1
234 selfflags
= self
.getmessageflags(uid
)
235 destflags
= dest
.getmessageflags(uid
)
237 addflags
= [x
for x
in selfflags
if x
not in destflags
]
239 __main__
.ui
.addingflags(uid
, addflags
, applyto
)
240 for object in applyto
:
241 object.addmessageflags(uid
, addflags
)
243 delflags
= [x
for x
in destflags
if x
not in selfflags
]
245 __main__
.ui
.deletingflags(uid
, delflags
, applyto
)
246 for object in applyto
:
247 object.deletemessageflags(uid
, delflags
)
249 def syncmessagesto(self
, dest
, applyto
= None):
250 """Syncs messages in this folder to the destination.
251 If applyto is specified, it should be a list of folders (don't forget
252 to include dest!) to which all write actions should be applied.
253 It defaults to [dest] if not specified. It is important that
254 the UID generator be listed first in applyto; that is, the other
255 applyto ones should be the ones that "copy" the main action."""
259 self
.syncmessagesto_neguid(dest
, applyto
)
260 self
.syncmessagesto_copy(dest
, applyto
)
261 self
.syncmessagesto_delete(dest
, applyto
)
263 # Now, the message lists should be identical wrt the uids present.
264 # (except for potential negative uids that couldn't be placed
267 self
.syncmessagesto_flags(dest
, applyto
)