Merge branch 'next'

This commit is contained in:
Eygene Ryabinkin 2014-11-26 23:16:06 +03:00
commit 50b727b15c
6 changed files with 142 additions and 10 deletions

View File

@ -8,6 +8,18 @@ ChangeLog
OfflineIMAP v6.5.6.1 (YYYY-MM-DD) OfflineIMAP v6.5.6.1 (YYYY-MM-DD)
================================= =================================
* Expand environment variables in the following
configuration items:
- general.pythonfile;
- general.metadata;
- mbnames.filename;
- Repository.localfolders.
- Repository.sslcacertfile.
Make tilde and environment variable expansion in the following
configuration items:
- Repository.sslclientcert;
- Repository.sslclientkey.
* Updated bundled imaplib2 to 2.37: * Updated bundled imaplib2 to 2.37:
- add missing idle_lock in _handler() - add missing idle_lock in _handler()

View File

@ -23,6 +23,12 @@
# NOTE2: This implies that any '%' needs to be encoded as '%%' # NOTE2: This implies that any '%' needs to be encoded as '%%'
# NOTE3: Any variables that are subject to the environment variables
# ($NAME) and tilde (~username/~) expansions will receive tilde
# expansion first and only after this environment variables will be
# expanded in the resulting string. This behaviour is intentional
# as it coincides with typical shell expansion strategy.
################################################## ##################################################
# General definitions # General definitions
################################################## ##################################################
@ -31,6 +37,8 @@
# This specifies where offlineimap is to store its metadata. # This specifies where offlineimap is to store its metadata.
# This directory will be created if it does not already exist. # This directory will be created if it does not already exist.
#
# Tilde and environment variable expansions will be performed.
#metadata = ~/.offlineimap #metadata = ~/.offlineimap
@ -89,6 +97,8 @@ accounts = Test
# source file and call them from this config file. You can find # source file and call them from this config file. You can find
# an example of this in the manual. # an example of this in the manual.
# #
# Tilde and environment variable expansions will be performed.
#
# pythonfile = ~/.offlineimap.py # pythonfile = ~/.offlineimap.py
# #
@ -135,6 +145,9 @@ accounts = Test
# - foldername: the name of the folder; # - foldername: the name of the folder;
# - localfolders: path to the local directory hosting all Maildir # - localfolders: path to the local directory hosting all Maildir
# folders for the account. # folders for the account.
#
# Tilde and environment variable expansions will be performed
# for "filename" knob.
enabled = no enabled = no
filename = ~/Mutt/muttrc.mailboxes filename = ~/Mutt/muttrc.mailboxes
@ -375,9 +388,15 @@ remotehost = examplehost
ssl = yes ssl = yes
# SSL Client certificate (optional) # SSL Client certificate (optional)
#
# Tilde and environment variable expansions will be performed.
# sslclientcert = /path/to/file.crt # sslclientcert = /path/to/file.crt
# SSL Client key (optional) # SSL Client key (optional)
#
# Tilde and environment variable expansions will be performed.
# sslclientkey = /path/to/file.key # sslclientkey = /path/to/file.key
# SSL CA Cert(s) to verify the server cert against (optional). # SSL CA Cert(s) to verify the server cert against (optional).
@ -385,6 +404,9 @@ ssl = yes
# specified, the CA Cert(s) need to verify the Server cert AND # specified, the CA Cert(s) need to verify the Server cert AND
# match the hostname (* wildcard allowed on the left hand side) # match the hostname (* wildcard allowed on the left hand side)
# The certificate should be in PEM format. # The certificate should be in PEM format.
#
# Tilde and environment variable expansions will be performed.
# sslcacertfile = /path/to/cacertfile.crt # sslcacertfile = /path/to/cacertfile.crt
# If you connect via SSL/TLS (ssl=true) and you have no CA certificate # If you connect via SSL/TLS (ssl=true) and you have no CA certificate

View File

