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