initial source import
diff --git a/src/RATIONALE b/src/RATIONALE
new file mode 100644
index 0000000..a0e389c
--- /dev/null
+++ b/src/RATIONALE
@@ -0,0 +1,61 @@
+  RATIONALE
+
+The reason this module exists at all is that the SSL support in the socket
+module in the Python 2.1 distribution (which is what we used, of course I
+cannot speak for later versions) is severely limited.
+
+<FIXME> Update this list whenever needed! The communications module isn't
+written yet, so we don't know exactly how this'll work! </FIXME>
+This is a list of things we need from an OpenSSL module:
+ + Context objects (in OpenSSL called SSL_CTX) that can be manipulated from
+   Python modules.  They must support a number of operations:
+     - Loading certificates from file and memory, both the client
+       certificate and the certificates used for the verification chain.
+     - Loading private keys from file and memory.
+     - Setting the verification mode (basically VERIFY_NONE and
+       VERIFY_PEER).
+     - Callbacks mechanism for prompting for pass phrases and verifying
+       certificates.  The callbacks have to work under a multi-threaded
+       environment (see the comment in ssl/context.c).  Of course the
+       callbacks will have to be written in Python!
+ + The Connection objects (in OpenSSL called SSL) have to support a few
+   things:
+     - Renegotiation, this is really important, especially for connections
+       that are up and running for a long time, since renegotiation
+       generates new encryption keys.
+     - Server-side SSL must work!  As far as I know this doesn't work in
+       the SSL support of the socket module as of Python 2.1.
+     - Wrapping the methods of the underlying transport object is nice, so
+       you don't have to keep track of more than one object per connection.
+       This could of course be done a lot better than the way it works now,
+       so more transport layers than sockets are possible!
+ + A well-organized error system that mimics OpenSSL's error system is
+   desireable.  Specifically there has to be a way to find out wether the
+   operation was successful, or if it failed, why it failed, so some sort
+   of interface to OpenSSL's error queue mechanism is needed.
+ + Certificate objects (X509) and certificate name objects (X509_NAME) are
+   needed, especially for verification purposes.  Certificates will
+   probably also be generated by the server which is another reason for
+   them to exist. The same thing goes for key objects (EVP_PKEY)
+ + Since this is an OpenSSL module, there has to be an interface to the
+   OpenSSL PRNG, so it can be seeded in a good way.
+
+When asking about SSL on the comp.lang.python newsgroup (or on
+python-list@python.org) people usually pointed you to the M2Crypto package.
+The M2Crypto.SSL module does implement a lot of OpenSSL's functionality but
+unfortunately its error handling system does not seem to be finished,
+especially for non-blocking I/O.  I think that much of the reason for this
+is that M2Crypto is developed using SWIG.  This makes it awkward to create
+functions that e.g. can return both an integer and NULL since (as far as I
+know) you basically write C functions and SWIG makes wrapper functions that
+parses the Python argument list and calls your C function, and finally
+transforms your return value to a Python object.
+
+Finally, a good book on the topic of SSL (that I read and learned a lot
+from) is "SSL and TLS - Designing and Building Secure Systems" (ISBN
+0201615983) by Eric Rescorla. A good mailinglist to subscribe to is the
+openssl-users@openssl.org list.
+
+This comment was written July 2001, discussing Python 2.1.  Feel free to
+modify it as the SSL support in the socket module changes.
+
diff --git a/src/crypto/crypto.c b/src/crypto/crypto.c
new file mode 100644
index 0000000..4fed5ba
--- /dev/null
+++ b/src/crypto/crypto.c
@@ -0,0 +1,738 @@
+/*
+ * crypto.c
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Main file of crypto sub module.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ */
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+static char crypto_doc[] = "\n\
+Main file of crypto sub module.\n\
+See the file RATIONALE for a short explanation of why this module was written.\n\
+";
+
+static char *CVSid = "@(#) $Id: crypto.c,v 1.28 2004/08/09 14:56:05 martin Exp $";
+
+void **ssl_API;
+
+PyObject *crypto_Error;
+
+static int
+global_passphrase_callback(char *buf, int len, int rwflag, void *cb_arg)
+{
+    PyObject *func, *argv, *ret;
+    int nchars;
+
+    func = (PyObject *)cb_arg;
+    argv = Py_BuildValue("(i)", rwflag);
+    ret = PyEval_CallObject(func, argv);
+    Py_DECREF(argv);
+    if (ret == NULL)
+        return 0;
+    if (!PyString_Check(ret))
+    {
+        PyErr_SetString(PyExc_ValueError, "String expected");
+        return 0;
+    }
+    nchars = PyString_Size(ret);
+    if (nchars > len)
+        nchars = len;
+    strncpy(buf, PyString_AsString(ret), nchars);
+    return nchars;
+}
+
+static char crypto_load_privatekey_doc[] = "\n\
+Load a private key from a buffer\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             type       - The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\
+             buffer     - The buffer the key is stored in\n\
+             passphrase - (optional) if encrypted PEM format, this can be\n\
+                          either the passphrase to use, or a callback for\n\
+                          providing the passphrase.\n\
+Returns:   The PKey object\n\
+";
+
+static PyObject *
+crypto_load_privatekey(PyObject *spam, PyObject *args)
+{
+    crypto_PKeyObj *crypto_PKey_New(EVP_PKEY *, int);
+    int type, len;
+    char *buffer;
+    PyObject *pw = NULL;
+    pem_password_cb *cb = NULL;
+    void *cb_arg = NULL;
+    BIO *bio;
+    EVP_PKEY *pkey;
+
+    if (!PyArg_ParseTuple(args, "is#|O:load_privatekey", &type, &buffer, &len, &pw))
+        return NULL;
+
+    if (pw != NULL)
+    {
+        if (PyString_Check(pw))
+        {
+            cb = NULL;
+            cb_arg = PyString_AsString(pw);
+        }
+        else if (PyCallable_Check(pw))
+        {
+            cb = global_passphrase_callback;
+            cb_arg = pw;
+        }
+        else
+        {
+            PyErr_SetString(PyExc_TypeError, "Last argument must be string or callable");
+            return NULL;
+        }
+    }
+
+    bio = BIO_new_mem_buf(buffer, len);
+    switch (type)
+    {
+        case X509_FILETYPE_PEM:
+            pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, cb_arg);
+            break;
+
+        case X509_FILETYPE_ASN1:
+            pkey = d2i_PrivateKey_bio(bio, NULL);
+            break;
+
+        default:
+            PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+            BIO_free(bio);
+            return NULL;
+    }
+    BIO_free(bio);
+
+    if (pkey == NULL)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    return (PyObject *)crypto_PKey_New(pkey, 1);
+}
+
+static char crypto_dump_privatekey_doc[] = "\n\
+Dump a private key to a buffer\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             type       - The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\
+             pkey       - The PKey to dump\n\
+             cipher     - (optional) if encrypted PEM format, the cipher to\n\
+                          use\n\
+             passphrase - (optional) if encrypted PEM format, this can be either\n\
+                          the passphrase to use, or a callback for providing the\n\
+                          passphrase.\n\
+Returns:   The buffer with the dumped key in\n\
+";
+
+static PyObject *
+crypto_dump_privatekey(PyObject *spam, PyObject *args)
+{
+    int type, ret, buf_len;
+    char *temp;
+    PyObject *buffer;
+    char *cipher_name = NULL;
+    const EVP_CIPHER *cipher = NULL;
+    PyObject *pw = NULL;
+    pem_password_cb *cb = NULL;
+    void *cb_arg = NULL;
+    BIO *bio;
+    crypto_PKeyObj *pkey;
+
+    if (!PyArg_ParseTuple(args, "iO!|sO:dump_privatekey", &type,
+			  &crypto_PKey_Type, &pkey, &cipher_name, &pw))
+        return NULL;
+
+    if (cipher_name != NULL && pw == NULL)
+    {
+        PyErr_SetString(PyExc_ValueError, "Illegal number of arguments");
+        return NULL;
+    }
+    if (cipher_name != NULL)
+    {
+        cipher = EVP_get_cipherbyname(cipher_name);
+        if (cipher == NULL)
+        {
+            PyErr_SetString(PyExc_ValueError, "Invalid cipher name");
+            return NULL;
+        }
+        if (PyString_Check(pw))
+        {
+            cb = NULL;
+            cb_arg = PyString_AsString(pw);
+        }
+        else if (PyCallable_Check(pw))
+        {
+            cb = global_passphrase_callback;
+            cb_arg = pw;
+        }
+        else
+        {
+            PyErr_SetString(PyExc_TypeError, "Last argument must be string or callable");
+            return NULL;
+        }
+    }
+
+    bio = BIO_new(BIO_s_mem());
+    switch (type)
+    {
+        case X509_FILETYPE_PEM:
+            ret = PEM_write_bio_PrivateKey(bio, pkey->pkey, cipher, NULL, 0, cb, cb_arg);
+            if (PyErr_Occurred())
+            {
+                BIO_free(bio);
+                return NULL;
+            }
+            break;
+
+        case X509_FILETYPE_ASN1:
+            ret = i2d_PrivateKey_bio(bio, pkey->pkey);
+            break;
+
+        default:
+            PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+            BIO_free(bio);
+            return NULL;
+    }
+
+    if (ret == 0)
+    {
+        BIO_free(bio);
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    buf_len = BIO_get_mem_data(bio, &temp);
+    buffer = PyString_FromStringAndSize(temp, buf_len);
+    BIO_free(bio);
+
+    return buffer;
+}
+
+static char crypto_load_certificate_doc[] = "\n\
+Load a certificate from a buffer\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             type   - The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\
+             buffer - The buffer the certificate is stored in\n\
+Returns:   The X509 object\n\
+";
+
+static PyObject *
+crypto_load_certificate(PyObject *spam, PyObject *args)
+{
+    crypto_X509Obj *crypto_X509_New(X509 *, int);
+    int type, len;
+    char *buffer;
+    BIO *bio;
+    X509 *cert;
+
+    if (!PyArg_ParseTuple(args, "is#:load_certificate", &type, &buffer, &len))
+        return NULL;
+
+    bio = BIO_new_mem_buf(buffer, len);
+    switch (type)
+    {
+        case X509_FILETYPE_PEM:
+            cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+            break;
+
+        case X509_FILETYPE_ASN1:
+            cert = d2i_X509_bio(bio, NULL);
+            break;
+
+        default:
+            PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+            BIO_free(bio);
+            return NULL;
+    }
+    BIO_free(bio);
+
+    if (cert == NULL)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    return (PyObject *)crypto_X509_New(cert, 1);
+}
+
+static char crypto_dump_certificate_doc[] = "\n\
+Dump a certificate to a buffer\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             type - The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\
+             cert - The certificate to dump\n\
+Returns:   The buffer with the dumped certificate in\n\
+";
+
+static PyObject *
+crypto_dump_certificate(PyObject *spam, PyObject *args)
+{
+    int type, ret, buf_len;
+    char *temp;
+    PyObject *buffer;
+    BIO *bio;
+    crypto_X509Obj *cert;
+
+    if (!PyArg_ParseTuple(args, "iO!:dump_certificate", &type,
+			  &crypto_X509_Type, &cert))
+        return NULL;
+
+    bio = BIO_new(BIO_s_mem());
+    switch (type)
+    {
+        case X509_FILETYPE_PEM:
+            ret = PEM_write_bio_X509(bio, cert->x509);
+            break;
+
+        case X509_FILETYPE_ASN1:
+            ret = i2d_X509_bio(bio, cert->x509);
+            break;
+
+        default:
+            PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+            BIO_free(bio);
+            return NULL;
+    }
+
+    if (ret == 0)
+    {
+        BIO_free(bio);
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    buf_len = BIO_get_mem_data(bio, &temp);
+    buffer = PyString_FromStringAndSize(temp, buf_len);
+    BIO_free(bio);
+
+    return buffer;
+}
+
+static char crypto_load_certificate_request_doc[] = "\n\
+Load a certificate request from a buffer\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             type   - The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\
+             buffer - The buffer the certificate request is stored in\n\
+Returns:   The X509Req object\n\
+";
+
+static PyObject *
+crypto_load_certificate_request(PyObject *spam, PyObject *args)
+{
+    crypto_X509ReqObj *crypto_X509Req_New(X509_REQ *, int);
+    int type, len;
+    char *buffer;
+    BIO *bio;
+    X509_REQ *req;
+
+    if (!PyArg_ParseTuple(args, "is#:load_certificate_request", &type, &buffer, &len))
+        return NULL;
+
+    bio = BIO_new_mem_buf(buffer, len);
+    switch (type)
+    {
+        case X509_FILETYPE_PEM:
+            req = PEM_read_bio_X509_REQ(bio, NULL, NULL, NULL);
+            break;
+
+        case X509_FILETYPE_ASN1:
+            req = d2i_X509_REQ_bio(bio, NULL);
+            break;
+
+        default:
+            PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+            BIO_free(bio);
+            return NULL;
+    }
+    BIO_free(bio);
+
+    if (req == NULL)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    return (PyObject *)crypto_X509Req_New(req, 1);
+}
+
+static char crypto_dump_certificate_request_doc[] = "\n\
+Dump a certificate request to a buffer\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             type - The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\
+             req  - The certificate request to dump\n\
+Returns:   The buffer with the dumped certificate request in\n\
+";
+
+static PyObject *
+crypto_dump_certificate_request(PyObject *spam, PyObject *args)
+{
+    int type, ret, buf_len;
+    char *temp;
+    PyObject *buffer;
+    BIO *bio;
+    crypto_X509ReqObj *req;
+
+    if (!PyArg_ParseTuple(args, "iO!:dump_certificate_request", &type,
+			  &crypto_X509Req_Type, &req))
+        return NULL;
+
+    bio = BIO_new(BIO_s_mem());
+    switch (type)
+    {
+        case X509_FILETYPE_PEM:
+            ret = PEM_write_bio_X509_REQ(bio, req->x509_req);
+            break;
+
+        case X509_FILETYPE_ASN1:
+            ret = i2d_X509_REQ_bio(bio, req->x509_req);
+            break;
+
+        default:
+            PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+            BIO_free(bio);
+            return NULL;
+    }
+
+    if (ret == 0)
+    {
+        BIO_free(bio);
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    buf_len = BIO_get_mem_data(bio, &temp);
+    buffer = PyString_FromStringAndSize(temp, buf_len);
+    BIO_free(bio);
+
+    return buffer;
+}
+
+static char crypto_load_pkcs7_data_doc[] = "\n\
+Load pkcs7 data from a buffer\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The argument tuple, should be:\n\
+             type - The file type (one of FILETYPE_PEM or FILETYPE_ASN1)\n\
+             buffer - The buffer with the pkcs7 data.\n\
+Returns: The PKCS7 object\n\
+";
+
+static PyObject *
+crypto_load_pkcs7_data(PyObject *spam, PyObject *args)
+{
+    int type, len;
+    char *buffer;
+    BIO *bio;
+    PKCS7 *pkcs7 = NULL;
+
+    if (!PyArg_ParseTuple(args, "is#:load_pkcs7_data", &type, &buffer, &len))
+        return NULL;
+
+    /* 
+     * Try to read the pkcs7 data from the bio 
+     */
+    bio = BIO_new_mem_buf(buffer, len);
+    switch (type)
+    {
+        case X509_FILETYPE_PEM:
+            pkcs7 = PEM_read_bio_PKCS7(bio, NULL, NULL, NULL);
+            break;
+
+        case X509_FILETYPE_ASN1:
+            pkcs7 = d2i_PKCS7_bio(bio, NULL);
+            break;
+
+        default:
+            PyErr_SetString(PyExc_ValueError,
+                    "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+            return NULL;
+    }
+    BIO_free(bio);
+
+    /*
+     * Check if we got a PKCS7 structure
+     */
+    if (pkcs7 == NULL)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    return (PyObject *)crypto_PKCS7_New(pkcs7, 1);
+}
+
+static char crypto_load_pkcs12_doc[] = "\n\
+Load a PKCS12 object from a buffer\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             buffer - The buffer the certificate is stored in\n\
+             passphrase (Optional) - The password to decrypt the PKCS12 lump\n\
+Returns:   The PKCS12 object\n\
+";
+
+static PyObject *
+crypto_load_pkcs12(PyObject *spam, PyObject *args)
+{
+    crypto_PKCS12Obj *crypto_PKCS12_New(PKCS12 *, char *);
+    int len;
+    char *buffer, *passphrase = NULL;
+    BIO *bio;
+    PKCS12 *p12;
+
+    if (!PyArg_ParseTuple(args, "s#|s:load_pkcs12", &buffer, &len, &passphrase))
+        return NULL;
+
+    bio = BIO_new_mem_buf(buffer, len);
+    if ((p12 = d2i_PKCS12_bio(bio, NULL)) == NULL)
+    {
+      BIO_free(bio);
+      exception_from_error_queue();
+      return NULL;
+    }
+    BIO_free(bio);
+
+    return (PyObject *)crypto_PKCS12_New(p12, passphrase);
+}
+
+
+static char crypto_X509_doc[] = "\n\
+The factory function inserted in the module dictionary to create X509\n\
+objects\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The X509 object\n\
+";
+
+static PyObject *
+crypto_X509(PyObject *spam, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":X509"))
+        return NULL;
+
+    return (PyObject *)crypto_X509_New(X509_new(), 1);
+}
+
+static char crypto_X509Name_doc[] = "\n\
+The factory function inserted in the module dictionary as a copy\n\
+constructor for X509Name objects.\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             name - An X509Name object to copy\n\
+Returns:   The X509Name object\n\
+";
+
+static PyObject *
+crypto_X509Name(PyObject *spam, PyObject *args)
+{
+    crypto_X509NameObj *name;
+
+    if (!PyArg_ParseTuple(args, "O!:X509Name", &crypto_X509Name_Type, &name))
+        return NULL;
+
+    return (PyObject *)crypto_X509Name_New(X509_NAME_dup(name->x509_name), 1);
+}
+
+static char crypto_X509Req_doc[] = "\n\
+The factory function inserted in the module dictionary to create X509Req\n\
+objects\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The X509Req object\n\
+";
+
+static PyObject *
+crypto_X509Req(PyObject *spam, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":X509Req"))
+        return NULL;
+
+    return (PyObject *)crypto_X509Req_New(X509_REQ_new(), 1);
+}
+
+static char crypto_PKey_doc[] = "\n\
+The factory function inserted in the module dictionary to create PKey\n\
+objects\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The PKey object\n\
+";
+
+static PyObject *
+crypto_PKey(PyObject *spam, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":PKey"))
+        return NULL;
+
+    return (PyObject *)crypto_PKey_New(EVP_PKEY_new(), 1);
+}
+
+static char crypto_X509Extension_doc[] = "\n\
+The factory function inserted in the module dictionary to create\n\
+X509Extension objects.\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be\n\
+             typename - ???\n\
+             critical - ???\n\
+             value    - ???\n\
+Returns:   The X509Extension object\n\
+";
+
+static PyObject *
+crypto_X509Extension(PyObject *spam, PyObject *args)
+{
+    char *type_name, *value;
+    int critical;
+
+    if (!PyArg_ParseTuple(args, "sis:X509Extension", &type_name, &critical,
+                &value))
+        return NULL;
+
+    return (PyObject *)crypto_X509Extension_New(type_name, critical, value);
+}
+
+static char crypto_NetscapeSPKI_doc[] = "\n\
+The factory function inserted in the module dictionary to create NetscapeSPKI\n\
+objects\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be empty or, optionally\n\
+             enc - Base64 encoded NetscapeSPKI object.\n\
+Returns:   The NetscapeSPKI object\n\
+";
+
+static PyObject *
+crypto_NetscapeSPKI(PyObject *spam, PyObject *args)
+{
+    char *enc = NULL;
+    int enc_len = -1;
+    NETSCAPE_SPKI *spki;
+
+    if (!PyArg_ParseTuple(args, "|s#:NetscapeSPKI", &enc, &enc_len))
+        return NULL;
+
+    if (enc_len >= 0)
+        spki = NETSCAPE_SPKI_b64_decode(enc, enc_len);
+    else
+        spki = NETSCAPE_SPKI_new();
+    if (spki == NULL)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+    return (PyObject *)crypto_NetscapeSPKI_New(spki, 1);
+}
+
+/* Methods in the OpenSSL.crypto module (i.e. none) */
+static PyMethodDef crypto_methods[] = {
+    /* Module functions */
+    { "load_privatekey",  (PyCFunction)crypto_load_privatekey,  METH_VARARGS, crypto_load_privatekey_doc },
+    { "dump_privatekey",  (PyCFunction)crypto_dump_privatekey,  METH_VARARGS, crypto_dump_privatekey_doc },
+    { "load_certificate", (PyCFunction)crypto_load_certificate, METH_VARARGS, crypto_load_certificate_doc },
+    { "dump_certificate", (PyCFunction)crypto_dump_certificate, METH_VARARGS, crypto_dump_certificate_doc },
+    { "load_certificate_request", (PyCFunction)crypto_load_certificate_request, METH_VARARGS, crypto_load_certificate_request_doc },
+    { "dump_certificate_request", (PyCFunction)crypto_dump_certificate_request, METH_VARARGS, crypto_dump_certificate_request_doc },
+    { "load_pkcs7_data", (PyCFunction)crypto_load_pkcs7_data, METH_VARARGS, crypto_load_pkcs7_data_doc },
+    { "load_pkcs12", (PyCFunction)crypto_load_pkcs12, METH_VARARGS, crypto_load_pkcs12_doc },
+    /* Factory functions */
+    { "X509",    (PyCFunction)crypto_X509,    METH_VARARGS, crypto_X509_doc },
+    { "X509Name",(PyCFunction)crypto_X509Name,METH_VARARGS, crypto_X509Name_doc },
+    { "X509Req", (PyCFunction)crypto_X509Req, METH_VARARGS, crypto_X509Req_doc },
+    { "PKey",    (PyCFunction)crypto_PKey,    METH_VARARGS, crypto_PKey_doc },
+    { "X509Extension", (PyCFunction)crypto_X509Extension, METH_VARARGS, crypto_X509Extension_doc },
+    { "NetscapeSPKI", (PyCFunction)crypto_NetscapeSPKI, METH_VARARGS, crypto_NetscapeSPKI_doc },
+    { NULL, NULL }
+};
+
+/*
+ * Initialize crypto sub module
+ *
+ * Arguments: None
+ * Returns:   None
+ */
+void
+initcrypto(void)
+{
+    static void *crypto_API[crypto_API_pointers];
+    PyObject *c_api_object;
+    PyObject *module, *dict;
+
+    ERR_load_crypto_strings();
+    OpenSSL_add_all_algorithms();
+
+    if ((module = Py_InitModule3("crypto", crypto_methods, crypto_doc)) == NULL)
+        return;
+
+    /* Initialize the C API pointer array */
+    crypto_API[crypto_X509_New_NUM]      = (void *)crypto_X509_New;
+    crypto_API[crypto_X509Name_New_NUM]  = (void *)crypto_X509Name_New;
+    crypto_API[crypto_X509Req_New_NUM]   = (void *)crypto_X509Req_New;
+    crypto_API[crypto_X509Store_New_NUM] = (void *)crypto_X509Store_New;
+    crypto_API[crypto_PKey_New_NUM]      = (void *)crypto_PKey_New;
+    crypto_API[crypto_X509Extension_New_NUM] = (void *)crypto_X509Extension_New;
+    crypto_API[crypto_PKCS7_New_NUM]     = (void *)crypto_PKCS7_New;
+    crypto_API[crypto_NetscapeSPKI_New_NUM]     = (void *)crypto_NetscapeSPKI_New;
+    c_api_object = PyCObject_FromVoidPtr((void *)crypto_API, NULL);
+    if (c_api_object != NULL)
+        PyModule_AddObject(module, "_C_API", c_api_object);
+
+    crypto_Error = PyErr_NewException("OpenSSL.crypto.Error", NULL, NULL);
+    if (crypto_Error == NULL)
+        goto error;
+    if (PyModule_AddObject(module, "Error", crypto_Error) != 0)
+        goto error;
+
+    PyModule_AddIntConstant(module, "FILETYPE_PEM",  X509_FILETYPE_PEM);
+    PyModule_AddIntConstant(module, "FILETYPE_ASN1", X509_FILETYPE_ASN1);
+
+    PyModule_AddIntConstant(module, "TYPE_RSA", crypto_TYPE_RSA);
+    PyModule_AddIntConstant(module, "TYPE_DSA", crypto_TYPE_DSA);
+
+    dict = PyModule_GetDict(module);
+    if (!init_crypto_x509(dict))
+        goto error;
+    if (!init_crypto_x509name(dict))
+        goto error;
+    if (!init_crypto_x509store(dict))
+        goto error;
+    if (!init_crypto_x509req(dict))
+        goto error;
+    if (!init_crypto_pkey(dict))
+        goto error;
+    if (!init_crypto_x509extension(dict))
+        goto error;
+    if (!init_crypto_pkcs7(dict))
+        goto error;
+    if (!init_crypto_pkcs12(dict))
+        goto error;
+    if (!init_crypto_netscape_spki(dict))
+        goto error;
+
+error:
+    ;
+}
+
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
new file mode 100644
index 0000000..0a71d2a
--- /dev/null
+++ b/src/crypto/crypto.h
@@ -0,0 +1,120 @@
+/*
+ * crypto.h
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Exports from crypto.c.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ *
+ * @(#) $Id: crypto.h,v 1.14 2004/08/09 13:41:25 martin Exp $
+ */
+#ifndef PyOpenSSL_CRYPTO_H_
+#define PyOpenSSL_CRYPTO_H_
+
+#include <Python.h>
+#include "x509.h"
+#include "x509name.h"
+#include "netscape_spki.h"
+#include "x509store.h"
+#include "x509req.h"
+#include "pkey.h"
+#include "x509ext.h"
+#include "pkcs7.h"
+#include "pkcs12.h"
+#include "../util.h"
+
+extern PyObject *crypto_Error;
+
+#ifdef exception_from_error_queue
+#  undef exception_from_error_queue
+#endif
+#define exception_from_error_queue()    do { \
+    PyObject *errlist = error_queue_to_list(); \
+    PyErr_SetObject(crypto_Error, errlist); \
+    Py_DECREF(errlist); \
+} while (0)
+
+#define crypto_X509_New_NUM             0
+#define crypto_X509_New_RETURN          crypto_X509Obj *
+#define crypto_X509_New_PROTO           (X509 *, int)
+
+#define crypto_X509Req_New_NUM          1
+#define crypto_X509Req_New_RETURN       crypto_X509ReqObj *
+#define crypto_X509Req_New_PROTO        (X509_REQ *, int)
+
+#define crypto_X509Store_New_NUM        2
+#define crypto_X509Store_New_RETURN     crypto_X509StoreObj *
+#define crypto_X509Store_New_PROTO      (X509_STORE *, int)
+
+#define crypto_PKey_New_NUM             3
+#define crypto_PKey_New_RETURN          crypto_PKeyObj *
+#define crypto_PKey_New_PROTO           (EVP_PKEY *, int)
+
+#define crypto_X509Name_New_NUM         4
+#define crypto_X509Name_New_RETURN      crypto_X509NameObj *
+#define crypto_X509Name_New_PROTO       (X509_NAME *, int)
+
+#define crypto_X509Extension_New_NUM    5
+#define crypto_X509Extension_New_RETURN crypto_X509ExtensionObj *
+#define crypto_X509Extension_New_PROTO  (char *, int, char *)
+
+#define crypto_PKCS7_New_NUM            6
+#define crypto_PKCS7_New_RETURN         crypto_PKCS7Obj *
+#define crypto_PKCS7_New_PROTO          (PKCS7 *, int)
+
+#define crypto_NetscapeSPKI_New_NUM         7
+#define crypto_NetscapeSPKI_New_RETURN      crypto_NetscapeSPKIObj *
+#define crypto_NetscapeSPKI_New_PROTO       (NETSCAPE_SPKI *, int)
+
+#define crypto_API_pointers             8
+
+#ifdef crypto_MODULE
+
+extern crypto_X509_New_RETURN      crypto_X509_New      crypto_X509_New_PROTO;
+extern crypto_X509Name_New_RETURN  crypto_X509Name_New  crypto_X509Name_New_PROTO;
+extern crypto_X509Req_New_RETURN   crypto_X509Req_New   crypto_X509Req_New_PROTO;
+extern crypto_X509Store_New_RETURN crypto_X509Store_New crypto_X509Store_New_PROTO;
+extern crypto_PKey_New_RETURN      crypto_PKey_New      crypto_PKey_New_PROTO;
+extern crypto_X509Extension_New_RETURN crypto_X509Extension_New crypto_X509Extension_New_PROTO;
+extern crypto_PKCS7_New_RETURN     crypto_PKCS7_New     crypto_PKCS7_New_PROTO;
+extern crypto_NetscapeSPKI_New_RETURN  crypto_NetscapeSPKI_New  crypto_NetscapeSPKI_New_PROTO;
+
+#else /* crypto_MODULE */
+
+extern void **crypto_API;
+
+#define crypto_X509_New         \
+ (*(crypto_X509_New_RETURN (*)crypto_X509_New_PROTO) crypto_API[crypto_X509_New_NUM])
+#define crypto_X509Name_New     \
+ (*(crypto_X509Name_New_RETURN (*)crypto_X509Name_New_PROTO) crypto_API[crypto_X509Name_New_NUM])
+#define crypto_X509Req_New      \
+ (*(crypto_X509Req_New_RETURN (*)crypto_X509Req_New_PROTO) crypto_API[crypto_X509Req_New_NUM])
+#define crypto_X509Store_New    \
+ (*(crypto_X509Store_New_RETURN (*)crypto_X509Store_New_PROTO) crypto_API[crypto_X509Store_New_NUM])
+#define crypto_PKey_New         \
+ (*(crypto_PKey_New_RETURN (*)crypto_PKey_New_PROTO) crypto_API[crypto_PKey_New_NUM])
+#define crypto_X509Extension_New\
+ (*(crypto_X509Extension_New_RETURN (*)crypto_X509Extension_New_PROTO) crypto_API[crypto_X509Extension_New_NUM])
+#define crypto_PKCS7_New        \
+ (*(crypto_PKCS7_New_RETURN (*)crypto_PKCS7_New_PROTO) crypto_API[crypto_PKCS7_New_NUM])
+#define crypto_NetscapeSPKI_New     \
+ (*(crypto_NetscapeSPKI_New_RETURN (*)crypto_NetscapeSPKI_New_PROTO) crypto_API[crypto_NetscapeSPKI_New_NUM])
+
+#define import_crypto() \
+{ \
+  PyObject *crypto_module = PyImport_ImportModule("OpenSSL.crypto"); \
+  if (crypto_module != NULL) { \
+    PyObject *crypto_dict, *crypto_api_object; \
+    crypto_dict = PyModule_GetDict(crypto_module); \
+    crypto_api_object = PyDict_GetItemString(crypto_dict, "_C_API"); \
+    if (PyCObject_Check(crypto_api_object)) { \
+      crypto_API = (void **)PyCObject_AsVoidPtr(crypto_api_object); \
+    } \
+  } \
+}
+
+#endif /* crypto_MODULE */
+
+#endif /* PyOpenSSL_CRYPTO_H_ */
diff --git a/src/crypto/netscape_spki.c b/src/crypto/netscape_spki.c
new file mode 100644
index 0000000..cc54783
--- /dev/null
+++ b/src/crypto/netscape_spki.c
@@ -0,0 +1,257 @@
+/*
+ * netscape_spki.c
+ *
+ * Copyright (C) Tollef Fog Heen 2003
+ *
+ * Netscape SPKI handling, thin wrapper
+ */
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+static char *CVSid = "@(#) $Id: netscape_spki.c,v 1.1 2004/08/09 13:41:25 martin Exp $";
+
+
+/*
+ * Constructor for Nestcape_SPKI, never called by Python code directly
+ *
+ * Arguments: name    - A "real" NetscapeSPKI object
+ *            dealloc - Boolean value to specify whether the destructor should
+ *                      free the "real" NetscapeSPKI object
+ * Returns:   The newly created NetscapeSPKI object
+ */
+crypto_NetscapeSPKIObj *
+crypto_NetscapeSPKI_New(NETSCAPE_SPKI *name, int dealloc)
+{
+    crypto_NetscapeSPKIObj *self;
+
+    self = PyObject_New(crypto_NetscapeSPKIObj, &crypto_NetscapeSPKI_Type);
+
+    if (self == NULL)
+        return NULL;
+
+    self->netscape_spki = name;
+    self->dealloc = dealloc;
+
+    return self;
+}
+
+/*
+ * Deallocate the memory used by the NetscapeSPKI object
+ *
+ * Arguments: self - The NetscapeSPKI object
+ * Returns:   None
+ */
+static void
+crypto_NetscapeSPKI_dealloc(crypto_NetscapeSPKIObj *self)
+{
+    /* Sometimes we don't have to dealloc this */
+    if (self->dealloc)
+        NETSCAPE_SPKI_free(self->netscape_spki);
+
+    PyObject_Del(self);
+}
+
+static char crypto_NetscapeSPKI_sign_doc[] = "\n\
+Sign the certificate request using the supplied key and digest\n\
+\n\
+Arguments: self - The NetscapeSPKI object\n\
+           args - The Python argument tuple, should be:\n\
+             pkey   - The key to sign with\n\
+             digest - The message digest to use\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_NetscapeSPKI_sign(crypto_NetscapeSPKIObj *self, PyObject *args)
+{
+    crypto_PKeyObj *pkey;
+    char *digest_name;
+    const EVP_MD *digest;
+
+    if (!PyArg_ParseTuple(args, "O!s:sign", &crypto_PKey_Type, &pkey,
+			  &digest_name))
+        return NULL;
+
+    if ((digest = EVP_get_digestbyname(digest_name)) == NULL)
+    {
+        PyErr_SetString(PyExc_ValueError, "No such digest method");
+        return NULL;
+    }
+
+    if (!NETSCAPE_SPKI_sign(self->netscape_spki, pkey->pkey, digest))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char crypto_NetscapeSPKI_verify_doc[] = "\n\
+Verifies a certificate request using the supplied public key\n\
+ \n\
+Arguments: self - NetscapeSPKI object\n\
+           args - The Python argument tuple, should be:\n\
+             key - a public key\n\
+Returns:   True, if the signature is correct, 0 otherwise.\n\
+";
+
+PyObject *
+crypto_NetscapeSPKI_verify(crypto_NetscapeSPKIObj *self, PyObject *args)
+{
+    crypto_PKeyObj *pkey;
+    int answer;
+
+    if (!PyArg_ParseTuple(args, "O!:verify", &crypto_PKey_Type, &pkey)) 
+        return NULL;
+
+    if ((answer = NETSCAPE_SPKI_verify(self->netscape_spki, pkey->pkey)) < 0)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    return PyInt_FromLong((long)answer);
+}
+
+static char crypto_NetscapeSPKI_b64_encode_doc[] = "\n\
+Generate a base64 encoded string from an SPKI\n\
+ \n\
+Arguments: self - NetscapeSPKI object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The base64 encoded string\n\
+";
+
+PyObject *
+crypto_NetscapeSPKI_b64_encode(crypto_NetscapeSPKIObj *self, PyObject *args)
+{
+    char *str;
+
+    if (!PyArg_ParseTuple(args, ":b64_encode"))
+        return NULL;
+
+    str = NETSCAPE_SPKI_b64_encode(self->netscape_spki);
+    return PyString_FromString(str);
+}
+
+
+static char crypto_NetscapeSPKI_get_pubkey_doc[] = "\n\
+Get the public key of the certificate\n\
+\n\
+Arguments: self - The NETSCAPE_SPKI object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The public key\n\
+";
+
+static PyObject *
+crypto_NetscapeSPKI_get_pubkey(crypto_NetscapeSPKIObj *self, PyObject *args)
+{
+    crypto_PKeyObj *crypto_PKey_New(EVP_PKEY *, int);
+    EVP_PKEY *pkey;
+
+    if (!PyArg_ParseTuple(args, ":get_pubkey"))
+        return NULL;
+
+    if ((pkey = NETSCAPE_SPKI_get_pubkey(self->netscape_spki)) == NULL)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    return (PyObject *)crypto_PKey_New(pkey, 0);
+}
+
+static char crypto_NetscapeSPKI_set_pubkey_doc[] = "\n\
+Set the public key of the certificate\n\
+\n\
+Arguments: self - The Netscape SPKI object\n\
+           args - The Python argument tuple, should be:\n\
+             pkey - The public key\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_NetscapeSPKI_set_pubkey(crypto_NetscapeSPKIObj *self, PyObject *args)
+{
+    crypto_PKeyObj *pkey;
+
+    if (!PyArg_ParseTuple(args, "O!:set_pubkey", &crypto_PKey_Type, &pkey))
+        return NULL;
+
+    if (!NETSCAPE_SPKI_set_pubkey(self->netscape_spki, pkey->pkey))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+/*
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ *   {  'name', (PyCFunction)crypto_NetscapeSPKI_name, METH_VARARGS }
+ * for convenience
+ */
+#define ADD_METHOD(name)        \
+    { #name, (PyCFunction)crypto_NetscapeSPKI_##name, METH_VARARGS, crypto_NetscapeSPKI_##name##_doc }
+static PyMethodDef crypto_NetscapeSPKI_methods[] =
+{
+    ADD_METHOD(get_pubkey),
+    ADD_METHOD(set_pubkey),
+    ADD_METHOD(b64_encode),
+    ADD_METHOD(sign),
+    ADD_METHOD(verify),
+    { NULL, NULL }
+};
+#undef ADD_METHOD
+
+/*
+ * Find attribute
+ *
+ * Arguments: self - The NetscapeSPKI object
+ *            name - The attribute name
+ * Returns:   A Python object for the attribute, or NULL if something went
+ *            wrong
+ */
+static PyObject *
+crypto_NetscapeSPKI_getattr(crypto_NetscapeSPKIObj *self, char *name)
+{
+    return Py_FindMethod(crypto_NetscapeSPKI_methods, (PyObject *)self, name);
+}
+
+PyTypeObject crypto_NetscapeSPKI_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "NetscapeSPKI",
+    sizeof(crypto_NetscapeSPKIObj),
+    0,
+    (destructor)crypto_NetscapeSPKI_dealloc,
+    NULL, /* print */
+    (getattrfunc)crypto_NetscapeSPKI_getattr,
+    NULL, /* setattr */
+    NULL, /* compare */
+    NULL, /* repr */
+    NULL, /* as_number */
+    NULL, /* as_sequence */
+    NULL, /* as_mapping */
+    NULL  /* hash */
+};
+
+
+/*
+ * Initialize the X509Name part of the crypto module
+ *
+ * Arguments: dict - The crypto module dictionary
+ * Returns:   None
+ */
+int
+init_crypto_netscape_spki(PyObject *dict)
+{
+    crypto_NetscapeSPKI_Type.ob_type = &PyType_Type;
+    Py_INCREF(&crypto_NetscapeSPKI_Type);
+    PyDict_SetItemString(dict, "NetscapeSPKIType", (PyObject *)&crypto_NetscapeSPKI_Type);
+    return 1;
+}
diff --git a/src/crypto/netscape_spki.h b/src/crypto/netscape_spki.h
new file mode 100644
index 0000000..19389d8
--- /dev/null
+++ b/src/crypto/netscape_spki.h
@@ -0,0 +1,29 @@
+/*
+ * netscape_spki.h
+ *
+ * Copyright (C) Tollef Fog Heen 2003, All rights reserved
+ *
+ * Handle Netscape SPKI (challenge response) certificate requests.
+ *
+ *
+ */
+#ifndef PyOpenSSL_crypto_Netscape_SPKI_H_
+#define PyOpenSSL_crypto_Netscape_SPKI_H_
+
+#include <Python.h>
+#include <openssl/ssl.h>
+
+extern  int     init_crypto_netscape_spki       (PyObject *);
+
+extern  PyTypeObject      crypto_NetscapeSPKI_Type;
+
+#define crypto_NetscapeSPKI_Check(v) ((v)->ob_type == &crypto_NetscapeSPKI_Type)
+
+typedef struct {
+    PyObject_HEAD
+    NETSCAPE_SPKI           *netscape_spki;
+    int                  dealloc;
+} crypto_NetscapeSPKIObj;
+
+
+#endif
diff --git a/src/crypto/pkcs12.c b/src/crypto/pkcs12.c
new file mode 100644
index 0000000..ab7562d
--- /dev/null
+++ b/src/crypto/pkcs12.c
@@ -0,0 +1,275 @@
+/*
+ * pkcs12.c
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Certificate transport (PKCS12) handling code, 
+ * mostly thin wrappers around OpenSSL.
+ * See the file RATIONALE for a short explanation of why 
+ * this module was written.
+ *
+ * Reviewed 2001-07-23
+ */
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+static char *CVSid = "@(#) $Id: pkcs12.c,v 1.3 2003/01/09 17:08:32 martin Exp $";
+
+/* 
+ * PKCS12 is a standard exchange format for digital certificates.  
+ * See e.g. the OpenSSL homepage http://www.openssl.org/ for more information
+ */
+
+static void crypto_PKCS12_dealloc(crypto_PKCS12Obj *self);
+
+static char crypto_PKCS12_get_certificate_doc[] = "\n\
+Return certificate portion of the PKCS12 structure\n\
+\n\
+Arguments: self - The PKCS12 object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   X509 object containing the certificate\n\
+";
+static PyObject *
+crypto_PKCS12_get_certificate(crypto_PKCS12Obj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":get_certificate"))
+        return NULL;
+
+    Py_INCREF(self->cert);
+    return self->cert;
+}
+
+static char crypto_PKCS12_get_privatekey_doc[] = "\n\
+Return private key portion of the PKCS12 structure\n\
+\n\
+Arguments: self - The PKCS12 object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   PKey object containing the private key\n\
+";
+static PyObject *
+crypto_PKCS12_get_privatekey(crypto_PKCS12Obj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":get_privatekey"))
+        return NULL;
+
+    Py_INCREF(self->key);
+    return self->key;
+}
+
+static char crypto_PKCS12_get_ca_certificates_doc[] = "\n\
+Return CA certificates within of the PKCS12 object\n\
+\n\
+Arguments: self - The PKCS12 object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   A newly created tuple containing the CA certificates in the chain,\n\
+           if any are present, or None if no CA certificates are present.\n\
+";
+static PyObject *
+crypto_PKCS12_get_ca_certificates(crypto_PKCS12Obj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":get_ca_certificates"))
+        return NULL;
+
+    Py_INCREF(self->cacerts);
+    return self->cacerts;
+}
+
+/*
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ *   {  'name', (PyCFunction)crypto_PKCS12_name, METH_VARARGS, crypto_PKCS12_name_doc }
+ * for convenience
+ */
+#define ADD_METHOD(name)        \
+    { #name, (PyCFunction)crypto_PKCS12_##name, METH_VARARGS, crypto_PKCS12_##name##_doc }
+static PyMethodDef crypto_PKCS12_methods[] =
+{
+    ADD_METHOD(get_certificate),
+    ADD_METHOD(get_privatekey),
+    ADD_METHOD(get_ca_certificates),
+    { NULL, NULL }
+};
+#undef ADD_METHOD
+
+/*
+ * Constructor for PKCS12 objects, never called by Python code directly.
+ * The strategy for this object is to create all the Python objects
+ * corresponding to the cert/key/CA certs right away
+ *
+ * Arguments: p12        - A "real" PKCS12 object
+ *            passphrase - Passphrase to use when decrypting the PKCS12 object
+ * Returns:   The newly created PKCS12 object
+ */
+crypto_PKCS12Obj *
+crypto_PKCS12_New(PKCS12 *p12, char *passphrase)
+{
+    crypto_PKCS12Obj *self;
+    PyObject *cacertobj = NULL;
+
+    X509 *cert = NULL;
+    EVP_PKEY *pkey = NULL;
+    STACK_OF(X509) *cacerts = NULL;
+
+    int i, cacert_count = 0;
+
+    /* allocate space for the CA cert stack */
+    cacerts = sk_X509_new_null();
+
+    /* parse the PKCS12 lump */
+    if (!(cacerts && PKCS12_parse(p12, passphrase, &pkey, &cert, &cacerts)))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    if (!(self = PyObject_GC_New(crypto_PKCS12Obj, &crypto_PKCS12_Type)))
+        return NULL;
+
+    self->cert = NULL;
+    self->key = NULL;
+    Py_INCREF(Py_None);
+    self->cacerts = Py_None;
+
+    if ((self->cert = (PyObject *)crypto_X509_New(cert, 1)) == NULL)
+        goto error;
+
+    if ((self->key = (PyObject *)crypto_PKey_New(pkey, 1)) == NULL)
+        goto error;
+
+    /* Make a tuple for the CA certs */
+    cacert_count = sk_X509_num(cacerts);
+    if (cacert_count > 0)
+    {
+        Py_DECREF(self->cacerts);
+        if ((self->cacerts = PyTuple_New(cacert_count)) == NULL)
+            goto error;
+
+        for (i = 0; i < cacert_count; i++)
+        {
+            cert = sk_X509_value(cacerts, i);
+            if ((cacertobj = (PyObject *)crypto_X509_New(cert, 1)) == NULL)
+                goto error;
+            PyTuple_SET_ITEM(self->cacerts, i, cacertobj);
+        }
+    }
+
+    sk_X509_free(cacerts); /* don't free the certs, just the stack */
+    PyObject_GC_Track(self);
+
+    return self;
+error:
+    crypto_PKCS12_dealloc(self);
+    return NULL;
+}
+
+/*
+ * Find attribute
+ *
+ * Arguments: self - The PKCS12 object
+ *            name - The attribute name
+ * Returns:   A Python object for the attribute, or NULL if something went
+ *            wrong
+ */
+static PyObject *
+crypto_PKCS12_getattr(crypto_PKCS12Obj *self, char *name)
+{
+    return Py_FindMethod(crypto_PKCS12_methods, (PyObject *)self, name);
+}
+
+/*
+ * Call the visitproc on all contained objects.
+ *
+ * Arguments: self - The PKCS12 object
+ *            visit - Function to call
+ *            arg - Extra argument to visit
+ * Returns:   0 if all goes well, otherwise the return code from the first
+ *            call that gave non-zero result.
+ */
+static int
+crypto_PKCS12_traverse(crypto_PKCS12Obj *self, visitproc visit, void *arg)
+{
+    int ret = 0;
+
+    if (ret == 0 && self->cert != NULL)
+        ret = visit(self->cert, arg);
+    if (ret == 0 && self->key != NULL)
+        ret = visit(self->key, arg);
+    if (ret == 0 && self->cacerts != NULL)
+        ret = visit(self->cacerts, arg);
+    return ret;
+}
+
+/*
+ * Decref all contained objects and zero the pointers.
+ *
+ * Arguments: self - The PKCS12 object
+ * Returns:   Always 0.
+ */
+static int
+crypto_PKCS12_clear(crypto_PKCS12Obj *self)
+{
+    Py_XDECREF(self->cert);
+    self->cert = NULL;
+    Py_XDECREF(self->key);
+    self->key = NULL;
+    Py_XDECREF(self->cacerts);
+    self->cacerts = NULL;
+    return 0;
+}
+
+/*
+ * Deallocate the memory used by the PKCS12 object
+ *
+ * Arguments: self - The PKCS12 object
+ * Returns:   None
+ */
+static void
+crypto_PKCS12_dealloc(crypto_PKCS12Obj *self)
+{
+    PyObject_GC_UnTrack(self);
+    crypto_PKCS12_clear(self);
+    PyObject_GC_Del(self);
+}
+
+PyTypeObject crypto_PKCS12_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "PKCS12",
+    sizeof(crypto_PKCS12Obj),
+    0,
+    (destructor)crypto_PKCS12_dealloc,
+    NULL, /* print */
+    (getattrfunc)crypto_PKCS12_getattr,
+    NULL, /* setattr */
+    NULL, /* compare */
+    NULL, /* repr */
+    NULL, /* as_number */
+    NULL, /* as_sequence */
+    NULL, /* as_mapping */
+    NULL, /* hash */
+    NULL, /* call */
+    NULL, /* str */
+    NULL, /* getattro */
+    NULL, /* setattro */
+    NULL, /* as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+    NULL, /* doc */
+    (traverseproc)crypto_PKCS12_traverse,
+    (inquiry)crypto_PKCS12_clear,
+};
+
+/*
+ * Initialize the PKCS12 part of the crypto sub module
+ *
+ * Arguments: dict - The crypto module dictionary
+ * Returns:   None
+ */
+int
+init_crypto_pkcs12(PyObject *dict)
+{
+    crypto_PKCS12_Type.ob_type = &PyType_Type;
+    Py_INCREF(&crypto_PKCS12_Type);
+    PyDict_SetItemString(dict, "PKCS12Type", (PyObject *)&crypto_PKCS12_Type);
+    return 1;
+}
+
diff --git a/src/crypto/pkcs12.h b/src/crypto/pkcs12.h
new file mode 100644
index 0000000..32c9ec4
--- /dev/null
+++ b/src/crypto/pkcs12.h
@@ -0,0 +1,30 @@
+/*
+ * pkcs12.h
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Export PKCS12 functions and data structure.
+ *
+ * @(#) $$
+ */
+#ifndef PyOpenSSL_crypto_PKCS12_H_
+#define PyOpenSSL_crypto_PKCS12_H_
+
+#include <Python.h>
+#include <openssl/pkcs12.h>
+#include <openssl/asn1.h>
+
+extern  int       init_crypto_pkcs12   (PyObject *);
+
+extern  PyTypeObject      crypto_PKCS12_Type;
+
+#define crypto_PKCS12_Check(v) ((v)->ob_type == &crypto_PKCS12_Type)
+
+typedef struct {
+    PyObject_HEAD
+    PyObject            *cert;
+    PyObject            *key;
+    PyObject            *cacerts;
+} crypto_PKCS12Obj;
+
+#endif
diff --git a/src/crypto/pkcs7.c b/src/crypto/pkcs7.c
new file mode 100644
index 0000000..1fb20a9
--- /dev/null
+++ b/src/crypto/pkcs7.c
@@ -0,0 +1,223 @@
+/*
+ * pkcs7.c
+ *
+ * Copyright (C) AB Strakt 2002, All rights reserved
+ *
+ * PKCS7 handling code, mostly thin wrappers around OpenSSL.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ */
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+static char *CVSid = "@(#) $Id: pkcs7.c,v 1.2 2002/07/09 12:55:13 martin Exp $";
+
+static char crypto_PKCS7_type_is_signed_doc[] = "\n\
+Check if this NID_pkcs7_signed object\n\
+\n\
+Arguments: self - The PKCS7 object\n\
+           args - An empty argument tuple\n\
+Returns:   True if the PKCS7 is of type signed\n\
+";
+
+static PyObject *
+crypto_PKCS7_type_is_signed(crypto_PKCS7Obj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":type_is_signed")) 
+        return NULL;
+
+    if (PKCS7_type_is_signed(self->pkcs7))
+        return PyInt_FromLong(1L);
+    else
+        return PyInt_FromLong(0L);
+}
+
+static char crypto_PKCS7_type_is_enveloped_doc[] = "\n\
+Check if this NID_pkcs7_enveloped object\n\
+\n\
+Arguments: self - The PKCS7 object\n\
+           args - An empty argument tuple\n\
+Returns:   True if the PKCS7 is of type enveloped\n\
+";
+
+static PyObject *
+crypto_PKCS7_type_is_enveloped(crypto_PKCS7Obj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":type_is_enveloped")) 
+        return NULL;
+
+    if (PKCS7_type_is_enveloped(self->pkcs7))
+        return PyInt_FromLong(1L);
+    else
+        return PyInt_FromLong(0L);
+}
+
+static char crypto_PKCS7_type_is_signedAndEnveloped_doc[] = "\n\
+Check if this NID_pkcs7_signedAndEnveloped object\n\
+\n\
+Arguments: self - The PKCS7 object\n\
+           args - An empty argument tuple\n\
+Returns:   True if the PKCS7 is of type signedAndEnveloped\n\
+";
+
+static PyObject *
+crypto_PKCS7_type_is_signedAndEnveloped(crypto_PKCS7Obj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":type_is_signedAndEnveloped")) 
+        return NULL;
+
+    if (PKCS7_type_is_signedAndEnveloped(self->pkcs7))
+        return PyInt_FromLong(1L);
+    else
+        return PyInt_FromLong(0L);
+}
+
+static char crypto_PKCS7_type_is_data_doc[] = "\n\
+Check if this NID_pkcs7_data object\n\
+\n\
+Arguments: self - The PKCS7 object\n\
+           args - An empty argument tuple\n\
+Returns:   True if the PKCS7 is of type data\n\
+";
+
+static PyObject *
+crypto_PKCS7_type_is_data(crypto_PKCS7Obj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":type_is_data")) 
+        return NULL;
+
+    if (PKCS7_type_is_data(self->pkcs7))
+        return PyInt_FromLong(1L);
+    else
+        return PyInt_FromLong(0L);
+}
+
+static char crypto_PKCS7_get_type_name_doc[] = "\n\
+Returns the type name of the PKCS7 structure\n\
+\n\
+Arguments: self - The PKCS7 object\n\
+           args - An empty argument tuple\n\
+Returns:   A string with the typename\n\
+";
+
+static PyObject *
+crypto_PKCS7_get_type_name(crypto_PKCS7Obj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":get_type_name")) 
+        return NULL;
+
+    /* 
+     * return a string with the typename
+     */
+    return PyString_FromString(OBJ_nid2sn(OBJ_obj2nid(self->pkcs7->type)));
+}
+
+/*
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ *   {  'name', (PyCFunction)crypto_PKCS7_name, METH_VARARGS }
+ * for convenience
+ */
+#define ADD_METHOD(name)        \
+    { #name, (PyCFunction)crypto_PKCS7_##name, METH_VARARGS, crypto_PKCS7_##name##_doc }
+static PyMethodDef crypto_PKCS7_methods[] =
+{
+    ADD_METHOD(type_is_signed),
+    ADD_METHOD(type_is_enveloped),
+    ADD_METHOD(type_is_signedAndEnveloped),
+    ADD_METHOD(type_is_data),
+    ADD_METHOD(get_type_name),
+    { NULL, NULL }
+};
+#undef ADD_METHOD
+
+
+/*
+ * Constructor for PKCS7 objects, never called by Python code directly
+ *
+ * Arguments: pkcs7    - A "real" pkcs7 certificate object
+ *            dealloc - Boolean value to specify whether the destructor should
+ *                      free the "real" pkcs7 object
+ * Returns:   The newly created pkcs7 object
+ */
+crypto_PKCS7Obj *
+crypto_PKCS7_New(PKCS7 *pkcs7, int dealloc)
+{
+    crypto_PKCS7Obj *self;
+
+    self = PyObject_New(crypto_PKCS7Obj, &crypto_PKCS7_Type);
+
+    if (self == NULL)
+        return NULL;
+
+    self->pkcs7 = pkcs7;
+    self->dealloc = dealloc;
+
+    return self;
+}
+
+/*
+ * Deallocate the memory used by the PKCS7 object
+ *
+ * Arguments: self - The PKCS7 object
+ * Returns:   None
+ */
+static void
+crypto_PKCS7_dealloc(crypto_PKCS7Obj *self)
+{
+    /* Sometimes we don't have to dealloc the "real" PKCS7 pointer ourselves */
+    if (self->dealloc)
+        PKCS7_free(self->pkcs7);
+
+    PyObject_Del(self);
+}
+
+/*
+ * Find attribute
+ *
+ * Arguments: self - The PKCS7 object
+ *            name - The attribute name
+ * Returns:   A Python object for the attribute, or NULL if something went
+ *            wrong
+ */
+static PyObject *
+crypto_PKCS7_getattr(crypto_PKCS7Obj *self, char *name)
+{
+    return Py_FindMethod(crypto_PKCS7_methods, (PyObject *)self, name);
+}
+
+PyTypeObject crypto_PKCS7_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "PKCS7",
+    sizeof(crypto_PKCS7Obj),
+    0,
+    (destructor)crypto_PKCS7_dealloc,
+    NULL, /* print */
+    (getattrfunc)crypto_PKCS7_getattr,
+    NULL, /* setattr */
+    NULL, /* compare */
+    NULL, /* repr */
+    NULL, /* as_number */
+    NULL, /* as_sequence */
+    NULL, /* as_mapping */
+    NULL, /* hash */
+    NULL, /* call */
+    NULL  /* str */
+};
+
+/*
+ * Initialize the PKCS7 part of the crypto sub module
+ *
+ * Arguments: dict - The crypto module dictionary
+ * Returns:   None
+ */
+int
+init_crypto_pkcs7(PyObject *dict)
+{
+    crypto_PKCS7_Type.ob_type = &PyType_Type;
+    Py_INCREF(&crypto_PKCS7_Type);
+    PyDict_SetItemString(dict, "PKCS7Type", (PyObject *)&crypto_PKCS7_Type);
+    return 1;
+}
+
diff --git a/src/crypto/pkcs7.h b/src/crypto/pkcs7.h
new file mode 100644
index 0000000..bdbb425
--- /dev/null
+++ b/src/crypto/pkcs7.h
@@ -0,0 +1,30 @@
+/*
+ * pkcs7.h
+ *
+ * Copyright (C) AB Strakt 2002, All rights reserved
+ *
+ * Export pkcs7 functions and data structure.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * @(#) $Id: pkcs7.h,v 1.2 2002/09/04 22:24:59 iko Exp $
+ */
+#ifndef PyOpenSSL_crypto_PKCS7_H_
+#define PyOpenSSL_crypto_PKCS7_H_
+
+#include <Python.h>
+#include <openssl/pkcs7.h>
+
+extern  int       init_crypto_pkcs7   (PyObject *);
+
+extern  PyTypeObject      crypto_PKCS7_Type;
+
+#define crypto_PKCS7_Check(v) ((v)->ob_type == &crypto_PKCS7_Type)
+
+typedef struct {
+    PyObject_HEAD
+    PKCS7                *pkcs7;
+    int                  dealloc;
+} crypto_PKCS7Obj;
+
+
+#endif
diff --git a/src/crypto/pkey.c b/src/crypto/pkey.c
new file mode 100644
index 0000000..201960f
--- /dev/null
+++ b/src/crypto/pkey.c
@@ -0,0 +1,215 @@
+/*
+ * pkey.c
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Public/rivate key handling code, mostly thin wrappers around OpenSSL.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ */
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+static char *CVSid = "@(#) $Id: pkey.c,v 1.9 2002/07/09 13:47:21 martin Exp $";
+
+/*
+ * This is done every time something fails, so turning it into a macro is
+ * really nice.
+ *
+ * Arguments:   None
+ * Returns:     Doesn't return
+ */
+#define FAIL() \
+do {                                    \
+    exception_from_error_queue();       \
+    return NULL;                        \
+} while (0)
+    
+
+static char crypto_PKey_generate_key_doc[] = "\n\
+Generate a key of a given type, with a given number of a bits\n\
+\n\
+Arguments: self - The PKey object\n\
+           args - The Python argument tuple, should be:\n\
+             type - The key type (TYPE_RSA or TYPE_DSA)\n\
+             bits - The number of bits\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_PKey_generate_key(crypto_PKeyObj *self, PyObject *args)
+{
+    int type, bits;
+    RSA *rsa;
+    DSA *dsa;
+
+    if (!PyArg_ParseTuple(args, "ii:generate_key", &type, &bits))
+        return NULL;
+
+    switch (type)
+    {
+        case crypto_TYPE_RSA:
+            if ((rsa = RSA_generate_key(bits, 0x10001, NULL, NULL)) == NULL)
+                FAIL();
+            if (!EVP_PKEY_assign_RSA(self->pkey, rsa))
+                FAIL();
+            Py_INCREF(Py_None);
+            return Py_None;
+
+        case crypto_TYPE_DSA:
+            if ((dsa = DSA_generate_parameters(bits, NULL, 0, NULL, NULL, NULL, NULL)) == NULL)
+                FAIL();
+            if (!DSA_generate_key(dsa))
+                FAIL();
+            if (!EVP_PKEY_assign_DSA(self->pkey, dsa))
+                FAIL();
+            Py_INCREF(Py_None);
+            return Py_None;
+    }
+
+    PyErr_SetString(crypto_Error, "No such key type");
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char crypto_PKey_bits_doc[] = "\n\
+Returns the number of bits of the key\n\
+\n\
+Arguments: self - The PKey object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns: The number of bits of the key.\n\
+";
+
+static PyObject *
+crypto_PKey_bits(crypto_PKeyObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":bits"))
+        return NULL;
+
+    return PyInt_FromLong(EVP_PKEY_bits(self->pkey));
+}
+
+static char crypto_PKey_type_doc[] = "\n\
+Returns the type of the key\n\
+\n\
+Arguments: self - The PKey object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns: The type of the key.\n\
+";
+
+static PyObject *
+crypto_PKey_type(crypto_PKeyObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":type"))
+        return NULL;
+
+    return PyInt_FromLong(self->pkey->type);
+}
+
+
+/*
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ *   {  'name', (PyCFunction)crypto_PKey_name, METH_VARARGS }
+ * for convenience
+ */
+#define ADD_METHOD(name)        \
+    { #name, (PyCFunction)crypto_PKey_##name, METH_VARARGS, crypto_PKey_##name##_doc }
+static PyMethodDef crypto_PKey_methods[] =
+{
+    ADD_METHOD(generate_key),
+    ADD_METHOD(bits),
+    ADD_METHOD(type),
+    { NULL, NULL }
+};
+#undef ADD_METHOD
+
+
+/*
+ * Constructor for PKey objects, never called by Python code directly
+ *
+ * Arguments: pkey    - A "real" EVP_PKEY object
+ *            dealloc - Boolean value to specify whether the destructor should
+ *                      free the "real" EVP_PKEY object
+ * Returns:   The newly created PKey object
+ */
+crypto_PKeyObj *
+crypto_PKey_New(EVP_PKEY *pkey, int dealloc)
+{
+    crypto_PKeyObj *self;
+
+    self = PyObject_New(crypto_PKeyObj, &crypto_PKey_Type);
+
+    if (self == NULL)
+        return NULL;
+
+    self->pkey = pkey;
+    self->dealloc = dealloc;
+
+    return self;
+}
+
+/*
+ * Deallocate the memory used by the PKey object
+ *
+ * Arguments: self - The PKey object
+ * Returns:   None
+ */
+static void
+crypto_PKey_dealloc(crypto_PKeyObj *self)
+{
+    /* Sometimes we don't have to dealloc the "real" EVP_PKEY pointer ourselves */
+    if (self->dealloc)
+        EVP_PKEY_free(self->pkey);
+
+    PyObject_Del(self);
+}
+
+/*
+ * Find attribute
+ *
+ * Arguments: self - The PKey object
+ *            name - The attribute name
+ * Returns:   A Python object for the attribute, or NULL if something went
+ *            wrong
+ */
+static PyObject *
+crypto_PKey_getattr(crypto_PKeyObj *self, char *name)
+{
+    return Py_FindMethod(crypto_PKey_methods, (PyObject *)self, name);
+}
+
+PyTypeObject crypto_PKey_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "PKey",
+    sizeof(crypto_PKeyObj),
+    0,
+    (destructor)crypto_PKey_dealloc,
+    NULL, /* print */
+    (getattrfunc)crypto_PKey_getattr,
+    NULL, /* setattr */
+    NULL, /* compare */
+    NULL, /* repr */
+    NULL, /* as_number */
+    NULL, /* as_sequence */
+    NULL, /* as_mapping */
+    NULL, /* hash */
+};
+
+
+/*
+ * Initialize the PKey part of the crypto sub module
+ *
+ * Arguments: dict - The crypto module dictionary
+ * Returns:   None
+ */
+int
+init_crypto_pkey(PyObject *dict)
+{
+    crypto_PKey_Type.ob_type = &PyType_Type;
+    Py_INCREF(&crypto_PKey_Type);
+    PyDict_SetItemString(dict, "PKeyType", (PyObject *)&crypto_PKey_Type);
+    return 1;
+}
+
diff --git a/src/crypto/pkey.h b/src/crypto/pkey.h
new file mode 100644
index 0000000..d46e360
--- /dev/null
+++ b/src/crypto/pkey.h
@@ -0,0 +1,29 @@
+/*
+ * pkey.h
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Export pkey functions and data structure.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * @(#) $Id: pkey.h,v 1.5 2002/09/04 22:24:59 iko Exp $
+ */
+#ifndef PyOpenSSL_crypto_PKEY_H_
+#define PyOpenSSL_crypto_PKEY_H_
+
+extern  int       init_crypto_pkey   (PyObject *);
+
+extern  PyTypeObject    crypto_PKey_Type;
+
+#define crypto_PKey_Check(v) ((v)->ob_type == &crypto_PKey_Type)
+
+typedef struct {
+    PyObject_HEAD
+    EVP_PKEY            *pkey;
+    int                  dealloc;
+} crypto_PKeyObj;
+
+#define crypto_TYPE_RSA           EVP_PKEY_RSA
+#define crypto_TYPE_DSA           EVP_PKEY_DSA
+
+#endif
diff --git a/src/crypto/x509.c b/src/crypto/x509.c
new file mode 100644
index 0000000..bcae5f6
--- /dev/null
+++ b/src/crypto/x509.c
@@ -0,0 +1,591 @@
+/*
+ * x509.c
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Certificate (X.509) handling code, mostly thin wrappers around OpenSSL.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ */
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+static char *CVSid = "@(#) $Id: x509.c,v 1.20 2004/08/10 10:37:31 martin Exp $";
+
+/* 
+ * X.509 is a standard for digital certificates.  See e.g. the OpenSSL homepage
+ * http://www.openssl.org/ for more information
+ */
+
+static char crypto_X509_get_version_doc[] = "\n\
+Return version number of the certificate\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   Version number as a Python integer\n\
+";
+
+static PyObject *
+crypto_X509_get_version(crypto_X509Obj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":get_version"))
+        return NULL;
+
+    return PyInt_FromLong((long)X509_get_version(self->x509));
+}
+
+static char crypto_X509_set_version_doc[] = "\n\
+Set version number of the certificate\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be:\n\
+             version - The version number\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509_set_version(crypto_X509Obj *self, PyObject *args)
+{
+    int version;
+
+    if (!PyArg_ParseTuple(args, "i:set_version", &version))
+        return NULL;
+
+    X509_set_version(self->x509, version);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char crypto_X509_get_serial_number_doc[] = "\n\
+Return serial number of the certificate\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   Serial number as a Python integer\n\
+";
+
+static PyObject *
+crypto_X509_get_serial_number(crypto_X509Obj *self, PyObject *args)
+{
+    ASN1_INTEGER *asn1_i;
+
+    if (!PyArg_ParseTuple(args, ":get_serial_number"))
+        return NULL;
+
+    asn1_i = X509_get_serialNumber(self->x509);
+    return PyInt_FromLong(ASN1_INTEGER_get(asn1_i));
+}
+
+static char crypto_X509_set_serial_number_doc[] = "\n\
+Set serial number of the certificate\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be:\n\
+             serial - The serial number\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509_set_serial_number(crypto_X509Obj *self, PyObject *args)
+{
+    long serial;
+
+    if (!PyArg_ParseTuple(args, "l:set_serial_number", &serial))
+        return NULL;
+
+    ASN1_INTEGER_set(X509_get_serialNumber(self->x509), serial);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char crypto_X509_get_issuer_doc[] = "\n\
+Create an X509Name object for the issuer of the certificate\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   An X509Name object\n\
+";
+
+static PyObject *
+crypto_X509_get_issuer(crypto_X509Obj *self, PyObject *args)
+{
+    crypto_X509NameObj *pyname;
+    X509_NAME *name;
+
+    if (!PyArg_ParseTuple(args, ":get_issuer"))
+        return NULL;
+
+    name = X509_get_issuer_name(self->x509);
+    pyname = crypto_X509Name_New(name, 0);
+    if (pyname != NULL)
+    {
+        pyname->parent_cert = (PyObject *)self;
+        Py_INCREF(self);
+    }
+    return (PyObject *)pyname;
+}
+
+static char crypto_X509_set_issuer_doc[] = "\n\
+Set the issuer of the certificate\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be:\n\
+             issuer - The issuer name\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509_set_issuer(crypto_X509Obj *self, PyObject *args)
+{
+    crypto_X509NameObj *issuer;
+
+    if (!PyArg_ParseTuple(args, "O!:set_issuer", &crypto_X509Name_Type,
+			  &issuer))
+        return NULL;
+
+    if (!X509_set_issuer_name(self->x509, issuer->x509_name))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char crypto_X509_get_subject_doc[] = "\n\
+Create an X509Name object for the subject of the certificate\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   An X509Name object\n\
+";
+
+static PyObject *
+crypto_X509_get_subject(crypto_X509Obj *self, PyObject *args)
+{
+    crypto_X509NameObj *pyname;
+    X509_NAME *name;
+
+    if (!PyArg_ParseTuple(args, ":get_subject"))
+        return NULL;
+
+    name = X509_get_subject_name(self->x509);
+    pyname = crypto_X509Name_New(name, 0);
+    if (pyname != NULL)
+    {
+        pyname->parent_cert = (PyObject *)self;
+        Py_INCREF(self);
+    }
+    return (PyObject *)pyname;
+}
+
+static char crypto_X509_set_subject_doc[] = "\n\
+Set the subject of the certificate\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be:\n\
+             subject - The subject name\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509_set_subject(crypto_X509Obj *self, PyObject *args)
+{
+    crypto_X509NameObj *subject;
+
+    if (!PyArg_ParseTuple(args, "O!:set_subject", &crypto_X509Name_Type,
+			  &subject))
+        return NULL;
+
+    if (!X509_set_subject_name(self->x509, subject->x509_name))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char crypto_X509_get_pubkey_doc[] = "\n\
+Get the public key of the certificate\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The public key\n\
+";
+
+static PyObject *
+crypto_X509_get_pubkey(crypto_X509Obj *self, PyObject *args)
+{
+    crypto_PKeyObj *crypto_PKey_New(EVP_PKEY *, int);
+    EVP_PKEY *pkey;
+
+    if (!PyArg_ParseTuple(args, ":get_pubkey"))
+        return NULL;
+
+    if ((pkey = X509_get_pubkey(self->x509)) == NULL)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    return (PyObject *)crypto_PKey_New(pkey, 0);
+}
+
+static char crypto_X509_set_pubkey_doc[] = "\n\
+Set the public key of the certificate\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be:\n\
+             pkey - The public key\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509_set_pubkey(crypto_X509Obj *self, PyObject *args)
+{
+    crypto_PKeyObj *pkey;
+
+    if (!PyArg_ParseTuple(args, "O!:set_pubkey", &crypto_PKey_Type, &pkey))
+        return NULL;
+
+    if (!X509_set_pubkey(self->x509, pkey->pkey))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char crypto_X509_gmtime_adj_notBefore_doc[] = "\n\
+Adjust the time stamp for when the certificate starts being valid\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be:\n\
+             i - The adjustment\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509_gmtime_adj_notBefore(crypto_X509Obj *self, PyObject *args)
+{
+    long i;
+
+    if (!PyArg_ParseTuple(args, "l:gmtime_adj_notBefore", &i))
+        return NULL;
+
+    X509_gmtime_adj(X509_get_notBefore(self->x509), i);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char crypto_X509_gmtime_adj_notAfter_doc[] = "\n\
+Adjust the time stamp for when the certificate stops being valid\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be:\n\
+             i - The adjustment\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509_gmtime_adj_notAfter(crypto_X509Obj *self, PyObject *args)
+{
+    long i;
+
+    if (!PyArg_ParseTuple(args, "l:gmtime_adj_notAfter", &i))
+        return NULL;
+
+    X509_gmtime_adj(X509_get_notAfter(self->x509), i);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char crypto_X509_sign_doc[] = "\n\
+Sign the certificate using the supplied key and digest\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be:\n\
+             pkey   - The key to sign with\n\
+             digest - The message digest to use\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509_sign(crypto_X509Obj *self, PyObject *args)
+{
+    crypto_PKeyObj *pkey;
+    char *digest_name;
+    const EVP_MD *digest;
+
+    if (!PyArg_ParseTuple(args, "O!s:sign", &crypto_PKey_Type, &pkey,
+			  &digest_name))
+        return NULL;
+
+    if ((digest = EVP_get_digestbyname(digest_name)) == NULL)
+    {
+        PyErr_SetString(PyExc_ValueError, "No such digest method");
+        return NULL;
+    }
+
+    if (!X509_sign(self->x509, pkey->pkey, digest))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char crypto_X509_has_expired_doc[] = "\n\
+Check whether the certificate has expired.\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   True if the certificate has expired, false otherwise\n\
+";
+
+static PyObject *
+crypto_X509_has_expired(crypto_X509Obj *self, PyObject *args)
+{
+    time_t tnow;
+
+    if (!PyArg_ParseTuple(args, ":has_expired"))
+        return NULL;
+
+    tnow = time(NULL);
+    if (ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(self->x509), tnow) < 0)
+        return PyInt_FromLong(1L);
+    else
+        return PyInt_FromLong(0L);
+}
+
+static char crypto_X509_subject_name_hash_doc[] = "\n\
+Return the hash of the X509 subject.\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The hash of the subject\n\
+";
+
+static PyObject *
+crypto_X509_subject_name_hash(crypto_X509Obj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":subject_name_hash"))
+        return NULL;
+
+    return PyLong_FromLong(X509_subject_name_hash(self->x509));
+}
+
+static char crypto_X509_digest_doc[] = "\n\
+Return the digest of the X509 object.\n\
+\n\
+Arguments: self - The X509 object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The digest of the object\n\
+";
+
+static PyObject *
+crypto_X509_digest(crypto_X509Obj *self, PyObject *args)
+{
+    unsigned char fp[EVP_MAX_MD_SIZE];
+    char *tmp;
+    char *digest_name;
+    int len,i;
+    PyObject *ret;
+    const EVP_MD *digest;
+
+    if (!PyArg_ParseTuple(args, "s:digest", &digest_name))
+        return NULL;
+
+    if ((digest = EVP_get_digestbyname(digest_name)) == NULL)
+    {
+        PyErr_SetString(PyExc_ValueError, "No such digest method");
+        return NULL;
+    }
+
+    if (!X509_digest(self->x509,digest,fp,&len))
+    {
+        exception_from_error_queue();
+    }
+    tmp = malloc(3*len+1);
+    memset(tmp, 0, 3*len+1);
+    for (i = 0; i < len; i++) {
+        sprintf(tmp+i*3,"%02X:",fp[i]);
+    }
+    tmp[3*len-1] = 0;
+    ret = PyString_FromStringAndSize(tmp,3*len-1);
+    free(tmp);
+    return ret;
+}
+
+
+static char crypto_X509_add_extensions_doc[] = "\n\
+Add extensions to the certificate.\n\
+\n\
+Arguments: self - X509 object\n\
+           args - The Python argument tuple, should be:\n\
+             extensions - a sequence of X509Extension objects\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509_add_extensions(crypto_X509Obj *self, PyObject *args)
+{   
+    PyObject *extensions, *seq;
+    crypto_X509ExtensionObj *ext;
+    int nr_of_extensions, i;
+
+    if (!PyArg_ParseTuple(args, "O:add_extensions", &extensions))
+        return NULL;
+
+    seq = PySequence_Fast(extensions, "Expected a sequence");
+    if (seq == NULL)
+        return NULL;
+
+    nr_of_extensions = PySequence_Fast_GET_SIZE(seq);
+
+    for (i = 0; i < nr_of_extensions; i++)
+    { 
+        ext = (crypto_X509ExtensionObj *)PySequence_Fast_GET_ITEM(seq, i);
+        if (!crypto_X509Extension_Check(ext))
+        {   
+            Py_DECREF(seq);
+            PyErr_SetString(PyExc_ValueError,
+                            "One of the elements is not an X509Extension");
+            return NULL;
+        }
+        if (!X509_add_ext(self->x509, ext->x509_extension, -1))
+        {
+            Py_DECREF(seq);
+            exception_from_error_queue();
+            return NULL;
+        }
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+/*
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ *   {  'name', (PyCFunction)crypto_X509_name, METH_VARARGS }
+ * for convenience
+ */
+#define ADD_METHOD(name)        \
+    { #name, (PyCFunction)crypto_X509_##name, METH_VARARGS, crypto_X509_##name##_doc }
+static PyMethodDef crypto_X509_methods[] =
+{
+    ADD_METHOD(get_version),
+    ADD_METHOD(set_version),
+    ADD_METHOD(get_serial_number),
+    ADD_METHOD(set_serial_number),
+    ADD_METHOD(get_issuer),
+    ADD_METHOD(set_issuer),
+    ADD_METHOD(get_subject),
+    ADD_METHOD(set_subject),
+    ADD_METHOD(get_pubkey),
+    ADD_METHOD(set_pubkey),
+    ADD_METHOD(gmtime_adj_notBefore),
+    ADD_METHOD(gmtime_adj_notAfter),
+    ADD_METHOD(sign),
+    ADD_METHOD(has_expired),
+    ADD_METHOD(subject_name_hash),
+    ADD_METHOD(digest),
+    ADD_METHOD(add_extensions),
+    { NULL, NULL }
+};
+#undef ADD_METHOD
+
+
+/*
+ * Constructor for X509 objects, never called by Python code directly
+ *
+ * Arguments: cert    - A "real" X509 certificate object
+ *            dealloc - Boolean value to specify whether the destructor should
+ *                      free the "real" X509 object
+ * Returns:   The newly created X509 object
+ */
+crypto_X509Obj *
+crypto_X509_New(X509 *cert, int dealloc)
+{
+    crypto_X509Obj *self;
+
+    self = PyObject_New(crypto_X509Obj, &crypto_X509_Type);
+
+    if (self == NULL)
+        return NULL;
+
+    self->x509 = cert;
+    self->dealloc = dealloc;
+
+    return self;
+}
+
+/*
+ * Deallocate the memory used by the X509 object
+ *
+ * Arguments: self - The X509 object
+ * Returns:   None
+ */
+static void
+crypto_X509_dealloc(crypto_X509Obj *self)
+{
+    /* Sometimes we don't have to dealloc the "real" X509 pointer ourselves */
+    if (self->dealloc)
+        X509_free(self->x509);
+
+    PyObject_Del(self);
+}
+
+/*
+ * Find attribute
+ *
+ * Arguments: self - The X509 object
+ *            name - The attribute name
+ * Returns:   A Python object for the attribute, or NULL if something went
+ *            wrong
+ */
+static PyObject *
+crypto_X509_getattr(crypto_X509Obj *self, char *name)
+{
+    return Py_FindMethod(crypto_X509_methods, (PyObject *)self, name);
+}
+
+PyTypeObject crypto_X509_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "X509",
+    sizeof(crypto_X509Obj),
+    0,
+    (destructor)crypto_X509_dealloc,
+    NULL, /* print */
+    (getattrfunc)crypto_X509_getattr,
+};
+
+/*
+ * Initialize the X509 part of the crypto sub module
+ *
+ * Arguments: dict - The crypto module dictionary
+ * Returns:   None
+ */
+int
+init_crypto_x509(PyObject *dict)
+{
+    crypto_X509_Type.ob_type = &PyType_Type;
+    Py_INCREF(&crypto_X509_Type);
+    PyDict_SetItemString(dict, "X509Type", (PyObject *)&crypto_X509_Type);
+    return 1;
+}
+
diff --git a/src/crypto/x509.h b/src/crypto/x509.h
new file mode 100644
index 0000000..40768cf
--- /dev/null
+++ b/src/crypto/x509.h
@@ -0,0 +1,32 @@
+/*
+ * x509.h
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Export x509 functions and data structure.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ *
+ * @(#) $Id: x509.h,v 1.9 2002/09/04 22:24:59 iko Exp $
+ */
+#ifndef PyOpenSSL_crypto_X509_H_
+#define PyOpenSSL_crypto_X509_H_
+
+#include <Python.h>
+#include <openssl/ssl.h>
+
+extern  int       init_crypto_x509   (PyObject *);
+
+extern  PyTypeObject      crypto_X509_Type;
+
+#define crypto_X509_Check(v) ((v)->ob_type == &crypto_X509_Type)
+
+typedef struct {
+    PyObject_HEAD
+    X509                *x509;
+    int                  dealloc;
+} crypto_X509Obj;
+
+
+#endif
diff --git a/src/crypto/x509ext.c b/src/crypto/x509ext.c
new file mode 100644
index 0000000..9a628c3
--- /dev/null
+++ b/src/crypto/x509ext.c
@@ -0,0 +1,254 @@
+/*
+ * x509ext.c
+ *
+ * Export X.509 extension functions and data structures.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * @(#) $Id: x509ext.c,v 1.1 2002/07/09 13:34:46 martin Exp $
+ */
+
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+static char *CVSid = "@(#) $Id: x509ext.c,v 1.1 2002/07/09 13:34:46 martin Exp $";
+
+static char crypto_X509Extension_get_critical_doc[] = "\n\
+Returns the critical field of the X509Extension\n\
+\n\
+Arguments: self - The X509Extension object\n\
+           args - The argument tuple, should be empty\n\
+Returns: The critical field.\n\
+";
+
+static PyObject *
+crypto_X509Extension_get_critical(crypto_X509ExtensionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":get_critical"))
+        return NULL;
+
+    return PyInt_FromLong(X509_EXTENSION_get_critical(self->x509_extension));
+}
+
+/*
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ *   {  'name', (PyCFunction)crypto_X509Extension_name, METH_VARARGS }
+ * for convenience
+ */
+#define ADD_METHOD(name)        \
+{ #name, (PyCFunction)crypto_X509Extension_##name, METH_VARARGS, crypto_X509Extension_##name##_doc }
+static PyMethodDef crypto_X509Extension_methods[] =
+{
+    ADD_METHOD(get_critical),
+    { NULL, NULL }
+};
+#undef ADD_METHOD
+
+/*
+ * Constructor for X509Extension, never called by Python code directly
+ *
+ * Arguments: type_name - ???
+ *            critical  - ???
+ *            value     - ???
+ * Returns:   The newly created X509Extension object
+ */
+crypto_X509ExtensionObj *
+crypto_X509Extension_New(char *type_name, int critical, char *value)
+{
+    crypto_X509ExtensionObj *self;
+    int ext_len, ext_nid;
+    unsigned char *ext_der;
+    X509V3_EXT_METHOD *ext_method = NULL;
+    ASN1_OCTET_STRING *ext_oct;
+    STACK_OF(CONF_VALUE) *nval;
+    void * ext_struct;
+    X509_EXTENSION *extension = NULL;
+
+    self = PyObject_New(crypto_X509ExtensionObj, &crypto_X509Extension_Type);
+
+    if (self == NULL)
+        return NULL;
+
+    /* Try to get a NID for the name */
+    if ((ext_nid = OBJ_sn2nid(type_name)) == NID_undef)
+    {
+        PyErr_SetString(PyExc_ValueError, "Unknown extension name");
+        return NULL;
+    }
+
+    /* Lookup the extension method structure */
+    if (!(ext_method = X509V3_EXT_get_nid(ext_nid)))
+    {
+        PyErr_SetString(PyExc_ValueError, "Unknown extension");
+        return NULL;
+    }
+
+    /* Look if it has a function to convert value to an 
+     * internal structure.
+     */
+    if (!ext_method->v2i)
+    {
+        PyErr_SetString(PyExc_ValueError, "Can't initialize exception");
+        return NULL;
+    }
+
+    /* Parse the value */
+    nval = X509V3_parse_list(value);
+    if (!nval)
+    {
+        PyErr_SetString(PyExc_ValueError, "Invalid extension string");
+        return NULL;
+    }
+
+    /* And use it to get the internal structure */
+    if(!(ext_struct = ext_method->v2i(ext_method, NULL, nval))) {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    /* Deallocate the configuration value stack */
+    sk_CONF_VALUE_pop_free(nval, X509V3_conf_free);
+        
+    /* Find out how much memory we need */
+
+
+    /* Convert internal representation to DER */
+    /* and Allocate */
+    if (ext_method->it) {
+        ext_der = NULL;
+        ext_len = ASN1_item_i2d(ext_struct, &ext_der, ASN1_ITEM_ptr(ext_method->it));
+        if (ext_len < 0) {
+            PyErr_SetString(PyExc_MemoryError, "Could not allocate memory");
+            return NULL;
+        }
+    } else {
+        unsigned char *p;
+        ext_len = ext_method->i2d(ext_struct, NULL);
+        if(!(ext_der = malloc(ext_len))) {
+            PyErr_SetString(PyExc_MemoryError, "Could not allocate memory");
+            return NULL;
+        }
+        p = ext_der;
+        ext_method->i2d(ext_struct, &p);
+    }
+
+    /* And create the ASN1_OCTET_STRING */
+    if(!(ext_oct = M_ASN1_OCTET_STRING_new())) {
+        exception_from_error_queue();
+        return NULL;
+    }
+        
+    ext_oct->data = ext_der;
+    ext_oct->length = ext_len;
+
+    /* Now that we got all ingredients, make the extension */
+    extension = X509_EXTENSION_create_by_NID(NULL, ext_nid, critical, ext_oct);
+    if (extension == NULL)
+    {
+        exception_from_error_queue();
+        M_ASN1_OCTET_STRING_free(ext_oct);
+        ext_method->ext_free(ext_struct);
+        return NULL;
+    }
+    
+    M_ASN1_OCTET_STRING_free(ext_oct);
+    //ext_method->ext_free(ext_struct);
+
+    self->x509_extension = extension;
+    self->dealloc = 1;
+
+    return self;
+}
+
+/*
+ * Deallocate the memory used by the X509Extension object
+ *
+ * Arguments: self - The X509Extension object
+ * Returns:   None
+ */
+static void
+crypto_X509Extension_dealloc(crypto_X509ExtensionObj *self)
+{
+    /* Sometimes we don't have to dealloc this */
+    if (self->dealloc)
+        X509_EXTENSION_free(self->x509_extension);
+
+    PyObject_Del(self);
+}
+
+/*
+ * Find attribute
+ *
+ * Arguments: self - The X509Extension object
+ *            name - The attribute name
+ * Returns: A Python object for the attribute, or NULL if something
+ *          went wrong.
+ */       
+static PyObject *
+crypto_X509Extension_getattr(crypto_X509ExtensionObj *self, char *name)
+{
+    return Py_FindMethod(crypto_X509Extension_methods, (PyObject *)self, name);
+}
+
+/*
+ * Print a nice text representation of the certificate request.
+ */
+static PyObject *
+crypto_X509Extension_str(crypto_X509ExtensionObj *self)
+{
+    int str_len;
+    char *tmp_str;
+    PyObject *str;
+    BIO *bio = BIO_new(BIO_s_mem());
+
+    if (!X509V3_EXT_print(bio, self->x509_extension, 0, 0))
+    {
+        BIO_free(bio);
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    str_len = BIO_get_mem_data(bio, &tmp_str);
+    str = PyString_FromStringAndSize(tmp_str, str_len);
+
+    BIO_free(bio);
+
+    return str;
+}
+
+PyTypeObject crypto_X509Extension_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "X509Extension",
+    sizeof(crypto_X509ExtensionObj),
+    0,
+    (destructor)crypto_X509Extension_dealloc, 
+    NULL, /* print */
+    (getattrfunc)crypto_X509Extension_getattr, 
+    NULL, /* setattr  (setattrfunc)crypto_X509Name_setattr, */
+    NULL, /* compare */
+    NULL, /* repr */ 
+    NULL, /* as_number */
+    NULL, /* as_sequence */
+    NULL, /* as_mapping */
+    NULL, /* hash */
+    NULL, /* call */
+    (reprfunc)crypto_X509Extension_str /* str */
+};
+
+/*
+ * Initialize the X509Extension part of the crypto module
+ *
+ * Arguments: dict - The crypto module dictionary
+ * Returns:   None
+ */
+int
+init_crypto_x509extension(PyObject *dict)
+{
+    crypto_X509Extension_Type.ob_type = &PyType_Type;
+    Py_INCREF(&crypto_X509Extension_Type);
+    PyDict_SetItemString(dict, "X509ExtensionType",
+            (PyObject *)&crypto_X509Extension_Type);
+    return 1;
+}
+
diff --git a/src/crypto/x509ext.h b/src/crypto/x509ext.h
new file mode 100644
index 0000000..f20e562
--- /dev/null
+++ b/src/crypto/x509ext.h
@@ -0,0 +1,32 @@
+/*
+ * x509ext.h
+ *
+ * Copyright (C) Awanim 2002, All rights reserved
+ *
+ * Export X.509 extension functions and data structures.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * @(#) $Id: x509ext.h,v 1.2 2002/09/04 22:24:59 iko Exp $
+ */
+#ifndef PyOpenSSL_crypto_X509EXTENSION_H_
+#define PyOpenSSL_crypto_X509EXTENSION_H_
+
+#include <Python.h>
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+
+extern  int     init_crypto_x509extension       (PyObject *);
+
+extern  PyTypeObject      crypto_X509Extension_Type;
+
+#define crypto_X509Extension_Check(v) ((v)->ob_type == \
+				       &crypto_X509Extension_Type)
+
+typedef struct {
+    PyObject_HEAD
+    X509_EXTENSION       *x509_extension;
+    int                  dealloc;
+} crypto_X509ExtensionObj;
+
+#endif
+
diff --git a/src/crypto/x509name.c b/src/crypto/x509name.c
new file mode 100644
index 0000000..b9c0233
--- /dev/null
+++ b/src/crypto/x509name.c
@@ -0,0 +1,307 @@
+/*
+ * x509name.c
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * X.509 Name handling, mostly thin wrapping.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ */
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+static char *CVSid = "@(#) $Id: x509name.c,v 1.16 2003/01/09 17:08:32 martin Exp $";
+
+
+/*
+ * Constructor for X509Name, never called by Python code directly
+ *
+ * Arguments: name    - A "real" X509_NAME object
+ *            dealloc - Boolean value to specify whether the destructor should
+ *                      free the "real" X509_NAME object
+ * Returns:   The newly created X509Name object
+ */
+crypto_X509NameObj *
+crypto_X509Name_New(X509_NAME *name, int dealloc)
+{
+    crypto_X509NameObj *self;
+
+    self = PyObject_GC_New(crypto_X509NameObj, &crypto_X509Name_Type);
+
+    if (self == NULL)
+        return NULL;
+
+    self->x509_name = name;
+    self->dealloc = dealloc;
+    self->parent_cert = NULL;
+
+    PyObject_GC_Track(self);
+    return self;
+}
+
+/*
+ * Return a name string given a X509_NAME object and a name identifier. Used
+ * by the getattr function.
+ *
+ * Arguments: name - The X509_NAME object
+ *            nid  - The name identifier
+ * Returns:   The name as a Python string object
+ */
+static int
+get_name_by_nid(X509_NAME *name, int nid, char **utf8string)
+{
+    int entry_idx;
+    X509_NAME_ENTRY *entry;
+    ASN1_STRING *data;
+    int len;
+
+    if ((entry_idx = X509_NAME_get_index_by_NID(name, nid, -1)) == -1)
+    {
+        return 0;
+    }
+    entry = X509_NAME_get_entry(name, entry_idx);
+    data = X509_NAME_ENTRY_get_data(entry);
+    if ((len = ASN1_STRING_to_UTF8((unsigned char **)utf8string, data)) < 0)
+    {
+        exception_from_error_queue();
+        return -1;
+    }
+
+    return len;
+}
+
+/*
+ * Given a X509_NAME object and a name identifier, set the corresponding
+ * attribute to the given string. Used by the setattr function.
+ *
+ * Arguments: name  - The X509_NAME object
+ *            nid   - The name identifier
+ *            value - The string to set
+ * Returns:   0 for success, -1 on failure
+ */
+static int
+set_name_by_nid(X509_NAME *name, int nid, char *utf8string)
+{
+    X509_NAME_ENTRY *ne;
+    int i, entry_count, temp_nid;
+
+    /* If there's an old entry for this NID, remove it */
+    entry_count = X509_NAME_entry_count(name);
+    for (i = 0; i < entry_count; i++)
+    {
+        ne = X509_NAME_get_entry(name, i);
+        temp_nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(ne));
+        if (temp_nid == nid)
+        {
+            ne = X509_NAME_delete_entry(name, i);
+            X509_NAME_ENTRY_free(ne);
+            break;
+        }
+    }
+
+    /* Add the new entry */
+    if (!X509_NAME_add_entry_by_NID(name, nid, MBSTRING_UTF8, utf8string,
+                -1, -1, 0))
+    {
+        exception_from_error_queue();
+        return -1;
+    }
+    return 0;
+}
+
+
+/*
+ * Find attribute. An X509Name object has the following attributes:
+ * countryName (alias C), stateOrProvince (alias ST), locality (alias L),
+ * organization (alias O), organizationalUnit (alias OU), commonName (alias
+ * CN) and more...
+ *
+ * Arguments: self - The X509Name object
+ *            name - The attribute name
+ * Returns:   A Python object for the attribute, or NULL if something went
+ *            wrong
+ */
+static PyObject *
+crypto_X509Name_getattr(crypto_X509NameObj *self, char *name)
+{
+    int nid, len;
+    char *utf8string;
+
+    if ((nid = OBJ_txt2nid(name)) == NID_undef)
+    {
+        PyErr_SetString(PyExc_AttributeError, "No such attribute");
+        return NULL;
+    }
+
+    len = get_name_by_nid(self->x509_name, nid, &utf8string);
+    if (len < 0)
+        return NULL;
+    else if (len == 0)
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    else
+        return PyUnicode_Decode(utf8string, len, "utf-8", NULL);
+}
+
+/*
+ * Set attribute
+ *
+ * Arguments: self  - The X509Name object
+ *            name  - The attribute name
+ *            value - The value to set
+ */
+static int
+crypto_X509Name_setattr(crypto_X509NameObj *self, char *name, PyObject *value)
+{
+    int nid;
+    char *buffer;
+
+    if ((nid = OBJ_txt2nid(name)) == NID_undef)
+    {
+        PyErr_SetString(PyExc_AttributeError, "No such attribute");
+        return -1;
+    }
+
+    /* Something of a hack to get nice unicode behaviour */
+    if (!PyArg_Parse(value, "es:setattr", "utf-8", &buffer))
+        return -1;
+    
+    return set_name_by_nid(self->x509_name, nid, buffer);
+}
+
+/*
+ * Compare two X509Name structures.
+ *
+ * Arguments: n - The first X509Name
+ *            m - The second X509Name
+ * Returns:   <0 if n < m, 0 if n == m and >0 if n > m
+ */
+static int
+crypto_X509Name_compare(crypto_X509NameObj *n, crypto_X509NameObj *m)
+{
+    return X509_NAME_cmp(n->x509_name, m->x509_name);
+}
+
+/*
+ * String representation of an X509Name
+ *
+ * Arguments: self - The X509Name object
+ * Returns:   A string representation of the object
+ */
+static PyObject *
+crypto_X509Name_repr(crypto_X509NameObj *self)
+{
+    char tmpbuf[512] = "";
+    char realbuf[512+64];
+
+    if (X509_NAME_oneline(self->x509_name, tmpbuf, 512) == NULL)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+    else
+    {
+        /* This is safe because tmpbuf is max 512 characters */
+        sprintf(realbuf, "<X509Name object '%s'>", tmpbuf);
+        return PyString_FromString(realbuf);
+    }
+}
+
+/*
+ * Call the visitproc on all contained objects.
+ *
+ * Arguments: self - The Connection object
+ *            visit - Function to call
+ *            arg - Extra argument to visit
+ * Returns:   0 if all goes well, otherwise the return code from the first
+ *            call that gave non-zero result.
+ */
+static int
+crypto_X509Name_traverse(crypto_X509NameObj *self, visitproc visit, void *arg)
+{
+    int ret = 0;
+
+    if (ret == 0 && self->parent_cert != NULL)
+        ret = visit(self->parent_cert, arg);
+    return ret;
+}
+
+/*
+ * Decref all contained objects and zero the pointers.
+ *
+ * Arguments: self - The Connection object
+ * Returns:   Always 0.
+ */
+static int
+crypto_X509Name_clear(crypto_X509NameObj *self)
+{
+    Py_XDECREF(self->parent_cert);
+    self->parent_cert = NULL;
+    return 0;
+}
+
+/*
+ * Deallocate the memory used by the X509Name object
+ *
+ * Arguments: self - The X509Name object
+ * Returns:   None
+ */
+static void
+crypto_X509Name_dealloc(crypto_X509NameObj *self)
+{
+    PyObject_GC_UnTrack(self);
+    /* Sometimes we don't have to dealloc this */
+    if (self->dealloc)
+        X509_NAME_free(self->x509_name);
+
+    crypto_X509Name_clear(self);
+
+    PyObject_GC_Del(self);
+}
+
+PyTypeObject crypto_X509Name_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "X509Name",
+    sizeof(crypto_X509NameObj),
+    0,
+    (destructor)crypto_X509Name_dealloc,
+    NULL, /* print */
+    (getattrfunc)crypto_X509Name_getattr,
+    (setattrfunc)crypto_X509Name_setattr,
+    (cmpfunc)crypto_X509Name_compare,
+    (reprfunc)crypto_X509Name_repr,
+    NULL, /* as_number */
+    NULL, /* as_sequence */
+    NULL, /* as_mapping */
+    NULL, /* hash */
+    NULL, /* call */
+    NULL, /* str */
+    NULL, /* getattro */
+    NULL, /* setattro */
+    NULL, /* as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+    NULL, /* doc */
+    (traverseproc)crypto_X509Name_traverse,
+    (inquiry)crypto_X509Name_clear,
+};
+
+
+/*
+ * Initialize the X509Name part of the crypto module
+ *
+ * Arguments: dict - The crypto module dictionary
+ * Returns:   None
+ */
+int
+init_crypto_x509name(PyObject *dict)
+{
+    crypto_X509Name_Type.ob_type = &PyType_Type;
+    Py_INCREF(&crypto_X509Name_Type);
+    PyDict_SetItemString(dict, "X509NameType", (PyObject *)&crypto_X509Name_Type);
+    return 1;
+}
diff --git a/src/crypto/x509name.h b/src/crypto/x509name.h
new file mode 100644
index 0000000..362ce35
--- /dev/null
+++ b/src/crypto/x509name.h
@@ -0,0 +1,33 @@
+/*
+ * x509name.h
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Export X.509 name functions and data structures.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ *
+ * @(#) $Id: x509name.h,v 1.8 2002/09/04 22:24:59 iko Exp $
+ */
+#ifndef PyOpenSSL_crypto_X509NAME_H_
+#define PyOpenSSL_crypto_X509NAME_H_
+
+#include <Python.h>
+#include <openssl/ssl.h>
+
+extern  int     init_crypto_x509name       (PyObject *);
+
+extern  PyTypeObject      crypto_X509Name_Type;
+
+#define crypto_X509Name_Check(v) ((v)->ob_type == &crypto_X509Name_Type)
+
+typedef struct {
+    PyObject_HEAD
+    X509_NAME           *x509_name;
+    int                  dealloc;
+    PyObject            *parent_cert;
+} crypto_X509NameObj;
+
+
+#endif
diff --git a/src/crypto/x509req.c b/src/crypto/x509req.c
new file mode 100644
index 0000000..d551de4
--- /dev/null
+++ b/src/crypto/x509req.c
@@ -0,0 +1,324 @@
+/*
+ * x509req.c
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * X.509 Request handling, mostly thin wrapping.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ */
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+static char *CVSid = "@(#) $Id: x509req.c,v 1.15 2002/09/04 22:24:59 iko Exp $";
+
+
+static char crypto_X509Req_get_subject_doc[] = "\n\
+Create an X509Name object for the subject of the certificate request\n\
+\n\
+Arguments: self - The X509Req object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   An X509Name object\n\
+";
+
+static PyObject *
+crypto_X509Req_get_subject(crypto_X509ReqObj *self, PyObject *args)
+{
+    crypto_X509NameObj *crypto_X509Name_New(X509_NAME *, int);
+    X509_NAME *name;
+
+    if (!PyArg_ParseTuple(args, ":get_subject"))
+        return NULL;
+
+    if ((name = X509_REQ_get_subject_name(self->x509_req)) == NULL)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    return (PyObject *)crypto_X509Name_New(name, 0);
+}
+
+static char crypto_X509Req_get_pubkey_doc[] = "\n\
+Get the public key from the certificate request\n\
+\n\
+Arguments: self - The X509Req object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The public key\n\
+";
+
+static PyObject *
+crypto_X509Req_get_pubkey(crypto_X509ReqObj *self, PyObject *args)
+{
+    crypto_PKeyObj *crypto_PKey_New(EVP_PKEY *, int);
+    EVP_PKEY *pkey;
+
+    if (!PyArg_ParseTuple(args, ":get_pubkey"))
+        return NULL;
+
+    if ((pkey = X509_REQ_get_pubkey(self->x509_req)) == NULL)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    return (PyObject *)crypto_PKey_New(pkey, 1);
+}
+
+static char crypto_X509Req_set_pubkey_doc[] = "\n\
+Set the public key of the certificate request\n\
+\n\
+Arguments: self - The X509Req object\n\
+           args - The Python argument tuple, should be:\n\
+             pkey - The public key to use\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509Req_set_pubkey(crypto_X509ReqObj *self, PyObject *args)
+{
+    crypto_PKeyObj *pkey;
+
+    if (!PyArg_ParseTuple(args, "O!:set_pubkey", &crypto_PKey_Type, &pkey))
+        return NULL;
+
+    if (!X509_REQ_set_pubkey(self->x509_req, pkey->pkey))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char crypto_X509Req_sign_doc[] = "\n\
+Sign the certificate request using the supplied key and digest\n\
+\n\
+Arguments: self - The X509Req object\n\
+           args - The Python argument tuple, should be:\n\
+             pkey   - The key to sign with\n\
+             digest - The message digest to use\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509Req_sign(crypto_X509ReqObj *self, PyObject *args)
+{
+    crypto_PKeyObj *pkey;
+    char *digest_name;
+    const EVP_MD *digest;
+
+    if (!PyArg_ParseTuple(args, "O!s:sign", &crypto_PKey_Type, &pkey,
+			  &digest_name))
+        return NULL;
+
+    if ((digest = EVP_get_digestbyname(digest_name)) == NULL)
+    {
+        PyErr_SetString(PyExc_ValueError, "No such digest method");
+        return NULL;
+    }
+
+    if (!X509_REQ_sign(self->x509_req, pkey->pkey, digest))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+ 
+static char crypto_X509Req_verify_doc[] = "\n\
+Verifies a certificate request using the supplied public key\n\
+ \n\
+Arguments: self - X509Req object\n\
+           args - The Python argument tuple, should be:\n\
+             key - a public key\n\
+Returns:   True, if the signature is correct, 0 otherwise.\n\
+";
+
+PyObject *
+crypto_X509Req_verify(crypto_X509ReqObj *self, PyObject *args)
+{
+    PyObject *obj;
+    crypto_PKeyObj *key;
+    int answer;
+
+    if (!PyArg_ParseTuple(args, "O!:verify", &crypto_PKey_Type, &obj)) 
+        return NULL;
+
+    key = (crypto_PKeyObj *)obj;
+
+    if ((answer = X509_REQ_verify(self->x509_req, key->pkey)) < 0)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    return PyInt_FromLong(answer);
+}
+
+static char crypto_X509Req_add_extensions_doc[] = "\n\
+Add extensions to the request.\n\
+\n\
+Arguments: self - X509Req object\n\
+           args - The Python argument tuple, should be:\n\
+             extensions - a sequence of X509Extension objects\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509Req_add_extensions(crypto_X509ReqObj *self, PyObject *args)
+{
+    PyObject *extensions;
+    crypto_X509ExtensionObj *ext;
+    STACK_OF(X509_EXTENSION) *exts;
+    int nr_of_extensions, i;
+
+    if (!PyArg_ParseTuple(args, "O:add_extensions", &extensions))
+        return NULL;
+
+    if (!PySequence_Check(extensions))
+    {
+        PyErr_SetString(PyExc_TypeError, "Expected a sequence");
+        return NULL;
+    }
+
+    /* Make a STACK_OF(X509_EXTENSION) from sequence */
+    if ((exts = sk_X509_EXTENSION_new_null()) == NULL)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    /* Put the extensions in a stack */
+    nr_of_extensions = PySequence_Length(extensions);
+
+    for (i = 0; i < nr_of_extensions; i++)
+    {
+        ext = (crypto_X509ExtensionObj *)PySequence_GetItem(extensions, i);
+	if (!(crypto_X509Extension_Check(ext)))
+        {
+            PyErr_SetString(PyExc_ValueError,
+                            "One of the elements is not an X509Extension");
+	    sk_X509_EXTENSION_free(exts);
+            return NULL;
+        }
+        sk_X509_EXTENSION_push(exts, ext->x509_extension);
+    }
+    
+    if (!X509_REQ_add_extensions(self->x509_req, exts))
+    {
+        sk_X509_EXTENSION_free(exts);
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    sk_X509_EXTENSION_free(exts);
+    
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+/*
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ *   {  'name', (PyCFunction)crypto_X509Req_name, METH_VARARGS }
+ * for convenience
+ */
+#define ADD_METHOD(name)        \
+    { #name, (PyCFunction)crypto_X509Req_##name, METH_VARARGS, crypto_X509Req_##name##_doc }
+static PyMethodDef crypto_X509Req_methods[] =
+{
+    ADD_METHOD(get_subject),
+    ADD_METHOD(get_pubkey),
+    ADD_METHOD(set_pubkey),
+    ADD_METHOD(sign),
+    ADD_METHOD(verify),
+    ADD_METHOD(add_extensions),
+    { NULL, NULL }
+};
+#undef ADD_METHOD
+
+
+/*
+ * Constructor for X509Req, never called by Python code directly
+ *
+ * Arguments: name    - A "real" X509_REQ object
+ *            dealloc - Boolean value to specify whether the destructor should
+ *                      free the "real" X509_REQ object
+ * Returns:   The newly created X509Req object
+ */
+crypto_X509ReqObj *
+crypto_X509Req_New(X509_REQ *req, int dealloc)
+{
+    crypto_X509ReqObj *self;
+
+    self = PyObject_New(crypto_X509ReqObj, &crypto_X509Req_Type);
+
+    if (self == NULL)
+        return NULL;
+
+    self->x509_req = req;
+    self->dealloc = dealloc;
+
+    return self;
+}
+
+/*
+ * Deallocate the memory used by the X509Req object
+ *
+ * Arguments: self - The X509Req object
+ * Returns:   None
+ */
+static void
+crypto_X509Req_dealloc(crypto_X509ReqObj *self)
+{
+    /* Sometimes we don't have to dealloc this */
+    if (self->dealloc)
+        X509_REQ_free(self->x509_req);
+
+    PyObject_Del(self);
+}
+
+
+/*
+ * Find attribute.
+ *
+ * Arguments: self - The X509Req object
+ *            name - The attribute name
+ * Returns:   A Python object for the attribute, or NULL if something went
+ *            wrong
+ */
+static PyObject *
+crypto_X509Req_getattr(crypto_X509ReqObj *self, char *name)
+{
+    return Py_FindMethod(crypto_X509Req_methods, (PyObject *)self, name);
+}
+
+PyTypeObject crypto_X509Req_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "X509Req",
+    sizeof(crypto_X509ReqObj),
+    0,
+    (destructor)crypto_X509Req_dealloc,
+    NULL, /* print */
+    (getattrfunc)crypto_X509Req_getattr,
+};
+
+
+/*
+ * Initialize the X509Req part of the crypto module
+ *
+ * Arguments: dict - The crypto module dictionary
+ * Returns:   None
+ */
+int
+init_crypto_x509req(PyObject *dict)
+{
+    crypto_X509Req_Type.ob_type = &PyType_Type;
+    Py_INCREF(&crypto_X509Req_Type);
+    PyDict_SetItemString(dict, "X509ReqType", (PyObject *)&crypto_X509Req_Type);
+    return 1;
+}
diff --git a/src/crypto/x509req.h b/src/crypto/x509req.h
new file mode 100644
index 0000000..db8043c
--- /dev/null
+++ b/src/crypto/x509req.h
@@ -0,0 +1,30 @@
+/*
+ * x509req.h
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Export X509 request functions and data structures.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * @(#) $Id: x509req.h,v 1.6 2002/09/04 22:24:59 iko Exp $
+ */
+#ifndef PyOpenSSL_SSL_X509REQ_H_
+#define PyOpenSSL_SSL_X509REQ_H_
+
+#include <Python.h>
+#include <openssl/ssl.h>
+
+extern  int       init_crypto_x509req   (PyObject *);
+
+extern  PyTypeObject      crypto_X509Req_Type;
+
+#define crypto_X509Req_Check(v) ((v)->ob_type == &crypto_X509Req_Type)
+
+typedef struct {
+    PyObject_HEAD
+    X509_REQ            *x509_req;
+    int                  dealloc;
+} crypto_X509ReqObj;
+
+
+#endif
diff --git a/src/crypto/x509store.c b/src/crypto/x509store.c
new file mode 100644
index 0000000..bd81f0a
--- /dev/null
+++ b/src/crypto/x509store.c
@@ -0,0 +1,145 @@
+/*
+ * x509store.c
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * X.509 Store handling, mostly thin wrapping.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ */
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+static char *CVSid = "@(#) $Id: x509store.c,v 1.9 2002/09/04 22:24:59 iko Exp $";
+
+static char crypto_X509Store_add_cert_doc[] = "\n\
+Add a certificate\n\
+\n\
+Arguments: self - The X509Store object\n\
+           args - The Python argument tuple, should be:\n\
+             cert - The certificate to add\n\
+Returns:   None\n\
+";
+
+static PyObject *
+crypto_X509Store_add_cert(crypto_X509StoreObj *self, PyObject *args)
+{
+    crypto_X509Obj *cert;
+
+    if (!PyArg_ParseTuple(args, "O!:add_cert", &crypto_X509_Type, &cert))
+        return NULL;
+
+    if (!X509_STORE_add_cert(self->x509_store, cert->x509))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+
+/*
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ *   {  'name', (PyCFunction)crypto_X509Store_name, METH_VARARGS }
+ * for convenience
+ */
+#define ADD_METHOD(name)        \
+    { #name, (PyCFunction)crypto_X509Store_##name, METH_VARARGS, crypto_X509Store_##name##_doc }
+static PyMethodDef crypto_X509Store_methods[] =
+{
+    ADD_METHOD(add_cert),
+    { NULL, NULL }
+};
+#undef ADD_METHOD
+
+
+/*
+ * Constructor for X509Store, never called by Python code directly
+ *
+ * Arguments: name    - A "real" X509_STORE object
+ *            dealloc - Boolean value to specify whether the destructor should
+ *                      free the "real" X509_STORE object
+ * Returns:   The newly created X509Store object
+ */
+crypto_X509StoreObj *
+crypto_X509Store_New(X509_STORE *store, int dealloc)
+{
+    crypto_X509StoreObj *self;
+
+    self = PyObject_New(crypto_X509StoreObj, &crypto_X509Store_Type);
+
+    if (self == NULL)
+        return NULL;
+
+    self->x509_store = store;
+    self->dealloc = dealloc;
+
+    return self;
+}
+
+/*
+ * Deallocate the memory used by the X509Store object
+ *
+ * Arguments: self - The X509Store object
+ * Returns:   None
+ */
+static void
+crypto_X509Store_dealloc(crypto_X509StoreObj *self)
+{
+    /* Sometimes we don't have to dealloc this */
+    if (self->dealloc)
+        X509_STORE_free(self->x509_store);
+
+    PyObject_Del(self);
+}
+
+
+/*
+ * Find attribute.
+ *
+ * Arguments: self - The X509Store object
+ *            name - The attribute name
+ * Returns:   A Python object for the attribute, or NULL if something went
+ *            wrong
+ */
+static PyObject *
+crypto_X509Store_getattr(crypto_X509StoreObj *self, char *name)
+{
+    return Py_FindMethod(crypto_X509Store_methods, (PyObject *)self, name);
+}
+
+PyTypeObject crypto_X509Store_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "X509Store",
+    sizeof(crypto_X509StoreObj),
+    0,
+    (destructor)crypto_X509Store_dealloc,
+    NULL, /* print */
+    (getattrfunc)crypto_X509Store_getattr,
+    NULL, /* setattr */
+    NULL, /* compare */
+    NULL, /* repr */
+    NULL, /* as_number */
+    NULL, /* as_sequence */
+    NULL, /* as_mapping */
+    NULL  /* hash */
+};
+
+
+/*
+ * Initialize the X509Store part of the crypto module
+ *
+ * Arguments: dict - The crypto module dictionary
+ * Returns:   None
+ */
+int
+init_crypto_x509store(PyObject *dict)
+{
+    crypto_X509Store_Type.ob_type = &PyType_Type;
+    Py_INCREF(&crypto_X509Store_Type);
+    PyDict_SetItemString(dict, "X509StoreType", (PyObject *)&crypto_X509Store_Type);
+    return 1;
+}
diff --git a/src/crypto/x509store.h b/src/crypto/x509store.h
new file mode 100644
index 0000000..9ed5073
--- /dev/null
+++ b/src/crypto/x509store.h
@@ -0,0 +1,30 @@
+/*
+ * x509store.h
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Export X509 store functions and data structures.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * @(#) $Id: x509store.h,v 1.4 2002/09/04 22:24:59 iko Exp $
+ */
+#ifndef PyOpenSSL_SSL_X509STORE_H_
+#define PyOpenSSL_SSL_X509STORE_H_
+
+#include <Python.h>
+#include <openssl/ssl.h>
+
+extern  int     init_crypto_x509store       (PyObject *);
+
+extern  PyTypeObject      crypto_X509Store_Type;
+
+#define crypto_X509Store_Check(v) ((v)->ob_type == &crypto_X509Store_Type)
+
+typedef struct {
+    PyObject_HEAD
+    X509_STORE           *x509_store;
+    int                  dealloc;
+} crypto_X509StoreObj;
+
+
+#endif
diff --git a/src/pymemcompat.h b/src/pymemcompat.h
new file mode 100644
index 0000000..24221ec
--- /dev/null
+++ b/src/pymemcompat.h
@@ -0,0 +1,86 @@
+/* The idea of this file is that you bundle it with your extension,
+   #include it, program to Python 2.3's memory API and have your
+   extension build with any version of Python from 1.5.2 through to
+   2.3 (and hopefully beyond). */
+
+#ifndef Py_PYMEMCOMPAT_H
+#define Py_PYMEMCOMPAT_H
+
+#include "Python.h"
+
+/* There are three "families" of memory API: the "raw memory", "object
+   memory" and "object" families.  (This is ignoring the matter of the
+   cycle collector, about which more is said below).
+
+   Raw Memory:
+
+       PyMem_Malloc, PyMem_Realloc, PyMem_Free
+
+   Object Memory:
+
+       PyObject_Malloc, PyObject_Realloc, PyObject_Free
+
+   Object:
+
+       PyObject_New, PyObject_NewVar, PyObject_Del
+
+   The raw memory and object memory allocators both mimic the
+   malloc/realloc/free interface from ANSI C, but the object memory
+   allocator can (and, since 2.3, does by default) use a different
+   allocation strategy biased towards lots of lots of "small"
+   allocations.
+
+   The object family is used for allocating Python objects, and the
+   initializers take care of some basic initialization (setting the
+   refcount to 1 and filling out the ob_type field) as well as having
+   a somewhat different interface.
+
+   Do not mix the families!  E.g. do not allocate memory with
+   PyMem_Malloc and free it with PyObject_Free.  You may get away with
+   it quite a lot of the time, but there *are* scenarios where this
+   will break.  You Have Been Warned. 
+
+   Also, in many versions of Python there are an insane amount of
+   memory interfaces to choose from.  Use the ones described above. */
+
+#if PY_VERSION_HEX < 0x01060000
+/* raw memory interface already present */
+
+/* there is no object memory interface in 1.5.2 */
+#define PyObject_Malloc		PyMem_Malloc
+#define PyObject_Realloc	PyMem_Realloc
+#define PyObject_Free		PyMem_Free
+
+/* the object interface is there, but the names have changed */
+#define PyObject_New		PyObject_NEW
+#define PyObject_NewVar		PyObject_NEW_VAR
+#define PyObject_Del		PyMem_Free
+#endif
+
+/* If your object is a container you probably want to support the
+   cycle collector, which was new in Python 2.0.
+
+   Unfortunately, the interface to the collector that was present in
+   Python 2.0 and 2.1 proved to be tricky to use, and so changed in
+   2.2 -- in a way that can't easily be papered over with macros.
+
+   This file contains macros that let you program to the 2.2 GC API.
+   Your module will compile against any Python since version 1.5.2,
+   but the type will only participate in the GC in versions 2.2 and
+   up.  Some work is still necessary on your part to only fill out the
+   tp_traverse and tp_clear fields when they exist and set tp_flags
+   appropriately.
+
+   It is possible to support both the 2.0 and 2.2 GC APIs, but it's
+   not pretty and this comment block is too narrow to contain a
+   desciption of what's required... */
+
+#if PY_VERSION_HEX < 0x020200B1
+#define PyObject_GC_New         PyObject_New
+#define PyObject_GC_NewVar      PyObject_NewVar
+#define PyObject_GC_Del         PyObject_Del
+#define PyObject_GC_Track(op)
+#define PyObject_GC_UnTrack(op)
+#endif
+
+#endif /* !Py_PYMEMCOMPAT_H */
diff --git a/src/rand/rand.c b/src/rand/rand.c
new file mode 100644
index 0000000..a87f2f9
--- /dev/null
+++ b/src/rand/rand.c
@@ -0,0 +1,240 @@
+/*
+ * rand.c
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * PRNG management routines, thin wrappers.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ */
+#include <Python.h>
+
+/* 
+ * In order to get the RAND_screen definition from the rand.h
+ * WIN32 or WINDOWS needs to be defined, otherwise we get a
+ * warning.
+ */
+#ifdef MS_WINDOWS 
+#define WIN32
+#endif
+#include <openssl/rand.h>
+
+static char rand_doc[] = "\n\
+PRNG management routines, thin wrappers.\n\
+See the file RATIONALE for a short explanation of why this module was written.\n\
+";
+
+static char *CVSid = "@(#) $Id: rand.c,v 1.10 2002/07/08 11:06:01 martin Exp $";
+
+static char rand_add_doc[] = "\n\
+Add data with a given entropy to the PRNG\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             buffer  - Buffer with random data\n\
+             entropy - The entropy (in bytes) measurement of the buffer\n\
+Returns:   None\n\
+";
+
+static PyObject *
+rand_add(PyObject *spam, PyObject *args)
+{
+    char *buf;
+    int size;
+    double entropy;
+
+    if (!PyArg_ParseTuple(args, "s#d:add", &buf, &size, &entropy))
+        return NULL;
+
+    RAND_add(buf, size, entropy);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char rand_seed_doc[] = "\n\
+Alias for rand_add, with entropy equal to length\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             buffer - Buffer with random data\n\
+Returns:   None\n\
+";
+
+static PyObject *
+rand_seed(PyObject *spam, PyObject *args)
+{
+    char *buf;
+    int size;
+
+    if (!PyArg_ParseTuple(args, "s#:seed", &buf, &size))
+        return NULL;
+
+    RAND_seed(buf, size);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char rand_status_doc[] = "\n\
+Retrieve the status of the PRNG\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   True if the PRNG is seeded enough, false otherwise\n\
+";
+
+static PyObject *
+rand_status(PyObject *spam, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":status"))
+        return NULL;
+
+    return PyInt_FromLong((long)RAND_status());
+}
+
+#ifdef MS_WINDOWS
+static char rand_screen_doc[] = "\n\
+Add the current contents of the screen to the PRNG state. Availability:\n\
+Windows.\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   None\n\
+";
+
+static PyObject *
+rand_screen(PyObject *spam, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":screen"))
+        return NULL;
+
+    RAND_screen();
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+#endif
+
+static char rand_egd_doc[] = "\n\
+Query an entropy gathering daemon (EGD) for random data and add it to the\n\
+PRNG. I haven't found any problems when the socket is missing, the function\n\
+just returns 0.\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             path  - The path to the EGD socket\n\
+             bytes - (optional) The number of bytes to read, default is 255\n\
+Returns:   The number of bytes read (NB: a value of 0 isn't necessarily an\n\
+           error, check rand.status())\n\
+";
+
+static PyObject *
+rand_egd(PyObject *spam, PyObject *args)
+{
+    char *path;
+    int bytes = 255;
+
+    if (!PyArg_ParseTuple(args, "s|i:egd", &path, &bytes))
+        return NULL;
+
+    return PyInt_FromLong((long)RAND_egd_bytes(path, bytes));
+}
+
+static char rand_cleanup_doc[] = "\n\
+Erase the memory used by the PRNG.\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   None\n\
+";
+
+static PyObject *
+rand_cleanup(PyObject *spam, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":cleanup"))
+        return NULL;
+
+    RAND_cleanup();
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char rand_load_file_doc[] = "\n\
+Seed the PRNG with data from a file\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             filename - The file to read data from\n\
+             maxbytes - (optional) The number of bytes to read, default is\n\
+                        to read the entire file\n\
+Returns:   The number of bytes read\n\
+";
+
+static PyObject *
+rand_load_file(PyObject *spam, PyObject *args)
+{
+    char *filename;
+    int maxbytes = -1;
+
+    if (!PyArg_ParseTuple(args, "s|i:load_file", &filename, &maxbytes))
+        return NULL;
+
+    return PyInt_FromLong((long)RAND_load_file(filename, maxbytes));
+}
+
+static char rand_write_file_doc[] = "\n\
+Save PRNG state to a file\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             filename - The file to write data to\n\
+Returns:   The number of bytes written\n\
+";
+
+static PyObject *
+rand_write_file(PyObject *spam, PyObject *args)
+{
+    char *filename;
+
+    if (!PyArg_ParseTuple(args, "s:write_file", &filename))
+        return NULL;
+
+    return PyInt_FromLong((long)RAND_write_file(filename));
+}
+
+
+/* Methods in the OpenSSL.rand module */
+static PyMethodDef rand_methods[] = {
+    { "add",       (PyCFunction)rand_add,          METH_VARARGS, rand_add_doc },
+    { "seed",      (PyCFunction)rand_seed,         METH_VARARGS, rand_seed_doc },
+    { "status",    (PyCFunction)rand_status,       METH_VARARGS, rand_status_doc },
+#ifdef MS_WINDOWS
+    { "screen",    (PyCFunction)rand_screen,       METH_VARARGS, rand_screen_doc },
+#endif
+    { "egd",       (PyCFunction)rand_egd,          METH_VARARGS, rand_egd_doc },
+    { "cleanup",   (PyCFunction)rand_cleanup,      METH_VARARGS, rand_cleanup_doc },
+    { "load_file", (PyCFunction)rand_load_file,    METH_VARARGS, rand_load_file_doc },
+    { "write_file",(PyCFunction)rand_write_file,   METH_VARARGS, rand_write_file_doc },
+    { NULL, NULL }
+};
+
+
+/*
+ * Initialize the rand sub module
+ *
+ * Arguments: None
+ * Returns:   None
+ */
+void
+initrand(void)
+{
+    PyObject *module;
+
+    ERR_load_RAND_strings();
+
+    if ((module = Py_InitModule3("rand", rand_methods, rand_doc)) == NULL)
+        return;
+}
+
diff --git a/src/ssl/connection.c b/src/ssl/connection.c
new file mode 100755
index 0000000..96111aa
--- /dev/null
+++ b/src/ssl/connection.c
@@ -0,0 +1,1076 @@
+/*
+ * connection.c
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * SSL Connection objects and methods.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ */
+#include <Python.h>
+#define SSL_MODULE
+#include <openssl/err.h>
+#include "ssl.h"
+
+#ifndef MS_WINDOWS
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  if !(defined(__BEOS__) || defined(__CYGWIN__))
+#    include <netinet/tcp.h>
+#  endif
+#else
+#  include <winsock.h>
+#endif
+
+static char *CVSid = "@(#) $Id: connection.c,v 1.28 2004/08/06 10:21:56 martin Exp $";
+
+
+/**
+ * If we are on UNIX, fine, just use PyErr_SetFromErrno. If we are on Windows,
+ * apply some black winsock voodoo. This is basically just copied from Python's
+ * socketmodule.c
+ *
+ * Arguments: None
+ * Returns:   None
+ */
+static void
+syscall_from_errno(void)
+{
+#ifdef MS_WINDOWS
+    int errnum = WSAGetLastError();
+    if (errnum)
+    {
+        static struct { int num; const char *msg; } *msgp, msgs[] = {
+            { WSAEINTR, "Interrupted system call" },
+            { WSAEBADF, "Bad file descriptor" },
+            { WSAEACCES, "Permission denied" },
+            { WSAEFAULT, "Bad address" },
+            { WSAEINVAL, "Invalid argument" },
+            { WSAEMFILE, "Too many open files" },
+            { WSAEWOULDBLOCK, "The socket operation could not complete "
+                    "without blocking" },
+            { WSAEINPROGRESS, "Operation now in progress" },
+            { WSAEALREADY, "Operation already in progress" },
+            { WSAENOTSOCK, "Socket operation on non-socket" },
+            { WSAEDESTADDRREQ, "Destination address required" },
+            { WSAEMSGSIZE, "Message too long" },
+            { WSAEPROTOTYPE, "Protocol wrong type for socket" },
+            { WSAENOPROTOOPT, "Protocol not available" },
+            { WSAEPROTONOSUPPORT, "Protocol not supported" },
+            { WSAESOCKTNOSUPPORT, "Socket type not supported" },
+            { WSAEOPNOTSUPP, "Operation not supported" },
+            { WSAEPFNOSUPPORT, "Protocol family not supported" },
+            { WSAEAFNOSUPPORT, "Address family not supported" },
+            { WSAEADDRINUSE, "Address already in use" },
+            { WSAEADDRNOTAVAIL, "Can't assign requested address" },
+            { WSAENETDOWN, "Network is down" },
+            { WSAENETUNREACH, "Network is unreachable" },
+            { WSAENETRESET, "Network dropped connection on reset" },
+            { WSAECONNABORTED, "Software caused connection abort" },
+            { WSAECONNRESET, "Connection reset by peer" },
+            { WSAENOBUFS, "No buffer space available" },
+            { WSAEISCONN, "Socket is already connected" },
+            { WSAENOTCONN, "Socket is not connected" },
+            { WSAESHUTDOWN, "Can't send after socket shutdown" },
+            { WSAETOOMANYREFS, "Too many references: can't splice" },
+            { WSAETIMEDOUT, "Operation timed out" },
+            { WSAECONNREFUSED, "Connection refused" },
+            { WSAELOOP, "Too many levels of symbolic links" },
+            { WSAENAMETOOLONG, "File name too long" },
+            { WSAEHOSTDOWN, "Host is down" },
+            { WSAEHOSTUNREACH, "No route to host" },
+            { WSAENOTEMPTY, "Directory not empty" },
+            { WSAEPROCLIM, "Too many processes" },
+            { WSAEUSERS, "Too many users" },
+            { WSAEDQUOT, "Disc quota exceeded" },
+            { WSAESTALE, "Stale NFS file handle" },
+            { WSAEREMOTE, "Too many levels of remote in path" },
+            { WSASYSNOTREADY, "Network subsystem is unvailable" },
+            { WSAVERNOTSUPPORTED, "WinSock version is not supported" },
+            { WSANOTINITIALISED, "Successful WSAStartup() not yet performed" },
+            { WSAEDISCON, "Graceful shutdown in progress" },
+            /* Resolver errors */
+            { WSAHOST_NOT_FOUND, "No such host is known" },
+            { WSATRY_AGAIN, "Host not found, or server failed" },
+            { WSANO_RECOVERY, "Unexpected server error encountered" },
+            { WSANO_DATA, "Valid name without requested data" },
+            { WSANO_ADDRESS, "No address, look for MX record" },
+            { 0, NULL }
+        };
+        PyObject *v;
+        const char *msg = "winsock error";
+
+        for (msgp = msgs; msgp->msg; msgp++)
+        {
+            if (errnum == msgp->num)
+            {
+                msg = msgp->msg;
+                break;
+            }
+        }
+
+        v = Py_BuildValue("(is)", errnum, msg);
+        if (v != NULL)
+        {
+            PyErr_SetObject(ssl_SysCallError, v);
+            Py_DECREF(v);
+        }
+        return;
+    }
+#else
+    PyErr_SetFromErrno(ssl_SysCallError);
+#endif
+}
+
+/*
+ * Handle errors raised by SSL I/O functions. NOTE: Not SSL_shutdown ;)
+ *
+ * Arguments: ssl - The SSL object
+ *            err - The return code from SSL_get_error
+ *            ret - The return code from the SSL I/O function
+ * Returns:   None, the calling function should return NULL
+ */
+static void
+handle_ssl_errors(SSL *ssl, int err, int ret)
+{
+    switch (err)
+    {
+	/*
+         * Strange as it may seem, ZeroReturn is not an error per se. It means
+         * that the SSL Connection has been closed correctly (note, not the
+         * transport layer!), i.e. closure alerts have been exchanged. This is
+         * an exception since
+         *  + There's an SSL "error" code for it
+         *  + You have to deal with it in any case, close the transport layer
+         *    etc
+         */
+        case SSL_ERROR_ZERO_RETURN:
+            PyErr_SetNone(ssl_ZeroReturnError);
+            break;
+
+        /*
+         * The WantXYZ exceptions don't mean that there's an error, just that
+         * nothing could be read/written just now, maybe because the transport
+         * layer would block on the operation, or that there's not enough data
+         * available to fill an entire SSL record.
+         */
+        case SSL_ERROR_WANT_READ:
+            PyErr_SetNone(ssl_WantReadError);
+            break;
+
+        case SSL_ERROR_WANT_WRITE:
+            PyErr_SetNone(ssl_WantWriteError);
+            break;
+
+        case SSL_ERROR_WANT_X509_LOOKUP:
+            PyErr_SetNone(ssl_WantX509LookupError);
+            break;
+
+        case SSL_ERROR_SYSCALL:
+            if (ERR_peek_error() == 0)
+            {
+                if (ret < 0)
+                {
+                    syscall_from_errno();
+                }
+                else
+                {
+                    PyObject *v;
+
+                    v = Py_BuildValue("(is)", -1, "Unexpected EOF");
+                    if (v != NULL)
+                    {
+                        PyErr_SetObject(ssl_SysCallError, v);
+                        Py_DECREF(v);
+                    }
+                }
+                break;
+            }
+
+	/* NOTE: Fall-through here, we don't want to duplicate code, right? */
+
+        case SSL_ERROR_SSL:
+            ;
+        default:
+	    exception_from_error_queue();
+            break;
+    }
+}
+
+/*
+ * Here be member methods of the Connection "class"
+ */
+
+static char ssl_Connection_get_context_doc[] = "\n\
+Get session context\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   A Context object\n\
+";
+static PyObject *
+ssl_Connection_get_context(ssl_ConnectionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":get_context"))
+        return NULL;
+
+    Py_INCREF(self->context);
+    return (PyObject *)self->context;
+}
+
+static char ssl_Connection_pending_doc[] = "\n\
+Get the number of bytes that can be safely read from the connection\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   \n\
+";
+static PyObject *
+ssl_Connection_pending(ssl_ConnectionObj *self, PyObject *args)
+{
+    int ret;
+
+    if (!PyArg_ParseTuple(args, ":pending"))
+        return NULL;
+
+    ret = SSL_pending(self->ssl);
+    return PyInt_FromLong((long)ret);
+}
+    
+static char ssl_Connection_send_doc[] = "\n\
+Send data on the connection. NOTE: If you get one of the WantRead,\n\
+WantWrite or WantX509Lookup exceptions on this, you have to call the\n\
+method again with the SAME buffer.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be:\n\
+             buf   - The string to send\n\
+             flags - (optional) Included for compatability with the socket\n\
+                     API, the value is ignored\n\
+Returns:   The number of bytes written\n\
+";
+static PyObject *
+ssl_Connection_send(ssl_ConnectionObj *self, PyObject *args)
+{
+    char *buf;
+    int len, ret, err, flags;
+
+    if (!PyArg_ParseTuple(args, "s#|i:send", &buf, &len, &flags))
+        return NULL;
+
+    MY_BEGIN_ALLOW_THREADS(self->tstate)
+    ret = SSL_write(self->ssl, buf, len);
+    MY_END_ALLOW_THREADS(self->tstate)
+
+    if (PyErr_Occurred())
+    {
+        flush_error_queue();
+        return NULL;
+    }
+
+    err = SSL_get_error(self->ssl, ret);
+    if (err == SSL_ERROR_NONE)
+    {
+        return PyInt_FromLong((long)ret);
+    }
+    else
+    {
+        handle_ssl_errors(self->ssl, err, ret);
+        return NULL;
+    }
+}
+
+static char ssl_Connection_sendall_doc[] = "\n\
+Send \"all\" data on the connection. This calls send() repeatedly until\n\
+all data is sent. If an error occurs, it's impossible to tell how much data\n\
+has been sent.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be:\n\
+             buf   - The string to send\n\
+             flags - (optional) Included for compatability with the socket\n\
+                     API, the value is ignored\n\
+Returns:   The number of bytes written\n\
+";
+static PyObject *
+ssl_Connection_sendall(ssl_ConnectionObj *self, PyObject *args)
+{
+    char *buf;
+    int len, ret, err, flags;
+    PyObject *pyret = Py_None;
+
+    if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags))
+        return NULL;
+
+    do {
+        MY_BEGIN_ALLOW_THREADS(self->tstate)
+        ret = SSL_write(self->ssl, buf, len);
+        MY_END_ALLOW_THREADS(self->tstate)
+        if (PyErr_Occurred())
+        {
+            flush_error_queue();
+            pyret = NULL;
+            break;
+        }
+        err = SSL_get_error(self->ssl, ret);
+        if (err == SSL_ERROR_NONE)
+        {
+            buf += ret;
+            len -= ret;
+        }
+        else if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL ||
+                 err == SSL_ERROR_ZERO_RETURN)
+        {
+            handle_ssl_errors(self->ssl, err, ret);
+            pyret = NULL;
+            break;
+        }    
+    } while (len > 0);
+
+    Py_XINCREF(pyret);
+    return pyret;
+}
+
+static char ssl_Connection_recv_doc[] = "\n\
+Receive data on the connection. NOTE: If you get one of the WantRead,\n\
+WantWrite or WantX509Lookup exceptions on this, you have to call the\n\
+method again with the SAME buffer.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be:\n\
+             bufsiz - The maximum number of bytes to read\n\
+             flags  - (optional) Included for compatability with the socket\n\
+                      API, the value is ignored\n\
+Returns:   The number of bytes read\n\
+";
+static PyObject *
+ssl_Connection_recv(ssl_ConnectionObj *self, PyObject *args)
+{
+    int bufsiz, ret, err, flags;
+    PyObject *buf;
+
+    if (!PyArg_ParseTuple(args, "i|i:recv", &bufsiz, &flags))
+        return NULL;
+
+    buf = PyString_FromStringAndSize(NULL, bufsiz);
+    if (buf == NULL)
+        return NULL;
+
+    MY_BEGIN_ALLOW_THREADS(self->tstate)
+    ret = SSL_read(self->ssl, PyString_AsString(buf), bufsiz);
+    MY_END_ALLOW_THREADS(self->tstate)
+
+    if (PyErr_Occurred())
+    {
+        Py_DECREF(buf);
+        flush_error_queue();
+        return NULL;
+    }
+
+    err = SSL_get_error(self->ssl, ret);
+    if (err == SSL_ERROR_NONE)
+    {
+        if (ret != bufsiz && _PyString_Resize(&buf, ret) < 0)
+            return NULL;
+        return buf;
+    }
+    else
+    {
+        handle_ssl_errors(self->ssl, err, ret);
+        Py_DECREF(buf);
+        return NULL;
+    }
+}
+
+static char ssl_Connection_renegotiate_doc[] = "\n\
+Renegotiate the session\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   True if the renegotiation can be started, false otherwise\n\
+";
+static PyObject *
+ssl_Connection_renegotiate(ssl_ConnectionObj *self, PyObject *args)
+{
+    int ret;
+
+    if (!PyArg_ParseTuple(args, ":renegotiate"))
+        return NULL;
+
+    MY_BEGIN_ALLOW_THREADS(self->tstate);
+    ret = SSL_renegotiate(self->ssl);
+    MY_END_ALLOW_THREADS(self->tstate);
+
+    if (PyErr_Occurred())
+    {
+        flush_error_queue();
+        return NULL;
+    }
+
+    return PyInt_FromLong((long)ret);
+}
+
+static char ssl_Connection_do_handshake_doc[] = "\n\
+Perform an SSL handshake (usually called after renegotiate() or one of\n\
+set_*_state()). This can raise the same exceptions as send and recv.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   None.\n\
+";
+static PyObject *
+ssl_Connection_do_handshake(ssl_ConnectionObj *self, PyObject *args)
+{
+    int ret, err;
+
+    if (!PyArg_ParseTuple(args, ":do_handshake"))
+        return NULL;
+
+    MY_BEGIN_ALLOW_THREADS(self->tstate);
+    ret = SSL_do_handshake(self->ssl);
+    MY_END_ALLOW_THREADS(self->tstate);
+
+    if (PyErr_Occurred())
+    {
+        flush_error_queue();
+        return NULL;
+    }
+
+    err = SSL_get_error(self->ssl, ret);
+    if (err == SSL_ERROR_NONE)
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    else
+    {
+        handle_ssl_errors(self->ssl, err, ret);
+        return NULL;
+    }
+}
+
+#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00907000L
+static char ssl_Connection_renegotiate_pending_doc[] = "\n\
+Check if there's a renegotiation in progress, it will return false once\n\
+a renegotiation is finished.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   Whether there's a renegotiation in progress\n\
+";
+static PyObject *
+ssl_Connection_renegotiate_pending(ssl_ConnectionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":renegotiate_pending"))
+        return NULL;
+
+    return PyInt_FromLong((long)SSL_renegotiate_pending(self->ssl));
+}
+#endif
+
+static char ssl_Connection_total_renegotiations_doc[] = "\n\
+Find out the total number of renegotiations.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The number of renegotiations.\n\
+";
+static PyObject *
+ssl_Connection_total_renegotiations(ssl_ConnectionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":total_renegotiations"))
+        return NULL;
+
+    return PyInt_FromLong(SSL_total_renegotiations(self->ssl));
+}
+
+static char ssl_Connection_set_accept_state_doc[] = "\n\
+Set the connection to work in server mode. The handshake will be handled\n\
+automatically by read/write.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Connection_set_accept_state(ssl_ConnectionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":set_accept_state"))
+        return NULL;
+
+    SSL_set_accept_state(self->ssl);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char ssl_Connection_set_connect_state_doc[] = "\n\
+Set the connection to work in client mode. The handshake will be handled\n\
+automatically by read/write.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Connection_set_connect_state(ssl_ConnectionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":set_connect_state"))
+        return NULL;
+
+    SSL_set_connect_state(self->ssl);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char ssl_Connection_connect_doc[] = "\n\
+Connect to remote host and set up client-side SSL\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be:\n\
+             addr - A remote address\n\
+Returns:   What the socket's connect method returns\n\
+";
+static PyObject *
+ssl_Connection_connect(ssl_ConnectionObj *self, PyObject *args)
+{
+    PyObject *meth, *ret;
+
+    if ((meth = PyObject_GetAttrString(self->socket, "connect")) == NULL)
+        return NULL;
+
+    SSL_set_connect_state(self->ssl);
+
+    ret = PyEval_CallObject(meth, args);
+    Py_DECREF(meth);
+    if (ret == NULL)
+        return NULL;
+
+    return ret;
+}
+
+static char ssl_Connection_connect_ex_doc[] = "\n\
+Connect to remote host and set up client-side SSL. Note that if the socket's\n\
+connect_ex method doesn't return 0, SSL won't be initialized.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be:\n\
+             addr - A remove address\n\
+Returns:   What the socket's connect_ex method returns\n\
+";
+static PyObject *
+ssl_Connection_connect_ex(ssl_ConnectionObj *self, PyObject *args)
+{
+    PyObject *meth, *ret;
+
+    if ((meth = PyObject_GetAttrString(self->socket, "connect_ex")) == NULL)
+        return NULL;
+
+    SSL_set_connect_state(self->ssl);
+
+    ret = PyEval_CallObject(meth, args);
+    Py_DECREF(meth);
+    if (ret == NULL)
+        return NULL;
+    if (PyInt_Check(ret) && PyInt_AsLong(ret) != 0)
+        return ret;
+
+    return ret;
+}
+
+static char ssl_Connection_accept_doc[] = "\n\
+Accept incoming connection and set up SSL on it\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   A (conn,addr) pair where conn is a Connection and addr is an\n\
+           address\n\
+";
+static PyObject *
+ssl_Connection_accept(ssl_ConnectionObj *self, PyObject *args)
+{
+    PyObject *tuple, *socket, *address, *meth;
+    ssl_ConnectionObj *conn;
+
+    if ((meth = PyObject_GetAttrString(self->socket, "accept")) == NULL)
+        return NULL;
+    tuple = PyEval_CallObject(meth, args);
+    Py_DECREF(meth);
+    if (tuple == NULL)
+        return NULL;
+
+    socket  = PyTuple_GetItem(tuple, 0);
+    Py_INCREF(socket);
+    address = PyTuple_GetItem(tuple, 1);
+    Py_INCREF(address);
+    Py_DECREF(tuple);
+
+    conn = ssl_Connection_New(self->context, socket);
+    Py_DECREF(socket);
+    if (conn == NULL)
+    {
+        Py_DECREF(address);
+        return NULL;
+    }
+
+    SSL_set_accept_state(conn->ssl);
+
+    tuple = Py_BuildValue("(OO)", conn, address);
+
+    Py_DECREF(conn);
+    Py_DECREF(address);
+
+    return tuple;
+}
+
+static char ssl_Connection_shutdown_doc[] = "\n\
+Send closure alert\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   True if the shutdown completed successfully (i.e. both sides\n\
+           have sent closure alerts), false otherwise (i.e. you have to\n\
+           wait for a ZeroReturnError on a recv() method call\n\
+";
+static PyObject *
+ssl_Connection_shutdown(ssl_ConnectionObj *self, PyObject *args)
+{
+    int ret;
+
+    if (!PyArg_ParseTuple(args, ":shutdown"))
+        return NULL;
+
+    MY_BEGIN_ALLOW_THREADS(self->tstate)
+    ret = SSL_shutdown(self->ssl);
+    MY_END_ALLOW_THREADS(self->tstate)
+
+    if (PyErr_Occurred())
+    {
+        flush_error_queue();
+        return NULL;
+    }
+
+    if (ret < 0)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+    else if (ret > 0)
+    {
+        Py_INCREF(Py_True);
+        return Py_True;
+    }
+    else
+    {
+        Py_INCREF(Py_False);
+        return Py_False;
+    }
+}
+
+static char ssl_Connection_get_cipher_list_doc[] = "\n\
+Get the session cipher list\n\
+WARNING: API change! This used to take an optional argument, and return a\n\
+string.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   A list of cipher strings\n\
+";
+static PyObject *
+ssl_Connection_get_cipher_list(ssl_ConnectionObj *self, PyObject *args)
+{
+    int idx = 0;
+    const char *ret;
+    PyObject *lst, *item;
+
+    if (!PyArg_ParseTuple(args, ":get_cipher_list"))
+        return NULL;
+
+    lst = PyList_New(0);
+    while ((ret = SSL_get_cipher_list(self->ssl, idx)) != NULL)
+    {
+        item = PyString_FromString(ret);
+        PyList_Append(lst, item);
+        Py_DECREF(item);
+        idx++;
+    }
+    return lst;
+}
+
+static char ssl_Connection_makefile_doc[] = "\n\
+The makefile() method is not implemented, since there is no dup semantics\n\
+for SSL connections\n\
+XXX: Return self instead?\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   NULL\n\
+";
+static PyObject *
+ssl_Connection_makefile(ssl_ConnectionObj *self, PyObject *args)
+{
+    PyErr_SetString(PyExc_NotImplementedError, "Cannot make file object of SSL.Connection");
+    return NULL;
+}
+
+static char ssl_Connection_get_app_data_doc[] = "\n\
+Get application data\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The application data\n\
+";
+static PyObject *
+ssl_Connection_get_app_data(ssl_ConnectionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":get_app_data"))
+        return NULL;
+
+    Py_INCREF(self->app_data);
+    return self->app_data;
+}
+
+static char ssl_Connection_set_app_data_doc[] = "\n\
+Set application data\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be\n\
+             data - The application data\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Connection_set_app_data(ssl_ConnectionObj *self, PyObject *args)
+{
+    PyObject *data;
+
+    if (!PyArg_ParseTuple(args, "O:set_app_data", &data))
+        return NULL;
+
+    Py_DECREF(self->app_data);
+    Py_INCREF(data);
+    self->app_data = data;
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char ssl_Connection_state_string_doc[] = "\n\
+Get a verbose state description\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   A string representing the state\n\
+";
+static PyObject *
+ssl_Connection_state_string(ssl_ConnectionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":state_string"))
+        return NULL;
+
+    return PyString_FromString(SSL_state_string_long(self->ssl));
+}
+
+static char ssl_Connection_sock_shutdown_doc[] = "\n\
+See shutdown(2)\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be whatever the\n\
+                  socket's shutdown() method expects\n\
+Returns:   What the socket's shutdown() method returns\n\
+";
+static PyObject *
+ssl_Connection_sock_shutdown(ssl_ConnectionObj *self, PyObject *args)
+{
+    PyObject *meth, *ret;
+
+    if ((meth = PyObject_GetAttrString(self->socket, "shutdown")) == NULL)
+        return NULL;
+    ret = PyEval_CallObject(meth, args);
+    Py_DECREF(meth);
+    return ret;
+}
+
+static char ssl_Connection_get_peer_certificate_doc[] = "\n\
+Retrieve the other side's certificate (if any)\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The peer's certificate\n\
+";
+static PyObject *
+ssl_Connection_get_peer_certificate(ssl_ConnectionObj *self, PyObject *args)
+{
+    X509 *cert;
+
+    if (!PyArg_ParseTuple(args, ":get_peer_certificate"))
+        return NULL;
+
+    cert = SSL_get_peer_certificate(self->ssl);
+    if (cert != NULL)
+    {
+        return (PyObject *)crypto_X509_New(cert, 1);
+    }
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static char ssl_Connection_want_read_doc[] = "\n\
+Checks if more data has to be read from the transport layer to complete an\n\
+operation.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   True iff more data has to be read\n\
+";
+static PyObject *
+ssl_Connection_want_read(ssl_ConnectionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":want_read"))
+        return NULL;
+
+    return PyInt_FromLong((long)SSL_want_read(self->ssl));
+}
+
+static char ssl_Connection_want_write_doc[] = "\n\
+Checks if there is data to write to the transport layer to complete an\n\
+operation.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   True iff there is data to write\n\
+";
+static PyObject *
+ssl_Connection_want_write(ssl_ConnectionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":want_write"))
+        return NULL;
+
+    return PyInt_FromLong((long)SSL_want_write(self->ssl));
+}
+
+/*
+ * Member methods in the Connection object
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ *   {  'name', (PyCFunction)ssl_Connection_name, METH_VARARGS }
+ * for convenience
+ * ADD_ALIAS(name,real) creates an "alias" of the ssl_Connection_real
+ * function with the name 'name'
+ */
+#define ADD_METHOD(name)        \
+    { #name, (PyCFunction)ssl_Connection_##name, METH_VARARGS, ssl_Connection_##name##_doc }
+#define ADD_ALIAS(name,real)    \
+    { #name, (PyCFunction)ssl_Connection_##real, METH_VARARGS, ssl_Connection_##real##_doc }
+static PyMethodDef ssl_Connection_methods[] =
+{
+    ADD_METHOD(get_context),
+    ADD_METHOD(pending),
+    ADD_METHOD(send),
+    ADD_ALIAS (write, send),
+    ADD_METHOD(sendall),
+    ADD_METHOD(recv),
+    ADD_ALIAS (read, recv),
+    ADD_METHOD(renegotiate),
+    ADD_METHOD(do_handshake),
+#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00907000L
+    ADD_METHOD(renegotiate_pending),
+#endif
+    ADD_METHOD(total_renegotiations),
+    ADD_METHOD(connect),
+    ADD_METHOD(connect_ex),
+    ADD_METHOD(accept),
+    ADD_METHOD(shutdown),
+    ADD_METHOD(get_cipher_list),
+    ADD_METHOD(makefile),
+    ADD_METHOD(get_app_data),
+    ADD_METHOD(set_app_data),
+    ADD_METHOD(state_string),
+    ADD_METHOD(sock_shutdown),
+    ADD_METHOD(get_peer_certificate),
+    ADD_METHOD(want_read),
+    ADD_METHOD(want_write),
+    ADD_METHOD(set_accept_state),
+    ADD_METHOD(set_connect_state),
+    { NULL, NULL }
+};
+#undef ADD_ALIAS
+#undef ADD_METHOD
+
+
+/*
+ * Constructor for Connection objects
+ *
+ * Arguments: ctx  - An SSL Context to use for this connection
+ *            sock - The socket to use for transport layer
+ * Returns:   The newly created Connection object
+ */
+ssl_ConnectionObj *
+ssl_Connection_New(ssl_ContextObj *ctx, PyObject *sock)
+{
+    ssl_ConnectionObj *self;
+    int fd;
+
+    self = PyObject_GC_New(ssl_ConnectionObj, &ssl_Connection_Type);
+    if (self == NULL)
+        return NULL;
+
+    Py_INCREF(ctx);
+    self->context = ctx;
+
+    Py_INCREF(sock);
+    self->socket = sock;
+
+    self->ssl = NULL;
+
+    Py_INCREF(Py_None);
+    self->app_data = Py_None;
+
+    self->tstate = NULL;
+
+    fd = PyObject_AsFileDescriptor(self->socket);
+    if (fd < 0)
+    {
+        Py_DECREF(self);
+        return NULL;
+    }
+
+    self->ssl = SSL_new(self->context->ctx);
+    SSL_set_app_data(self->ssl, self);
+    SSL_set_fd(self->ssl, (SOCKET_T)fd);
+
+    PyObject_GC_Track(self);
+
+    return self;
+}
+
+/*
+ * Find attribute
+ *
+ * Arguments: self - The Connection object
+ *            name - The attribute name
+ * Returns:   A Python object for the attribute, or NULL if something went
+ *            wrong
+ */
+static PyObject *
+ssl_Connection_getattr(ssl_ConnectionObj *self, char *name)
+{
+    PyObject *meth;
+    
+    meth = Py_FindMethod(ssl_Connection_methods, (PyObject *)self, name);
+
+    if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError))
+    {
+        PyErr_Clear();
+        /* Try looking it up in the "socket" instead. */
+        meth = PyObject_GetAttrString(self->socket, name);
+    }
+
+    return meth;
+}
+
+/*
+ * Call the visitproc on all contained objects.
+ *
+ * Arguments: self - The Connection object
+ *            visit - Function to call
+ *            arg - Extra argument to visit
+ * Returns:   0 if all goes well, otherwise the return code from the first
+ *            call that gave non-zero result.
+ */
+static int
+ssl_Connection_traverse(ssl_ConnectionObj *self, visitproc visit, void *arg)
+{
+    int ret = 0;
+
+    if (ret == 0 && self->context != NULL)
+        ret = visit((PyObject *)self->context, arg);
+    if (ret == 0 && self->socket != NULL)
+        ret = visit(self->socket, arg);
+    if (ret == 0 && self->app_data != NULL)
+        ret = visit(self->app_data, arg);
+    return ret;
+}
+
+/*
+ * Decref all contained objects and zero the pointers.
+ *
+ * Arguments: self - The Connection object
+ * Returns:   Always 0.
+ */
+static int
+ssl_Connection_clear(ssl_ConnectionObj *self)
+{
+    Py_XDECREF(self->context);
+    self->context = NULL;
+    Py_XDECREF(self->socket);
+    self->socket = NULL;
+    Py_XDECREF(self->app_data);
+    self->app_data = NULL;
+    return 0;
+}
+
+/*
+ * Deallocate the memory used by the Connection object
+ *
+ * Arguments: self - The Connection object
+ * Returns:   None
+ */
+static void
+ssl_Connection_dealloc(ssl_ConnectionObj *self)
+{
+    PyObject_GC_UnTrack(self);
+    if (self->ssl != NULL)
+        SSL_free(self->ssl);
+    ssl_Connection_clear(self);
+    PyObject_GC_Del(self);
+}
+
+PyTypeObject ssl_Connection_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "Connection",
+    sizeof(ssl_ConnectionObj),
+    0,
+    (destructor)ssl_Connection_dealloc,
+    NULL, /* print */
+    (getattrfunc)ssl_Connection_getattr,
+    NULL, /* setattr */
+    NULL, /* compare */
+    NULL, /* repr */
+    NULL, /* as_number */
+    NULL, /* as_sequence */
+    NULL, /* as_mapping */
+    NULL, /* hash */
+    NULL, /* call */
+    NULL, /* str */
+    NULL, /* getattro */
+    NULL, /* setattro */
+    NULL, /* as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+    NULL, /* doc */
+    (traverseproc)ssl_Connection_traverse,
+    (inquiry)ssl_Connection_clear,
+};
+
+
+/*
+ * Initiailze the Connection part of the SSL sub module
+ *
+ * Arguments: dict - Dictionary of the OpenSSL.SSL module
+ * Returns:   1 for success, 0 otherwise
+ */
+int
+init_ssl_connection(PyObject *dict)
+{
+    ssl_Connection_Type.ob_type = &PyType_Type;
+    Py_INCREF(&ssl_Connection_Type);
+    if (PyDict_SetItemString(dict, "ConnectionType", (PyObject *)&ssl_Connection_Type) != 0)
+        return 0;
+
+    return 1;
+}
+
diff --git a/src/ssl/connection.h b/src/ssl/connection.h
new file mode 100644
index 0000000..dedb73e
--- /dev/null
+++ b/src/ssl/connection.h
@@ -0,0 +1,52 @@
+/*
+ * connection.h
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Export SSL Connection data structures and functions.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ *
+ * @(#) $Id: connection.h,v 1.11 2002/09/04 22:24:59 iko Exp $
+ */
+#ifndef PyOpenSSL_SSL_CONNECTION_H_
+#define PyOpenSSL_SSL_CONNECTION_H_
+
+#include <Python.h>
+#include <openssl/ssl.h>
+
+/* shamelessly stolen from socketmodule.c */
+#ifdef MS_WINDOWS
+#  include <winsock.h>
+typedef SOCKET SOCKET_T;
+#  ifdef MS_WIN64
+#    define SIZEOF_SOCKET_T 8
+#  else
+#    define SIZEOF_SOCKET_T 4
+#  endif
+#else
+typedef int SOCKET_T;
+#  define SIZEOF_SOCKET_T SIZEOF_INT
+#endif
+
+
+extern  int                      init_ssl_connection      (PyObject *);
+
+extern  PyTypeObject      ssl_Connection_Type;
+
+#define ssl_Connection_Check(v) ((v)->ob_type == &ssl_Connection_Type)
+
+typedef struct {
+    PyObject_HEAD
+    SSL                 *ssl;
+    ssl_ContextObj      *context;
+    PyObject            *socket;
+    PyThreadState       *tstate;
+    PyObject            *app_data;
+} ssl_ConnectionObj;
+
+
+
+#endif
+
diff --git a/src/ssl/context.c b/src/ssl/context.c
new file mode 100644
index 0000000..93f1c83
--- /dev/null
+++ b/src/ssl/context.c
@@ -0,0 +1,1074 @@
+/*
+ * context.c
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * SSL Context objects and their methods.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ */
+#include <Python.h>
+#define SSL_MODULE
+#include "ssl.h"
+
+static char *CVSid = "@(#) $Id: context.c,v 1.17 2004/08/06 10:21:56 martin Exp $";
+
+/*
+ * CALLBACKS
+ *
+ * Callbacks work like this: We provide a "global" callback in C which
+ * transforms the arguments into a Python argument tuple and calls the
+ * corresponding Python callback, and then parsing the return value back into
+ * things the C function can return.
+ *
+ * Three caveats:
+ *  + How do we find the Context object where the Python callbacks are stored?
+ *  + What about multithreading and execution frames?
+ *  + What about Python callbacks that raise exceptions?
+ *
+ * The solution to the first issue is trivial if the callback provides
+ * "userdata" functionality. Since the only callbacks that don't provide
+ * userdata do provide a pointer to an SSL structure, we can associate an SSL
+ * object and a Connection one-to-one via the SSL_set/get_app_data()
+ * functions.
+ *
+ * The solution to the other issue is to rewrite the Py_BEGIN_ALLOW_THREADS
+ * macro allowing it (or rather a new macro) to specify where to save the
+ * thread state (in our case, as a member of the Connection/Context object) so
+ * we can retrieve it again before calling the Python callback.
+ */
+
+/*
+ * Globally defined passphrase callback.
+ *
+ * Arguments: buf    - Buffer to store the returned passphrase in
+ *            maxlen - Maximum length of the passphrase
+ *            verify - If true, the passphrase callback should ask for a
+ *                     password twice and verify they're equal. If false, only
+ *                     ask once.
+ *            arg    - User data, always a Context object
+ * Returns:   The length of the password if successful, 0 otherwise
+ */
+static int
+global_passphrase_callback(char *buf, int maxlen, int verify, void *arg)
+{
+    int len;
+    char *str;
+    PyObject *argv, *ret = NULL;
+    ssl_ContextObj *ctx = (ssl_ContextObj *)arg;
+
+    /* The Python callback is called with a (maxlen,verify,userdata) tuple */
+    argv = Py_BuildValue("(iiO)", maxlen, verify, ctx->passphrase_userdata);
+    if (ctx->tstate != NULL)
+    {
+        /* We need to get back our thread state before calling the callback */
+        MY_END_ALLOW_THREADS(ctx->tstate);
+        ret = PyEval_CallObject(ctx->passphrase_callback, argv);
+        MY_BEGIN_ALLOW_THREADS(ctx->tstate);
+    }
+    else
+    {
+        ret = PyEval_CallObject(ctx->passphrase_callback, argv);
+    }
+    Py_DECREF(argv);
+
+    if (ret == NULL)
+        return 0;
+
+    if (!PyObject_IsTrue(ret))
+    {
+        Py_DECREF(ret);
+	return 0;
+    }
+
+    if (!PyString_Check(ret))
+    {
+        Py_DECREF(ret);
+        return 0;
+    }
+
+    len = PyString_Size(ret);
+    if (len > maxlen)
+        len = maxlen;
+
+    str = PyString_AsString(ret);
+    strncpy(buf, str, len);
+    Py_XDECREF(ret);
+
+    return len;
+}
+
+/*
+ * Globally defined verify callback
+ *
+ * Arguments: ok       - True everything is OK "so far", false otherwise
+ *            x509_ctx - Contains the certificate being checked, the current
+ *                       error number and depth, and the Connection we're
+ *                       dealing with
+ * Returns:   True if everything is okay, false otherwise
+ */
+static int
+global_verify_callback(int ok, X509_STORE_CTX *x509_ctx)
+{
+    PyObject *argv, *ret;
+    SSL *ssl;
+    ssl_ConnectionObj *conn;
+    crypto_X509Obj *cert;
+    int errnum, errdepth, c_ret;
+
+    cert = crypto_X509_New(X509_STORE_CTX_get_current_cert(x509_ctx), 0);
+    errnum = X509_STORE_CTX_get_error(x509_ctx);
+    errdepth = X509_STORE_CTX_get_error_depth(x509_ctx);
+    ssl = (SSL *)X509_STORE_CTX_get_app_data(x509_ctx);
+    conn = (ssl_ConnectionObj *)SSL_get_app_data(ssl);
+
+    argv = Py_BuildValue("(OOiii)", (PyObject *)conn, (PyObject *)cert,
+                                    errnum, errdepth, ok);
+    Py_DECREF(cert);
+    if (conn->tstate != NULL)
+    {
+        /* We need to get back our thread state before calling the callback */
+        MY_END_ALLOW_THREADS(conn->tstate);
+        ret = PyEval_CallObject(conn->context->verify_callback, argv);
+        MY_BEGIN_ALLOW_THREADS(conn->tstate);
+    }
+    else
+    {
+        ret = PyEval_CallObject(conn->context->verify_callback, argv);
+    }
+    Py_DECREF(argv);
+
+    if (ret == NULL)
+        return 0;
+
+    if (PyObject_IsTrue(ret))
+    {
+        X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
+        c_ret = 1;
+    }
+    else
+        c_ret = 0;
+
+    Py_DECREF(ret);
+
+    return c_ret;
+}
+
+/*
+ * Globally defined info callback
+ *
+ * Arguments: ssl   - The Connection
+ *            where - The part of the SSL code that called us
+ *            _ret  - The return code of the SSL function that called us
+ * Returns:   None
+ */
+static void
+global_info_callback(SSL *ssl, int where, int _ret)
+{
+    ssl_ConnectionObj *conn = (ssl_ConnectionObj *)SSL_get_app_data(ssl);
+    PyObject *argv, *ret;
+
+    argv = Py_BuildValue("(Oii)", (PyObject *)conn, where, _ret);
+    if (conn->tstate != NULL)
+    {
+        /* We need to get back our thread state before calling the callback */
+        MY_END_ALLOW_THREADS(conn->tstate);
+        ret = PyEval_CallObject(conn->context->info_callback, argv);
+        if (ret == NULL)
+            PyErr_Clear();
+        else
+            Py_DECREF(ret);
+        MY_BEGIN_ALLOW_THREADS(conn->tstate);
+    }
+    else
+    {
+        ret = PyEval_CallObject(conn->context->info_callback, argv);
+        if (ret == NULL)
+            PyErr_Clear();
+        else
+            Py_DECREF(ret);
+    }
+    Py_DECREF(argv);
+
+    return;
+}
+
+
+
+
+static char ssl_Context_load_verify_locations_doc[] = "\n\
+Let SSL know where we can find trusted certificates for the certificate\n\
+chain\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             cafile - Which file we can find the certificates\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_load_verify_locations(ssl_ContextObj *self, PyObject *args)
+{
+    char *cafile;
+
+    if (!PyArg_ParseTuple(args, "s:load_verify_locations", &cafile))
+        return NULL;
+
+    if (!SSL_CTX_load_verify_locations(self->ctx, cafile, NULL))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static char ssl_Context_set_passwd_cb_doc[] = "\n\
+Set the passphrase callback\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             callback - The Python callback to use\n\
+             userdata - (optional) A Python object which will be given as\n\
+                        argument to the callback\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_set_passwd_cb(ssl_ContextObj *self, PyObject *args)
+{
+    PyObject *callback = NULL, *userdata = Py_None;
+
+    if (!PyArg_ParseTuple(args, "O|O:set_passwd_cb", &callback, &userdata))
+        return NULL;
+
+    if (!PyCallable_Check(callback))
+    {
+        PyErr_SetString(PyExc_TypeError, "expected PyCallable");
+        return NULL;
+    }
+
+    Py_DECREF(self->passphrase_callback);
+    Py_INCREF(callback);
+    self->passphrase_callback = callback;
+    SSL_CTX_set_default_passwd_cb(self->ctx, global_passphrase_callback);
+
+    Py_DECREF(self->passphrase_userdata);
+    Py_INCREF(userdata);
+    self->passphrase_userdata = userdata;
+    SSL_CTX_set_default_passwd_cb_userdata(self->ctx, (void *)self);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char ssl_Context_use_certificate_chain_file_doc[] = "\n\
+Load a certificate chain from a file\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             certfile - The name of the certificate chain file\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_use_certificate_chain_file(ssl_ContextObj *self, PyObject *args)
+{
+    char *certfile;
+
+    if (!PyArg_ParseTuple(args, "s:use_certificate_chain_file", &certfile))
+        return NULL;
+
+    if (!SSL_CTX_use_certificate_chain_file(self->ctx, certfile))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+
+static char ssl_Context_use_certificate_file_doc[] = "\n\
+Load a certificate from a file\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             certfile - The name of the certificate file\n\
+             filetype - (optional) The encoding of the file, default is PEM\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_use_certificate_file(ssl_ContextObj *self, PyObject *args)
+{
+    char *certfile;
+    int filetype = SSL_FILETYPE_PEM;
+
+    if (!PyArg_ParseTuple(args, "s|i:use_certificate_file", &certfile, &filetype))
+        return NULL;
+
+    if (!SSL_CTX_use_certificate_file(self->ctx, certfile, filetype))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static char ssl_Context_use_certificate_doc[] = "\n\
+Load a certificate from a X509 object\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             cert - The X509 object\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_use_certificate(ssl_ContextObj *self, 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, "O:use_certificate", &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;
+    }
+    else
+	if (!PyArg_ParseTuple(args, "O!:use_certificate", crypto_X509_type,
+			      &cert))
+	    return NULL;
+    
+    if (!SSL_CTX_use_certificate(self->ctx, cert->x509))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static char ssl_Context_use_privatekey_file_doc[] = "\n\
+Load a private key from a file\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             keyfile  - The name of the key file\n\
+             filetype - (optional) The encoding of the file, default is PEM\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_use_privatekey_file(ssl_ContextObj *self, PyObject *args)
+{
+    char *keyfile;
+    int filetype = SSL_FILETYPE_PEM, ret;
+
+    if (!PyArg_ParseTuple(args, "s|i:use_privatekey_file", &keyfile, &filetype))
+        return NULL;
+
+    MY_BEGIN_ALLOW_THREADS(self->tstate);
+    ret = SSL_CTX_use_PrivateKey_file(self->ctx, keyfile, filetype);
+    MY_END_ALLOW_THREADS(self->tstate);
+
+    if (PyErr_Occurred())
+    {
+        flush_error_queue();
+        return NULL;
+    }
+
+    if (!ret)
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static char ssl_Context_use_privatekey_doc[] = "\n\
+Load a private key from a PKey object\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             pkey - The PKey object\n\
+Returns:   None\n\
+";
+static PyObject *
+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, "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;
+    }
+    else
+    if (!PyArg_ParseTuple(args, "O!:use_privatekey", crypto_PKey_type, &pkey))
+        return NULL;
+
+    if (!SSL_CTX_use_PrivateKey(self->ctx, pkey->pkey))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static char ssl_Context_check_privatekey_doc[] = "\n\
+Check that the private key and certificate match up\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   None (raises an exception if something's wrong)\n\
+";
+static PyObject *
+ssl_Context_check_privatekey(ssl_ContextObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":check_privatekey"))
+        return NULL;
+
+    if (!SSL_CTX_check_private_key(self->ctx))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static char ssl_Context_load_client_ca_doc[] = "\n\
+Load the trusted certificates that will be sent to the client (basically\n\
+telling the client \"These are the guys I trust\")\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             cafile - The name of the certificates file\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_load_client_ca(ssl_ContextObj *self, PyObject *args)
+{
+    char *cafile;
+
+    if (!PyArg_ParseTuple(args, "s:load_client_ca", &cafile))
+        return NULL;
+
+    SSL_CTX_set_client_CA_list(self->ctx, SSL_load_client_CA_file(cafile));
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char ssl_Context_set_session_id_doc[] = "\n\
+Set the session identifier, this is needed if you want to do session\n\
+resumption (which, ironically, isn't implemented yet)\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             buf - A Python object that can be safely converted to a string\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_set_session_id(ssl_ContextObj *self, PyObject *args)
+{
+    char *buf;
+    int len;
+
+    if (!PyArg_ParseTuple(args, "s#:set_session_id", &buf, &len))
+        return NULL;
+
+    if (!SSL_CTX_set_session_id_context(self->ctx, buf, len))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static char ssl_Context_set_verify_doc[] = "\n\
+Set the verify mode and verify callback\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             mode     - The verify mode, this is either SSL_VERIFY_NONE or\n\
+                        SSL_VERIFY_PEER combined with possible other flags\n\
+             callback - The Python callback to use\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_set_verify(ssl_ContextObj *self, PyObject *args)
+{
+    int mode;
+    PyObject *callback = NULL;
+
+    if (!PyArg_ParseTuple(args, "iO:set_verify", &mode, &callback))
+        return NULL;
+
+    if (!PyCallable_Check(callback))
+    {
+        PyErr_SetString(PyExc_TypeError, "expected PyCallable");
+        return NULL;
+    }
+
+    Py_DECREF(self->verify_callback);
+    Py_INCREF(callback);
+    self->verify_callback = callback;
+    SSL_CTX_set_verify(self->ctx, mode, global_verify_callback);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char ssl_Context_set_verify_depth_doc[] = "\n\
+Set the verify depth\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             depth - An integer specifying the verify depth\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_set_verify_depth(ssl_ContextObj *self, PyObject *args)
+{
+    int depth;
+
+    if (!PyArg_ParseTuple(args, "i:set_verify_depth", &depth))
+        return NULL;
+
+    SSL_CTX_set_verify_depth(self->ctx, depth);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char ssl_Context_get_verify_mode_doc[] = "\n\
+Get the verify mode\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The verify mode\n\
+";
+static PyObject *
+ssl_Context_get_verify_mode(ssl_ContextObj *self, PyObject *args)
+{
+    int mode;
+
+    if (!PyArg_ParseTuple(args, ":get_verify_mode"))
+        return NULL;
+
+    mode = SSL_CTX_get_verify_mode(self->ctx);
+    return PyInt_FromLong((long)mode);
+}
+
+static char ssl_Context_get_verify_depth_doc[] = "\n\
+Get the verify depth\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The verify depth\n\
+";
+static PyObject *
+ssl_Context_get_verify_depth(ssl_ContextObj *self, PyObject *args)
+{
+    int depth;
+
+    if (!PyArg_ParseTuple(args, ":get_verify_depth"))
+        return NULL;
+
+    depth = SSL_CTX_get_verify_depth(self->ctx);
+    return PyInt_FromLong((long)depth);
+}
+
+static char ssl_Context_load_tmp_dh_doc[] = "\n\
+Load parameters for Ephemeral Diffie-Hellman\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             dhfile - The file to load EDH parameters from\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_load_tmp_dh(ssl_ContextObj *self, PyObject *args)
+{
+    char *dhfile;
+    BIO *bio;
+    DH *dh;
+
+    if (!PyArg_ParseTuple(args, "s:load_tmp_dh", &dhfile))
+        return NULL;
+
+    bio = BIO_new_file(dhfile, "r");
+    if (bio == NULL)
+        return PyErr_NoMemory();
+
+    dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+    SSL_CTX_set_tmp_dh(self->ctx, dh);
+    DH_free(dh);
+    BIO_free(bio);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char ssl_Context_set_cipher_list_doc[] = "\n\
+Change the cipher list\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             cipher_list - A cipher list, see ciphers(1)\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_set_cipher_list(ssl_ContextObj *self, PyObject *args)
+{
+    char *cipher_list;
+
+    if (!PyArg_ParseTuple(args, "s:set_cipher_list", &cipher_list))
+        return NULL;
+
+    if (!SSL_CTX_set_cipher_list(self->ctx, cipher_list))
+    {
+        exception_from_error_queue();
+        return NULL;
+    }
+    else
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static char ssl_Context_set_timeout_doc[] = "\n\
+Set session timeout\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             t - The timeout in seconds\n\
+Returns:   The previous session timeout\n\
+";
+static PyObject *
+ssl_Context_set_timeout(ssl_ContextObj *self, PyObject *args)
+{
+    long t, ret;
+
+    if (!PyArg_ParseTuple(args, "l:set_timeout", &t))
+        return NULL;
+
+    ret = SSL_CTX_set_timeout(self->ctx, t);
+    return PyLong_FromLong(ret);
+}
+
+static char ssl_Context_get_timeout_doc[] = "\n\
+Get the session timeout\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The session timeout\n\
+";
+static PyObject *
+ssl_Context_get_timeout(ssl_ContextObj *self, PyObject *args)
+{
+    long ret;
+
+    if (!PyArg_ParseTuple(args, ":get_timeout"))
+        return NULL;
+
+    ret = SSL_CTX_get_timeout(self->ctx);
+    return PyLong_FromLong(ret);
+}
+
+static char ssl_Context_set_info_callback_doc[] = "\n\
+Set the info callback\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             callback - The Python callback to use\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_set_info_callback(ssl_ContextObj *self, PyObject *args)
+{
+    PyObject *callback;
+
+    if (!PyArg_ParseTuple(args, "O:set_info_callback", &callback))
+        return NULL;
+
+    if (!PyCallable_Check(callback))
+    {
+        PyErr_SetString(PyExc_TypeError, "expected PyCallable");
+        return NULL;
+    }
+
+    Py_DECREF(self->info_callback);
+    Py_INCREF(callback);
+    self->info_callback = callback;
+    SSL_CTX_set_info_callback(self->ctx, global_info_callback);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char ssl_Context_get_app_data_doc[] = "\n\
+Get the application data (supplied via set_app_data())\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   The application data\n\
+";
+static PyObject *
+ssl_Context_get_app_data(ssl_ContextObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":get_app_data"))
+        return NULL;
+
+    Py_INCREF(self->app_data);
+    return self->app_data;
+}
+
+static char ssl_Context_set_app_data_doc[] = "\n\
+Set the application data (will be returned from get_app_data())\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             data - Any Python object\n\
+Returns:   None\n\
+";
+static PyObject *
+ssl_Context_set_app_data(ssl_ContextObj *self, PyObject *args)
+{
+    PyObject *data;
+
+    if (!PyArg_ParseTuple(args, "O:set_app_data", &data))
+        return NULL;
+
+    Py_DECREF(self->app_data);
+    Py_INCREF(data);
+    self->app_data = data;
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char ssl_Context_get_cert_store_doc[] = "\n\
+Get the certificate store for the context\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   A X509Store object\n\
+";
+static PyObject *
+ssl_Context_get_cert_store(ssl_ContextObj *self, PyObject *args)
+{
+    X509_STORE *store;
+
+    if (!PyArg_ParseTuple(args, ":get_cert_store"))
+        return NULL;
+
+    if ((store = SSL_CTX_get_cert_store(self->ctx)) == NULL)
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    else
+    {
+        return (PyObject *)crypto_X509Store_New(store, 0);
+    }
+}
+
+static char ssl_Context_set_options_doc[] = "\n\
+Add options. Options set before are not cleared!\n\
+\n\
+Arguments: self - The Context object\n\
+           args - The Python argument tuple, should be:\n\
+             options - The options to add.\n\
+Returns:   The new option bitmask.\n\
+";
+static PyObject *
+ssl_Context_set_options(ssl_ContextObj *self, PyObject *args)
+{
+    long options;
+
+    if (!PyArg_ParseTuple(args, "l:set_options", &options))
+        return NULL;
+
+    return PyInt_FromLong(SSL_CTX_set_options(self->ctx, options));
+}
+
+
+/*
+ * Member methods in the Context object
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ *   {  'name', (PyCFunction)ssl_Context_name, METH_VARARGS }
+ * for convenience
+ * ADD_ALIAS(name,real) creates an "alias" of the ssl_Context_real
+ * function with the name 'name'
+ */
+#define ADD_METHOD(name) { #name, (PyCFunction)ssl_Context_##name, METH_VARARGS, ssl_Context_##name##_doc }
+static PyMethodDef ssl_Context_methods[] = {
+    ADD_METHOD(load_verify_locations),
+    ADD_METHOD(set_passwd_cb),
+    ADD_METHOD(use_certificate_chain_file),
+    ADD_METHOD(use_certificate_file),
+    ADD_METHOD(use_certificate),
+    ADD_METHOD(use_privatekey_file),
+    ADD_METHOD(use_privatekey),
+    ADD_METHOD(check_privatekey),
+    ADD_METHOD(load_client_ca),
+    ADD_METHOD(set_session_id),
+    ADD_METHOD(set_verify),
+    ADD_METHOD(set_verify_depth),
+    ADD_METHOD(get_verify_mode),
+    ADD_METHOD(get_verify_depth),
+    ADD_METHOD(load_tmp_dh),
+    ADD_METHOD(set_cipher_list),
+    ADD_METHOD(set_timeout),
+    ADD_METHOD(get_timeout),
+    ADD_METHOD(set_info_callback),
+    ADD_METHOD(get_app_data),
+    ADD_METHOD(set_app_data),
+    ADD_METHOD(get_cert_store),
+    ADD_METHOD(set_options),
+    { NULL, NULL }
+};
+#undef ADD_METHOD
+
+
+/* Constructor, takes an int specifying which method to use */
+/*
+ * Constructor for Context objects
+ *
+ * Arguments: i_method - The SSL method to use, one of the SSLv2_METHOD,
+ *                       SSLv3_METHOD, SSLv23_METHOD and TLSv1_METHOD
+ *                       constants.
+ * Returns:   The newly created Context object
+ */
+ssl_ContextObj *
+ssl_Context_New(int i_method)
+{
+    SSL_METHOD *method;
+    ssl_ContextObj *self;
+
+    switch (i_method)
+    {
+        /* Too bad TLSv1 servers can't accept SSLv3 clients */
+        case ssl_SSLv2_METHOD:    method = SSLv2_method();  break;
+        case ssl_SSLv23_METHOD:   method = SSLv23_method(); break;
+        case ssl_SSLv3_METHOD:    method = SSLv3_method();  break;
+        case ssl_TLSv1_METHOD:    method = TLSv1_method();  break;
+        default:
+            PyErr_SetString(PyExc_ValueError, "No such protocol");
+            return NULL;
+    }
+
+    self = PyObject_GC_New(ssl_ContextObj, &ssl_Context_Type);
+    if (self == NULL)
+        return (ssl_ContextObj *)PyErr_NoMemory();
+
+    self->ctx = SSL_CTX_new(method);
+    Py_INCREF(Py_None);
+    self->passphrase_callback = Py_None;
+    Py_INCREF(Py_None);
+    self->verify_callback = Py_None;
+    Py_INCREF(Py_None);
+    self->info_callback = Py_None;
+
+    Py_INCREF(Py_None);
+    self->passphrase_userdata = Py_None;
+
+    Py_INCREF(Py_None);
+    self->app_data = Py_None;
+
+    /* Some initialization that's required to operate smoothly in Python */
+    SSL_CTX_set_app_data(self->ctx, self);
+    SSL_CTX_set_mode(self->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE |
+                                SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |
+                                SSL_MODE_AUTO_RETRY);
+
+    self->tstate = NULL;
+    PyObject_GC_Track((PyObject *)self);
+
+    return self;
+}
+
+/*
+ * Find attribute
+ *
+ * Arguments: self - The Context object
+ *            name - The attribute name
+ * Returns:   A Python object for the attribute, or NULL if something went
+ *            wrong
+ */
+static PyObject *
+ssl_Context_getattr(ssl_ContextObj *self, char *name)
+{
+    return Py_FindMethod(ssl_Context_methods, (PyObject *)self, name);
+}
+
+/*
+ * Call the visitproc on all contained objects.
+ *
+ * Arguments: self - The Context object
+ *            visit - Function to call
+ *            arg - Extra argument to visit
+ * Returns:   0 if all goes well, otherwise the return code from the first
+ *            call that gave non-zero result.
+ */
+static int
+ssl_Context_traverse(ssl_ContextObj *self, visitproc visit, void *arg)
+{
+    int ret = 0;
+
+    if (ret == 0 && self->passphrase_callback != NULL)
+        ret = visit((PyObject *)self->passphrase_callback, arg);
+    if (ret == 0 && self->passphrase_userdata != NULL)
+        ret = visit((PyObject *)self->passphrase_userdata, arg);
+    if (ret == 0 && self->verify_callback != NULL)
+        ret = visit((PyObject *)self->verify_callback, arg);
+    if (ret == 0 && self->info_callback != NULL)
+        ret = visit((PyObject *)self->info_callback, arg);
+    if (ret == 0 && self->app_data != NULL)
+        ret = visit(self->app_data, arg);
+    return ret;
+}
+
+/*
+ * Decref all contained objects and zero the pointers.
+ *
+ * Arguments: self - The Context object
+ * Returns:   Always 0.
+ */
+static int
+ssl_Context_clear(ssl_ContextObj *self)
+{
+    Py_XDECREF(self->passphrase_callback);
+    self->passphrase_callback = NULL;
+    Py_XDECREF(self->passphrase_userdata);
+    self->passphrase_userdata = NULL;
+    Py_XDECREF(self->verify_callback);
+    self->verify_callback = NULL;
+    Py_XDECREF(self->info_callback);
+    self->info_callback = NULL;
+    Py_XDECREF(self->app_data);
+    self->app_data = NULL;
+    return 0;
+}
+
+/*
+ * Deallocate the memory used by the Context object
+ *
+ * Arguments: self - The Context object
+ * Returns:   None
+ */
+static void
+ssl_Context_dealloc(ssl_ContextObj *self)
+{
+    PyObject_GC_UnTrack((PyObject *)self);
+    SSL_CTX_free(self->ctx);
+    ssl_Context_clear(self);
+    PyObject_GC_Del(self);
+}
+
+
+PyTypeObject ssl_Context_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "Context",
+    sizeof(ssl_ContextObj),
+    0,
+    (destructor)ssl_Context_dealloc,
+    NULL, /* print */
+    (getattrfunc)ssl_Context_getattr,
+    NULL, /* setattr */
+    NULL, /* compare */
+    NULL, /* repr */
+    NULL, /* as_number */
+    NULL, /* as_sequence */
+    NULL, /* as_mapping */
+    NULL, /* hash */
+    NULL, /* call */
+    NULL, /* str */
+    NULL, /* getattro */
+    NULL, /* setattro */
+    NULL, /* as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+    NULL, /* doc */
+    (traverseproc)ssl_Context_traverse,
+    (inquiry)ssl_Context_clear,
+};
+
+
+/*
+ * Initialize the Context part of the SSL sub module
+ *
+ * Arguments: dict - Dictionary of the OpenSSL.SSL module
+ * Returns:   1 for success, 0 otherwise
+ */
+int
+init_ssl_context(PyObject *dict)
+{
+    ssl_Context_Type.ob_type = &PyType_Type;
+    Py_INCREF(&ssl_Context_Type);
+    if (PyDict_SetItemString(dict, "ContextType", (PyObject *)&ssl_Context_Type) != 0)
+        return 0;
+
+    return 1;
+}
+
diff --git a/src/ssl/context.h b/src/ssl/context.h
new file mode 100644
index 0000000..b52acf1
--- /dev/null
+++ b/src/ssl/context.h
@@ -0,0 +1,42 @@
+/*
+ * context.h
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Export SSL Context object data structures and functions.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ *
+ * @(#) $Id: context.h,v 1.6 2002/09/04 22:24:59 iko Exp $
+ */
+#ifndef PyOpenSSL_SSL_CONTEXT_H_
+#define PyOpenSSL_SSL_CONTEXT_H_
+
+#include <Python.h>
+#include <openssl/ssl.h>
+
+extern  int                   init_ssl_context      (PyObject *);
+
+extern  PyTypeObject      ssl_Context_Type;
+
+#define ssl_Context_Check(v) ((v)->ob_type == &ssl_Context_Type)
+
+typedef struct {
+    PyObject_HEAD
+    SSL_CTX             *ctx;
+    PyObject            *passphrase_callback,
+                        *passphrase_userdata,
+                        *verify_callback,
+                        *info_callback,
+                        *app_data;
+    PyThreadState       *tstate;
+} ssl_ContextObj;
+
+#define ssl_SSLv2_METHOD      (1)
+#define ssl_SSLv3_METHOD      (2)
+#define ssl_SSLv23_METHOD     (3)
+#define ssl_TLSv1_METHOD      (4)
+
+
+#endif
diff --git a/src/ssl/ssl.c b/src/ssl/ssl.c
new file mode 100644
index 0000000..9d3abd2
--- /dev/null
+++ b/src/ssl/ssl.c
@@ -0,0 +1,191 @@
+/*
+ * ssl.c
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Main file of the SSL sub module.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ */
+#include <Python.h>
+#define SSL_MODULE
+#include "ssl.h"
+
+static char ssl_doc[] = "\n\
+Main file of the SSL sub module.\n\
+See the file RATIONALE for a short explanation of hy this module was written.\n\
+";
+
+static char *CVSid = "@(#) $Id: ssl.c,v 1.12 2004/08/10 21:42:51 martin Exp $";
+
+void **crypto_API;
+
+/* Exceptions defined by the SSL submodule */
+PyObject *ssl_Error,                   /* Base class              */
+         *ssl_ZeroReturnError,         /* Used with SSL_get_error */
+         *ssl_WantReadError,           /* ...                     */
+         *ssl_WantWriteError,          /* ...                     */
+         *ssl_WantX509LookupError,     /* ...                     */
+         *ssl_SysCallError;            /* Uses (errno,errstr)     */
+
+static char ssl_Context_doc[] = "\n\
+The factory function inserted in the module dictionary to create Context\n\
+objects\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             method - The SSL method to use\n\
+Returns:   The Context object\n\
+";
+
+static PyObject *
+ssl_Context(PyObject *spam, PyObject *args)
+{
+    int method;
+
+    if (!PyArg_ParseTuple(args, "i:Context", &method))
+        return NULL;
+
+    return (PyObject *)ssl_Context_New(method);
+}
+
+static char ssl_Connection_doc[] = "\n\
+The factory function inserted in the module dictionary to create Connection\n\
+objects\n\
+\n\
+Arguments: spam - Always NULL\n\
+           args - The Python argument tuple, should be:\n\
+             ctx  - An SSL Context to use for this connection\n\
+             sock - The socket to use for transport layer\n\
+Returns:   The Connection object\n\
+";
+
+static PyObject *
+ssl_Connection(PyObject *spam, PyObject *args)
+{
+    ssl_ContextObj *ctx;
+    PyObject *sock;
+
+    if (!PyArg_ParseTuple(args, "O!O:Connection", &ssl_Context_Type, &ctx, &sock))
+        return NULL;
+
+    return (PyObject *)ssl_Connection_New(ctx, sock);
+}
+
+
+/* Methods in the OpenSSL.SSL module */
+static PyMethodDef ssl_methods[] = {
+    { "Context",        ssl_Context,    METH_VARARGS, ssl_Context_doc },
+    { "Connection",     ssl_Connection, METH_VARARGS, ssl_Connection_doc },
+    { NULL, NULL }
+};
+
+/*
+ * Initialize SSL sub module
+ *
+ * Arguments: None
+ * Returns:   None
+ */
+void
+initSSL(void)
+{
+    static void *ssl_API[ssl_API_pointers];
+    PyObject *ssl_api_object;
+    PyObject *module, *dict;
+
+    SSL_library_init();
+    ERR_load_SSL_strings();
+
+    import_crypto();
+
+    if ((module = Py_InitModule3("SSL", ssl_methods, ssl_doc)) == NULL)
+        return;
+
+    /* Initialize the C API pointer array */
+    ssl_API[ssl_Context_New_NUM]    = (void *)ssl_Context_New;
+    ssl_API[ssl_Connection_New_NUM] = (void *)ssl_Connection_New;
+    ssl_api_object = PyCObject_FromVoidPtr((void *)ssl_API, NULL);
+    if (ssl_api_object != NULL)
+        PyModule_AddObject(module, "_C_API", ssl_api_object);
+
+    /* Exceptions */
+/*
+ * ADD_EXCEPTION(dict,name,base) expands to a correct Exception declaration,
+ * inserting OpenSSL.SSL.name into dict, derviving the exception from base.
+ */
+#define ADD_EXCEPTION(_name, _base)                                    \
+do {                                                                          \
+    ssl_##_name = PyErr_NewException("OpenSSL.SSL."#_name, _base, NULL);\
+    if (ssl_##_name == NULL)                                            \
+        goto error;                                                           \
+    if (PyModule_AddObject(module, #_name, ssl_##_name) != 0)           \
+        goto error;                                                           \
+} while (0)
+
+    ssl_Error = PyErr_NewException("OpenSSL.SSL.Error", NULL, NULL);
+    if (ssl_Error == NULL)
+        goto error;
+    if (PyModule_AddObject(module, "Error", ssl_Error) != 0)
+        goto error;
+
+    ADD_EXCEPTION(ZeroReturnError,     ssl_Error);
+    ADD_EXCEPTION(WantReadError,       ssl_Error);
+    ADD_EXCEPTION(WantWriteError,      ssl_Error);
+    ADD_EXCEPTION(WantX509LookupError, ssl_Error);
+    ADD_EXCEPTION(SysCallError,        ssl_Error);
+#undef ADD_EXCEPTION
+
+    /* Method constants */
+    PyModule_AddIntConstant(module, "SSLv2_METHOD",  ssl_SSLv2_METHOD);
+    PyModule_AddIntConstant(module, "SSLv3_METHOD",  ssl_SSLv3_METHOD);
+    PyModule_AddIntConstant(module, "SSLv23_METHOD", ssl_SSLv23_METHOD);
+    PyModule_AddIntConstant(module, "TLSv1_METHOD",  ssl_TLSv1_METHOD);
+
+    /* Verify constants */
+    PyModule_AddIntConstant(module, "VERIFY_NONE", SSL_VERIFY_NONE);
+    PyModule_AddIntConstant(module, "VERIFY_PEER", SSL_VERIFY_PEER);
+    PyModule_AddIntConstant(module, "VERIFY_FAIL_IF_NO_PEER_CERT",
+                            SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+    PyModule_AddIntConstant(module, "VERIFY_CLIENT_ONCE",
+                            SSL_VERIFY_CLIENT_ONCE);
+
+    /* File type constants */
+    PyModule_AddIntConstant(module, "FILETYPE_PEM",  SSL_FILETYPE_PEM);
+    PyModule_AddIntConstant(module, "FILETYPE_ASN1", SSL_FILETYPE_ASN1);
+
+    /* SSL option constants */
+    PyModule_AddIntConstant(module, "OP_SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE);
+    PyModule_AddIntConstant(module, "OP_EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA);
+    PyModule_AddIntConstant(module, "OP_NO_SSLv2", SSL_OP_NO_SSLv2);
+    PyModule_AddIntConstant(module, "OP_NO_SSLv3", SSL_OP_NO_SSLv3);
+    PyModule_AddIntConstant(module, "OP_NO_TLSv1", SSL_OP_NO_TLSv1);
+
+    /* More SSL option constants */
+    PyModule_AddIntConstant(module, "OP_MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG);
+    PyModule_AddIntConstant(module, "OP_NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG);
+    PyModule_AddIntConstant(module, "OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG);
+    PyModule_AddIntConstant(module, "OP_SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG);
+    PyModule_AddIntConstant(module, "OP_MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);
+    PyModule_AddIntConstant(module, "OP_MSIE_SSLV2_RSA_PADDING", SSL_OP_MSIE_SSLV2_RSA_PADDING);
+    PyModule_AddIntConstant(module, "OP_SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG);
+    PyModule_AddIntConstant(module, "OP_TLS_D5_BUG", SSL_OP_TLS_D5_BUG);
+    PyModule_AddIntConstant(module, "OP_TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG);
+    PyModule_AddIntConstant(module, "OP_DONT_INSERT_EMPTY_FRAGMENTS", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+    PyModule_AddIntConstant(module, "OP_ALL", SSL_OP_ALL);
+    PyModule_AddIntConstant(module, "OP_CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE);
+    PyModule_AddIntConstant(module, "OP_TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG);
+    PyModule_AddIntConstant(module, "OP_PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1);
+    PyModule_AddIntConstant(module, "OP_PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2);
+    PyModule_AddIntConstant(module, "OP_NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG);
+    PyModule_AddIntConstant(module, "OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
+
+    dict = PyModule_GetDict(module);
+    if (!init_ssl_context(dict))
+        goto error;
+    if (!init_ssl_connection(dict))
+        goto error;
+
+error:
+    ;
+}
diff --git a/src/ssl/ssl.h b/src/ssl/ssl.h
new file mode 100644
index 0000000..e8d3e93
--- /dev/null
+++ b/src/ssl/ssl.h
@@ -0,0 +1,76 @@
+/*
+ * ssl.h
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Export functions and exceptions from the SSL sub module.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ *
+ * @(#) $Id: ssl.h,v 1.6 2002/04/08 19:25:43 martin Exp $
+ */
+#ifndef PyOpenSSL_SSL_H_
+#define PyOpenSSL_SSL_H_
+
+#include <Python.h>
+#include "context.h"
+#include "connection.h"
+#include "../util.h"
+#include "../crypto/crypto.h"
+
+extern PyObject *ssl_Error,               /* Base class              */
+                *ssl_ZeroReturnError,     /* Used with SSL_get_erorr */
+                *ssl_WantReadError,       /* ...                     */
+                *ssl_WantWriteError,      /* ...                     */
+                *ssl_WantX509LookupError, /* ...                     */
+                *ssl_SysCallError;        /* Uses (errno,errstr)     */
+
+#ifdef exception_from_error_queue
+#  undef exception_from_error_queue
+#endif
+#define exception_from_error_queue()    do { \
+    PyObject *errlist = error_queue_to_list(); \
+    PyErr_SetObject(ssl_Error, errlist); \
+    Py_DECREF(errlist); \
+} while (0)
+
+#define ssl_Context_New_NUM       0
+#define ssl_Context_New_RETURN    ssl_ContextObj *
+#define ssl_Context_New_PROTO     (int method)
+
+#define ssl_Connection_New_NUM    1
+#define ssl_Connection_New_RETURN ssl_ConnectionObj *
+#define ssl_Connection_New_PROTO  (ssl_ContextObj *ctx, PyObject *sock)
+
+#define ssl_API_pointers          2
+
+#ifdef SSL_MODULE
+
+extern ssl_Context_New_RETURN    ssl_Context_New    ssl_Context_New_PROTO;
+extern ssl_Connection_New_RETURN ssl_Connection_New ssl_Connection_New_PROTO;
+
+#else /* SSL_MODULE */
+
+extern void **ssl_API;
+
+#define ssl_Context_New \
+ (*(ssl_Context_New_RETURN (*)ssl_Context_New_PROTO) ssl_API[ssl_Context_New_NUM])
+#define ssl_Connection_New \
+ (*(ssl_Connection_New_RETURN (*)ssl_Connection_New_PROTO) ssl_API[ssl_Connection_New_NUM])
+
+#define import_SSL() \
+{ \
+  PyObject *module = PyImport_ImportModule("OpenSSL.SSL"); \
+  if (module != NULL) { \
+    PyObject *module_dict = PyModule_GetDict(module); \
+    PyObject *c_api_object = PyDict_GetItemString(module_dict, "_C_API"); \
+    if (PyCObject_Check(c_api_object)) { \
+      ssl_API = (void **)PyCObject_AsVoidPtr(c_api_object); \
+    } \
+  } \
+}
+
+#endif /* SSL_MODULE */
+
+#endif /* PyOpenSSL_SSL_H_ */
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..3ae72c6
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,55 @@
+/*
+ * util.c
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Utility functions.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ */
+#include <Python.h>
+#include "util.h"
+
+static char *CVSid = "@(#) $Id: util.c,v 1.5 2001/08/09 11:26:36 martin Exp $";
+
+
+/*
+ * Flush OpenSSL's error queue and return a list of errors (a (library,
+ * function, reason) string tuple)
+ *
+ * Arguments: None
+ * Returns:   A list of errors (new reference)
+ */
+PyObject *
+error_queue_to_list(void)
+{
+    PyObject *errlist, *tuple;
+    long err;
+
+    errlist = PyList_New(0);
+
+    while ((err = ERR_get_error()) != 0)
+    {
+	tuple = Py_BuildValue("(sss)", ERR_lib_error_string(err),
+		                       ERR_func_error_string(err),
+				       ERR_reason_error_string(err));
+        PyList_Append(errlist, tuple);
+        Py_DECREF(tuple);
+    }
+
+    return errlist;
+}
+
+/*
+ * Flush OpenSSL's error queue and ignore the result
+ *
+ * Arguments: None
+ * Returns:   None
+ */
+void
+flush_error_queue(void)
+{
+    Py_DECREF(error_queue_to_list());
+}
+
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..592660e
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,111 @@
+/*
+ * util.h
+ *
+ * Copyright (C) AB Strakt 2001, All rights reserved
+ *
+ * Export utility functions and macros.
+ * See the file RATIONALE for a short explanation of why this module was written.
+ *
+ * Reviewed 2001-07-23
+ *
+ * @(#) $Id: util.h,v 1.8 2002/08/16 10:08:09 martin Exp $
+ */
+#ifndef PyOpenSSL_UTIL_H_
+#define PyOpenSSL_UTIL_H_
+
+#include <Python.h>
+#include <openssl/err.h>
+
+/*
+ * pymemcompat written by Michael Hudson and lets you program to the
+ * Python 2.3 memory API while keeping backwards compatability.
+ */
+#include "pymemcompat.h"
+
+extern  PyObject *error_queue_to_list(void);
+extern  void      flush_error_queue(void);
+
+/*
+ * These are needed because there is no "official" way to specify
+ * WHERE to save the thread state.
+ */
+#ifdef WITH_THREAD
+#  define MY_BEGIN_ALLOW_THREADS(st)    \
+    { st = PyEval_SaveThread(); }
+#  define MY_END_ALLOW_THREADS(st)      \
+    { PyEval_RestoreThread(st); st = NULL; }
+#else
+#  define MY_BEGIN_ALLOW_THREADS(st)
+#  define MY_END_ALLOW_THREADS(st)      { st = NULL; }
+#endif
+
+#if !defined(PY_MAJOR_VERSION) || PY_VERSION_HEX < 0x02000000
+static int
+PyModule_AddObject(PyObject *m, char *name, PyObject *o)
+{
+    PyObject *dict;
+    if (!PyModule_Check(m) || o == NULL)
+        return -1;
+    dict = PyModule_GetDict(m);
+    if (dict == NULL)
+        return -1;
+    if (PyDict_SetItemString(dict, name, o))
+        return -1;
+    Py_DECREF(o);
+    return 0;
+}
+
+static int
+PyModule_AddIntConstant(PyObject *m, char *name, long value)
+{
+    return PyModule_AddObject(m, name, PyInt_FromLong(value));
+}
+
+static int PyObject_AsFileDescriptor(PyObject *o)
+{
+    int fd;
+    PyObject *meth;
+
+    if (PyInt_Check(o)) {
+        fd = PyInt_AsLong(o);
+    }
+    else if (PyLong_Check(o)) {
+        fd = PyLong_AsLong(o);
+    }
+    else if ((meth = PyObject_GetAttrString(o, "fileno")) != NULL)
+    {
+        PyObject *fno = PyEval_CallObject(meth, NULL);
+        Py_DECREF(meth);
+        if (fno == NULL)
+            return -1;
+
+        if (PyInt_Check(fno)) {
+            fd = PyInt_AsLong(fno);
+            Py_DECREF(fno);
+        }
+        else if (PyLong_Check(fno)) {
+            fd = PyLong_AsLong(fno);
+            Py_DECREF(fno);
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "fileno() returned a non-integer");
+            Py_DECREF(fno);
+            return -1;
+        }
+    }
+    else {
+        PyErr_SetString(PyExc_TypeError, "argument must be an int, or have a fileno() method.");
+        return -1;
+    }
+
+    if (fd < 0) {
+        PyErr_Format(PyExc_ValueError, "file descriptor cannot be a negative integer (%i)", fd);
+        return -1;
+    }
+    return fd;
+}
+#endif
+
+
+
+#endif