Add Context.{s,g}et_session_cache_mode
diff --git a/ChangeLog b/ChangeLog
index a361ad4..27c7556 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,12 @@
-2011-11-01  Jean-Paul Calderone <exarkun@twistedmatrix.com>
+2012-02-13  Jean-Paul Calderone  <exarkun@twistedmatrix.com>
+
+	* OpenSSL/ssl/ssl.c: Add session cache related constants for use
+	  with the new Context.set_session_cache_mode method.
+
+	* OpenSSL/ssl/context.c: Add new Context methods
+	  set_session_cache_mode and get_session_cache_mode.
+
+2011-11-01  Jean-Paul Calderone  <exarkun@twistedmatrix.com>
 
 	* OpenSSL/crypto/pkey.c: Raise TypeError when trying to check a
 	  PKey instance which has no private component, instead of crashing.
diff --git a/OpenSSL/ssl/context.c b/OpenSSL/ssl/context.c
index aa4976d..0b9f4b6 100644
--- a/OpenSSL/ssl/context.c
+++ b/OpenSSL/ssl/context.c
@@ -690,8 +690,8 @@
 }
 
 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\
+Set the session identifier.  This is needed if you want to do session\n\
+resumption.\n\
 \n\
 :param buf: A Python object that can be safely converted to a string\n\
 :returns: None\n\
@@ -717,6 +717,39 @@
     }
 }
 
+static char ssl_Context_set_session_cache_mode_doc[] = "\n\
+Enable/disable session caching and specify the mode used.\n\
+\n\
+:param mode: One or more of the SESS_CACHE_* flags (combine using bitwise or)\n\
+:returns: The previously set caching mode.\n\
+";
+static PyObject *
+ssl_Context_set_session_cache_mode(ssl_ContextObj *self, PyObject *args) {
+    long mode, result;
+
+    if (!PyArg_ParseTuple(args, "l:set_session_cache_mode", &mode)) {
+        return NULL;
+    }
+
+    result = SSL_CTX_set_session_cache_mode(self->ctx, mode);
+    return PyLong_FromLong(result);
+
+}
+
+static char ssl_Context_get_session_cache_mode_doc[] = "\n\
+:returns: The currently used cache mode.\n\
+";
+static PyObject *
+ssl_Context_get_session_cache_mode(ssl_ContextObj *self, PyObject *args) {
+    long result;
+
+    if (!PyArg_ParseTuple(args, ":get_session_cache_mode")) {
+        return NULL;
+    }
+    result = SSL_CTX_get_session_cache_mode(self->ctx);
+    return PyLong_FromLong(result);
+}
+
 static char ssl_Context_set_verify_doc[] = "\n\
 Set the verify mode and verify callback\n\
 \n\
@@ -1176,6 +1209,8 @@
     ADD_METHOD(check_privatekey),
     ADD_METHOD(load_client_ca),
     ADD_METHOD(set_session_id),
+    ADD_METHOD(set_session_cache_mode),
+    ADD_METHOD(get_session_cache_mode),
     ADD_METHOD(set_verify),
     ADD_METHOD(set_verify_depth),
     ADD_METHOD(get_verify_mode),
diff --git a/OpenSSL/ssl/ssl.c b/OpenSSL/ssl/ssl.c
index cee3661..a68f447 100644
--- a/OpenSSL/ssl/ssl.c
+++ b/OpenSSL/ssl/ssl.c
@@ -274,6 +274,20 @@
     PyModule_AddIntConstant(module, "SSLEAY_PLATFORM", SSLEAY_PLATFORM);
     PyModule_AddIntConstant(module, "SSLEAY_DIR", SSLEAY_DIR);
 
