<commit_before>/* cURL Python module by <NAME> <<EMAIL>> */

#include "Python.h"
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

static PyObject *ErrorObject;

typedef struct {
    PyObject_HEAD
    CURL *handle;
    char *url;
    struct HttpPost *httppost;
    struct curl_slist *httpheader;
    struct curl_slist *quote;
    struct curl_slist *postquote;
    PyObject *w_cb;
  PyThreadState *state;
    char error[CURL_ERROR_SIZE];
} CurlObject;

staticforward PyTypeObject Curl_Type;

/* --------------------------------------------------------------------- */

static void
self_cleanup(CurlObject *self)
{
    if (self->handle != NULL) {
	Py_BEGIN_ALLOW_THREADS
	curl_easy_cleanup(self->handle);
	Py_END_ALLOW_THREADS
        self->handle = NULL;
    }
    if (self->url != NULL) {
	free(self->url);
	self->url = NULL;
    }
    if (self->httpheader != NULL) {
	curl_slist_free_all(self->httpheader);
	self->httpheader = NULL;
    }
    if (self->quote != NULL) {
	curl_slist_free_all(self->quote);
	self->quote = NULL;
    }
    if (self->postquote != NULL) {
	curl_slist_free_all(self->postquote);
	self->postquote = NULL;
    }
    if (self->httppost != NULL) {
	curl_formfree(self->httppost);
	self->httppost = NULL;
    }
}


static void
curl_dealloc(CurlObject *self)
{
    self_cleanup(self);
    PyObject_Del(self);
}


static PyObject *
do_cleanup(CurlObject *self, PyObject *args)
{
    if (!PyArg_ParseTuple(args, ":cleanup")) {
	return NULL;
    }
    self_cleanup(self);
    Py_INCREF(Py_None);
    return Py_None;
}

/* --------------------------------------------------------------------- */

static int 
write_callback(void *ptr,
	       size_t size,
	       size_t nmemb,
	       FILE  *stream)
{
    PyObject *arglist;
    PyObject *result;
    CurlObject *self;
    int write_size;
    
    self = (CurlObject *)stream;
    arglist = Py_BuildValue("(s#)", (char *)ptr, size*nmemb);

    PyEval_AcquireThread(self->state);
    result = PyEval_CallObject(self->w_cb, arglist);
    Py_DECREF(arglist);
    if (result == NULL) {
	PyErr_Print();
	write_size = -1;
    }
    else
	write_size = (int)PyInt_AsLong(result);
    Py_XDECREF(result);
    PyEval_ReleaseThread(self->state);

    return write_size;
}


