2002-06-19 06:39:00 +02:00
|
|
|
# IMAP repository support
|
|
|
|
# Copyright (C) 2002 John Goerzen
|
|
|
|
# <jgoerzen@complete.org>
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
2003-04-16 21:23:45 +02:00
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
2002-06-19 06:39:00 +02:00
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
2006-08-12 06:15:55 +02:00
|
|
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
2002-06-19 06:39:00 +02:00
|
|
|
|
2011-03-03 11:05:15 +01:00
|
|
|
from offlineimap.repository.Base import BaseRepository
|
2003-04-18 04:18:34 +02:00
|
|
|
from offlineimap import folder, imaputil, imapserver
|
|
|
|
from offlineimap.folder.UIDMaps import MappedIMAPFolder
|
|
|
|
from offlineimap.threadutil import ExitNotifyThread
|
2011-05-11 18:25:13 +02:00
|
|
|
from threading import Event
|
|
|
|
import re
|
|
|
|
import types
|
|
|
|
import os
|
|
|
|
import netrc
|
|
|
|
import errno
|
2002-06-19 06:39:00 +02:00
|
|
|
|
|
|
|
class IMAPRepository(BaseRepository):
|
2003-04-18 04:18:34 +02:00
|
|
|
def __init__(self, reposname, account):
|
|
|
|
"""Initialize an IMAPRepository object."""
|
|
|
|
BaseRepository.__init__(self, reposname, account)
|
2011-03-03 11:43:23 +01:00
|
|
|
# self.ui is being set by the BaseRepository
|
2003-04-18 04:18:34 +02:00
|
|
|
self.imapserver = imapserver.ConfigedIMAPServer(self)
|
2002-06-19 07:55:12 +02:00
|
|
|
self.folders = None
|
2002-06-21 08:51:21 +02:00
|
|
|
self.nametrans = lambda foldername: foldername
|
2002-07-04 08:10:51 +02:00
|
|
|
self.folderfilter = lambda foldername: 1
|
2002-07-09 04:32:35 +02:00
|
|
|
self.folderincludes = []
|
2002-08-09 23:10:38 +02:00
|
|
|
self.foldersort = cmp
|
2003-04-18 04:18:34 +02:00
|
|
|
localeval = self.localeval
|
|
|
|
if self.config.has_option(self.getsection(), 'nametrans'):
|
|
|
|
self.nametrans = localeval.eval(self.getconf('nametrans'),
|
|
|
|
{'re': re})
|
|
|
|
if self.config.has_option(self.getsection(), 'folderfilter'):
|
|
|
|
self.folderfilter = localeval.eval(self.getconf('folderfilter'),
|
|
|
|
{'re': re})
|
|
|
|
if self.config.has_option(self.getsection(), 'folderincludes'):
|
|
|
|
self.folderincludes = localeval.eval(self.getconf('folderincludes'),
|
|
|
|
{'re': re})
|
|
|
|
if self.config.has_option(self.getsection(), 'foldersort'):
|
|
|
|
self.foldersort = localeval.eval(self.getconf('foldersort'),
|
|
|
|
{'re': re})
|
|
|
|
|
|
|
|
def startkeepalive(self):
|
|
|
|
keepalivetime = self.getkeepalive()
|
|
|
|
if not keepalivetime: return
|
|
|
|
self.kaevent = Event()
|
|
|
|
self.kathread = ExitNotifyThread(target = self.imapserver.keepalive,
|
|
|
|
name = "Keep alive " + self.getname(),
|
|
|
|
args = (keepalivetime, self.kaevent))
|
|
|
|
self.kathread.setDaemon(1)
|
|
|
|
self.kathread.start()
|
|
|
|
|
2008-08-03 00:04:32 +02:00
|
|
|
def stopkeepalive(self):
|
2003-04-18 04:18:34 +02:00
|
|
|
if not hasattr(self, 'kaevent'):
|
|
|
|
# Keepalive is not active.
|
|
|
|
return
|
|
|
|
|
|
|
|
self.kaevent.set()
|
|
|
|
del self.kathread
|
|
|
|
del self.kaevent
|
|
|
|
|
|
|
|
def holdordropconnections(self):
|
|
|
|
if not self.getholdconnectionopen():
|
|
|
|
self.dropconnections()
|
|
|
|
|
|
|
|
def dropconnections(self):
|
|
|
|
self.imapserver.close()
|
|
|
|
|
|
|
|
def getholdconnectionopen(self):
|
|
|
|
return self.getconfboolean("holdconnectionopen", 0)
|
|
|
|
|
|
|
|
def getkeepalive(self):
|
2009-08-12 21:49:12 +02:00
|
|
|
return self.getconfint("keepalive", 0)
|
2002-06-19 07:22:21 +02:00
|
|
|
|
2002-06-19 08:16:19 +02:00
|
|
|
def getsep(self):
|
|
|
|
return self.imapserver.delim
|
|
|
|
|
2003-04-18 04:18:34 +02:00
|
|
|
def gethost(self):
|
2007-07-04 23:00:14 +02:00
|
|
|
host = None
|
2006-10-17 21:55:03 +02:00
|
|
|
localeval = self.localeval
|
|
|
|
|
|
|
|
if self.config.has_option(self.getsection(), 'remotehosteval'):
|
2007-07-04 23:00:14 +02:00
|
|
|
host = self.getconf('remotehosteval')
|
|
|
|
if host != None:
|
|
|
|
return localeval.eval(host)
|
2006-10-17 21:55:03 +02:00
|
|
|
|
2007-07-04 23:00:14 +02:00
|
|
|
host = self.getconf('remotehost')
|
|
|
|
if host != None:
|
|
|
|
return host
|
2003-04-18 04:18:34 +02:00
|
|
|
|
|
|
|
def getuser(self):
|
2007-07-04 23:00:14 +02:00
|
|
|
user = None
|
2006-10-17 21:55:03 +02:00
|
|
|
localeval = self.localeval
|
|
|
|
|
|
|
|
if self.config.has_option(self.getsection(), 'remoteusereval'):
|
2007-07-04 23:00:14 +02:00
|
|
|
user = self.getconf('remoteusereval')
|
|
|
|
if user != None:
|
|
|
|
return localeval.eval(user)
|
2006-10-17 21:55:03 +02:00
|
|
|
|
2007-07-04 23:00:14 +02:00
|
|
|
user = self.getconf('remoteuser')
|
|
|
|
if user != None:
|
|
|
|
return user
|
2003-04-18 04:18:34 +02:00
|
|
|
|
2008-03-03 09:21:33 +01:00
|
|
|
try:
|
2010-08-19 20:56:51 +02:00
|
|
|
netrcentry = netrc.netrc().authenticators(self.gethost())
|
2008-03-03 09:21:33 +01:00
|
|
|
except IOError, inst:
|
|
|
|
if inst.errno != errno.ENOENT:
|
|
|
|
raise
|
|
|
|
else:
|
|
|
|
if netrcentry:
|
|
|
|
return netrcentry[0]
|
2003-04-18 04:18:34 +02:00
|
|
|
|
2008-12-02 20:15:44 +01:00
|
|
|
try:
|
2010-08-19 20:56:51 +02:00
|
|
|
netrcentry = netrc.netrc('/etc/netrc').authenticators(self.gethost())
|
2008-12-02 20:15:44 +01:00
|
|
|
except IOError, inst:
|
|
|
|
if inst.errno != errno.ENOENT:
|
|
|
|
raise
|
|
|
|
else:
|
|
|
|
if netrcentry:
|
|
|
|
return netrcentry[0]
|
|
|
|
|
|
|
|
|
2003-04-18 04:18:34 +02:00
|
|
|
def getport(self):
|
|
|
|
return self.getconfint('remoteport', None)
|
|
|
|
|
|
|
|
def getssl(self):
|
|
|
|
return self.getconfboolean('ssl', 0)
|
|
|
|
|
2008-05-23 21:58:18 +02:00
|
|
|
def getsslclientcert(self):
|
|
|
|
return self.getconf('sslclientcert', None)
|
|
|
|
|
|
|
|
def getsslclientkey(self):
|
|
|
|
return self.getconf('sslclientkey', None)
|
|
|
|
|
2010-12-16 13:43:47 +01:00
|
|
|
def getsslcacertfile(self):
|
2011-03-15 11:18:19 +01:00
|
|
|
"""Return the absolute path of the CA certfile to use, if any"""
|
|
|
|
cacertfile = self.getconf('sslcacertfile', None)
|
|
|
|
if cacertfile is None:
|
|
|
|
return None
|
|
|
|
cacertfile = os.path.expanduser(cacertfile)
|
|
|
|
cacertfile = os.path.abspath(cacertfile)
|
|
|
|
if not os.path.isfile(cacertfile):
|
|
|
|
raise SyntaxWarning("CA certfile for repository '%s' could "
|
|
|
|
"not be found. No such file: '%s'" \
|
|
|
|
% (self.name, cacertfile))
|
|
|
|
return cacertfile
|
2010-12-16 13:43:47 +01:00
|
|
|
|
2003-04-18 04:18:34 +02:00
|
|
|
def getpreauthtunnel(self):
|
|
|
|
return self.getconf('preauthtunnel', None)
|
|
|
|
|
|
|
|
def getreference(self):
|
|
|
|
return self.getconf('reference', '""')
|
|
|
|
|
|
|
|
def getmaxconnections(self):
|
2009-08-12 21:49:58 +02:00
|
|
|
return self.getconfint('maxconnections', 1)
|
2003-04-18 04:18:34 +02:00
|
|
|
|
|
|
|
def getexpunge(self):
|
|
|
|
return self.getconfboolean('expunge', 1)
|
|
|
|
|
|
|
|
def getpassword(self):
|
2010-12-13 18:45:42 +01:00
|
|
|
"""Return the IMAP password for this repository.
|
2006-10-17 21:55:03 +02:00
|
|
|
|
2010-12-13 18:45:42 +01:00
|
|
|
It tries to get passwords in the following order:
|
|
|
|
|
|
|
|
1. evaluate Repository 'remotepasseval'
|
|
|
|
2. read password from Repository 'remotepass'
|
|
|
|
3. read password from file specified in Repository 'remotepassfile'
|
|
|
|
4. read password from ~/.netrc
|
|
|
|
5. read password from /etc/netrc
|
2006-10-17 21:55:03 +02:00
|
|
|
|
2010-12-13 18:45:42 +01:00
|
|
|
On success we return the password.
|
|
|
|
If all strategies fail we return None.
|
|
|
|
"""
|
|
|
|
# 1. evaluate Repository 'remotepasseval'
|
|
|
|
passwd = self.getconf('remotepasseval', None)
|
|
|
|
if passwd != None:
|
|
|
|
return self.localeval.eval(passwd)
|
|
|
|
# 2. read password from Repository 'remotepass'
|
2003-04-18 04:18:34 +02:00
|
|
|
password = self.getconf('remotepass', None)
|
|
|
|
if password != None:
|
|
|
|
return password
|
2010-12-13 18:45:42 +01:00
|
|
|
# 3. read password from file specified in Repository 'remotepassfile'
|
2003-04-18 04:18:34 +02:00
|
|
|
passfile = self.getconf('remotepassfile', None)
|
|
|
|
if passfile != None:
|
|
|
|
fd = open(os.path.expanduser(passfile))
|
2003-04-29 03:52:03 +02:00
|
|
|
password = fd.readline().strip()
|
|
|
|
fd.close()
|
2007-07-04 23:00:14 +02:00
|
|
|
return password
|
2010-12-13 18:45:42 +01:00
|
|
|
# 4. read password from ~/.netrc
|
2008-03-03 09:21:33 +01:00
|
|
|
try:
|
|
|
|
netrcentry = netrc.netrc().authenticators(self.gethost())
|
|
|
|
except IOError, inst:
|
|
|
|
if inst.errno != errno.ENOENT:
|
|
|
|
raise
|
|
|
|
else:
|
|
|
|
if netrcentry:
|
|
|
|
user = self.getconf('remoteuser')
|
|
|
|
if user == None or user == netrcentry[0]:
|
|
|
|
return netrcentry[2]
|
2010-12-13 18:45:42 +01:00
|
|
|
# 5. read password from /etc/netrc
|
2008-12-02 20:15:44 +01:00
|
|
|
try:
|
|
|
|
netrcentry = netrc.netrc('/etc/netrc').authenticators(self.gethost())
|
|
|
|
except IOError, inst:
|
|
|
|
if inst.errno != errno.ENOENT:
|
|
|
|
raise
|
|
|
|
else:
|
|
|
|
if netrcentry:
|
|
|
|
user = self.getconf('remoteuser')
|
|
|
|
if user == None or user == netrcentry[0]:
|
|
|
|
return netrcentry[2]
|
2010-12-13 18:45:42 +01:00
|
|
|
# no strategy yielded a password!
|
2003-04-18 04:18:34 +02:00
|
|
|
return None
|
|
|
|
|
2010-12-13 18:45:42 +01:00
|
|
|
|
2002-06-20 04:55:24 +02:00
|
|
|
def getfolder(self, foldername):
|
2003-04-18 04:18:34 +02:00
|
|
|
return self.getfoldertype()(self.imapserver, foldername,
|
|
|
|
self.nametrans(foldername),
|
|
|
|
self.accountname, self)
|
|
|
|
|
|
|
|
def getfoldertype(self):
|
|
|
|
return folder.IMAP.IMAPFolder
|
2002-06-20 04:55:24 +02:00
|
|
|
|
2007-07-05 06:04:14 +02:00
|
|
|
def connect(self):
|
|
|
|
imapobj = self.imapserver.acquireconnection()
|
|
|
|
self.imapserver.releaseconnection(imapobj)
|
|
|
|
|
2007-07-06 18:46:29 +02:00
|
|
|
def forgetfolders(self):
|
|
|
|
self.folders = None
|
|
|
|
|
2002-06-19 07:22:21 +02:00
|
|
|
def getfolders(self):
|
2002-06-19 07:55:12 +02:00
|
|
|
if self.folders != None:
|
|
|
|
return self.folders
|
2002-06-19 07:22:21 +02:00
|
|
|
retval = []
|
2002-07-04 02:15:32 +02:00
|
|
|
imapobj = self.imapserver.acquireconnection()
|
2008-10-01 07:33:57 +02:00
|
|
|
# check whether to list all folders, or subscribed only
|
|
|
|
listfunction = imapobj.list
|
|
|
|
if self.config.has_option(self.getsection(), 'subscribedonly'):
|
|
|
|
if self.getconf('subscribedonly') == "yes":
|
|
|
|
listfunction = imapobj.lsub
|
2002-07-04 02:15:32 +02:00
|
|
|
try:
|
2008-10-01 07:33:57 +02:00
|
|
|
listresult = listfunction(directory = self.imapserver.reference)[1]
|
2002-07-04 02:15:32 +02:00
|
|
|
finally:
|
|
|
|
self.imapserver.releaseconnection(imapobj)
|
|
|
|
for string in listresult:
|
2003-04-18 04:18:34 +02:00
|
|
|
if string == None or \
|
|
|
|
(type(string) == types.StringType and string == ''):
|
2002-10-16 07:43:02 +02:00
|
|
|
# Bug in imaplib: empty strings in results from
|
|
|
|
# literals.
|
|
|
|
continue
|
2002-06-19 07:22:21 +02:00
|
|
|
flags, delim, name = imaputil.imapsplit(string)
|
2002-07-06 02:22:23 +02:00
|
|
|
flaglist = [x.lower() for x in imaputil.flagsplit(flags)]
|
|
|
|
if '\\noselect' in flaglist:
|
2002-06-19 07:22:21 +02:00
|
|
|
continue
|
2002-07-04 08:10:51 +02:00
|
|
|
foldername = imaputil.dequote(name)
|
|
|
|
if not self.folderfilter(foldername):
|
2011-03-03 11:43:23 +01:00
|
|
|
self.ui.debug('imap',"Filtering out '%s' due to folderfilter" %\
|
|
|
|
foldername)
|
2002-07-04 08:10:51 +02:00
|
|
|
continue
|
2003-04-18 04:18:34 +02:00
|
|
|
retval.append(self.getfoldertype()(self.imapserver, foldername,
|
|
|
|
self.nametrans(foldername),
|
|
|
|
self.accountname, self))
|
2003-04-18 06:31:25 +02:00
|
|
|
if len(self.folderincludes):
|
|
|
|
imapobj = self.imapserver.acquireconnection()
|
|
|
|
try:
|
|
|
|
for foldername in self.folderincludes:
|
2003-04-18 09:06:04 +02:00
|
|
|
try:
|
|
|
|
imapobj.select(foldername, readonly = 1)
|
|
|
|
except ValueError:
|
|
|
|
continue
|
|
|
|
retval.append(self.getfoldertype()(self.imapserver,
|
|
|
|
foldername,
|
|
|
|
self.nametrans(foldername),
|
|
|
|
self.accountname, self))
|
2003-04-18 06:31:25 +02:00
|
|
|
finally:
|
|
|
|
self.imapserver.releaseconnection(imapobj)
|
|
|
|
|
2002-08-09 23:10:38 +02:00
|
|
|
retval.sort(lambda x, y: self.foldersort(x.getvisiblename(), y.getvisiblename()))
|
2002-06-19 07:55:12 +02:00
|
|
|
self.folders = retval
|
2002-06-19 07:22:21 +02:00
|
|
|
return retval
|
2003-04-18 04:18:34 +02:00
|
|
|
|
|
|
|
def makefolder(self, foldername):
|
|
|
|
#if self.getreference() != '""':
|
|
|
|
# newname = self.getreference() + self.getsep() + foldername
|
|
|
|
#else:
|
|
|
|
# newname = foldername
|
|
|
|
newname = foldername
|
|
|
|
imapobj = self.imapserver.acquireconnection()
|
|
|
|
try:
|
|
|
|
result = imapobj.create(newname)
|
|
|
|
if result[0] != 'OK':
|
|
|
|
raise RuntimeError, "Repository %s could not create folder %s: %s" % (self.getname(), foldername, str(result))
|
|
|
|
finally:
|
|
|
|
self.imapserver.releaseconnection(imapobj)
|
|
|
|
|
|
|
|
class MappedIMAPRepository(IMAPRepository):
|
|
|
|
def getfoldertype(self):
|
|
|
|
return MappedIMAPFolder
|