+    /* Cache modes */
+#define CACHE_MODE(mode) \
+    PyModule_AddIntConstant(module, "SESS_CACHE_" #mode, SSL_SESS_CACHE_##mode)
+
+    CACHE_MODE(OFF);
+    CACHE_MODE(CLIENT);
+    CACHE_MODE(SERVER);
+    CACHE_MODE(BOTH);
+    CACHE_MODE(NO_AUTO_CLEAR);
+    CACHE_MODE(NO_INTERNAL_LOOKUP);
+    CACHE_MODE(NO_INTERNAL_STORE);
+    CACHE_MODE(NO_INTERNAL);
+#undef CACHE_MODE
+
     /* Straight up version number */
     PyModule_AddIntConstant(module, "OPENSSL_VERSION_NUMBER", OPENSSL_VERSION_NUMBER);
 
diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py
index c9417b9..cda6d53 100644
--- a/OpenSSL/test/test_ssl.py
+++ b/OpenSSL/test/test_ssl.py
@@ -10,7 +10,7 @@
 from sys import platform, version_info
 from socket import error, socket
 from os import makedirs
-from os.path import join
+from os.path import join, dirname
 from unittest import main
 from weakref import ref
 
@@ -28,6 +28,11 @@
     VERIFY_PEER, VERIFY_FAIL_IF_NO_PEER_CERT, VERIFY_CLIENT_ONCE, VERIFY_NONE)
 
 from OpenSSL.SSL import (
+    SESS_CACHE_OFF, SESS_CACHE_CLIENT, SESS_CACHE_SERVER, SESS_CACHE_BOTH,
+    SESS_CACHE_NO_AUTO_CLEAR, SESS_CACHE_NO_INTERNAL_LOOKUP,
+    SESS_CACHE_NO_INTERNAL_STORE, SESS_CACHE_NO_INTERNAL)
+
+from OpenSSL.SSL import (
     Error, SysCallError, WantReadError, ZeroReturnError, SSLeay_version)
 from OpenSSL.SSL import Context, ContextType, Connection, ConnectionType
 
@@ -909,6 +914,37 @@
         self.assertEquals(conn.get_cipher_list(), ["EXP-RC4-MD5"])
 
 