@ -94,14 +94,17 @@ class CustomConfigParser(SafeConfigParser):
return default return default
def getmetadatadir(self): def getmetadatadir(self):
metadatadir = os.path.expanduser(self.getdefault("general", "metadata", "~/.offlineimap")) xforms = [os.path.expanduser, os.path.expandvars]
d = self.getdefault("general", "metadata", "~/.offlineimap")
metadatadir = self.apply_xforms(d, xforms)
if not os.path.exists(metadatadir): if not os.path.exists(metadatadir):
os.mkdir(metadatadir, 0o700) os.mkdir(metadatadir, 0o700)
return metadatadir return metadatadir
def getlocaleval(self): def getlocaleval(self):
xforms = [os.path.expanduser, os.path.expandvars]
if self.has_option("general", "pythonfile"): if self.has_option("general", "pythonfile"):
path = os.path.expanduser(self.get("general", "pythonfile")) path = self.apply_xforms(self.get("general", "pythonfile"), xforms)
else: else:
path = None path = None
return LocalEval(path) return LocalEval(path)
@ -132,6 +135,26 @@ class CustomConfigParser(SafeConfigParser):
self.set(section, option, value) self.set(section, option, value)
def apply_xforms(self, string, transforms):
"""
Applies set of transformations to a string.
Arguments:
- string: source string; if None, then no processing will
take place.
- transforms: iterable that returns transformation function
on each turn.
Returns transformed string.
"""
if string == None:
return None
for f in transforms:
string = f(string)
return string
def CustomConfigDefault(): def CustomConfigDefault():
""" """
@ -161,9 +184,10 @@ class ConfigHelperMixin:
""" """
def _confighelper_runner(self, option, default, defaultfunc, mainfunc, *args): def _confighelper_runner(self, option, default, defaultfunc, mainfunc, *args):
""" """
Return configuration or default value for option Returns configuration or default value for option
that contains in section identified by getsection(). that contains in section identified by getsection().
Arguments: Arguments:
@ -213,27 +237,96 @@ class ConfigHelperMixin:
def getconf(self, option, default = CustomConfigDefault): def getconf(self, option, default = CustomConfigDefault):
"""
Retrieves string from the configuration.
Arguments:
- option: option name whose value is to be retrieved;
- default: default return value if no such option
exists.
"""
return self._confighelper_runner(option, default, return self._confighelper_runner(option, default,
self.getconfig().getdefault, self.getconfig().getdefault,
self.getconfig().get) self.getconfig().get)
def getconf_xform(self, option, xforms, default = CustomConfigDefault):
"""
Retrieves string from the configuration transforming the result.
Arguments:
- option: option name whose value is to be retrieved;
- xforms: iterable that returns transform functions
to be applied to the value of the option,
both retrieved and default one;
- default: default value for string if no such option
exists.
"""
value = self.getconf(option, default)
return self.getconfig().apply_xforms(value, xforms)
def getconfboolean(self, option, default = CustomConfigDefault): def getconfboolean(self, option, default = CustomConfigDefault):
"""
Retrieves boolean value from the configuration.
Arguments:
- option: option name whose value is to be retrieved;
- default: default return value if no such option
exists.
"""
return self._confighelper_runner(option, default, return self._confighelper_runner(option, default,
self.getconfig().getdefaultboolean, self.getconfig().getdefaultboolean,
self.getconfig().getboolean) self.getconfig().getboolean)
def getconfint(self, option, default = CustomConfigDefault): def getconfint(self, option, default = CustomConfigDefault):
"""
Retrieves integer value from the configuration.
Arguments:
- option: option name whose value is to be retrieved;
- default: default return value if no such option
exists.
"""
return self._confighelper_runner(option, default, return self._confighelper_runner(option, default,
self.getconfig().getdefaultint, self.getconfig().getdefaultint,
self.getconfig().getint) self.getconfig().getint)
def getconffloat(self, option, default = CustomConfigDefault): def getconffloat(self, option, default = CustomConfigDefault):
"""
Retrieves floating-point value from the configuration.
Arguments:
- option: option name whose value is to be retrieved;
- default: default return value if no such option
exists.
"""
return self._confighelper_runner(option, default, return self._confighelper_runner(option, default,
self.getconfig().getdefaultfloat, self.getconfig().getdefaultfloat,
self.getconfig().getfloat) self.getconfig().getfloat)
def getconflist(self, option, separator_re, def getconflist(self, option, separator_re,
default = CustomConfigDefault): default = CustomConfigDefault):
"""
Retrieves strings from the configuration and splits it
into the list of strings.
Arguments:
- option: option name whose value is to be retrieved;
- separator_re: regular expression for separator
to be used for split operation;
- default: default return value if no such option
exists.
"""
return self._confighelper_runner(option, default, return self._confighelper_runner(option, default,
self.getconfig().getdefaultlist, self.getconfig().getdefaultlist,
self.getconfig().getlist, separator_re) self.getconfig().getlist, separator_re)