static PyObject *
do_setopt(CurlObject *self, PyObject *args)
{
    int option;
    char *stringdata;
    long longdata;
    char *buf;
    PyObject *obj, *listitem;
    FILE *fp;
    int res;
    struct curl_slist **slist;
    int len;
    char *str;
    int i;
    struct HttpPost *last;

    /* Check that we have a valid curl handle for the object */
    if (self->handle == NULL) {
	PyErr_SetString(ErrorObject, "cannot invoke setopt, no curl handle");
	return NULL;
    }

    /* Handle the case of string arguments */
    if (PyArg_ParseTuple(args, "is:setopt", &option, &stringdata)) {
	/* Check that the option specified a string as well as the input */
	if (!(option == CURLOPT_URL ||
	      option == CURLOPT_PROXY ||
	      option == CURLOPT_USERPWD ||
	      option == CURLOPT_PROXYUSERPWD ||
	      option == CURLOPT_RANGE ||
	      option == CURLOPT_POSTFIELDS ||
	      option == CURLOPT_REFERER ||
	      option == CURLOPT_USERAGENT ||
	      option == CURLOPT_FTPPORT ||
	      option == CURLOPT_COOKIE ||
	      option == CURLOPT_SSLCERT ||
	      option == CURLOPT_SSLCERTPASSWD ||
	      option == CURLOPT_COOKIEFILE ||
	      option == CURLOPT_CUSTOMREQUEST ||
	      option == CURLOPT_INTERFACE ||
	      option == CURLOPT_KRB4LEVEL ||
	      option == CURLOPT_CAINFO ||
	      option == CURLOPT_RANDOM_FILE ||
	      option == CURLOPT_EGDSOCKET))
	    {
		PyErr_SetString(ErrorObject, "invalid options to setopt");
		return NULL;
	    }
	if (option == CURLOPT_URL) {
	    /* Need to store uri for later use if the option is OPTCURL_URL */
	    buf = (char *)malloc((strlen(stringdata)*sizeof(char))+sizeof(char));
	    if (buf == NULL) {
		PyErr_SetString(ErrorObject, "unable to allocate memory for url");
		return NULL;
	    }
	    if (self->url != NULL) {
		free(self->url);
	    }
	    strcpy(buf, stringdata);
	    self->url = buf;
	    res = curl_easy_setopt(self->handle, CURLOPT_URL, self->url);
	} else {
	    /* Handle the regular cases of string arguments */
	    res = curl_easy_setopt(self->handle, option, stringdata);
	}
	/* Check for errors */
	if (res == 0) {
	    Py_INCREF(Py_None);
	    return Py_None;
	} else {
	    PyErr_SetString(ErrorObject, self->error);
	    return NULL;
	}
    }

    PyErr_Clear();

    /* Handle the case of integer arguments */
    if (PyArg_ParseTuple(args, "il:setopt", &option, &longdata)) {    
	/* Check that option is integer as well as the input data */
	if (option >= CURLOPTTYPE_OBJECTPOINT) {
	    PyErr_SetString(ErrorObject, "invalid options to setopt");
	    return NULL;
	}
	res = curl_easy_setopt(self->handle, option, longdata);
	/* Check for errors */
	if (res == 0) {
	    Py_INCREF(Py_None);
	    return Py_None;
	} else {
	    PyErr_SetString(ErrorObject, self->error);
	    return NULL;
	}
    }

    PyErr_Clear();

    /* Handle the case of file objects */
    if (PyArg_ParseTuple(args, "iO!:setopt", &option, &PyFile_Type, &obj)) {
	/* Ensure the option specified a file as well as the input */
	if (!(option == CURLOPT_FILE || 
	      option == CURLOPT_INFILE ||
	      option == CURLOPT_WRITEHEADER || 
	      option == CURLOPT_PROGRESSDATA ||
	      option == CURLOPT_PASSWDDATA))
	    {
		PyErr_SetString(PyExc_TypeError, "invalid options to setopt");
		return NULL;
	    }
	fp = PyFile_AsFile(obj);
	if (fp == NULL) {
	    PyErr_SetString(PyExc_TypeError, "second argument must be open file");
	    return NULL;
	}
	res = curl_easy_setopt(self->handle, option, fp);
	/* Check for errors */
	if (res == 0) {
	    Py_INCREF(Py_None);
	    return Py_None;
	} else {
	    PyErr_SetString(ErrorObject, self->error);
	    return NULL;
	}      
    }

    PyErr_Clear();

    /* Handle the case of list objects */
    if (PyArg_ParseTuple(args, "iO!:setopt", &option, &PyList_Type, &obj)) {
	switch (option) {
	case CURLOPT_HTTPHEADER:
	    slist = &self->httpheader; 
	    break;
	case CURLOPT_QUOTE:
	    slist = &self->quote; 
	    break;
	case CURLOPT_POSTQUOTE:
	    slist = &self->postquote; 
	    break;
	case CURLOPT_HTTPPOST:
	    slist = NULL;
	    break;
	default:
	    /* None of the list options were recognized, throw exception */
	    PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt");
	    return NULL;
	}
	
	/* Handle HTTPPOST different since we construct a HttpPost form struct */
	if (option == CURLOPT_HTTPPOST) {
	    if (self->httppost != NULL) {
		curl_formfree(self->httppost);
		self->httppost = NULL;
	    }
	    len = PyList_Size(obj);
	    last = NULL;
	    for (i = 0; i < len; i++) {
		listitem = PyList_GetItem(obj, i);
		if (!PyString_Check(listitem)) {
		    PyErr_SetString(PyExc_TypeError, "list items must be string objects");
		    curl_formfree(self->httppost);
		    return NULL;
		}
		str = PyString_AsString(listitem);
		buf = (char *)malloc((sizeof(char)*strlen(str)) + sizeof(char));
		if (buf == NULL) {
		    PyErr_SetString(ErrorObject, "unable to allocate memory for list element");
		    return NULL;
		}
		strcpy(buf, str);
		res = curl_formparse(buf, &self->httppost, &last);
		if (res != 0) {
		    curl_formfree(self->httppost);
		    PyErr_SetString(ErrorObject, self->error);
		    return NULL;
		}
	    }
	    res = curl_easy_setopt(self->handle, CURLOPT_HTTPPOST, self->httppost);
	    /* Check for errors */
	    if (res == 0) {
		Py_INCREF(Py_None);
		return Py_None;
	    } else {
		curl_formfree(self->httppost);
		PyErr_SetString(ErrorObject, self->error);
		return NULL;
	    }
	}

	/* Just to be sure we do not bug off here */
	assert(slist != NULL);
	
	/* Handle regular list operations on the other options */
	if (*slist != NULL) {
	    /* Free previously allocated list */
	    curl_slist_free_all(*slist);
	    *slist = NULL;
	}
	len = PyList_Size(obj);
	for (i = 0; i < len; i++) {
	    listitem = PyList_GetItem(obj, i);
	    if (!PyString_Check(listitem)) {
		curl_slist_free_all(*slist);
		PyErr_SetString(PyExc_TypeError, "list items must be string objects");
		return NULL;
	    }
	    str = PyString_AsString(listitem);
	    buf = (char *)malloc((sizeof(char)*strlen(str)) + sizeof(char));
	    if (buf == NULL) {
		PyErr_SetString(ErrorObject, "unable to allocate memory for list element");
		return NULL;
	    }
	    strcpy(buf, str);
	    *slist = curl_slist_append(*slist, buf);
	}
	res = curl_easy_setopt(self->handle, option, *slist);
	/* Check for errors */
	if (res == 0) {
	    Py_INCREF(Py_None);
	    return Py_None;
	} else {
	    curl_slist_free_all(*slist);
	    PyErr_SetString(ErrorObject, self->error);
	    return NULL;
	}
    }

    /* Handle the case of function objects for callbacks */
    if (PyArg_ParseTuple(args, "iO!:setopt", &option, &PyFunction_Type, &obj)) {
	switch(option) {
	case CURLOPT_WRITEFUNCTION:
	    /* Must store the function pointer in self */
	    Py_INCREF(obj);
	    Py_XDECREF(self->w_cb);
	    self->w_cb = obj;
	    curl_easy_setopt(self->handle, CURLOPT_WRITEFUNCTION, write_callback);
	    curl_easy_setopt(self->handle, CURLOPT_FILE, self);
	    break;
	}
	Py_INCREF(Py_None);
	return Py_None;
    }

    /* Failed to match any of the function signatures -- return error */
    PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt");
    return NULL;
}


