]> code.delx.au - offlineimap/blob - offlineimap/repository/IMAP.py
Apply tabspace.diff
[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, netrc, errno
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 try:
113 netrcentry = netrc.netrc().authentificator(self.gethost())
114 except IOError, inst:
115 if inst.errno != errno.ENOENT:
116 raise
117 else:
118 if netrcentry:
119 return netrcentry[0]
120
121 def getport(self):
122 return self.getconfint('remoteport', None)
123
124 def getssl(self):
125 return self.getconfboolean('ssl', 0)
126
127 def getpreauthtunnel(self):
128 return self.getconf('preauthtunnel', None)
129
130 def getreference(self):
131 return self.getconf('reference', '""')
132
133 def getmaxconnections(self):
134 return self.getconfint('maxconnections', 1)
135
136 def getexpunge(self):
137 return self.getconfboolean('expunge', 1)
138
139 def getpassword(self):
140 passwd = None
141 localeval = self.localeval
142
143 if self.config.has_option(self.getsection(), 'remotepasseval'):
144 passwd = self.getconf('remotepasseval')
145 if passwd != None:
146 return localeval.eval(passwd)
147
148 password = self.getconf('remotepass', None)
149 if password != None:
150 return password
151 passfile = self.getconf('remotepassfile', None)
152 if passfile != None:
153 fd = open(os.path.expanduser(passfile))
154 password = fd.readline().strip()
155 fd.close()
156 return password
157 try:
158 netrcentry = netrc.netrc().authenticators(self.gethost())
159 except IOError, inst:
160 if inst.errno != errno.ENOENT:
161 raise
162 else:
163 if netrcentry:
164 user = self.getconf('remoteuser')
165 if user == None or user == netrcentry[0]:
166 return netrcentry[2]
167 return None
168
169 def getfolder(self, foldername):
170 return self.getfoldertype()(self.imapserver, foldername,
171 self.nametrans(foldername),
172 self.accountname, self)
173
174 def getfoldertype(self):
175 return folder.IMAP.IMAPFolder
176
177 def getfolders(self):
178 if self.folders != None:
179 return self.folders
180 retval = []
181 imapobj = self.imapserver.acquireconnection()
182 try:
183 listresult = imapobj.list(directory = self.imapserver.reference)[1]
184 finally:
185 self.imapserver.releaseconnection(imapobj)
186 for string in listresult:
187 if string == None or \
188 (type(string) == types.StringType and string == ''):
189 # Bug in imaplib: empty strings in results from
190 # literals.
191 continue
192 flags, delim, name = imaputil.imapsplit(string)
193 flaglist = [x.lower() for x in imaputil.flagsplit(flags)]
194 if '\\noselect' in flaglist:
195 continue
196 foldername = imaputil.dequote(name)
197 if not self.folderfilter(foldername):
198 continue
199 retval.append(self.getfoldertype()(self.imapserver, foldername,
200 self.nametrans(foldername),
201 self.accountname, self))
202 if len(self.folderincludes):
203 imapobj = self.imapserver.acquireconnection()
204 try:
205 for foldername in self.folderincludes:
206 try:
207 imapobj.select(foldername, readonly = 1)
208 except ValueError:
209 continue
210 retval.append(self.getfoldertype()(self.imapserver,
211 foldername,
212 self.nametrans(foldername),
213 self.accountname, self))
214 finally:
215 self.imapserver.releaseconnection(imapobj)
216
217 retval.sort(lambda x, y: self.foldersort(x.getvisiblename(), y.getvisiblename()))
218 self.folders = retval
219 return retval
220
221 def makefolder(self, foldername):
222 #if self.getreference() != '""':
223 # newname = self.getreference() + self.getsep() + foldername
224 #else:
225 # newname = foldername
226 newname = foldername
227 imapobj = self.imapserver.acquireconnection()
228 try:
229 result = imapobj.create(newname)
230 if result[0] != 'OK':
231 raise RuntimeError, "Repository %s could not create folder %s: %s" % (self.getname(), foldername, str(result))
232 finally:
233 self.imapserver.releaseconnection(imapobj)
234
235 class MappedIMAPRepository(IMAPRepository):
236 def getfoldertype(self):
237 return MappedIMAPFolder