]> code.delx.au - offlineimap/blob - offlineimap/repository/IMAP.py
Clarify instructions for dealing with UID validity issues
[offlineimap] / offlineimap / repository / IMAP.py
1 # IMAP repository 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 from Base import BaseRepository
20 from offlineimap import folder, imaputil, imapserver
21 from offlineimap.folder.UIDMaps import MappedIMAPFolder
22 from offlineimap.threadutil import ExitNotifyThread
23 import re, types, os
24 from threading import *
25
26 class IMAPRepository(BaseRepository):
27 def __init__(self, reposname, account):
28 """Initialize an IMAPRepository object."""
29 BaseRepository.__init__(self, reposname, account)
30 self.imapserver = imapserver.ConfigedIMAPServer(self)
31 self.folders = None
32 self.nametrans = lambda foldername: foldername
33 self.folderfilter = lambda foldername: 1
34 self.folderincludes = []
35 self.foldersort = cmp
36 localeval = self.localeval
37 if self.config.has_option(self.getsection(), 'nametrans'):
38 self.nametrans = localeval.eval(self.getconf('nametrans'),
39 {'re': re})
40 if self.config.has_option(self.getsection(), 'folderfilter'):
41 self.folderfilter = localeval.eval(self.getconf('folderfilter'),
42 {'re': re})
43 if self.config.has_option(self.getsection(), 'folderincludes'):
44 self.folderincludes = localeval.eval(self.getconf('folderincludes'),
45 {'re': re})
46 if self.config.has_option(self.getsection(), 'foldersort'):
47 self.foldersort = localeval.eval(self.getconf('foldersort'),
48 {'re': re})
49
50 def startkeepalive(self):
51 keepalivetime = self.getkeepalive()
52 if not keepalivetime: return
53 self.kaevent = Event()
54 self.kathread = ExitNotifyThread(target = self.imapserver.keepalive,
55 name = "Keep alive " + self.getname(),
56 args = (keepalivetime, self.kaevent))
57 self.kathread.setDaemon(1)
58 self.kathread.start()
59
60 def stopkeepalive(self, abrupt = 0):
61 if not hasattr(self, 'kaevent'):
62 # Keepalive is not active.
63 return
64
65 self.kaevent.set()
66 if not abrupt:
67 self.kathread.join()
68 del self.kathread
69 del self.kaevent
70
71 def holdordropconnections(self):
72 if not self.getholdconnectionopen():
73 self.dropconnections()
74
75 def dropconnections(self):
76 self.imapserver.close()
77
78 def getholdconnectionopen(self):
79 return self.getconfboolean("holdconnectionopen", 0)
80
81 def getkeepalive(self):
82 return self.getconfint("keepalive", 0)
83
84 def getsep(self):
85 return self.imapserver.delim
86
87 def gethost(self):
88 host = None
89 localeval = self.localeval
90
91 if self.config.has_option(self.getsection(), 'remotehosteval'):
92 host = self.getconf('remotehosteval')
93 if host != None:
94 return localeval.eval(host)
95
96 host = self.getconf('remotehost')
97 if host != None:
98 return host
99
100 def getuser(self):
101 user = None
102 localeval = self.localeval
103
104 if self.config.has_option(self.getsection(), 'remoteusereval'):
105 user = self.getconf('remoteusereval')
106 if user != None:
107 return localeval.eval(user)
108
109 user = self.getconf('remoteuser')
110 if user != None:
111 return user
112
113 def getport(self):
114 return self.getconfint('remoteport', None)
115
116 def getssl(self):
117 return self.getconfboolean('ssl', 0)
118
119 def getpreauthtunnel(self):
120 return self.getconf('preauthtunnel', None)
121
122 def getreference(self):
123 return self.getconf('reference', '""')
124
125 def getmaxconnections(self):
126 return self.getconfint('maxconnections', 1)
127
128 def getexpunge(self):
129 return self.getconfboolean('expunge', 1)
130
131 def getpassword(self):
132 passwd = None
133 localeval = self.localeval
134
135 if self.config.has_option(self.getsection(), 'remotepasseval'):
136 passwd = self.getconf('remotepasseval')
137 if passwd != None:
138 return localeval.eval(passwd)
139
140 password = self.getconf('remotepass', None)
141 if password != None:
142 return password
143 passfile = self.getconf('remotepassfile', None)
144 if passfile != None:
145 fd = open(os.path.expanduser(passfile))
146 password = fd.readline().strip()
147 fd.close()
148 return password
149 return None
150
151 def getfolder(self, foldername):
152 return self.getfoldertype()(self.imapserver, foldername,
153 self.nametrans(foldername),
154 self.accountname, self)
155
156 def getfoldertype(self):
157 return folder.IMAP.IMAPFolder
158
159 def connect(self):
160 imapobj = self.imapserver.acquireconnection()
161 self.imapserver.releaseconnection(imapobj)
162
163 def forgetfolders(self):
164 self.folders = None
165
166 def getfolders(self):
167 if self.folders != None:
168 return self.folders
169 retval = []
170 imapobj = self.imapserver.acquireconnection()
171 try:
172 listresult = imapobj.list(directory = self.imapserver.reference)[1]
173 finally:
174 self.imapserver.releaseconnection(imapobj)
175 for string in listresult:
176 if string == None or \
177 (type(string) == types.StringType and string == ''):
178 # Bug in imaplib: empty strings in results from
179 # literals.
180 continue
181 flags, delim, name = imaputil.imapsplit(string)
182 flaglist = [x.lower() for x in imaputil.flagsplit(flags)]
183 if '\\noselect' in flaglist:
184 continue
185 foldername = imaputil.dequote(name)
186 if not self.folderfilter(foldername):
187 continue
188 retval.append(self.getfoldertype()(self.imapserver, foldername,
189 self.nametrans(foldername),
190 self.accountname, self))
191 if len(self.folderincludes):
192 imapobj = self.imapserver.acquireconnection()
193 try:
194 for foldername in self.folderincludes:
195 try:
196 imapobj.select(foldername, readonly = 1)
197 except ValueError:
198 continue
199 retval.append(self.getfoldertype()(self.imapserver,
200 foldername,
201 self.nametrans(foldername),
202 self.accountname, self))
203 finally:
204 self.imapserver.releaseconnection(imapobj)
205
206 retval.sort(lambda x, y: self.foldersort(x.getvisiblename(), y.getvisiblename()))
207 self.folders = retval
208 return retval
209
210 def makefolder(self, foldername):
211 #if self.getreference() != '""':
212 # newname = self.getreference() + self.getsep() + foldername
213 #else:
214 # newname = foldername
215 newname = foldername
216 imapobj = self.imapserver.acquireconnection()
217 try:
218 result = imapobj.create(newname)
219 if result[0] != 'OK':
220 raise RuntimeError, "Repository %s could not create folder %s: %s" % (self.getname(), foldername, str(result))
221 finally:
222 self.imapserver.releaseconnection(imapobj)
223
224 class MappedIMAPRepository(IMAPRepository):
225 def getfoldertype(self):
226 return MappedIMAPFolder