static PyObject *
do_perform(CurlObject *self, PyObject *args)
{
    int res;

    if (!PyArg_ParseTuple(args, ":perform")) {
	return NULL;
    }

    if (self->handle == NULL) {
	PyErr_SetString(ErrorObject, "cannot invoke perform, no curl handle");
	return NULL;
    }

    /* Save handle to current thread (used to run the callbacks in) */
    PyEval_InitThreads();
    self->state = PyThreadState_New(PyThreadState_Get()->interp);

    /* Release global lock and start */
    Py_BEGIN_ALLOW_THREADS
    res = curl_easy_perform(self->handle);
    Py_END_ALLOW_THREADS

    if (res == 0) {
	Py_INCREF(Py_None);
	return Py_None;
    } else {
	PyErr_SetString(ErrorObject, self->error);
	return NULL;
    }
}


static PyObject *
do_getinfo(CurlObject *self, PyObject *args)
{
    int option;
    int res;
    double d_res;
    long l_res;
    char *s_res;

    /* Check that we have a valid curl handle for the object */
    if (self->handle == NULL) {
	PyErr_SetString(ErrorObject, "cannot invoke getinfo, no curl handle");
	return NULL;
    }

    /* Parse option */
    if (PyArg_ParseTuple(args, "i:getinfo", &option)) {
	if (option == CURLINFO_HEADER_SIZE || 
	    option == CURLINFO_REQUEST_SIZE ||
	    option == CURLINFO_SSL_VERIFYRESULT ||
	    option == CURLINFO_FILETIME ||
	    option == CURLINFO_HTTP_CODE) 
	    {
		/* Return long as result */
		Py_BEGIN_ALLOW_THREADS
		res = curl_easy_getinfo(self->handle, option, &l_res);
		Py_END_ALLOW_THREADS
		/* Check for errors and return result */
		if (res == 0) {
		    return PyLong_FromLong(l_res);
		} else {
		    PyErr_SetString(ErrorObject, self->error);
		    return NULL;
		}
	    }

	if (option == CURLINFO_EFFECTIVE_URL)
	    {
		/* Return string as result */
		Py_BEGIN_ALLOW_THREADS
		res = curl_easy_getinfo(self->handle, option, &s_res);
		Py_END_ALLOW_THREADS
		/* Check for errors and return result */
		if (res == 0) {
		    return PyString_FromString(s_res);
		} else {
		    PyErr_SetString(ErrorObject, self->error);
		    return NULL;
		}
	    }

	if (option == CURLINFO_TOTAL_TIME ||
	    option == CURLINFO_NAMELOOKUP_TIME ||
	    option == CURLINFO_CONNECT_TIME ||
	    option == CURLINFO_PRETRANSFER_TIME ||
	    option == CURLINFO_SIZE_UPLOAD ||
	    option == CURLINFO_SIZE_DOWNLOAD ||
	    option == CURLINFO_SPEED_DOWNLOAD ||
	    option == CURLINFO_SPEED_UPLOAD ||
	    option == CURLINFO_CONTENT_LENGTH_DOWNLOAD ||
	    option == CURLINFO_CONTENT_LENGTH_UPLOAD)
	    {
		/* Return float as result */
		Py_BEGIN_ALLOW_THREADS
		res = curl_easy_getinfo(self->handle, option, &d_res);
		Py_END_ALLOW_THREADS
		/* Check for errors and return result */
		if (res == 0) {
		    return PyFloat_FromDouble(d_res);
		} else {
		    PyErr_SetString(ErrorObject, self->error);
		    return NULL;
		}
	    }
    } 

    /* Got wrong signature on the method call */
    PyErr_SetString(PyExc_TypeError, "invalid arguments to getinfo");
    return NULL;
}


