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