From bb7d7a803665005cc72ad68a388e9e937ff3d2f6 Mon Sep 17 00:00:00 2001
From: Josef Schlehofer <pepe.schlehofer@gmail.com>
Date: Sat, 23 Mar 2019 21:02:17 +0100
Subject: [PATCH] support for mbedTLS

---
 INSTALL.rst           |  4 ++--
 doc/thread-safety.rst |  2 +-
 setup.py              | 28 +++++++++++++++++++++-------
 src/module.c          |  6 ++++--
 src/pycurl.h          |  7 ++++++-
 src/threadsupport.c   | 39 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 73 insertions(+), 13 deletions(-)

diff --git a/INSTALL.rst b/INSTALL.rst
index 8ad8b4f..da70d25 100644
--- a/INSTALL.rst
+++ b/INSTALL.rst
@@ -53,7 +53,7 @@ It will then fail at runtime as follows::
 
 To fix this, you need to tell ``setup.py`` what SSL backend is used::
 
-    python setup.py --with-[openssl|gnutls|nss] install
+    python setup.py --with-[openssl|gnutls|nss|mbedtls] install
 
 Note: as of PycURL 7.21.5, setup.py accepts ``--with-openssl`` option to
 indicate that libcurl is built against OpenSSL. ``--with-ssl`` is an alias
@@ -85,7 +85,7 @@ environment variable::
 The same applies to the SSL backend, if you need to specify it (see the SSL
 note above)::
 
-    export PYCURL_SSL_LIBRARY=[openssl|gnutls|nss]
+    export PYCURL_SSL_LIBRARY=[openssl|gnutls|nss|mbedtls]
     easy_install pycurl
 
 
diff --git a/doc/thread-safety.rst b/doc/thread-safety.rst
index 5ba3f3e..ae2b9e5 100644
--- a/doc/thread-safety.rst
+++ b/doc/thread-safety.rst
@@ -21,7 +21,7 @@ For Python programs using PycURL, this means:
   Python code *outside of a libcurl callback for the PycURL object in question*
   is unsafe.
 
-PycURL handles the necessary SSL locks for OpenSSL/LibreSSL, GnuTLS and NSS.
+PycURL handles the necessary SSL locks for OpenSSL/LibreSSL, GnuTLS, NSS and mbedTLS.
 
 A special situation exists when libcurl uses the standard C library
 name resolver (i.e., not threaded nor c-ares resolver). By default libcurl
diff --git a/setup.py b/setup.py
index e1e6925..5ab437f 100644
--- a/setup.py
+++ b/setup.py
@@ -143,6 +143,7 @@ class ExtensionConfiguration(object):
             '--with-ssl': self.using_openssl,
             '--with-gnutls': self.using_gnutls,
             '--with-nss': self.using_nss,
+            '--with-mbedtls': self.using_mbedtls,
         }
 
     def detect_ssl_option(self):
@@ -152,20 +153,20 @@ class ExtensionConfiguration(object):
                     if option != other_option:
                         if scan_argv(self.argv, other_option) is not None:
                             raise ConfigurationError('Cannot give both %s and %s' % (option, other_option))
-                
+
                 return option
 
     def detect_ssl_backend(self):
         ssl_lib_detected = False
-        
+
         if 'PYCURL_SSL_LIBRARY' in os.environ:
             ssl_lib = os.environ['PYCURL_SSL_LIBRARY']
-            if ssl_lib in ['openssl', 'gnutls', 'nss']:
+            if ssl_lib in ['openssl', 'gnutls', 'nss', 'mbedtls']:
                 ssl_lib_detected = True
                 getattr(self, 'using_%s' % ssl_lib)()
             else:
                 raise ConfigurationError('Invalid value "%s" for PYCURL_SSL_LIBRARY' % ssl_lib)
-        
+
         option = self.detect_ssl_option()
         if option:
             ssl_lib_detected = True
@@ -194,6 +195,10 @@ class ExtensionConfiguration(object):
                         self.using_nss()
                         ssl_lib_detected = True
                         break
+                    if arg[2:] == 'mbedtls':
+                        self.using_nss()
+                        ssl_lib_detected = True
+                        break
 
         if not ssl_lib_detected and len(self.argv) == len(self.original_argv) \
                 and not os.environ.get('PYCURL_CURL_CONFIG') \
@@ -201,7 +206,7 @@ class ExtensionConfiguration(object):
             # this path should only be taken when no options or
             # configuration environment variables are given to setup.py
             ssl_lib_detected = self.detect_ssl_lib_on_centos6()
-            
+
         self.ssl_lib_detected = ssl_lib_detected
 
     def curl_config(self):
@@ -301,7 +306,7 @@ class ExtensionConfiguration(object):
             if errtext:
                 msg += ":\n" + errtext
             raise ConfigurationError(msg)
-            
+
         # hack
         self.sslhintbuf = sslhintbuf
 
@@ -327,7 +332,7 @@ specify the SSL backend manually.''')
                 self.library_dirs.append(arg[2:])
             else:
                 self.extra_link_args.append(arg)
-            
+
         if not self.libraries:
             self.libraries.append("curl")
 
@@ -354,6 +359,9 @@ specify the SSL backend manually.''')
         elif ssl_version.startswith('NSS/'):
             self.using_nss()
             ssl_lib_detected = True