/* --------------------------------------------------------------------- */

static PyMethodDef curlobject_methods[] = {
    {"cleanup", (PyCFunction)do_cleanup, METH_VARARGS, NULL},
    {"setopt", (PyCFunction)do_setopt, METH_VARARGS, NULL},
    {"perform", (PyCFunction)do_perform, METH_VARARGS, NULL},
    {"getinfo", (PyCFunction)do_getinfo, METH_VARARGS, NULL},
    {NULL, NULL}
};


static PyObject *
curl_getattr(CurlObject *co, char *name)
{
    return Py_FindMethod(curlobject_methods, (PyObject *)co, name);
}


statichere PyTypeObject Curl_Type = {
    PyObject_HEAD_INIT(NULL)
    0,			/*ob_size*/
    "Curl",	        /*tp_name*/
    sizeof(CurlObject),	/*tp_basicsize*/
    0,			/*tp_itemsize*/
    /* Methods */
    (destructor)curl_dealloc, /*dealloc*/
    0,   	              /*tp_print*/
    (getattrfunc)curl_getattr,/*getattr*/
    0,                        /*setattr*/
    0,			    /*tp_compare*/
    0,			    /*tp_repr*/
    0,			    /*tp_as_number*/
    0,			    /*tp_as_sequence*/
    0,			    /*tp_as_mapping*/
    0,			    /*tp_hash*/
};

/* --------------------------------------------------------------------- */

static CurlObject *
do_init(PyObject *arg)
{
    CURL *curlhandle;
    CurlObject *self;
    int res;

    /* Initialize curl */
    curlhandle = curl_easy_init();
    if (curlhandle == NULL) {
	return NULL;
    }

    /* Allocate python curl object */
    self = PyObject_New(CurlObject, &Curl_Type);
    if (self == NULL) {
	curl_easy_cleanup(curlhandle);
	return NULL;
    }

    /* Set error buffer */
    res = curl_easy_setopt(curlhandle, CURLOPT_ERRORBUFFER, self->error);
    if (res != 0) {
	curl_easy_cleanup(curlhandle);
	return NULL;
    }
    memset(self->error, 0, sizeof(char) * CURL_ERROR_SIZE);

    /* Setup python curl object initial values and return object */
    self->handle = curlhandle;
    self->url = NULL;
    self->httpheader = NULL;
    self->quote = NULL;
    self->postquote = NULL;
    self->httppost = NULL;
    self->w_cb = NULL;
    return self;
}


/* List of functions defined in the curl module */<commit_msg>only initialize global interpreter lock once (in init instead of perform)<commit_after>/* cURL Python module by <NAME> <kjetilja @ cs.uit.no> */

#include "Python.h"
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

static PyObject *ErrorObject;

