From 818486283ed2ccf55e9a0d6be8a1acd8727fb69e Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Sun, 21 Sep 2014 11:15:38 +0400 Subject: [PATCH] Add support for OS-specific CA bundle locations GitHub pull: #19 Suggested-by: Michael Vogt Signed-off-by: Eygene Ryabinkin --- Changelog.rst | 3 ++ offlineimap/repository/IMAP.py | 3 +- offlineimap/utils/distro.py | 74 ++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 offlineimap/utils/distro.py diff --git a/Changelog.rst b/Changelog.rst index 082950f..13eba38 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -8,6 +8,9 @@ ChangeLog OfflineIMAP v6.5.6.1 (YYYY-MM-DD) ================================= +* Support default CA bundle locations for a couple of + known Unix systems (Michael Vogt, GutHub pull #19) + * Create SQLite database directory if it doesn't exist yet; warn if path is not a directory (Nick Farrell, GutHub pull #102) diff --git a/offlineimap/repository/IMAP.py b/offlineimap/repository/IMAP.py index ff1d5e2..c98419d 100644 --- a/offlineimap/repository/IMAP.py +++ b/offlineimap/repository/IMAP.py @@ -19,6 +19,7 @@ from offlineimap.repository.Base import BaseRepository from offlineimap import folder, imaputil, imapserver, OfflineImapError from offlineimap.folder.UIDMaps import MappedIMAPFolder from offlineimap.threadutil import ExitNotifyThread +from offlineimap.utils.distro import get_os_sslcertfile from threading import Event import os from sys import exc_info @@ -200,7 +201,7 @@ class IMAPRepository(BaseRepository): def getsslcacertfile(self): """Return the absolute path of the CA certfile to use, if any""" - cacertfile = self.getconf('sslcacertfile', None) + cacertfile = self.getconf('sslcacertfile', get_os_sslcertfile()) if cacertfile is None: return None cacertfile = os.path.expanduser(cacertfile) diff --git a/offlineimap/utils/distro.py b/offlineimap/utils/distro.py new file mode 100644 index 0000000..96aab1b --- /dev/null +++ b/offlineimap/utils/distro.py @@ -0,0 +1,74 @@ +# Copyright 2014 Eygene A. Ryabinkin. +# +# Module that supports distribution-specific functions. + +import platform +import os + + +# Each dictionary value is either string or some iterable. +# +# For the former we will just return the value, for an iterable +# we will walk through the values and will return the first +# one that corresponds to the existing file. +__DEF_OS_LOCATIONS = { + 'freebsd': '/usr/local/share/certs/ca-root-nss.crt', + 'openbsd': None, + 'netbsd': None, + 'dragonfly': None, + 'darwin': [ + # MacPorts, port curl-ca-bundle + '/opt/local/share/curl/curl-ca-bundle.crt', + ], + 'linux-ubuntu': '/etc/ssl/certs/ca-certificates.crt', + 'linux-debian': '/etc/ssl/certs/ca-certificates.crt', + 'linux-fedora': '/etc/pki/tls/certs/ca-bundle.crt', + 'linux-redhat': '/etc/pki/tls/certs/ca-bundle.crt', + 'linux-suse': '/etc/ssl/ca-bundle.pem', +} + + +def get_os_name(): + """ + Finds out OS name. For non-Linux system it will be just a plain + OS name (like FreeBSD), for Linux it will be "linux-", + where is the name of the distribution, as returned by + the first component of platform.linux_distribution. + + Return value will be all-lowercase to avoid confusion about + proper name capitalisation. + + """ + OS = platform.system().lower() + + if OS.startswith('linux'): + DISTRO = platform.linux_distribution()[0] + if DISTRO: + OS = OS + "-%s" % DISTRO.lower() + + return OS + + +def get_os_sslcertfile(): + """ + Finds out the location for the distribution-specific + CA certificate file bundle. + + Returns the location of the file or None if there is + no known CA certificate file or all known locations + correspond to non-existing filesystem objects. + + """ + OS = get_os_name() + + if OS in __DEF_OS_LOCATIONS: + l = __DEF_OS_LOCATIONS[OS] + if not hasattr(l, '__iter__'): + l = (l, ) + for f in l: + assert (type(f) == type("")) + if os.path.exists(f) and \ + (os.path.isfile(f) or os.path.islink(f)): + return f + + return None