Re-merge the client CA changes, this time with Windows build fixes to so I can tell if anything actually works
diff --git a/ChangeLog b/ChangeLog
index 70cdb28..4681c0b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,13 +1,23 @@
+2009-11-07  Žiga Seilnacht, Jean-Paul Calderone  <exarkun@twistedmatrix.com>
+
+	* src/ssl/connection.c, src/ssl/context.c: Add set_client_ca_list,
+	  add_client_ca, and get_client_ca_list to Context for manipulating
+	  the list of certificate authority names which are sent by servers
+	  with the certificate request message.
+	* src/util.h: Add ssize-related defines if the version of Python
+	  being used does not have them.
+	* setup.py: Significant changes to the way Windows builds are done,
+	  particularly the way OpenSSL headers and libraries are found (with
+	  the new --with-openssl argument to build_ext).
+
 2009-08-27  Rick Dean  <rick@fdd.com>, Jean-Paul Calderone  <exarkun@twistedmatrix.com>
 
 	* src/crypto/pkcs12.c: Add setters to the PKCS12 type for the
 	  certificate, private key, ca certificate list, and friendly
 	  name, and add a getter for the friendly name.  Also add a method
 	  for exporting a PKCS12 object as a string.
-
 	* test/test_crypto.py: Add lots of additional tests for the PKCS12
 	  type.
-
 	* doc/pyOpenSSL.tex: Documentation for the new PKCS12 methods.
 
 2009-07-17  Rick Dean  <rick@fdd.com>, Jean-Paul Calderone  <exarkun@twistedmatrix.com>
diff --git a/INSTALL b/INSTALL
index b650ba6..f4635c5 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 follow 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
@@ -72,8 +118,9 @@
 to get more information.
 
 Big thanks to Itamar Shtull-Trauring, Oleg Orlov, Zooko O'Whielacronx, Chris
-Galvan, and #python and #distutils on FreeNode for their help with Windows
-build instructions.
+Galvan, Žiga Seilnacht, and #python and #distutils on FreeNode for their
+help with Windows build instructions and to Michael Schneider for providing
+Windows build hosts.
 
 -- Documentation --
 
diff --git a/doc/pyOpenSSL.tex b/doc/pyOpenSSL.tex
index cea48e9..cedeccb 100644
--- a/doc/pyOpenSSL.tex
+++ b/doc/pyOpenSSL.tex
@@ -189,8 +189,8 @@
 \end{datadesc}
 
 \begin{classdesc}{X509Extension}{typename, critical, value\optional{, subject}\optional{, issuer}}