typedef struct {
    PyObject_HEAD
    CURL *handle;
    char *url;
    struct HttpPost *httppost;
    struct curl_slist *httpheader;
    struct curl_slist *quote;
    struct curl_slist *postquote;
    PyObject *w_cb;
  PyThreadState *state;
    char error[CURL_ERROR_SIZE];
} CurlObject;

staticforward PyTypeObject Curl_Type;

/* --------------------------------------------------------------------- */

static void
self_cleanup(CurlObject *self)
{
    if (self->handle != NULL) {
	Py_BEGIN_ALLOW_THREADS
	curl_easy_cleanup(self->handle);
	Py_END_ALLOW_THREADS
        self->handle = NULL;
    }
    if (self->url != NULL) {
	free(self->url);
	self->url = NULL;
    }
    if (self->httpheader != NULL) {
	curl_slist_free_all(self->httpheader);
	self->httpheader = NULL;
    }
    if (self->quote != NULL) {
	curl_slist_free_all(self->quote);
	self->quote = NULL;
    }
    if (self->postquote != NULL) {
	curl_slist_free_all(self->postquote);
	self->postquote = NULL;
    }
    if (self->httppost != NULL) {
	curl_formfree(self->httppost);
	self->httppost = NULL;
    }
}


static void
curl_dealloc(CurlObject *self)
{
    self_cleanup(self);
    PyObject_Del(self);
}


static PyObject *
do_cleanup(CurlObject *self, PyObject *args)
{
    if (!PyArg_ParseTuple(args, ":cleanup")) {
	return NULL;
    }
    self_cleanup(self);
    Py_INCREF(Py_None);
    return Py_None;
}

/* --------------------------------------------------------------------- */

static int 
write_callback(void *ptr,
	       size_t size,
	       size_t nmemb,
	       FILE  *stream)
{
    PyObject *arglist;
    PyObject *result;
    CurlObject *self;
    int write_size;
    
    self = (CurlObject *)stream;
    arglist = Py_BuildValue("(s#)", (char *)ptr, size*nmemb);

    PyEval_AcquireThread(self->state);
    result = PyEval_CallObject(self->w_cb, arglist);
    Py_DECREF(arglist);
    if (result == NULL) {
	PyErr_Print();
	write_size = -1;
    }
    else
	write_size = (int)PyInt_AsLong(result);
    Py_XDECREF(result);
    PyEval_ReleaseThread(self->state);

    return write_size;
}