View File

@ -49,12 +49,14 @@ def write():
def __genmbnames(): def __genmbnames():
"""Takes a configparser object and a boxlist, which is a list of hashes """Takes a configparser object and a boxlist, which is a list of hashes
containing 'accountname' and 'foldername' keys.""" containing 'accountname' and 'foldername' keys."""
xforms = [os.path.expanduser, os.path.expandvars]
mblock.acquire() mblock.acquire()
try: try:
localeval = config.getlocaleval() localeval = config.getlocaleval()
if not config.getdefaultboolean("mbnames", "enabled", 0): if not config.getdefaultboolean("mbnames", "enabled", 0):
return return
file = open(os.path.expanduser(config.get("mbnames", "filename")), "wt") path = config.apply_xform(config.get("mbnames", "filename"), xforms)
file = open(path, "wt")
file.write(localeval.eval(config.get("mbnames", "header"))) file.write(localeval.eval(config.get("mbnames", "header")))
folderfilter = lambda accountname, foldername: 1 folderfilter = lambda accountname, foldername: 1
if config.has_option("mbnames", "folderfilter"): if config.has_option("mbnames", "folderfilter"):

View File

@ -194,18 +194,20 @@ class IMAPRepository(BaseRepository):
return self.getconfboolean('ssl', 0) return self.getconfboolean('ssl', 0)
def getsslclientcert(self): def getsslclientcert(self):
return self.getconf('sslclientcert', None) xforms = [os.path.expanduser, os.path.expandvars, os.path.abspath]
return self.getconf_xform('sslclientcert', xforms, None)
def getsslclientkey(self): def getsslclientkey(self):
return self.getconf('sslclientkey', None) xforms = [os.path.expanduser, os.path.expandvars, os.path.abspath]
return self.getconf_xform('sslclientkey', xforms, None)
def getsslcacertfile(self): def getsslcacertfile(self):
"""Return the absolute path of the CA certfile to use, if any""" """Return the absolute path of the CA certfile to use, if any"""
cacertfile = self.getconf('sslcacertfile', get_os_sslcertfile()) xforms = [os.path.expanduser, os.path.expandvars, os.path.abspath]
cacertfile = self.getconf_xform('sslcacertfile', xforms,
get_os_sslcertfile())
if cacertfile is None: if cacertfile is None:
return None return None
cacertfile = os.path.expanduser(cacertfile)
cacertfile = os.path.abspath(cacertfile)
if not os.path.isfile(cacertfile): if not os.path.isfile(cacertfile):
raise SyntaxWarning("CA certfile for repository '%s' could " raise SyntaxWarning("CA certfile for repository '%s' could "
"not be found. No such file: '%s'" \ "not be found. No such file: '%s'" \

View File

@ -61,7 +61,8 @@ class MaildirRepository(BaseRepository):
os.utime(cur_dir, (cur_atime, os.path.getmtime(cur_dir))) os.utime(cur_dir, (cur_atime, os.path.getmtime(cur_dir)))
def getlocalroot(self): def getlocalroot(self):
return os.path.expanduser(self.getconf('localfolders')) xforms = [os.path.expanduser]
return self.getconf_xform('localfolders', xforms)
def debug(self, msg): def debug(self, msg):
self.ui.debug('maildir', msg) self.ui.debug('maildir', msg)