-A class representing an X.509 v3 certificate extensions.  
-See \url{http://openssl.org/docs/apps/x509v3_config.html\#STANDARD_EXTENSIONS} 
+A class representing an X.509 v3 certificate extensions.
+See \url{http://openssl.org/docs/apps/x509v3_config.html\#STANDARD_EXTENSIONS}
 for \var{typename} strings and their options.
 Optional parameters \var{subject} and \var{issuer} must be X509 objects.
 \end{classdesc}
@@ -673,7 +673,7 @@
 \end{funcdesc}
 
 \begin{excdesc}{Error}
-If the current RAND method supports any errors, this is raised when needed.  
+If the current RAND method supports any errors, this is raised when needed.
 The default method does not raise this when the entropy pool is depleted.
 
 Whenever this exception is raised directly, it has a list of error messages
@@ -853,6 +853,22 @@
 when requesting a client certificate.
 \end{methoddesc}
 
+\begin{methoddesc}[Context]{set_client_ca_list}{certificate_authorities}
+Replace the current list of preferred certificate signers that would be
+sent to the client when requesting a client certificate with the
+\var{certificate_authorities} sequence of \class{OpenSSL.crypto.X509Name}s.
+
+\versionadded{0.10}
+\end{methoddesc}
+
+\begin{methoddesc}[Context]{add_client_ca}{certificate_authority}
+Extract a \class{OpenSSL.crypto.X509Name} from the \var{certificate_authority}
+\class{OpenSSL.crypto.X509} certificate and add it to the list of preferred
+certificate signers sent to the client when requesting a client certificate.
+
+\versionadded{0.10}
+\end{methoddesc}
+
 \begin{methoddesc}[Context]{load_verify_locations}{pemfile, capath}
 Specify where CA certificates for verification purposes are located. These
 are trusted certificates. Note that the certificates have to be in PEM
@@ -1026,6 +1042,20 @@
 but not it returns the entire list in one go.
 \end{methoddesc}
 
+\begin{methoddesc}[Connection]{get_client_ca_list}{}
+Retrieve the list of preferred client certificate issuers sent by the server
+as \class{OpenSSL.crypto.X509Name} objects.
+
+If this is a client \class{Connection}, the list will be empty until the
+connection with the server is established.
+
+If this is a server \class{Connection}, return the list of certificate
+authorities that will be sent or has been sent to the client, as controlled
+by this \class{Connection}'s \class{Context}.
+
+\versionadded{0.10}
+\end{methoddesc}
+
 \begin{methoddesc}[Connection]{get_context}{}
 Retrieve the Context object associated with this Connection.
 \end{methoddesc}
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',
diff --git a/src/ssl/connection.c b/src/ssl/connection.c
index e10989b..1c59ff3 100755
--- a/src/ssl/connection.c
+++ b/src/ssl/connection.c
@@ -829,6 +829,59 @@
     return lst;
 }
 
+static char ssl_Connection_get_client_ca_list_doc[] = "\n\
+Get CAs whose certificates are suggested for client authentication.\n\
+\n\
+@return: If this is a server connection, a list of X509Names representing\n\
+    the acceptable CAs as set by L{OpenSSL.SSL.Context.set_client_ca_list} or\n\
+    L{OpenSSL.SSL.Context.add_client_ca}.  If this is a client connection,\n\
+    the list of such X509Names sent by the server, or an empty list if that\n\
+    has not yet happened.\n\
+";
+
+static PyObject *
+ssl_Connection_get_client_ca_list(ssl_ConnectionObj *self, PyObject *args) {
+    STACK_OF(X509_NAME) *CANames;
+    PyObject *CAList;
+    int i, n;
+
+    if (!PyArg_ParseTuple(args, ":get_client_ca_list")) {
+        return NULL;
+    }
+    CANames = SSL_get_client_CA_list(self->ssl);
+    if (CANames == NULL) {
+        return PyList_New(0);
+    }
+    n = sk_X509_NAME_num(CANames);
+    CAList = PyList_New(n);
+    if (CAList == NULL) {
+        return NULL;
+    }
+    for (i = 0; i < n; i++) {
+        X509_NAME *CAName;
+        PyObject *CA;
+
+        CAName = X509_NAME_dup(sk_X509_NAME_value(CANames, i));
+        if (CAName == NULL) {
+            Py_DECREF(CAList);
+            exception_from_error_queue(ssl_Error);
+            return NULL;
+        }
+        CA = (PyObject *)crypto_X509Name_New(CAName, 1);
+        if (CA == NULL) {
+            X509_NAME_free(CAName);
+            Py_DECREF(CAList);
+            return NULL;
+        }
+        if (PyList_SetItem(CAList, i, CA)) {
+            Py_DECREF(CA);
+            Py_DECREF(CAList);
+            return NULL;
+        }
+    }
+    return CAList;
+}
+
 static char ssl_Connection_makefile_doc[] = "\n\
 The makefile() method is not implemented, since there is no dup semantics\n\
 for SSL connections\n\
@@ -1087,6 +1140,7 @@
     ADD_METHOD(bio_shutdown),
     ADD_METHOD(shutdown),
     ADD_METHOD(get_cipher_list),
+    ADD_METHOD(get_client_ca_list),
     ADD_METHOD(makefile),
     ADD_METHOD(get_app_data),
     ADD_METHOD(set_app_data),
diff --git a/src/ssl/context.c b/src/ssl/context.c
index df7411f..ebaf164 100644
--- a/src/ssl/context.c
+++ b/src/ssl/context.c
@@ -13,8 +13,10 @@
 
 #if PY_VERSION_HEX >= 0x02050000
 # define PYARG_PARSETUPLE_FORMAT const char
+# define PYOBJECT_GETATTRSTRING_TYPE const char*
 #else
 # define PYARG_PARSETUPLE_FORMAT char
+# define PYOBJECT_GETATTRSTRING_TYPE char*
 #endif
 
 #ifndef MS_WINDOWS
@@ -335,37 +337,64 @@
     return Py_None;
 }
 