static PyObject *
do_setopt(CurlObject *self, PyObject *args)
{
    int option;
    char *stringdata;
    long longdata;
    char *buf;
    PyObject *obj, *listitem;
    FILE *fp;
    int res;
    struct curl_slist **slist;
    int len;
    char *str;
    int i;
    struct HttpPost *last;

    /* Check that we have a valid curl handle for the object */
    if (self->handle == NULL) {
	PyErr_SetString(ErrorObject, "cannot invoke setopt, no curl handle");
	return NULL;
    }

    /* Handle the case of string arguments */
    if (PyArg_ParseTuple(args, "is:setopt", &option, &stringdata)) {
	/* Check that the option specified a string as well as the input */
	if (!(option == CURLOPT_URL ||
	      option == CURLOPT_PROXY ||
	      option == CURLOPT_USERPWD ||
	      option == CURLOPT_PROXYUSERPWD ||
	      option == CURLOPT_RANGE ||
	      option == CURLOPT_POSTFIELDS ||
	      option == CURLOPT_REFERER ||
	      option == CURLOPT_USERAGENT ||
	      option == CURLOPT_FTPPORT ||
	      option == CURLOPT_COOKIE ||
	      option == CURLOPT_SSLCERT ||
	      option == CURLOPT_SSLCERTPASSWD ||
	      option == CURLOPT_COOKIEFILE ||
	      option == CURLOPT_CUSTOMREQUEST ||
	      option == CURLOPT_INTERFACE ||
	      option == CURLOPT_KRB4LEVEL ||
	      option == CURLOPT_CAINFO ||
	      option == CURLOPT_RANDOM_FILE ||
	      option == CURLOPT_EGDSOCKET))
	    {
		PyErr_SetString(ErrorObject, "invalid options to setopt");
		return NULL;
	    }
	if (option == CURLOPT_URL) {
	    /* Need to store uri for later use if the option is OPTCURL_URL */
	    buf = (char *)malloc((strlen(stringdata)*sizeof(char))+sizeof(char));
	    if (buf == NULL) {
		PyErr_SetString(ErrorObject, "unable to allocate memory for url");
		return NULL;
	    }
	    if (self->url != NULL) {
		free(self->url);
	    }
	    strcpy(buf, stringdata);
	    self->url = buf;
	    res = curl_easy_setopt(self->handle, CURLOPT_URL, self->url);
	} else {
	    /* Handle the regular cases of string arguments */
	    res = curl_easy_setopt(self->handle, option, stringdata);
	}
	/* Check for errors */
	if (res == 0) {
	    Py_INCREF(Py_None);
	    return Py_None;
	} else {
	    PyErr_SetString(ErrorObject, self->error);
	    return NULL;
	}
    }

    PyErr_Clear();

    /* Handle the case of integer arguments */
    if (PyArg_ParseTuple(args, "il:setopt", &option, &longdata)) {    
	/* Check that option is integer as well as the input data */
	if (option >= CURLOPTTYPE_OBJECTPOINT) {
	    PyErr_SetString(ErrorObject, "invalid options to setopt");
	    return NULL;
	}
	res = curl_easy_setopt(self->handle, option, longdata);
	/* Check for errors */
	if (res == 0) {
	    Py_INCREF(Py_None);
	    return Py_None;
	} else {
	    PyErr_SetString(ErrorObject, self->error);
	    return NULL;
	}
    }

    PyErr_Clear();

    /* Handle the case of file objects */
    if (PyArg_ParseTuple(args, "iO!:setopt", &option, &PyFile_Type, &obj)) {
	/* Ensure the option specified a file as well as the input */
	if (!(option == CURLOPT_FILE || 
	      option == CURLOPT_INFILE ||
	      option == CURLOPT_WRITEHEADER || 
	      option == CURLOPT_PROGRESSDATA ||
	      option == CURLOPT_PASSWDDATA))
	    {
		PyErr_SetString(PyExc_TypeError, "invalid options to setopt");
		return NULL;
	    }
	fp = PyFile_AsFile(obj);
	if (fp == NULL) {
	    PyErr_SetString(PyExc_TypeError, "second argument must be open file");
	    return NULL;
	}
	res = curl_easy_setopt(self->handle, option, fp);
	/* Check for errors */
	if (res == 0) {
	    Py_INCREF(Py_None);
	    return Py_None;
	} else {
	    PyErr_SetString(ErrorObject, self->error);
	    return NULL;
	}      
    }

    PyErr_Clear();

    /* Handle the case of list objects */
    if (PyArg_ParseTuple(args, "iO!:setopt", &option, &PyList_Type, &obj)) {
	switch (option) {
	case CURLOPT_HTTPHEADER:
	    slist = &self->httpheader; 
	    break;
	case CURLOPT_QUOTE:
	    slist = &self->quote; 
	    break;
	case CURLOPT_POSTQUOTE:
	    slist = &self->postquote; 
	    break;
	case CURLOPT_HTTPPOST:
	    slist = NULL;
	    break;
	default:
	    /* None of the list options were recognized, throw exception */
	    PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt");
	    return NULL;
	}
	
	/* Handle HTTPPOST different since we construct a HttpPost form struct */
	if (option == CURLOPT_HTTPPOST) {
	    if (self->httppost != NULL) {
		curl_formfree(self->httppost);
		self->httppost = NULL;
	    }
	    len = PyList_Size(obj);
	    last = NULL;
	    for (i = 0; i < len; i++) {
		listitem = PyList_GetItem(obj, i);
		if (!PyString_Check(listitem)) {
		    PyErr_SetString(PyExc_TypeError, "list items must be string objects");
		    curl_formfree(self->httppost);
		    return NULL;
		}
		str = PyString_AsString(listitem);
		buf = (char *)malloc((sizeof(char)*strlen(str)) + sizeof(char));
		if (buf == NULL) {
		    PyErr_SetString(ErrorObject, "unable to allocate memory for list element");
		    return NULL;
		}
		strcpy(buf, str);
		res = curl_formparse(buf, &self->httppost, &last);
		if (res != 0) {
		    curl_formfree(self->httppost);
		    PyErr_SetString(ErrorObject, self->error);
		    return NULL;
		}
	    }
	    res = curl_easy_setopt(self->handle, CURLOPT_HTTPPOST, self->httppost);
	    /* Check for errors */
	    if (res == 0) {
		Py_INCREF(Py_None);
		return Py_None;
	    } else {
		curl_formfree(self->httppost);
		PyErr_SetString(ErrorObject, self->error);
		return NULL;
	    }
	}

	/* Just to be sure we do not bug off here */
	assert(slist != NULL);
	
	/* Handle regular list operations on the other options */
	if (*slist != NULL) {
	    /* Free previously allocated list */
	    curl_slist_free_all(*slist);
	    *slist = NULL;
	}
	len = PyList_Size(obj);
	for (i = 0; i < len; i++) {
	    listitem = PyList_GetItem(obj, i);
	    if (!PyString_Check(listitem)) {
		curl_slist_free_all(*slist);
		PyErr_SetString(PyExc_TypeError, "list items must be string objects");
		return NULL;
	    }
	    str = PyString_AsString(listitem);
	    buf = (char *)malloc((sizeof(char)*strlen(str)) + sizeof(char));
	    if (buf == NULL) {
		PyErr_SetString(ErrorObject, "unable to allocate memory for list element");
		return NULL;
	    }
	    strcpy(buf, str);
	    *slist = curl_slist_append(*slist, buf);
	}
	res = curl_easy_setopt(self->handle, option, *slist);
	/* Check for errors */
	if (res == 0) {
	    Py_INCREF(Py_None);
	    return Py_None;
	} else {
	    curl_slist_free_all(*slist);
	    PyErr_SetString(ErrorObject, self->error);
	    return NULL;
	}
    }

    /* Handle the case of function objects for callbacks */
    if (PyArg_ParseTuple(args, "iO!:setopt", &option, &PyFunction_Type, &obj)) {
	switch(option) {
	case CURLOPT_WRITEFUNCTION:
	    /* Must store the function pointer in self */
	    Py_INCREF(obj);
	    Py_XDECREF(self->w_cb);
	    self->w_cb = obj;
	    curl_easy_setopt(self->handle, CURLOPT_WRITEFUNCTION, write_callback);
	    curl_easy_setopt(self->handle, CURLOPT_FILE, self);
	    break;
	}
	Py_INCREF(Py_None);
	return Py_None;
    }

    /* Failed to match any of the function signatures -- return error */
    PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt");
    return NULL;
}


