#include "pycurl.h" /* IMPORTANT NOTE: due to threading issues, we cannot call _any_ Python * function without acquiring the thread state in the callback handlers. */ static size_t util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *stream) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; size_t ret = 0; /* assume error */ PyObject *cb; int total_size; PYCURL_DECLARE_THREAD_STATE; /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return ret; /* check args */ cb = flags ? self->h_cb : self->w_cb; if (cb == NULL) goto silent_error; if (size <= 0 || nmemb <= 0) goto done; total_size = (int)(size * nmemb); if (total_size < 0 || (size_t)total_size / size != nmemb) { PyErr_SetString(ErrorObject, "integer overflow in write callback"); goto verbose_error; } /* run callback */ #if PY_MAJOR_VERSION >= 3 arglist = Py_BuildValue("(y#)", ptr, total_size); #else arglist = Py_BuildValue("(s#)", ptr, total_size); #endif if (arglist == NULL) goto verbose_error; result = PyObject_Call(cb, arglist, NULL); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* handle result */ if (result == Py_None) { ret = total_size; /* None means success */ } else if (PyInt_Check(result) || PyLong_Check(result)) { /* if the cast to long fails, PyLong_AsLong() returns -1L */ ret = (size_t) PyLong_AsLong(result); } else { PyErr_SetString(ErrorObject, "write callback must return int or None"); goto verbose_error; } done: silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } PYCURL_INTERNAL size_t write_callback(char *ptr, size_t size, size_t nmemb, void *stream) { return util_write_callback(0, ptr, size, nmemb, stream); } PYCURL_INTERNAL size_t header_callback(char *ptr, size_t size, size_t nmemb, void *stream) { return util_write_callback(1, ptr, size, nmemb, stream); } /* convert protocol address from C to python, returns a tuple of protocol specific values */ static PyObject * convert_protocol_address(struct sockaddr* saddr, unsigned int saddrlen) { PyObject *res_obj = NULL; switch (saddr->sa_family) { case AF_INET: { struct sockaddr_in* sin = (struct sockaddr_in*)saddr; char *addr_str = PyMem_New(char, INET_ADDRSTRLEN); if (addr_str == NULL) { PyErr_NoMemory(); goto error; } if (inet_ntop(saddr->sa_family, &sin->sin_addr, addr_str, INET_ADDRSTRLEN) == NULL) { PyErr_SetFromErrno(ErrorObject); PyMem_Free(addr_str); goto error; } res_obj = Py_BuildValue("(si)", addr_str, ntohs(sin->sin_port)); PyMem_Free(addr_str); } break; case AF_INET6: { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)saddr; char *addr_str = PyMem_New(char, INET6_ADDRSTRLEN); if (addr_str == NULL) { PyErr_NoMemory(); goto error; } if (inet_ntop(saddr->sa_family, &sin6->sin6_addr, addr_str, INET6_ADDRSTRLEN) == NULL) { PyErr_SetFromErrno(ErrorObject); PyMem_Free(addr_str); goto error; } res_obj = Py_BuildValue("(siii)", addr_str, (int) ntohs(sin6->sin6_port), (int) ntohl(sin6->sin6_flowinfo), (int) ntohl(sin6->sin6_scope_id)); PyMem_Free(addr_str); } break; #if !defined(WIN32) case AF_UNIX: { struct sockaddr_un* s_un = (struct sockaddr_un*)saddr; #if PY_MAJOR_VERSION >= 3 res_obj = Py_BuildValue("y", s_un->sun_path); #else res_obj = Py_BuildValue("s", s_un->sun_path); #endif } break; #endif default: /* We (currently) only support IPv4/6 addresses. Can curl even be used with anything else? */ PyErr_SetString(ErrorObject, "Unsupported address family"); } error: return res_obj; } /* curl_socket_t is just an int on unix/windows (with limitations that * are not important here) */ PYCURL_INTERNAL curl_socket_t opensocket_callback(void *clientp, curlsocktype purpose, struct curl_sockaddr *address) { PyObject *arglist; PyObject *result = NULL; PyObject *fileno_result = NULL; CurlObject *self; int ret = CURL_SOCKET_BAD; PyObject *converted_address; PyObject *python_address; PYCURL_DECLARE_THREAD_STATE; self = (CurlObject *)clientp; PYCURL_ACQUIRE_THREAD(); converted_address = convert_protocol_address(&address->addr, address->addrlen); if (converted_address == NULL) { goto verbose_error; } arglist = Py_BuildValue("(iiiN)", address->family, address->socktype, address->protocol, converted_address); if (arglist == NULL) { Py_DECREF(converted_address); goto verbose_error; } python_address = PyObject_Call(curl_sockaddr_type, arglist, NULL); Py_DECREF(arglist); if (python_address == NULL) { goto verbose_error; } arglist = Py_BuildValue("(iN)", purpose, python_address); if (arglist == NULL) { Py_DECREF(python_address); goto verbose_error; } result = PyObject_Call(self->opensocket_cb, arglist, NULL); Py_DECREF(arglist); if (result == NULL) { goto verbose_error; } if (PyInt_Check(result) && PyInt_AsLong(result) == CURL_SOCKET_BAD) { ret = CURL_SOCKET_BAD; } else if (PyObject_HasAttrString(result, "fileno")) { fileno_result = PyObject_CallMethod(result, "fileno", NULL); if (fileno_result == NULL) { ret = CURL_SOCKET_BAD; goto verbose_error; } // normal operation: if (PyInt_Check(fileno_result)) { int sock_fd = PyInt_AsLong(fileno_result); #if defined(WIN32) ret = dup_winsock(sock_fd, address); #else ret = dup(sock_fd); #endif goto done; } else { PyErr_SetString(ErrorObject, "Open socket callback returned an object whose fileno method did not return an integer"); ret = CURL_SOCKET_BAD; } } else { PyErr_SetString(ErrorObject, "Open socket callback's return value must be a socket"); ret = CURL_SOCKET_BAD; goto verbose_error; } silent_error: done: Py_XDECREF(result); Py_XDECREF(fileno_result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } PYCURL_INTERNAL int sockopt_cb(void *clientp, curl_socket_t curlfd, curlsocktype purpose) { PyObject *arglist; CurlObject *self; int ret = -1; PyObject *ret_obj = NULL; PYCURL_DECLARE_THREAD_STATE; self = (CurlObject *)clientp; PYCURL_ACQUIRE_THREAD(); arglist = Py_BuildValue("(ii)", (int) curlfd, (int) purpose); if (arglist == NULL) goto verbose_error; ret_obj = PyObject_Call(self->sockopt_cb, arglist, NULL); Py_DECREF(arglist); if (!PyInt_Check(ret_obj) && !PyLong_Check(ret_obj)) { PyObject *ret_repr = PyObject_Repr(ret_obj); if (ret_repr) { PyObject *encoded_obj; char *str = PyText_AsString_NoNUL(ret_repr, &encoded_obj); fprintf(stderr, "sockopt callback returned %s which is not an integer\n", str); /* PyErr_Format(PyExc_TypeError, "sockopt callback returned %s which is not an integer", str); */ Py_XDECREF(encoded_obj); Py_DECREF(ret_repr); } goto silent_error; } if (PyInt_Check(ret_obj)) { /* long to int cast */ ret = (int) PyInt_AsLong(ret_obj); } else { /* long to int cast */ ret = (int) PyLong_AsLong(ret_obj); } goto done; silent_error: ret = -1; done: Py_XDECREF(ret_obj); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 7) PYCURL_INTERNAL int closesocket_callback(void *clientp, curl_socket_t curlfd) { PyObject *arglist; CurlObject *self; int ret = -1; PyObject *ret_obj = NULL; PYCURL_DECLARE_THREAD_STATE; self = (CurlObject *)clientp; PYCURL_ACQUIRE_THREAD(); arglist = Py_BuildValue("(i)", (int) curlfd); if (arglist == NULL) goto verbose_error; ret_obj = PyObject_Call(self->closesocket_cb, arglist, NULL); Py_DECREF(arglist); if (!ret_obj) goto silent_error; if (!PyInt_Check(ret_obj) && !PyLong_Check(ret_obj)) { PyObject *ret_repr = PyObject_Repr(ret_obj); if (ret_repr) { PyObject *encoded_obj; char *str = PyText_AsString_NoNUL(ret_repr, &encoded_obj); fprintf(stderr, "closesocket callback returned %s which is not an integer\n", str); /* PyErr_Format(PyExc_TypeError, "closesocket callback returned %s which is not an integer", str); */ Py_XDECREF(encoded_obj); Py_DECREF(ret_repr); } goto silent_error; } if (PyInt_Check(ret_obj)) { /* long to int cast */ ret = (int) PyInt_AsLong(ret_obj); } else { /* long to int cast */ ret = (int) PyLong_AsLong(ret_obj); } goto done; silent_error: ret = -1; done: Py_XDECREF(ret_obj); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } #endif #ifdef HAVE_CURL_7_19_6_OPTS static PyObject * khkey_to_object(const struct curl_khkey *khkey) { PyObject *arglist, *ret; if (khkey == NULL) { Py_INCREF(Py_None); return Py_None; } if (khkey->len) { #if PY_MAJOR_VERSION >= 3 arglist = Py_BuildValue("(y#i)", khkey->key, khkey->len, khkey->keytype); #else arglist = Py_BuildValue("(s#i)", khkey->key, khkey->len, khkey->keytype); #endif } else { #if PY_MAJOR_VERSION >= 3 arglist = Py_BuildValue("(yi)", khkey->key, khkey->keytype); #else arglist = Py_BuildValue("(si)", khkey->key, khkey->keytype); #endif } if (arglist == NULL) { return NULL; } ret = PyObject_Call(khkey_type, arglist, NULL); Py_DECREF(arglist); return ret; } PYCURL_INTERNAL int ssh_key_cb(CURL *easy, const struct curl_khkey *knownkey, const struct curl_khkey *foundkey, int khmatch, void *clientp) { PyObject *arglist; CurlObject *self; int ret = -1; PyObject *knownkey_obj = NULL; PyObject *foundkey_obj = NULL; PyObject *ret_obj = NULL; PYCURL_DECLARE_THREAD_STATE; self = (CurlObject *)clientp; PYCURL_ACQUIRE_THREAD(); knownkey_obj = khkey_to_object(knownkey); if (knownkey_obj == NULL) { goto silent_error; } foundkey_obj = khkey_to_object(foundkey); if (foundkey_obj == NULL) { goto silent_error; } arglist = Py_BuildValue("(OOi)", knownkey_obj, foundkey_obj, khmatch); if (arglist == NULL) goto verbose_error; ret_obj = PyObject_Call(self->ssh_key_cb, arglist, NULL); Py_DECREF(arglist); if (!PyInt_Check(ret_obj) && !PyLong_Check(ret_obj)) { PyObject *ret_repr = PyObject_Repr(ret_obj); if (ret_repr) { PyObject *encoded_obj; char *str = PyText_AsString_NoNUL(ret_repr, &encoded_obj); fprintf(stderr, "ssh key callback returned %s which is not an integer\n", str); /* PyErr_Format(PyExc_TypeError, "ssh key callback returned %s which is not an integer", str); */ Py_XDECREF(encoded_obj); Py_DECREF(ret_repr); } goto silent_error; } if (PyInt_Check(ret_obj)) { /* long to int cast */ ret = (int) PyInt_AsLong(ret_obj); } else { /* long to int cast */ ret = (int) PyLong_AsLong(ret_obj); } goto done; silent_error: ret = -1; done: Py_XDECREF(knownkey_obj); Py_XDECREF(foundkey_obj); Py_XDECREF(ret_obj); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } #endif PYCURL_INTERNAL int seek_callback(void *stream, curl_off_t offset, int origin) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; int ret = 2; /* assume error 2 (can't seek, libcurl free to work around). */ PyObject *cb; int source = 0; /* assume beginning */ PYCURL_DECLARE_THREAD_STATE; /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return ret; /* check arguments */ switch (origin) { case SEEK_SET: source = 0; break; case SEEK_CUR: source = 1; break; case SEEK_END: source = 2; break; default: source = origin; break; } /* run callback */ cb = self->seek_cb; if (cb == NULL) goto silent_error; arglist = Py_BuildValue("(L,i)", (PY_LONG_LONG) offset, source); if (arglist == NULL) goto verbose_error; result = PyObject_Call(cb, arglist, NULL); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* handle result */ if (result == Py_None) { ret = 0; /* None means success */ } else if (PyInt_Check(result)) { int ret_code = PyInt_AsLong(result); if (ret_code < 0 || ret_code > 2) { PyErr_Format(ErrorObject, "invalid return value for seek callback %d not in (0, 1, 2)", ret_code); goto verbose_error; } ret = ret_code; /* pass the return code from the callback */ } else { PyErr_SetString(ErrorObject, "seek callback must return 0 (CURL_SEEKFUNC_OK), 1 (CURL_SEEKFUNC_FAIL), 2 (CURL_SEEKFUNC_CANTSEEK) or None"); goto verbose_error; } silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } PYCURL_INTERNAL size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; size_t ret = CURL_READFUNC_ABORT; /* assume error, this actually works */ int total_size; PYCURL_DECLARE_THREAD_STATE; /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return ret; /* check args */ if (self->r_cb == NULL) goto silent_error; if (size <= 0 || nmemb <= 0) goto done; total_size = (int)(size * nmemb); if (total_size < 0 || (size_t)total_size / size != nmemb) { PyErr_SetString(ErrorObject, "integer overflow in read callback"); goto verbose_error; } /* run callback */ arglist = Py_BuildValue("(i)", total_size); if (arglist == NULL) goto verbose_error; result = PyObject_Call(self->r_cb, arglist, NULL); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* handle result */ if (PyByteStr_Check(result)) { char *buf = NULL; Py_ssize_t obj_size = -1; Py_ssize_t r; r = PyByteStr_AsStringAndSize(result, &buf, &obj_size); if (r != 0 || obj_size < 0 || obj_size > total_size) { PyErr_Format(ErrorObject, "invalid return value for read callback (%ld bytes returned when at most %ld bytes were wanted)", (long)obj_size, (long)total_size); goto verbose_error; } memcpy(ptr, buf, obj_size); ret = obj_size; /* success */ } else if (PyUnicode_Check(result)) { char *buf = NULL; Py_ssize_t obj_size = -1; Py_ssize_t r; /* Encode with ascii codec. HTTP requires sending content-length for request body to the server before the request body is sent, therefore typically content length is given via POSTFIELDSIZE before read function is invoked to provide the data. However, if we encode the string using any encoding other than ascii, the length of encoded string may not match the length of unicode string we are encoding. Therefore, if client code does a simple len(source_string) to determine the value to supply in content-length, the length of bytes read may be different. To avoid this situation, we only accept ascii bytes in the string here. Encode data yourself to bytes when dealing with non-ascii data. */ PyObject *encoded = PyUnicode_AsEncodedString(result, "ascii", "strict"); if (encoded == NULL) { goto verbose_error; } r = PyByteStr_AsStringAndSize(encoded, &buf, &obj_size); if (r != 0 || obj_size < 0 || obj_size > total_size) { Py_DECREF(encoded); PyErr_Format(ErrorObject, "invalid return value for read callback (%ld bytes returned after encoding to utf-8 when at most %ld bytes were wanted)", (long)obj_size, (long)total_size); goto verbose_error; } memcpy(ptr, buf, obj_size); Py_DECREF(encoded); ret = obj_size; /* success */ } #if PY_MAJOR_VERSION < 3 else if (PyInt_Check(result)) { long r = PyInt_AsLong(result); if (r != CURL_READFUNC_ABORT && r != CURL_READFUNC_PAUSE) goto type_error; ret = r; /* either CURL_READFUNC_ABORT or CURL_READFUNC_PAUSE */ } #endif else if (PyLong_Check(result)) { long r = PyLong_AsLong(result); if (r != CURL_READFUNC_ABORT && r != CURL_READFUNC_PAUSE) goto type_error; ret = r; /* either CURL_READFUNC_ABORT or CURL_READFUNC_PAUSE */ } else { type_error: PyErr_SetString(ErrorObject, "read callback must return a byte string or Unicode string with ASCII code points only"); goto verbose_error; } done: silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } PYCURL_INTERNAL int progress_callback(void *stream, double dltotal, double dlnow, double ultotal, double ulnow) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; int ret = 1; /* assume error */ PYCURL_DECLARE_THREAD_STATE; /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return ret; /* check args */ if (self->pro_cb == NULL) goto silent_error; /* run callback */ arglist = Py_BuildValue("(dddd)", dltotal, dlnow, ultotal, ulnow); if (arglist == NULL) goto verbose_error; result = PyObject_Call(self->pro_cb, arglist, NULL); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* handle result */ if (result == Py_None) { ret = 0; /* None means success */ } else if (PyInt_Check(result)) { ret = (int) PyInt_AsLong(result); } else { ret = PyObject_IsTrue(result); /* FIXME ??? */ } silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 32, 0) PYCURL_INTERNAL int xferinfo_callback(void *stream, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; int ret = 1; /* assume error */ PYCURL_DECLARE_THREAD_STATE; /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return ret; /* check args */ if (self->xferinfo_cb == NULL) goto silent_error; /* run callback */ arglist = Py_BuildValue("(LLLL)", (PY_LONG_LONG) dltotal, (PY_LONG_LONG) dlnow, (PY_LONG_LONG) ultotal, (PY_LONG_LONG) ulnow); if (arglist == NULL) goto verbose_error; result = PyObject_Call(self->xferinfo_cb, arglist, NULL); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* handle result */ if (result == Py_None) { ret = 0; /* None means success */ } else if (PyInt_Check(result)) { ret = (int) PyInt_AsLong(result); } else { ret = PyObject_IsTrue(result); /* FIXME ??? */ } silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } #endif PYCURL_INTERNAL int debug_callback(CURL *curlobj, curl_infotype type, char *buffer, size_t total_size, void *stream) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; int ret = 0; /* always success */ PYCURL_DECLARE_THREAD_STATE; UNUSED(curlobj); /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return ret; /* check args */ if (self->debug_cb == NULL) goto silent_error; if ((int)total_size < 0 || (size_t)((int)total_size) != total_size) { PyErr_SetString(ErrorObject, "integer overflow in debug callback"); goto verbose_error; } /* run callback */ #if PY_MAJOR_VERSION >= 3 arglist = Py_BuildValue("(iy#)", (int)type, buffer, (int)total_size); #else arglist = Py_BuildValue("(is#)", (int)type, buffer, (int)total_size); #endif if (arglist == NULL) goto verbose_error; result = PyObject_Call(self->debug_cb, arglist, NULL); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* return values from debug callbacks should be ignored */ silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } PYCURL_INTERNAL curlioerr ioctl_callback(CURL *curlobj, int cmd, void *stream) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; int ret = CURLIOE_FAILRESTART; /* assume error */ PYCURL_DECLARE_THREAD_STATE; UNUSED(curlobj); /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return (curlioerr) ret; /* check args */ if (self->ioctl_cb == NULL) goto silent_error; /* run callback */ arglist = Py_BuildValue("(i)", cmd); if (arglist == NULL) goto verbose_error; result = PyObject_Call(self->ioctl_cb, arglist, NULL); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* handle result */ if (result == Py_None) { ret = CURLIOE_OK; /* None means success */ } else if (PyInt_Check(result)) { ret = (int) PyInt_AsLong(result); if (ret >= CURLIOE_LAST || ret < 0) { PyErr_SetString(ErrorObject, "ioctl callback returned invalid value"); goto verbose_error; } } silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return (curlioerr) ret; verbose_error: PyErr_Print(); goto silent_error; } #if defined(HAVE_CURL_OPENSSL) /* internal helper that load certificates from buffer, returns -1 on error */ static int add_ca_certs(SSL_CTX *context, void *data, Py_ssize_t len) { // this code was copied from _ssl module BIO *biobuf = NULL; X509_STORE *store; int retval = 0, err, loaded = 0; if (len <= 0) { PyErr_SetString(PyExc_ValueError, "Empty certificate data"); return -1; } else if (len > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "Certificate data is too long."); return -1; } biobuf = BIO_new_mem_buf(data, (int)len); if (biobuf == NULL) { PyErr_SetString(PyExc_MemoryError, "Can't allocate buffer"); ERR_clear_error(); return -1; } store = SSL_CTX_get_cert_store(context); assert(store != NULL); while (1) { X509 *cert = NULL; int r; cert = PEM_read_bio_X509(biobuf, NULL, 0, NULL); if (cert == NULL) { break; } r = X509_STORE_add_cert(store, cert); X509_free(cert); if (!r) { err = ERR_peek_last_error(); if ((ERR_GET_LIB(err) == ERR_LIB_X509) && (ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE)) { /* cert already in hash table, not an error */ ERR_clear_error(); } else { break; } } loaded++; } err = ERR_peek_last_error(); if ((loaded > 0) && (ERR_GET_LIB(err) == ERR_LIB_PEM) && (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) { /* EOF PEM file, not an error */ ERR_clear_error(); retval = 0; } else { PyErr_SetString(ErrorObject, ERR_reason_error_string(err)); ERR_clear_error(); retval = -1; } BIO_free(biobuf); return retval; } PYCURL_INTERNAL CURLcode ssl_ctx_callback(CURL *curl, void *ssl_ctx, void *ptr) { CurlObject *self; PYCURL_DECLARE_THREAD_STATE; int r; UNUSED(curl); /* acquire thread */ self = (CurlObject *)ptr; if (!PYCURL_ACQUIRE_THREAD()) return CURLE_FAILED_INIT; r = add_ca_certs((SSL_CTX*)ssl_ctx, PyBytes_AS_STRING(self->ca_certs_obj), PyBytes_GET_SIZE(self->ca_certs_obj)); if (r != 0) PyErr_Print(); PYCURL_RELEASE_THREAD(); return r == 0 ? CURLE_OK : CURLE_FAILED_INIT; } #endif