+static PyTypeObject *
+type_modified_error(const char *name) {
+    PyErr_Format(PyExc_RuntimeError,
+                 "OpenSSL.crypto's '%s' attribute has been modified",
+                 name);
+    return NULL;
+}
+
+static PyTypeObject *
+import_crypto_type(const char *name, size_t objsize) {
+    PyObject *module, *type, *name_attr;
+    PyTypeObject *res;
+    int right_name;
+
+    module = PyImport_ImportModule("OpenSSL.crypto");
+    if (module == NULL) {
+        return NULL;
+    }
+    type = PyObject_GetAttrString(module, (PYOBJECT_GETATTRSTRING_TYPE)name);
+    Py_DECREF(module);
+    if (type == NULL) {
+        return NULL;
+    }
+    if (!(PyType_Check(type))) {
+        Py_DECREF(type);
+        return type_modified_error(name);
+    }
+    name_attr = PyObject_GetAttrString(type, "__name__");
+    if (name_attr == NULL) {
+        Py_DECREF(type);
+        return NULL;
+    }
+    right_name = (PyString_CheckExact(name_attr) &&
+                  strcmp(name, PyString_AsString(name_attr)) == 0);
+    Py_DECREF(name_attr);
+    res = (PyTypeObject *)type;
+    if (!right_name || res->tp_basicsize != objsize) {
+        Py_DECREF(type);
+        return type_modified_error(name);
+    }
+    return res;
+}
+
 static crypto_X509Obj *
