Add client ca stuff
diff --git a/INSTALL b/INSTALL
index b650ba6..feaeebf 100644
--- a/INSTALL
+++ b/INSTALL
@@ -38,31 +38,77 @@
 
 -- Building the Module on a Windows System --
 
-pyOpenSSL is known to build with mingw32 for Python 2.3 through Python 2.5. 
-For Python 2.6, the official Windows installer of which is built with
-Microsoft Visual Studio 2008 (version 9.0), Microsoft Visual Studio 2008
-(version 9.0) is required.  You can specify that mingw32 be used by passing
-the --compiler argument to build_ext.  You will also need to specify the
-location of the OpenSSL headers and libraries:
+First you should get OpenSSL linked with the same runtime library that Python
+uses.  If you are using Python 2.6 you can use the installer at:
 
-  C:\pyOpenSSL-X.Y> setup.py build_ext -c mingw32 -I C:\OpenSSL\include ^
-                      -L C:\OpenSSL bdist_msi
+  http://www.slproweb.com/products/Win32OpenSSL.html
 
-The correct header and library paths depend on how you have OpenSSL
-installed.  The above paths are correct for the default installation of
-(<http://www.slproweb.com/products/Win32OpenSSL.html>).
+The binaries in the installer are built with Visual Studio 2008 at the
+time of this writing, which is the same compiler used for building the
+official Python 2.6 installers.
+
+If you want to build PyOpenSSL for an older Python version, it is preferred
+to build OpenSSL yourself, either with the Visual Studio 2003 compiler or
+with the MinGW compiler.  This way you avoid all potential incompatibilities
+between different versions of runtime library (msvcrt.dll). To build OpenSSL
+folow the instructions in its source distribution and make sure that you build
+a shared library, not a static one.  PyOpenSSL fails some of its tests when
+linked with the static OpenSSL libraries. Use the same compiler for OpenSSL
+that you will use for PyOpenSSL later. Make sure that OpenSSL is properly
+installed before continuing.  To install OpenSSL when building with MinGW, use
+the folowing script:
+
+set OPENSSL_INSTALL_DIR=%1
+mkdir %OPENSSL_INSTALL_DIR%
+mkdir %OPENSSL_INSTALL_DIR%\bin
+mkdir %OPENSSL_INSTALL_DIR%\include
+mkdir %OPENSSL_INSTALL_DIR%\include\openssl
+mkdir %OPENSSL_INSTALL_DIR%\lib
+copy /b .\*.dll            %OPENSSL_INSTALL_DIR%\bin
+copy /b .\out\openssl.exe  %OPENSSL_INSTALL_DIR%\bin
+copy /b .\outinc\openssl\* %OPENSSL_INSTALL_DIR%\include\openssl
+copy /b .\out\*.a          %OPENSSL_INSTALL_DIR%\lib
+
+Ensure that OpenSSL's openssl.exe executable can be found on PATH before
+running PyOpenSSL's setup script. The setup script finds OpenSSL's include dir
+and lib dir based on the location of openssl.exe, and the test suite requires
+openssl.exe for output comparison. Alternatively, you can specify the
+--with-openssl option to setup.py's build_ext command with the path to the
+OpenSSL installation dir:
+
+  > python setup.py build_ext --with-openssl=C:\path\to\openssl build
+
+PyOpenSSL is known to build with mingw32 for Python 2.3 through Python 2.5.
+Before using the mingw32 compiler for Python 2.3, you will have to create
+a Python library that MinGW understands. Find and download the pexports
+program, put it and MinGW's bin directory on path, then run from Python's
+install dir:
+
+> pexports python23.dll > libs\python23.def
+> dlltool --dllname python23.dll --def libs\python23.def \
+          --output-lib libs\libpython23.a
+
+For Python 2.4 and 2.5, no special preparation is needed, just make sure that
+MinGW's gcc is on PATH.  You can specify that mingw32 be used by passing
+the --compiler argument to build_ext:
+
+  C:\pyOpenSSL-X.Y> setup.py build_ext -c mingw32 bdist_msi
 
 The bdist_msi command will build an MSI installer.  It can be substituted
-with another bdist command if another kind of installer is desired.
+with another bdist command if another kind of installer is desired or with
+the install command if you want to install directly.
 
-To build with MSVC instead, omit the -c option and pass a slightly different
-library directory:
+For Python 2.4 and 2.5 you can use Visual Studio 2003 in addition to MinGW.
+For Python 2.6, the official Windows installer of which is built with
+Microsoft Visual Studio 2008 (version 9.0), Microsoft Visual Studio 2008
+(version 9.0) is required.
 
-  C:\pyOpenSSL-X.Y> setup.py build_ext -I C:\OpenSSL\include ^
-                      -L C:\OpenSSL\lib bdist_msi
+To build with MSVC, just omit the compiler specific option:
+
+  C:\pyOpenSSL-X.Y> setup.py bdist_msi
 
 The resulting binary distribution will be placed in the dist directory. To
-install it, dDepending on what kind of distribution you create, run it,
+install it, depending on what kind of distribution you create, run it,
 unzip it, or copy it to Python installation's site-packages.
 
 And similarily, you can do
diff --git a/setup.py b/setup.py
index a6a446c..af02aae 100755
--- a/setup.py
+++ b/setup.py
@@ -11,10 +11,13 @@
 Installation script for the OpenSSL module
 """
 
+import distutils.log
+distutils.log.set_verbosity(3)
+
 import sys, os
 from distutils.core import Extension, setup
-
-from glob import glob
+from distutils.errors import DistutilsFileError
+from distutils.command.build_ext import build_ext
 
 from version import __version__
 
@@ -44,25 +47,143 @@
 if os.name == 'nt' or sys.platform == 'win32':
 
     Libraries = ['Ws2_32']
-    def makeTellMeIf(original, what):
-        class tellMeIf(original):
-            def __init__(*a, **kw):
-                Libraries.extend(what)
-                return original.__init__(*a, **kw)
-        return tellMeIf
 
-    from distutils import cygwinccompiler
-    cygwinccompiler.Mingw32CCompiler = makeTellMeIf(cygwinccompiler.Mingw32CCompiler, ['eay32', 'ssl32'])
-    from distutils import msvccompiler
-    msvccompiler.MSVCCompiler = makeTellMeIf(msvccompiler.MSVCCompiler, ['libeay32', 'ssleay32'])
 
-    import shutil
-    shutil.copy("C:\\OpenSSL\\ssleay32.dll", os.path.split(os.path.abspath(__file__))[0])
-    shutil.copy("C:\\OpenSSL\\libeay32.dll", os.path.split(os.path.abspath(__file__))[0])
-    package_data = {'': ['ssleay32.dll', 'libeay32.dll']}
+
+    class BuildExtension(build_ext):
+        """
+        A custom command that semiautomatically finds dependencies required by
+        PyOpenSSL.
+        """
+
+        user_options = (build_ext.user_options +
+                        [("with-openssl=", None,
+                          "directory where OpenSSL is installed")])
+        with_openssl = None
+        openssl_dlls = ()
+        openssl_mingw = False
+
+
+        def finalize_options(self):
+            """
+            Update build options with details about OpenSSL.
+            """
+            build_ext.finalize_options(self)
+            if self.with_openssl is None:
+                self.find_openssl()
+            self.find_openssl_dlls()
+            self.add_openssl_compile_info()
+
+
+        def find_openssl(self):
+            """
+            Find OpenSSL's install directory.
+            """
+            potentials = []
+            dirs = os.environ.get("PATH").split(os.pathsep)
+            for d in dirs:
+                if os.path.exists(os.path.join(d, "openssl.exe")):
+                    ssldir, bin = os.path.split(d)
+                    if not bin:
+                        ssldir, bin = os.path.split(ssldir)
+                    potentials.append(ssldir)
+                    childdirs = os.listdir(ssldir)
+                    if "lib" in childdirs and "include" in childdirs:
+                        self.with_openssl = ssldir
+                        return
+            if potentials:
+                raise DistutilsFileError(
+                    "Only found improper OpenSSL directories: %r" % (
+                        potentials,))
+            else:
+                raise DistutilsFileError("Could not find 'openssl.exe'")
+
+
+        def find_openssl_dlls(self):
+            """
+            Find OpenSSL's shared libraries.
+            """
+            self.openssl_dlls = []
+            self.find_openssl_dll("libssl32.dll", False)
+            if self.openssl_dlls:
+                self.openssl_mingw = True
+            else:
+                self.find_openssl_dll("ssleay32.dll", True)
+            self.find_openssl_dll("libeay32.dll", True)
+            # add zlib to the mix if it looks like OpenSSL
+            # was linked with a private copy of it
+            self.find_openssl_dll("zlib1.dll", False)
+
+
+        def find_openssl_dll(self, name, required):
+            """
+            Find OpenSSL's shared library and its path after installation.
+            """
+            dllpath = os.path.join(self.with_openssl, "bin", name)
+            if not os.path.exists(dllpath):
+                if required:
+                    raise DistutilsFileError("could not find '%s'" % name)
+                else:
+                    return
+            newpath = os.path.join(self.build_lib, "OpenSSL", name)
+            self.openssl_dlls.append((dllpath, newpath))
+
+
+        def add_openssl_compile_info(self):
+            """
+            Set up various compile and link parameters.
+            """
+            if self.compiler == "mingw32":
+                if self.openssl_mingw:
+                    # Library path and library names are sane when OpenSSL is
+                    # built with MinGW .
+                    libdir = "lib"
+                    libs = ["eay32", "ssl32"]
+                else:
+                    libdir = ""
+                    libs = []
+                    # Unlike when using the binary installer, which creates
+                    # an atypical shared library name 'ssleay32', so we have
+                    # to use this workaround.
+                    if self.link_objects is None:
+                        self.link_objects = []
+                    for dllpath, _ in self.openssl_dlls:
+                        dllname = os.path.basename(dllpath)
+                        libname = os.path.splitext(dllname)[0] + ".a"
+                        libpath = os.path.join(self.with_openssl,
+                                               "lib", "MinGW", libname)
+                        self.link_objects.append(libpath)
+            else:
+                libdir = "lib"
+                libs = ["libeay32", "ssleay32"]
+            self.include_dirs.append(os.path.join(self.with_openssl, "include"))
+            self.library_dirs.append(os.path.join(self.with_openssl, libdir))
+            self.libraries.extend(libs)
+
+
+        def run(self):
+            """
+            Build extension modules and copy shared libraries.
+            """
+            build_ext.run(self)
+            for dllpath, newpath in self.openssl_dlls:
+                self.copy_file(dllpath, newpath)
+
+
+        def get_outputs(self):
+            """
+            Return a list of file paths built by this comand.
+            """
+            output = [pathpair[1] for pathpair in self.openssl_dlls]
+            output.extend(build_ext.get_outputs(self))
+            return output
+
+
+
 else:
     Libraries = ['ssl', 'crypto']
-    package_data = {}
+    BuildExtension = build_ext
+
 
 
 def mkExtension(name):
@@ -85,7 +206,7 @@
                      'OpenSSL.test.test_rand',
                      'OpenSSL.test.test_ssl'],
       zip_safe = False,
-      package_data = package_data,
+      cmdclass = {"build_ext": BuildExtension},
       description = 'Python wrapper module around the OpenSSL library',
       author = 'Martin Sjögren, AB Strakt',
       author_email = 'msjogren@gmail.com',