static PyObject *
do_perform(CurlObject *self, PyObject *args)
{
    int res;

    if (!PyArg_ParseTuple(args, ":perform")) {
	return NULL;
    }

    if (self->handle == NULL) {
	PyErr_SetString(ErrorObject, "cannot invoke perform, no curl handle");
	return NULL;
    }

    /* Save handle to current thread (used to run the callbacks in) */
    self->state = PyThreadState_New(PyThreadState_Get()->interp);

    /* Release global lock and start */
    Py_BEGIN_ALLOW_THREADS
    res = curl_easy_perform(self->handle);
    Py_END_ALLOW_THREADS

    if (res == 0) {
	Py_INCREF(Py_None);
	return Py_None;
    } else {
	PyErr_SetString(ErrorObject, self->error);
	return NULL;
    }
}


static PyObject *
do_getinfo(CurlObject *self, PyObject *args)
{
    int option;
    int res;
    double d_res;
    long l_res;
    char *s_res;

    /* Check that we have a valid curl handle for the object */
    if (self->handle == NULL) {
	PyErr_SetString(ErrorObject, "cannot invoke getinfo, no curl handle");
	return NULL;
    }

    /* Parse option */
    if (PyArg_ParseTuple(args, "i:getinfo", &option)) {
	if (option == CURLINFO_HEADER_SIZE || 
	    option == CURLINFO_REQUEST_SIZE ||
	    option == CURLINFO_SSL_VERIFYRESULT ||
	    option == CURLINFO_FILETIME ||
	    option == CURLINFO_HTTP_CODE) 
	    {
		/* Return long as result */
		Py_BEGIN_ALLOW_THREADS
		res = curl_easy_getinfo(self->handle, option, &l_res);
		Py_END_ALLOW_THREADS
		/* Check for errors and return result */
		if (res == 0) {
		    return PyLong_FromLong(l_res);
		} else {
		    PyErr_SetString(ErrorObject, self->error);
		    return NULL;
		}
	    }

	if (option == CURLINFO_EFFECTIVE_URL)
	    {
		/* Return string as result */
		Py_BEGIN_ALLOW_THREADS
		res = curl_easy_getinfo(self->handle, option, &s_res);
		Py_END_ALLOW_THREADS
		/* Check for errors and return result */
		if (res == 0) {
		    return PyString_FromString(s_res);
		} else {
		    PyErr_SetString(ErrorObject, self->error);
		    return NULL;
		}
	    }

	if (option == CURLINFO_TOTAL_TIME ||
	    option == CURLINFO_NAMELOOKUP_TIME ||
	    option == CURLINFO_CONNECT_TIME ||
	    option == CURLINFO_PRETRANSFER_TIME ||
	    option == CURLINFO_SIZE_UPLOAD ||
	    option == CURLINFO_SIZE_DOWNLOAD ||
	    option == CURLINFO_SPEED_DOWNLOAD ||
	    option == CURLINFO_SPEED_UPLOAD ||
	    option == CURLINFO_CONTENT_LENGTH_DOWNLOAD ||
	    option == CURLINFO_CONTENT_LENGTH_UPLOAD)
	    {
		/* Return float as result */
		Py_BEGIN_ALLOW_THREADS
		res = curl_easy_getinfo(self->handle, option, &d_res);
		Py_END_ALLOW_THREADS
		/* Check for errors and return result */
		if (res == 0) {
		    return PyFloat_FromDouble(d_res);
		} else {
		    PyErr_SetString(ErrorObject, self->error);
		    return NULL;
		}
	    }
    } 

    /* Got wrong signature on the method call */
    PyErr_SetString(PyExc_TypeError, "invalid arguments to getinfo");
    return NULL;
}