-parse_certificate_argument(const char* format1, const char* format2, PyObject* args)
-{
+parse_certificate_argument(const char* format, PyObject* args) {
     static PyTypeObject *crypto_X509_type = NULL;
     crypto_X509Obj *cert;
 
-    /* We need to check that cert really is an X509 object before
-       we deal with it. The problem is we can't just quickly verify
-       the type (since that comes from another module). This should
-       do the trick (reasonably well at least): Once we have one
-       verified object, we use it's type object for future
-       comparisons. */
-
-    if (!crypto_X509_type)
-    {
-	if (!PyArg_ParseTuple(args, (PYARG_PARSETUPLE_FORMAT *)format1, &cert))
-	    return NULL;
-
-	if (strcmp(cert->ob_type->tp_name, "X509") != 0 || 
-	    cert->ob_type->tp_basicsize != sizeof(crypto_X509Obj))
-	{
-	    PyErr_SetString(PyExc_TypeError, "Expected an X509 object");
-	    return NULL;
-	}
-
-	crypto_X509_type = cert->ob_type;
+    if (!crypto_X509_type) {
+        crypto_X509_type = import_crypto_type("X509", sizeof(crypto_X509Obj));
+        if (!crypto_X509_type) {
+            return NULL;
+        }
     }
-    else
-	if (!PyArg_ParseTuple(args, (PYARG_PARSETUPLE_FORMAT *)format2, crypto_X509_type,
-			      &cert))
-	    return NULL;
+    if (!PyArg_ParseTuple(args, (PYARG_PARSETUPLE_FORMAT *)format,
+                          crypto_X509_type, &cert)) {
+        return NULL;
+    }
     return cert;
 }
 
@@ -381,7 +410,7 @@
 {
     X509* cert_original;
     crypto_X509Obj *cert = parse_certificate_argument(
-        "O:add_extra_chain_cert", "O!:add_extra_chain_cert", args);
+        "O!:add_extra_chain_cert", args);
     if (cert == NULL)
     {
         return NULL;
@@ -471,7 +500,7 @@
 ssl_Context_use_certificate(ssl_ContextObj *self, PyObject *args)
 {
     crypto_X509Obj *cert = parse_certificate_argument(
-        "O:use_certificate", "O!:use_certificate", args);
+        "O!:use_certificate", args);
     if (cert == NULL) {
         return NULL;
     }
@@ -533,43 +562,24 @@
 @return: None\n\
 ";
 static PyObject *
-ssl_Context_use_privatekey(ssl_ContextObj *self, PyObject *args)
-{
+ssl_Context_use_privatekey(ssl_ContextObj *self, PyObject *args) {
     static PyTypeObject *crypto_PKey_type = NULL;
     crypto_PKeyObj *pkey;
 
-    /* We need to check that cert really is a PKey object before
-       we deal with it. The problem is we can't just quickly verify
-       the type (since that comes from another module). This should
-       do the trick (reasonably well at least): Once we have one
-       verified object, we use it's type object for future
-       comparisons. */
-
-    if (!crypto_PKey_type)
-    {
-	if (!PyArg_ParseTuple(args, "O:use_privatekey", &pkey))
-	    return NULL;
-
-	if (strcmp(pkey->ob_type->tp_name, "OpenSSL.crypto.PKey") != 0 ||
-	    pkey->ob_type->tp_basicsize != sizeof(crypto_PKeyObj))
-	{
-	    PyErr_SetString(PyExc_TypeError, "Expected a PKey object");
-	    return NULL;
-	}
-
-	crypto_PKey_type = pkey->ob_type;
+    if (!crypto_PKey_type) {
+        crypto_PKey_type = import_crypto_type("PKey", sizeof(crypto_PKeyObj));
+        if (!crypto_PKey_type) {
+            return NULL;
+        }
     }
-    else
-    if (!PyArg_ParseTuple(args, "O!:use_privatekey", crypto_PKey_type, &pkey))
+    if (!PyArg_ParseTuple(args, "O!:use_privatekey", crypto_PKey_type, &pkey)) {
         return NULL;
+    }
 
-    if (!SSL_CTX_use_PrivateKey(self->ctx, pkey->pkey))
-    {
+    if (!SSL_CTX_use_PrivateKey(self->ctx, pkey->pkey)) {
         exception_from_error_queue(ssl_Error);
         return NULL;
-    }
-    else
-    {
+    } else {
         Py_INCREF(Py_None);
         return Py_None;
     }
@@ -789,6 +799,111 @@
     }
 }
 
+static char ssl_Context_set_client_ca_list_doc[] = "\n\
+Set the list of preferred client certificate signers for this server context.\n\
+\n\
+This list of certificate authorities will be sent to the client when the\n\
+server requests a client certificate.\n\
+\n\
+@param certificate_authorities: a sequence of X509Names.\n\
+@return: None\n\
+";
+
+static PyObject *
+ssl_Context_set_client_ca_list(ssl_ContextObj *self, PyObject *args)
+{
+    static PyTypeObject *X509NameType;
+    PyObject *sequence, *tuple, *item;
+    crypto_X509NameObj *name;
+    X509_NAME *sslname;
+    STACK_OF(X509_NAME) *CANames;
+    Py_ssize_t length;
+    int i;
+
+    if (X509NameType == NULL) {
+        X509NameType = import_crypto_type("X509Name", sizeof(crypto_X509NameObj));
+        if (X509NameType == NULL) {
+            return NULL;
+        }
+    }
+    if (!PyArg_ParseTuple(args, "O:set_client_ca_list", &sequence)) {
+        return NULL;
+    }
+    tuple = PySequence_Tuple(sequence);
+    if (tuple == NULL) {
+        return NULL;
+    }
+    length = PyTuple_Size(tuple);
+    if (length >= INT_MAX) {
+        PyErr_SetString(PyExc_ValueError, "client CA list is too long");
+        Py_DECREF(tuple);
+        return NULL;
+    }
+    CANames = sk_X509_NAME_new_null();
+    if (CANames == NULL) {
+        Py_DECREF(tuple);
+        exception_from_error_queue(ssl_Error);
+        return NULL;
+    }
+    for (i = 0; i < length; i++) {
+        item = PyTuple_GetItem(tuple, i);
+        if (item->ob_type != X509NameType) {
+            PyErr_Format(PyExc_TypeError,
+                         "client CAs must be X509Name objects, not %s objects",
+                         item->ob_type->tp_name);
+            sk_X509_NAME_free(CANames);
+            Py_DECREF(tuple);
+            return NULL;
+        }
+        name = (crypto_X509NameObj *)item;
+        sslname = X509_NAME_dup(name->x509_name);
+        if (sslname == NULL) {
+            sk_X509_NAME_free(CANames);
+            Py_DECREF(tuple);
+            exception_from_error_queue(ssl_Error);
+            return NULL;
+        }
+        if (!sk_X509_NAME_push(CANames, sslname)) {
+            X509_NAME_free(sslname);
+            sk_X509_NAME_free(CANames);
+            Py_DECREF(tuple);
+            exception_from_error_queue(ssl_Error);
+            return NULL;
+        }
+    }
+    Py_DECREF(tuple);
+    SSL_CTX_set_client_CA_list(self->ctx, CANames);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char ssl_Context_add_client_ca_doc[] = "\n\
+Add the CA certificate to the list of preferred signers for this context.\n\
+\n\
+The list of certificate authorities will be sent to the client when the\n\
+server requests a client certificate.\n\
+\n\
+@param certificate_authority: certificate authority's X509 certificate.\n\
+@return: None\n\
+";
+
+static PyObject *
+ssl_Context_add_client_ca(ssl_ContextObj *self, PyObject *args)
+{
+    crypto_X509Obj *cert;
+
+    cert = parse_certificate_argument("O!:add_client_ca", args);
+    if (cert == NULL) {
+        return NULL;
+    }
+    if (!SSL_CTX_add_client_CA(self->ctx, cert->x509)) {
+        exception_from_error_queue(ssl_Error);
+        return NULL;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
 static char ssl_Context_set_timeout_doc[] = "\n\
 Set session timeout\n\
 \n\
@@ -960,6 +1075,8 @@
     ADD_METHOD(get_verify_depth),
     ADD_METHOD(load_tmp_dh),
     ADD_METHOD(set_cipher_list),
+    ADD_METHOD(set_client_ca_list),
+    ADD_METHOD(add_client_ca),
     ADD_METHOD(set_timeout),
     ADD_METHOD(get_timeout),
     ADD_METHOD(set_info_callback),
diff --git a/src/util.h b/src/util.h
index d9dc7d2..6724f5a 100644
--- a/src/util.h
+++ b/src/util.h
@@ -119,6 +119,10 @@
 }
 #endif
 
-
+#if !defined(PY_SSIZE_T_MIN)
+typedef int Py_ssize_t;
+#define PY_SSIZE_T_MAX INT_MAX
+#define PY_SSIZE_T_MIN INT_MIN
+#endif
 
 #endif
diff --git a/test/test_ssl.py b/test/test_ssl.py
index 5d386a9..c460741 100644
--- a/test/test_ssl.py
+++ b/test/test_ssl.py
@@ -194,6 +194,7 @@
         cert = clientSSL.get_peer_certificate()
         self.assertEqual(cert.get_subject().CN, 'Testing Root CA')
 
+
     def test_load_verify_file(self):
         """
         L{Context.load_verify_locations} accepts a file name and uses the
@@ -255,7 +256,7 @@
             context = Context(SSLv3_METHOD)
             context.set_default_verify_paths()
             context.set_verify(
-                VERIFY_PEER, 
+                VERIFY_PEER,
                 lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
 
             client = socket()
@@ -277,6 +278,28 @@
         self.assertRaises(TypeError, context.set_default_verify_paths, 1)
         self.assertRaises(TypeError, context.set_default_verify_paths, "")
 
+    def test_add_extra_chain_cert_invalid_cert(self):
+        """
+        L{Context.add_extra_chain_cert} raises L{TypeError} if called with
+        other than one argument or if called with an object which is not an
+        instance of L{X509}.
+        """
+        context = Context(TLSv1_METHOD)
+        self.assertRaises(TypeError, context.add_extra_chain_cert)
+        self.assertRaises(TypeError, context.add_extra_chain_cert, object())
+        self.assertRaises(TypeError, context.add_extra_chain_cert, object(), object())
+
+
+    def test_add_extra_chain_cert(self):
+        """
+        L{Context.add_extra_chain_cert} accepts an L{X509} instance to add to
+        the certificate chain.
+        """
+        context = Context(TLSv1_METHOD)
+        context.add_extra_chain_cert(load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
+        # XXX Oh no, actually asserting something about its behavior would be really hard.
+        # See #477521.
+
 
 
 class ConnectionTests(TestCase):
@@ -511,8 +534,8 @@
             established = True  # assume the best
             for ssl in client_conn, server_conn:
                 try:
-                    # Generally a recv() or send() could also work instead 
-                    # of do_handshake(), and we would stop on the first 
+                    # Generally a recv() or send() could also work instead
+                    # of do_handshake(), and we would stop on the first
                     # non-exception.
                     ssl.do_handshake()
                 except WantReadError:
@@ -583,6 +606,218 @@
         self.assertEquals(e.__class__, Error)
 
 
+    def _check_client_ca_list(self, func):
+        """
+        Verify the return value of the C{get_client_ca_list} method for server and client connections.
+
+        @param func: A function which will be called with the server context
+            before the client and server are connected to each other.  This
+            function should specify a list of CAs for the server to send to the
+            client and return that same list.  The list will be used to verify
+            that C{get_client_ca_list} returns the proper value at various
+            times.
+        """
+        server = self._server(None)
+        client = self._client(None)
+        self.assertEqual(client.get_client_ca_list(), [])
+        self.assertEqual(server.get_client_ca_list(), [])
+        ctx = server.get_context()
+        expected = func(ctx)
+        self.assertEqual(client.get_client_ca_list(), [])
+        self.assertEqual(server.get_client_ca_list(), expected)
+        self._loopback(client, server)
+        self.assertEqual(client.get_client_ca_list(), expected)
+        self.assertEqual(server.get_client_ca_list(), expected)
+
+
+    def test_set_client_ca_list_errors(self):
+        """
+        L{Context.set_client_ca_list} raises a L{TypeError} if called with a
+        non-list or a list that contains objects other than X509Names.
+        """
+        ctx = Context(TLSv1_METHOD)
+        self.assertRaises(TypeError, ctx.set_client_ca_list, "spam")
+        self.assertRaises(TypeError, ctx.set_client_ca_list, ["spam"])
+        self.assertIdentical(ctx.set_client_ca_list([]), None)
+
+
+    def test_set_empty_ca_list(self):
+        """
+        If passed an empty list, L{Context.set_client_ca_list} configures the
+        context to send no CA names to the client and, on both the server and
+        client sides, L{Connection.get_client_ca_list} returns an empty list
+        after the connection is set up.
+        """
+        def no_ca(ctx):
+            ctx.set_client_ca_list([])
+            return []
+        self._check_client_ca_list(no_ca)
+
+
+    def test_set_one_ca_list(self):
+        """
+        If passed a list containing a single X509Name,
+        L{Context.set_client_ca_list} configures the context to send that CA
+        name to the client and, on both the server and client sides,
+        L{Connection.get_client_ca_list} returns a list containing that
+        X509Name after the connection is set up.
+        """
+        cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+        cadesc = cacert.get_subject()
+        def single_ca(ctx):
+            ctx.set_client_ca_list([cadesc])
+            return [cadesc]
+        self._check_client_ca_list(single_ca)
+
+
+    def test_set_multiple_ca_list(self):
+        """
+        If passed a list containing multiple X509Name objects,
+        L{Context.set_client_ca_list} configures the context to send those CA
+        names to the client and, on both the server and client sides,
+        L{Connection.get_client_ca_list} returns a list containing those
+        X509Names after the connection is set up.
+        """
+        secert = load_certificate(FILETYPE_PEM, server_cert_pem)
+        clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+        sedesc = secert.get_subject()
+        cldesc = clcert.get_subject()
+
+        def multiple_ca(ctx):
+            L = [sedesc, cldesc]
+            ctx.set_client_ca_list(L)
+            return L
+        self._check_client_ca_list(multiple_ca)
+
+
+    def test_reset_ca_list(self):
+        """
+        If called multiple times, only the X509Names passed to the final call
+        of L{Context.set_client_ca_list} are used to configure the CA names
+        sent to the client.
+        """
+        cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+        secert = load_certificate(FILETYPE_PEM, server_cert_pem)
+        clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+        cadesc = cacert.get_subject()
+        sedesc = secert.get_subject()
+        cldesc = clcert.get_subject()
+
+        def changed_ca(ctx):
+            ctx.set_client_ca_list([sedesc, cldesc])
+            ctx.set_client_ca_list([cadesc])
+            return [cadesc]
+        self._check_client_ca_list(changed_ca)
+
+
+    def test_mutated_ca_list(self):
+        """
+        If the list passed to L{Context.set_client_ca_list} is mutated
+        afterwards, this does not affect the list of CA names sent to the
+        client.
+        """
+        cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+        secert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+        cadesc = cacert.get_subject()
+        sedesc = secert.get_subject()
+
+        def mutated_ca(ctx):
+            L = [cadesc]
+            ctx.set_client_ca_list([cadesc])
+            L.append(sedesc)
+            return [cadesc]
+        self._check_client_ca_list(mutated_ca)
+
+
+    def test_add_client_ca_errors(self):
+        """
+        L{Context.add_client_ca} raises L{TypeError} if called with a non-X509
+        object or with a number of arguments other than one.
+        """
+        ctx = Context(TLSv1_METHOD)
+        cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+        self.assertRaises(TypeError, ctx.add_client_ca)
+        self.assertRaises(TypeError, ctx.add_client_ca, "spam")
+        self.assertRaises(TypeError, ctx.add_client_ca, cacert, cacert)
+
+
+    def test_one_add_client_ca(self):
+        """
+        A certificate's subject can be added as a CA to be sent to the client
+        with L{Context.add_client_ca}.
+        """
+        cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+        cadesc = cacert.get_subject()
+        def single_ca(ctx):
+            ctx.add_client_ca(cacert)
+            return [cadesc]
+        self._check_client_ca_list(single_ca)
+
+
+    def test_multiple_add_client_ca(self):
+        """
+        Multiple CA names can be sent to the client by calling
+        L{Context.add_client_ca} with multiple X509 objects.
+        """
+        cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+        secert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+        cadesc = cacert.get_subject()
+        sedesc = secert.get_subject()
+
+        def multiple_ca(ctx):
+            ctx.add_client_ca(cacert)
+            ctx.add_client_ca(secert)
+            return [cadesc, sedesc]
+        self._check_client_ca_list(multiple_ca)
+
+
+    def test_set_and_add_client_ca(self):
+        """
+        A call to L{Context.set_client_ca_list} followed by a call to
+        L{Context.add_client_ca} results in using the CA names from the first
+        call and the CA name from the second call.
+        """
+        cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+        secert = load_certificate(FILETYPE_PEM, server_cert_pem)
+        clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+        cadesc = cacert.get_subject()
+        sedesc = secert.get_subject()
+        cldesc = clcert.get_subject()
+
+        def mixed_set_add_ca(ctx):
+            ctx.set_client_ca_list([cadesc, sedesc])
+            ctx.add_client_ca(clcert)
+            return [cadesc, sedesc, cldesc]
+        self._check_client_ca_list(mixed_set_add_ca)
+
+
+    def test_set_after_add_client_ca(self):
+        """
+        A call to L{Context.set_client_ca_list} after a call to
+        L{Context.add_client_ca} replaces the CA name specified by the former
+        call with the names specified by the latter cal.
+        """
+        cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+        secert = load_certificate(FILETYPE_PEM, server_cert_pem)
+        clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+        cadesc = cacert.get_subject()
+        sedesc = secert.get_subject()
+        cldesc = clcert.get_subject()
+
+        def set_replaces_add_ca(ctx):
+            ctx.add_client_ca(clcert)
+            ctx.set_client_ca_list([cadesc])
+            ctx.add_client_ca(secert)
+            return [cadesc, sedesc]
+        self._check_client_ca_list(set_replaces_add_ca)
+
+
 
 if __name__ == '__main__':
     main()