+    def test_set_session_cache_mode_wrong_args(self):
+        """
+        L{Context.set_session_cache_mode} raises L{TypeError} if called with
+        other than one integer argument.
+        """
+        context = Context(TLSv1_METHOD)
+        self.assertRaises(TypeError, context.set_session_cache_mode)
+        self.assertRaises(TypeError, context.set_session_cache_mode, object())
+
+
+    def test_get_session_cache_mode_wrong_args(self):
+        """
+        L{Context.get_session_cache_mode} raises L{TypeError} if called with any
+        arguments.
+        """
+        context = Context(TLSv1_METHOD)
+        self.assertRaises(TypeError, context.get_session_cache_mode, 1)
+
+
+    def test_session_cache_mode(self):
+        """
+        L{Context.set_session_cache_mode} specifies how sessions are cached.
+        The setting can be retrieved via L{Context.get_session_cache_mode}.
+        """
+        context = Context(TLSv1_METHOD)
+        old = context.set_session_cache_mode(SESS_CACHE_OFF)
+        off = context.set_session_cache_mode(SESS_CACHE_BOTH)
+        self.assertEqual(SESS_CACHE_OFF, off)
+        self.assertEqual(SESS_CACHE_BOTH, context.get_session_cache_mode())
+
+
 
 class ServerNameCallbackTests(TestCase, _LoopbackMixin):
     """
@@ -1627,6 +1663,72 @@
         "OP_NO_COMPRESSION unavailable - OpenSSL version may be too old"
 
 
+    def test_sess_cache_off(self):
+        """
+        The value of L{OpenSSL.SSL.SESS_CACHE_OFF} 0x0, the value of
+        L{SSL_SESS_CACHE_OFF} defined by I{openssl/ssl.h}.
+        """
+        self.assertEqual(0x0, SESS_CACHE_OFF)
+
+
+    def test_sess_cache_client(self):
+        """
+        The value of L{OpenSSL.SSL.SESS_CACHE_CLIENT} 0x1, the value of
+        L{SSL_SESS_CACHE_CLIENT} defined by I{openssl/ssl.h}.
+        """
+        self.assertEqual(0x1, SESS_CACHE_CLIENT)
+
+
+    def test_sess_cache_server(self):
+        """
+        The value of L{OpenSSL.SSL.SESS_CACHE_SERVER} 0x2, the value of
+        L{SSL_SESS_CACHE_SERVER} defined by I{openssl/ssl.h}.
+        """
+        self.assertEqual(0x2, SESS_CACHE_SERVER)
+
+
+    def test_sess_cache_both(self):
+        """
+        The value of L{OpenSSL.SSL.SESS_CACHE_BOTH} 0x3, the value of
+        L{SSL_SESS_CACHE_BOTH} defined by I{openssl/ssl.h}.
+        """
+        self.assertEqual(0x3, SESS_CACHE_BOTH)
+
+
+    def test_sess_cache_no_auto_clear(self):
+        """
+        The value of L{OpenSSL.SSL.SESS_CACHE_NO_AUTO_CLEAR} 0x80, the value of
+        L{SSL_SESS_CACHE_NO_AUTO_CLEAR} defined by I{openssl/ssl.h}.
+        """
+        self.assertEqual(0x80, SESS_CACHE_NO_AUTO_CLEAR)
+
+
+    def test_sess_cache_no_internal_lookup(self):
+        """
+        The value of L{OpenSSL.SSL.SESS_CACHE_NO_INTERNAL_LOOKUP} 0x100, the
+        value of L{SSL_SESS_CACHE_NO_INTERNAL_LOOKUP} defined by
+        I{openssl/ssl.h}.
+        """
+        self.assertEqual(0x100, SESS_CACHE_NO_INTERNAL_LOOKUP)
+
+
+    def test_sess_cache_no_internal_store(self):
+        """
+        The value of L{OpenSSL.SSL.SESS_CACHE_NO_INTERNAL_STORE} 0x200, the
+        value of L{SSL_SESS_CACHE_NO_INTERNAL_STORE} defined by
+        I{openssl/ssl.h}.
+        """
+        self.assertEqual(0x200, SESS_CACHE_NO_INTERNAL_STORE)
+
+
+    def test_sess_cache_no_internal(self):
+        """
+        The value of L{OpenSSL.SSL.SESS_CACHE_NO_INTERNAL} 0x300, the value of
+        L{SSL_SESS_CACHE_NO_INTERNAL} defined by I{openssl/ssl.h}.
+        """
+        self.assertEqual(0x300, SESS_CACHE_NO_INTERNAL)
+
+
 
 class MemoryBIOTests(TestCase, _LoopbackMixin):
     """
diff --git a/doc/api/ssl.rst b/doc/api/ssl.rst
index 1eed876..5014889 100644
--- a/doc/api/ssl.rst
+++ b/doc/api/ssl.rst
@@ -69,6 +69,20 @@
     information to retrieve.  See the man page for the :py:func:`SSLeay_version` C
     API for details.
 
+.. py:data:: SESS_CACHE_OFF
+             SESS_CACHE_CLIENT
+             SESS_CACHE_SERVER
+             SESS_CACHE_BOTH
+             SESS_CACHE_NO_AUTO_CLEAR
+             SESS_CACHE_NO_INTERNAL_LOOKUP
+             SESS_CACHE_NO_INTERNAL_STORE
+             SESS_CACHE_NO_INTERNAL
+
+     Constants used with :py:meth:`Context.set_session_cache_mode` to specify
+     the behavior of the session cache and potential session reuse.  See the man
+     page for the :py:func:`SSL_CTX_set_session_cache_mode` C API for details.
+
+     .. versionadded:: 0.14
 
 .. py:data:: OPENSSL_VERSION_NUMBER
 
@@ -315,6 +329,22 @@
     *callback* should return a false value (e.g. an empty string).
 
 
+.. py:method:: Context.set_session_cache_mode(mode)
+
+    Set the behavior of the session cache used by all connections using this
+    Context.  The previously set mode is returned.  See :py:const:`SESS_CACHE_*`
+    for details about particular modes.
+
+    .. versionadded:: 0.14
+
+
+.. py:method:: Context.get_session_cache_mode()
+
+    Get the current session cache mode.
+
+    .. versionadded:: 0.14
+
+
 .. py:method:: Context.set_session_id(name)
 
     Set the context *name* within which a session can be reused for this