/* --------------------------------------------------------------------- */

static PyMethodDef curlobject_methods[] = {
    {"cleanup", (PyCFunction)do_cleanup, METH_VARARGS, NULL},
    {"setopt", (PyCFunction)do_setopt, METH_VARARGS, NULL},
    {"perform", (PyCFunction)do_perform, METH_VARARGS, NULL},
    {"getinfo", (PyCFunction)do_getinfo, METH_VARARGS, NULL},
    {NULL, NULL}
};


static PyObject *
curl_getattr(CurlObject *co, char *name)
{
    return Py_FindMethod(curlobject_methods, (PyObject *)co, name);
}


statichere PyTypeObject Curl_Type = {
    PyObject_HEAD_INIT(NULL)
    0,			/*ob_size*/
    "Curl",	        /*tp_name*/
    sizeof(CurlObject),	/*tp_basicsize*/
    0,			/*tp_itemsize*/
    /* Methods */
    (destructor)curl_dealloc, /*dealloc*/
    0,   	              /*tp_print*/
    (getattrfunc)curl_getattr,/*getattr*/
    0,                        /*setattr*/
    0,			    /*tp_compare*/
    0,			    /*tp_repr*/
    0,			    /*tp_as_number*/
    0,			    /*tp_as_sequence*/
    0,			    /*tp_as_mapping*/
    0,			    /*tp_hash*/
};

/* --------------------------------------------------------------------- */

static CurlObject *
do_init(PyObject *arg)
{
    CURL *curlhandle;
    CurlObject *self;
    int res;

    /* Initialize curl */
    curlhandle = curl_easy_init();
    if (curlhandle == NULL) {
	return NULL;
    }

    /* Allocate python curl object */
    self = PyObject_New(CurlObject, &Curl_Type);
    if (self == NULL) {
	curl_easy_cleanup(curlhandle);
	return NULL;
    }

    /* Set error buffer */
    res = curl_easy_setopt(curlhandle, CURLOPT_ERRORBUFFER, self->error);
    if (res != 0) {
	curl_easy_cleanup(curlhandle);
	return NULL;
    }
    memset(self->error, 0, sizeof(char) * CURL_ERROR_SIZE);

    /* Setup python curl object initial values and return object */
    self->handle = curlhandle;
    self->url = NULL;
    self->httpheader = NULL;
    self->quote = NULL;
    self->postquote = NULL;
    self->httppost = NULL;
    self->w_cb = NULL;

    /* Initialize global interpreter lock */
    PyEval_InitThreads();

    return self;
}


/* List of functions defined in the curl module */
{'fraction_non_alphanumeric': 0.1423809196508351, 'fraction_numerical': 0.0017870644030517561, 'mean_word_length': 2.8072746303807405, 'pattern_counts': {'":': 4, '<': 20, '<?xml version=': 0, '>': 166, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1585052', 'n_tokens_mistral': 10637, 'n_tokens_neox': 9871, 'n_words': 2321}