+        elif ssl_version.startswith('mbedTLS/'):
+            self.using_mbedtls()
+            ssl_lib_detected = 'mbedtls'
         return ssl_lib_detected
 
     def detect_ssl_lib_on_centos6(self):
@@ -505,6 +513,11 @@ specify the SSL backend manually.''')
         self.libraries.append('ssl3')
         self.define_macros.append(('HAVE_CURL_SSL', 1))
 
+    def using_mbedtls(self):
+        self.define_macros.append(('HAVE_CURL_MBEDTLS', 1))
+        self.libraries.append('mbedtls')
+        self.define_macros.append(('HAVE_CURL_SSL', 1))
+
 def get_bdist_msi_version_hack():
     # workaround for distutils/msi version requirement per
     # epydoc.sourceforge.net/stdlib/distutils.version.StrictVersion-class.html -
@@ -871,6 +884,7 @@ PycURL Unix options:
  --with-ssl                          legacy alias for --with-openssl
  --with-gnutls                       libcurl is linked against GnuTLS
  --with-nss                          libcurl is linked against NSS
+ --with-mbedtls                      libcurl is linked against mbedTLS
 '''
 
 windows_help = '''\
diff --git a/src/module.c b/src/module.c
index 2331ae8..7fdb25a 100644
--- a/src/module.c
+++ b/src/module.c
@@ -328,7 +328,7 @@ initpycurl(void)
     PyObject *collections_module = NULL;
     PyObject *named_tuple = NULL;
     PyObject *arglist = NULL;
-    
+
     assert(Curl_Type.tp_weaklistoffset > 0);
     assert(CurlMulti_Type.tp_weaklistoffset > 0);
     assert(CurlShare_Type.tp_weaklistoffset > 0);
@@ -355,6 +355,8 @@ initpycurl(void)
         runtime_ssl_lib = "gnutls";
     } else if (!strncmp(vi->ssl_version, "NSS/", 4)) {
         runtime_ssl_lib = "nss";
+    } else if (!strncmp(vi->ssl_version, "mbedTLS/", 2)) {
+        runtime_ssl_lib = "mbedtls";
     } else {
         runtime_ssl_lib = "none/other";
     }
@@ -461,7 +463,7 @@ initpycurl(void)
     /* constants for ioctl callback argument values */
     insint_c(d, "IOCMD_NOP", CURLIOCMD_NOP);
     insint_c(d, "IOCMD_RESTARTREAD", CURLIOCMD_RESTARTREAD);
-    
+
     /* opensocketfunction return value */
     insint_c(d, "SOCKET_BAD", CURL_SOCKET_BAD);
 
diff --git a/src/pycurl.h b/src/pycurl.h
index 65290f7..2294cb8 100644
--- a/src/pycurl.h
+++ b/src/pycurl.h
@@ -174,6 +174,11 @@ pycurl_inet_ntop (int family, void *addr, char *string, size_t string_size);
 #   define COMPILE_SSL_LIB "gnutls"
 # elif defined(HAVE_CURL_NSS)
 #   define COMPILE_SSL_LIB "nss"
+# elif defined(HAVE_CURL_MBEDTLS)
+#   include <mbedtls/ssl.h>
+#   define PYCURL_NEED_SSL_TSL
+#   define PYCURL_NEED_MBEDTLS_TSL
+#   define COMPILE_SSL_LIB "mbedtls"
 # else
 #  ifdef _MSC_VER
     /* sigh */
@@ -190,7 +195,7 @@ pycurl_inet_ntop (int family, void *addr, char *string, size_t string_size);
    /* since we have no crypto callbacks for other ssl backends,
     * no reason to require users match those */
 #  define COMPILE_SSL_LIB "none/other"
-# endif /* HAVE_CURL_OPENSSL || HAVE_CURL_GNUTLS || HAVE_CURL_NSS */
+# endif /* HAVE_CURL_OPENSSL || HAVE_CURL_GNUTLS || HAVE_CURL_NSS || HAVE_CURL_MBEDTLS */
 #else
 # define COMPILE_SSL_LIB "none/other"
 #endif /* HAVE_CURL_SSL */
diff --git a/src/threadsupport.c b/src/threadsupport.c
index 6ca07f5..51abffd 100644
--- a/src/threadsupport.c
+++ b/src/threadsupport.c
@@ -232,6 +232,45 @@ pycurl_ssl_cleanup(void)
 }
 #endif
 
+/* mbedTLS */
+
+#ifdef PYCURL_NEED_MBEDTLS_TSL
+static int
+pycurl_ssl_mutex_create(void **m)
+{
+    if ((*((PyThread_type_lock *) m) = PyThread_allocate_lock()) == NULL) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+static int
+pycurl_ssl_mutex_destroy(void **m)
+{
+    PyThread_free_lock(*((PyThread_type_lock *) m));
+    return 0;
+}
+
+static int
+pycurl_ssl_mutex_lock(void **m)
+{
+    return !PyThread_acquire_lock(*((PyThread_type_lock *) m), 1);
+}
+
+PYCURL_INTERNAL int
+pycurl_ssl_init(void)
+{
+    return 0;
+}
+
+PYCURL_INTERNAL void
+pycurl_ssl_cleanup(void)
+{
+    return;
+}
+#endif
+
 /*************************************************************************
 // CurlShareObject
 **************************************************************************/
-- 
2.17.0.windows.1