/* $Id: curl.c,v 1.3 2004/05/18 19:32:15 agraef Exp $ */
/* This file is part of the Q programming system.
The Q programming system is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
The Q programming system is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* This module currently supports all options up to libcurl 7.10.2. Older
libcurl versions (>= 7.9.x) are supported (7.9.8 has been tested), versions
< 7.9 surely need some work. */
#if defined (HAVE_CONFIG_H)
# include "config.h"
#endif
/* system headers */
#include <stdio.h>
#include <ctype.h>
#include <math.h>
/* check for standard C headers */
#if STDC_HEADERS
# include <stdlib.h>
# include <string.h>
#else
# ifndef HAVE_STRCHR
# define strchr index
# define strrchr rindex
# endif
char *strchr (), *strrchr ();
#endif
#ifdef HAVE_MALLOC_H
#include <stdlib.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef DMALLOC
#include <dmalloc.h>
#endif
#include <libq.h>
#include <curl/curl.h>
MODULE(curl)
/* ByteStr data structure, see clib.c */
typedef struct bstr {
long size;
unsigned char *v;
} bstr_t;
/* Curl handle data structure */
typedef struct exprl {
long key;
expr x;
struct exprl *next;
} exprl;
typedef struct {
CURL *handle;
/* error handling */
CURLcode code;
char msg[CURL_ERROR_SIZE];
/* cached data */
exprl *cache;
struct curl_slist *header, *quote, *postquote;
struct curl_httppost *post;
} curl_t;
static inline expr mkerr(curl_t *curl)
{
expr x = mkapp(mkapp(mksym(sym(curl_error)), mkint(curl->code)),
(curl->msg[0])?mkstr(strdup(curl->msg)):mkvoid);
curl->code = CURLE_OK;
curl->msg[0] = 0;
return x;
}
static exprl *add_cache(curl_t *curl, long key, expr x)
{
exprl *xl = curl->cache;
/* first check for an existing entry under the same key */
while (xl && xl->key != key) xl = xl->next;
if (xl) {
freeref(xl->x);
xl->x = newref(x);
return xl;
}
/* nothing found, allocate a new entry */
xl = (exprl*)malloc(sizeof(exprl));
if (!xl) return NULL;
xl->key = key;
xl->x = newref(x);
xl->next = curl->cache;
curl->cache = xl;
return xl;
}
static void free_cache(curl_t *curl)
{
exprl *xl = curl->cache;
while (xl) {
exprl *next = xl->next;
freeref(xl->x);
free(xl);
xl = next;
}
if (curl->header) curl_slist_free_all(curl->header);
if (curl->quote) curl_slist_free_all(curl->quote);
if (curl->postquote) curl_slist_free_all(curl->postquote);
if (curl->post) curl_formfree(curl->post);
}
DESTRUCTOR(curl,Curl,ptr)
{
curl_t *curl = (curl_t*)ptr;
free_cache(curl);
if (curl->handle) curl_easy_cleanup(curl->handle);
}
/* initialization and finalization */
INIT(curl)
{
curl_global_init(CURL_GLOBAL_SSL);
}
FINI(curl)
{
curl_global_cleanup();
}
/* manifest constants */
/* error codes:
CURLE_OK,
CURLE_UNSUPPORTED_PROTOCOL,
CURLE_FAILED_INIT,
CURLE_URL_MALFORMAT,
CURLE_URL_MALFORMAT_USER,
CURLE_COULDNT_RESOLVE_PROXY,
CURLE_COULDNT_RESOLVE_HOST,
CURLE_COULDNT_CONNECT,
CURLE_FTP_WEIRD_SERVER_REPLY,
CURLE_FTP_ACCESS_DENIED,
CURLE_FTP_USER_PASSWORD_INCORRECT,
CURLE_FTP_WEIRD_PASS_REPLY,
CURLE_FTP_WEIRD_USER_REPLY,
CURLE_FTP_WEIRD_PASV_REPLY,
CURLE_FTP_WEIRD_227_FORMAT,
CURLE_FTP_CANT_GET_HOST,
CURLE_FTP_CANT_RECONNECT,
CURLE_FTP_COULDNT_SET_BINARY,
CURLE_PARTIAL_FILE,
CURLE_FTP_COULDNT_RETR_FILE,
CURLE_FTP_WRITE_ERROR,
CURLE_FTP_QUOTE_ERROR,
CURLE_HTTP_NOT_FOUND,
CURLE_WRITE_ERROR,
CURLE_MALFORMAT_USER,
CURLE_FTP_COULDNT_STOR_FILE,
CURLE_READ_ERROR,
CURLE_OUT_OF_MEMORY,
CURLE_OPERATION_TIMEOUTED,
CURLE_FTP_COULDNT_SET_ASCII,
CURLE_FTP_PORT_FAILED,
CURLE_FTP_COULDNT_USE_REST,
CURLE_FTP_COULDNT_GET_SIZE,
CURLE_HTTP_RANGE_ERROR,
CURLE_HTTP_POST_ERROR,
CURLE_SSL_CONNECT_ERROR,
CURLE_BAD_DOWNLOAD_RESUME,
CURLE_FILE_COULDNT_READ_FILE,
CURLE_LDAP_CANNOT_BIND,
CURLE_LDAP_SEARCH_FAILED,
CURLE_LIBRARY_NOT_FOUND,
CURLE_FUNCTION_NOT_FOUND,
CURLE_ABORTED_BY_CALLBACK,
CURLE_BAD_FUNCTION_ARGUMENT,
CURLE_BAD_CALLING_ORDER,
CURLE_HTTP_PORT_FAILED,
CURLE_BAD_PASSWORD_ENTERED,
CURLE_TOO_MANY_REDIRECTS,
CURLE_UNKNOWN_TELNET_OPTION,
CURLE_TELNET_OPTION_SYNTAX,
CURLE_OBSOLETE,
CURLE_SSL_PEER_CERTIFICATE,
CURLE_GOT_NOTHING,
CURLE_SSL_ENGINE_NOTFOUND,
CURLE_SSL_ENGINE_SETFAILED,
CURLE_SEND_ERROR,
CURLE_RECV_ERROR,
CURLE_SHARE_IN_USE,
CURLE_SSL_CERTPROBLEM,
CURLE_SSL_CIPHER,
CURLE_SSL_CACERT,
CURLE_BAD_CONTENT_ENCODING
*/
/* various option values:
CURLINFO_TEXT,
CURLINFO_HEADER_IN,
CURLINFO_HEADER_OUT,
CURLINFO_DATA_IN,
CURLINFO_DATA_OUT,
CURLFORM_PTRCONTENTS, // a.k.a. CURLFORM_CONTENT
CURLFORM_FILECONTENT,
CURLFORM_FILE,
CURLFORM_BUFFER,
CURL_NETRC_OPTIONAL,
CURL_NETRC_IGNORED,
CURL_NETRC_REQUIRED,
CURL_TIMECOND_IFMODSINCE,
CURL_TIMECOND_IFUNMODSINCE,
CURL_HTTP_VERSION_NONE,
CURL_HTTP_VERSION_1_0,
CURL_HTTP_VERSION_1_1,
CURLCLOSEPOLICY_LEAST_RECENTLY_USED,
CURLCLOSEPOLICY_OLDEST
*/
/* info values:
CURLINFO_EFFECTIVE_URL,
CURLINFO_HTTP_CODE,
CURLINFO_FILETIME,
CURLINFO_TOTAL_TIME,
CURLINFO_NAMELOOKUP_TIME,
CURLINFO_CONNECT_TIME,
CURLINFO_PRETRANSFER_TIME,
CURLINFO_STARTTRANSFER_TIME,
CURLINFO_REDIRECT_TIME,
CURLINFO_REDIRECT_COUNT,
CURLINFO_SIZE_UPLOAD,
CURLINFO_SIZE_DOWNLOAD,
CURLINFO_SPEED_DOWNLOAD,
CURLINFO_SPEED_UPLOAD,
CURLINFO_HEADER_SIZE,
CURLINFO_REQUEST_SIZE,
CURLINFO_SSL_VERIFYRESULT,
CURLINFO_CONTENT_LENGTH_DOWNLOAD,
CURLINFO_CONTENT_LENGTH_UPLOAD,
CURLINFO_CONTENT_TYPE
*/
/* FIXME: to be implemented (libcurl > 7.10.2):
CURLOPT_NETRC_FILE
CURLOPT_HTTPAUTH
CURLAUTH_BASIC
CURLAUTH_DIGEST
CURLAUTH_GSSNEGOTIATE
CURLAUTH_NTLM
CURLAUTH_ANY
CURLAUTH_ANYSAFE
CURLOPT_PROXYAUTH
CURLOPT_HTTP200ALIASES
CURLOPT_FTP_USE_EPRT
CURLOPT_FTP_CREATE_MISSING_DIRS
CURLOPT_FTP_RESPONSE_TIMEOUT
CURLOPT_RESUME_FROM_LARGE
CURLOPT_INFILESIZE_LARGE
CURLOPT_MAXFILESIZE_LARGE
CURLOPT_PRIVATE
CURLINFO_RESPONSE_CODE
CURLINFO_PRIVATE
CURLINFO_HTTPAUTH_AVAIL
CURLINFO_PROXYAUTH_AVAIL
*/
/* Options and infos not supported by older libcurl versions. We just remap
options not present to -1 and reject them in the curl_setopt and
curl_getinfo functions. */
#if LIBCURL_VERSION_NUM < 0x070a00
#define CURLOPT_NOSIGNAL (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070402
#define CURLOPT_PASSWDFUNCTION (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070702
#define CURLOPT_HEADERFUNCTION (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070a00
#define CURLOPT_PROXYTYPE (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070300
#define CURLOPT_HTTPPROXYTUNNEL (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070300
#define CURLOPT_INTERFACE (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070903
#define CURLOPT_DNS_CACHE_TIMEOUT (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070903
#define CURLOPT_DNS_USE_GLOBAL_CACHE (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070a00
#define CURLOPT_BUFFERSIZE (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070500
#define CURLOPT_MAXREDIRS (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070200
#define CURLOPT_POSTFIELDSIZE (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070900
#define CURLOPT_COOKIEJAR (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070801
#define CURLOPT_HTTPGET (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070500
#define CURLOPT_FILETIME (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070700
#define CURLOPT_MAXCONNECTS (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070700
#define CURLOPT_CLOSEPOLICY (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070700
#define CURLCLOSEPOLICY_LEAST_RECENTLY_USED (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070700
#define CURLCLOSEPOLICY_OLDEST (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070700
#define CURLOPT_FRESH_CONNECT (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070700
#define CURLOPT_FORBID_REUSE (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070903
#define CURLOPT_SSLCERTTYPE (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070903
#define CURLOPT_SSLKEY (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070903
#define CURLOPT_SSLKEYTYPE (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070903
#define CURLOPT_SSLKEYPASSWD (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070903
#define CURLOPT_SSLENGINE (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070903
#define CURLOPT_SSLENGINE_DEFAULT (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070402
#define CURLOPT_SSL_VERIFYPEER (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070402
#define CURLOPT_CAINFO (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070908
#define CURLOPT_CAPATH (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070801
#define CURLOPT_SSL_VERIFYHOST (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070300
#define CURLOPT_KRB4LEVEL (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070500
#define CURLINFO_FILETIME (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070907
#define CURLINFO_REDIRECT_TIME (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070907
#define CURLINFO_REDIRECT_COUNT (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070402
#define CURLINFO_SSL_VERIFYRESULT (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070601
#define CURLINFO_CONTENT_LENGTH_DOWNLOAD (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070a00
#define CURLINFO_CONTENT_LENGTH_UPLOAD (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070904
#define CURLINFO_CONTENT_TYPE (-1)
#endif
/* These aren't mentioned in the docs, but appear to be available for
version >= 7.10.0 only, so they are listed here. */
#if LIBCURL_VERSION_NUM < 0x070a00
#define CURLOPT_ENCODING (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070a00
#define CURLE_BAD_DOWNLOAD_RESUME (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070a00
#define CURLE_SHARE_IN_USE (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070a00
#define CURLE_SSL_CERTPROBLEM (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070a00
#define CURLE_SSL_CIPHER (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070a00
#define CURLE_SSL_CACERT (-1)
#endif
#if LIBCURL_VERSION_NUM < 0x070a00
#define CURLE_BAD_CONTENT_ENCODING (-1)
#endif
/* supported CURL options (setopt): */
static CURLoption options[] = {
CURLOPT_VERBOSE,
CURLOPT_HEADER,
CURLOPT_NOPROGRESS,
CURLOPT_NOSIGNAL,
CURLOPT_WRITEFUNCTION,
CURLOPT_READFUNCTION,
CURLOPT_PROGRESSFUNCTION,
#if LIBCURL_VERSION_NUM < 0x071000
CURLOPT_PASSWDFUNCTION,
#endif
CURLOPT_HEADERFUNCTION,
CURLOPT_DEBUGFUNCTION,
CURLOPT_FAILONERROR,
CURLOPT_URL,
CURLOPT_PROXY,
CURLOPT_PROXYPORT,
CURLOPT_PROXYTYPE,
CURLOPT_HTTPPROXYTUNNEL,
CURLOPT_INTERFACE,
CURLOPT_DNS_CACHE_TIMEOUT,
CURLOPT_DNS_USE_GLOBAL_CACHE,
CURLOPT_BUFFERSIZE,
CURLOPT_NETRC,
CURLOPT_USERPWD,
CURLOPT_PROXYUSERPWD,
CURLOPT_ENCODING,
CURLOPT_FOLLOWLOCATION,
CURLOPT_MAXREDIRS,
CURLOPT_PUT,
CURLOPT_POST,
CURLOPT_POSTFIELDS,
CURLOPT_POSTFIELDSIZE,
CURLOPT_HTTPPOST,
CURLOPT_REFERER,
CURLOPT_USERAGENT,
CURLOPT_HTTPHEADER,
CURLOPT_COOKIE,
CURLOPT_COOKIEFILE,
CURLOPT_COOKIEJAR,
CURLOPT_TIMECONDITION,
CURLOPT_TIMEVALUE,
CURLOPT_HTTPGET,
CURLOPT_HTTP_VERSION,
CURLOPT_FTPPORT,
CURLOPT_QUOTE,
CURLOPT_POSTQUOTE,
CURLOPT_PREQUOTE,
CURLOPT_FTPLISTONLY,
CURLOPT_FTPAPPEND,
CURLOPT_FTP_USE_EPSV,
CURLOPT_TRANSFERTEXT,
CURLOPT_CRLF,
CURLOPT_RANGE,
CURLOPT_RESUME_FROM,
CURLOPT_CUSTOMREQUEST,
CURLOPT_FILETIME,
CURLOPT_NOBODY,
CURLOPT_INFILESIZE,
CURLOPT_UPLOAD,
CURLOPT_TIMEOUT,
CURLOPT_LOW_SPEED_LIMIT,
CURLOPT_LOW_SPEED_TIME,
CURLOPT_MAXCONNECTS,
CURLOPT_CLOSEPOLICY,
CURLOPT_FRESH_CONNECT,
CURLOPT_FORBID_REUSE,
CURLOPT_CONNECTTIMEOUT,
CURLOPT_SSLCERT,
CURLOPT_SSLCERTTYPE,
CURLOPT_SSLCERTPASSWD,
CURLOPT_SSLKEY,
CURLOPT_SSLKEYTYPE,
CURLOPT_SSLKEYPASSWD,
CURLOPT_SSLENGINE,
CURLOPT_SSLENGINE_DEFAULT,
CURLOPT_SSLVERSION,
CURLOPT_SSL_VERIFYPEER,
CURLOPT_CAINFO,
CURLOPT_CAPATH,
CURLOPT_RANDOM_FILE,
CURLOPT_EGDSOCKET,
CURLOPT_SSL_VERIFYHOST,
CURLOPT_SSL_CIPHER_LIST,
CURLOPT_KRB4LEVEL
};
static int valid_option(CURLoption option)
{
if (option > 0) {
int i, n = sizeof(options)/sizeof(CURLoption);
for (i = 0; i < n; i++)
if (options[i] == option)
return 1;
return 0;
} else
return 0;
}
FUNCTION(curl,curl_vars,argc,argv)
{
if (argc != 0) return __FAIL;
return mktuplel
(183,
mkint(CURLE_OK),
mkint(CURLE_UNSUPPORTED_PROTOCOL),
mkint(CURLE_FAILED_INIT),
mkint(CURLE_URL_MALFORMAT),
mkint(CURLE_URL_MALFORMAT_USER),
mkint(CURLE_COULDNT_RESOLVE_PROXY),
mkint(CURLE_COULDNT_RESOLVE_HOST),
mkint(CURLE_COULDNT_CONNECT),
mkint(CURLE_FTP_WEIRD_SERVER_REPLY),
mkint(CURLE_FTP_ACCESS_DENIED),
mkint(CURLE_FTP_USER_PASSWORD_INCORRECT),
mkint(CURLE_FTP_WEIRD_PASS_REPLY),
mkint(CURLE_FTP_WEIRD_USER_REPLY),
mkint(CURLE_FTP_WEIRD_PASV_REPLY),
mkint(CURLE_FTP_WEIRD_227_FORMAT),
mkint(CURLE_FTP_CANT_GET_HOST),
mkint(CURLE_FTP_CANT_RECONNECT),
mkint(CURLE_FTP_COULDNT_SET_BINARY),
mkint(CURLE_PARTIAL_FILE),
mkint(CURLE_FTP_COULDNT_RETR_FILE),
mkint(CURLE_FTP_WRITE_ERROR),
mkint(CURLE_FTP_QUOTE_ERROR),
mkint(CURLE_HTTP_NOT_FOUND),
mkint(CURLE_WRITE_ERROR),
mkint(CURLE_MALFORMAT_USER),
mkint(CURLE_FTP_COULDNT_STOR_FILE),
mkint(CURLE_READ_ERROR),
mkint(CURLE_OUT_OF_MEMORY),
mkint(CURLE_OPERATION_TIMEOUTED),
mkint(CURLE_FTP_COULDNT_SET_ASCII),
mkint(CURLE_FTP_PORT_FAILED),
mkint(CURLE_FTP_COULDNT_USE_REST),
mkint(CURLE_FTP_COULDNT_GET_SIZE),
mkint(CURLE_HTTP_RANGE_ERROR),
mkint(CURLE_HTTP_POST_ERROR),
mkint(CURLE_SSL_CONNECT_ERROR),
mkint(CURLE_BAD_DOWNLOAD_RESUME),
mkint(CURLE_FILE_COULDNT_READ_FILE),
mkint(CURLE_LDAP_CANNOT_BIND),
mkint(CURLE_LDAP_SEARCH_FAILED),
mkint(CURLE_LIBRARY_NOT_FOUND),
mkint(CURLE_FUNCTION_NOT_FOUND),
mkint(CURLE_ABORTED_BY_CALLBACK),
mkint(CURLE_BAD_FUNCTION_ARGUMENT),
mkint(CURLE_BAD_CALLING_ORDER),
mkint(CURLE_HTTP_PORT_FAILED),
mkint(CURLE_BAD_PASSWORD_ENTERED),
mkint(CURLE_TOO_MANY_REDIRECTS),
mkint(CURLE_UNKNOWN_TELNET_OPTION),
mkint(CURLE_TELNET_OPTION_SYNTAX),
mkint(CURLE_OBSOLETE),
mkint(CURLE_SSL_PEER_CERTIFICATE),
mkint(CURLE_GOT_NOTHING),
mkint(CURLE_SSL_ENGINE_NOTFOUND),
mkint(CURLE_SSL_ENGINE_SETFAILED),
mkint(CURLE_SEND_ERROR),
mkint(CURLE_RECV_ERROR),
mkint(CURLE_SHARE_IN_USE),
mkint(CURLE_SSL_CERTPROBLEM),
mkint(CURLE_SSL_CIPHER),
mkint(CURLE_SSL_CACERT),
mkint(CURLE_BAD_CONTENT_ENCODING),
mkint(CURLINFO_TEXT),
mkint(CURLINFO_HEADER_IN),
mkint(CURLINFO_HEADER_OUT),
mkint(CURLINFO_DATA_IN),
mkint(CURLINFO_DATA_OUT),
mkint(CURLFORM_PTRCONTENTS),
mkint(CURLFORM_FILECONTENT),
mkint(CURLFORM_FILE),
mkint(CURLFORM_BUFFER),
mkint(CURL_NETRC_OPTIONAL),
mkint(CURL_NETRC_IGNORED),
mkint(CURL_NETRC_REQUIRED),
mkint(CURL_TIMECOND_IFMODSINCE),
mkint(CURL_TIMECOND_IFUNMODSINCE),
mkint(CURL_HTTP_VERSION_NONE),
mkint(CURL_HTTP_VERSION_1_0),
mkint(CURL_HTTP_VERSION_1_1),
mkint(CURLCLOSEPOLICY_LEAST_RECENTLY_USED),
mkint(CURLCLOSEPOLICY_OLDEST),
mkint(CURLOPT_VERBOSE),
mkint(CURLOPT_HEADER),
mkint(CURLOPT_NOPROGRESS),
mkint(CURLOPT_NOSIGNAL),
mkint(CURLOPT_WRITEFUNCTION),
mkint(CURLOPT_READFUNCTION),
mkint(CURLOPT_PROGRESSFUNCTION),
#if LIBCURL_VERSION_NUM < 0x071000
mkint(CURLOPT_PASSWDFUNCTION),
#endif
mkint(CURLOPT_HEADERFUNCTION),
mkint(CURLOPT_DEBUGFUNCTION),
mkint(CURLOPT_FAILONERROR),
mkint(CURLOPT_URL),
mkint(CURLOPT_PROXY),
mkint(CURLOPT_PROXYPORT),
mkint(CURLOPT_PROXYTYPE),
mkint(CURLOPT_HTTPPROXYTUNNEL),
mkint(CURLOPT_INTERFACE),
mkint(CURLOPT_DNS_CACHE_TIMEOUT),
mkint(CURLOPT_DNS_USE_GLOBAL_CACHE),
mkint(CURLOPT_BUFFERSIZE),
mkint(CURLOPT_NETRC),
mkint(CURLOPT_USERPWD),
mkint(CURLOPT_PROXYUSERPWD),
mkint(CURLOPT_ENCODING),
mkint(CURLOPT_FOLLOWLOCATION),
mkint(CURLOPT_MAXREDIRS),
mkint(CURLOPT_PUT),
mkint(CURLOPT_POST),
mkint(CURLOPT_POSTFIELDS),
mkint(CURLOPT_POSTFIELDSIZE),
mkint(CURLOPT_HTTPPOST),
mkint(CURLOPT_REFERER),
mkint(CURLOPT_USERAGENT),
mkint(CURLOPT_HTTPHEADER),
mkint(CURLOPT_COOKIE),
mkint(CURLOPT_COOKIEFILE),
mkint(CURLOPT_COOKIEJAR),
mkint(CURLOPT_TIMECONDITION),
mkint(CURLOPT_TIMEVALUE),
mkint(CURLOPT_HTTPGET),
mkint(CURLOPT_HTTP_VERSION),
mkint(CURLOPT_FTPPORT),
mkint(CURLOPT_QUOTE),
mkint(CURLOPT_POSTQUOTE),
mkint(CURLOPT_PREQUOTE),
mkint(CURLOPT_FTPLISTONLY),
mkint(CURLOPT_FTPAPPEND),
mkint(CURLOPT_FTP_USE_EPSV),
mkint(CURLOPT_TRANSFERTEXT),
mkint(CURLOPT_CRLF),
mkint(CURLOPT_RANGE),
mkint(CURLOPT_RESUME_FROM),
mkint(CURLOPT_CUSTOMREQUEST),
mkint(CURLOPT_FILETIME),
mkint(CURLOPT_NOBODY),
mkint(CURLOPT_INFILESIZE),
mkint(CURLOPT_UPLOAD),
mkint(CURLOPT_TIMEOUT),
mkint(CURLOPT_LOW_SPEED_LIMIT),
mkint(CURLOPT_LOW_SPEED_TIME),
mkint(CURLOPT_MAXCONNECTS),
mkint(CURLOPT_CLOSEPOLICY),
mkint(CURLOPT_FRESH_CONNECT),
mkint(CURLOPT_FORBID_REUSE),
mkint(CURLOPT_CONNECTTIMEOUT),
mkint(CURLOPT_SSLCERT),
mkint(CURLOPT_SSLCERTTYPE),
mkint(CURLOPT_SSLCERTPASSWD),
mkint(CURLOPT_SSLKEY),
mkint(CURLOPT_SSLKEYTYPE),
mkint(CURLOPT_SSLKEYPASSWD),
mkint(CURLOPT_SSLENGINE),
mkint(CURLOPT_SSLENGINE_DEFAULT),
mkint(CURLOPT_SSLVERSION),
mkint(CURLOPT_SSL_VERIFYPEER),
mkint(CURLOPT_CAINFO),
mkint(CURLOPT_CAPATH),
mkint(CURLOPT_RANDOM_FILE),
mkint(CURLOPT_EGDSOCKET),
mkint(CURLOPT_SSL_VERIFYHOST),
mkint(CURLOPT_SSL_CIPHER_LIST),
mkint(CURLOPT_KRB4LEVEL),
mkint(CURLINFO_EFFECTIVE_URL),
mkint(CURLINFO_HTTP_CODE),
mkint(CURLINFO_FILETIME),
mkint(CURLINFO_TOTAL_TIME),
mkint(CURLINFO_NAMELOOKUP_TIME),
mkint(CURLINFO_CONNECT_TIME),
mkint(CURLINFO_PRETRANSFER_TIME),
mkint(CURLINFO_STARTTRANSFER_TIME),
mkint(CURLINFO_REDIRECT_TIME),
mkint(CURLINFO_REDIRECT_COUNT),
mkint(CURLINFO_SIZE_UPLOAD),
mkint(CURLINFO_SIZE_DOWNLOAD),
mkint(CURLINFO_SPEED_DOWNLOAD),
mkint(CURLINFO_SPEED_UPLOAD),
mkint(CURLINFO_HEADER_SIZE),
mkint(CURLINFO_REQUEST_SIZE),
mkint(CURLINFO_SSL_VERIFYRESULT),
mkint(CURLINFO_CONTENT_LENGTH_DOWNLOAD),
mkint(CURLINFO_CONTENT_LENGTH_UPLOAD),
mkint(CURLINFO_CONTENT_TYPE)
);
}
/* interface functions */
FUNCTION(curl,curl_init,argc,argv)
{
if (argc == 0) {
curl_t *curl = (curl_t*)malloc(sizeof(curl_t));
if (!curl || !(curl->handle = curl_easy_init())) return __ERROR;
curl->cache = NULL;
curl->code = CURLE_OK;
curl->msg[0] = 0;
curl_easy_setopt(curl->handle, CURLOPT_ERRORBUFFER, curl->msg);
curl->header = curl->quote = curl->postquote = NULL;
curl->post = NULL;
return mkobj(type(Curl), curl);
} else
return __FAIL;
}
FUNCTION(curl,curl_cleanup,argc,argv)
{
curl_t *curl;
if (argc == 1 && isobj(argv[0], type(Curl), (void**)&curl) &&
curl->handle) {
free_cache(curl);
curl_easy_cleanup(curl->handle);
curl->handle = NULL;
curl->cache = NULL;
curl->code = CURLE_OK;
curl->msg[0] = 0;
curl->header = curl->quote = curl->postquote = NULL;
curl->post = NULL;
return mkvoid;
} else
return __FAIL;
}
static size_t write_cb(void *ptr, size_t size, size_t nmemb, expr x)
{
bstr_t *m = malloc(sizeof(bstr_t));
expr y, b;
long ret;
if (!m) return 0;
acquire_lock();
m->size = 0; m->v = NULL;
if (!(b = mkobj(type(ByteStr), (void*)m))) {
free(m);
release_lock();
return 0;
}
newref(b);
m->size = size*nmemb; m->v = ptr;
if (!(y = eval(mkapp(x, b))) || !isint(y, &ret))
ret = 0;
dispose(y);
m->size = 0; m->v = NULL;
freeref(b);
release_lock();
return ret;
}
static size_t read_cb(void *ptr, size_t size, size_t nmemb, expr x)
{
expr y;
bstr_t *m;
long ret, count;
acquire_lock();
if (!(y = eval(mkapp(x, mkuint(size*nmemb)))) ||
!isobj(y, type(ByteStr), (void**)&m))
ret = 0;
else {
ret = m->size; count = ret;
if (count > size*nmemb) count = size*nmemb;
memcpy(ptr, m->v, count);
}
dispose(y);
release_lock();
return ret;
}
static size_t header_cb(void *ptr, size_t size, size_t nmemb, expr x)
{
bstr_t *m = malloc(sizeof(bstr_t));
expr y, b;
long ret;
if (!m) return -1;
acquire_lock();
m->size = 0; m->v = NULL;
if (!(b = mkobj(type(ByteStr), (void*)m))) {
free(m);
release_lock();
return -1;
}
newref(b);
m->size = size*nmemb; m->v = ptr;
if (!(y = eval(mkapp(x, b))) || !isint(y, &ret))
ret = -1;
dispose(y);
m->size = 0; m->v = NULL;
freeref(b);
release_lock();
return ret;
}
static int progress_cb(expr x, double dltotal, double dlnow,
double ultotal, double ulnow)
{
expr y;
long ret = 0;
acquire_lock();
if (!(y = eval(mkapp(x, mktuplel(4, mkfloat(dltotal),
mkfloat(dlnow),
mkfloat(ultotal),
mkfloat(ulnow))))) ||
!isvoid(y))
ret = -1;
dispose(y);
release_lock();
return ret;
}
static int passwd_cb(expr x, char *prompt, char *buffer, int buflen)
{
expr y;
long ret = 0;
char *s;
if (buflen <= 0) return -1;
acquire_lock();
if (!(y = eval(mkapp(mkapp(x, mkstr(strdup(prompt))),
mkint(buflen)))) ||
!isstr(y, &s))
ret = -1;
else {
int l = strlen(s);
/* FIXME: do we always need the terminating NUL here? */
if (l >= buflen) {
strncpy(buffer, s, buflen-1);
buffer[buflen-1] = 0;
} else
strcpy(buffer, s);
}
dispose(y);
release_lock();
return ret;
}
static int debug_cb(CURL *handle, curl_infotype info, char *data, size_t size,
expr x)
{
bstr_t *m = malloc(sizeof(bstr_t));
expr b, y;
if (!m) return 0;
acquire_lock();
m->size = 0; m->v = NULL;
if (!(b = mkobj(type(ByteStr), (void*)m))) {
free(m);
release_lock();
return 0;
}
newref(b);
m->size = size; m->v = data;
y = eval(mkapp(mkapp(x, mkint(info)), b));
dispose(y);
m->size = 0; m->v = NULL;
freeref(b);
release_lock();
return 0;
}
FUNCTION(curl,curl_setopt,argc,argv)
{
curl_t *curl;
long option;
if (argc == 3 && isobj(argv[0], type(Curl), (void**)&curl) &&
curl->handle && isint(argv[1], &option) &&
valid_option(option)) {
if (option >= CURLOPTTYPE_FUNCTIONPOINT) {
/* callback */
int (*cb)();
void *dflt = NULL;
expr x = argv[2], y = x;
CURLoption dataopt;
switch (option) {
case CURLOPT_WRITEFUNCTION:
cb = (int(*)())write_cb;
dataopt = CURLOPT_FILE; /* a.k.a. CURLOPT_WRITEDATA */
dflt = stdout;
break;
case CURLOPT_READFUNCTION:
cb = (int(*)())read_cb;
dataopt = CURLOPT_INFILE; /* a.k.a. CURLOPT_READDATA */
dflt = stdin;
break;
case CURLOPT_PROGRESSFUNCTION:
cb = (int(*)())progress_cb;
dataopt = CURLOPT_PROGRESSDATA;
break;
#if LIBCURL_VERSION_NUM < 0x071000
case CURLOPT_PASSWDFUNCTION:
cb = (int(*)())passwd_cb;
dataopt = CURLOPT_PASSWDDATA;
break;
#endif
case CURLOPT_HEADERFUNCTION:
cb = (int(*)())header_cb;
dataopt = CURLOPT_WRITEHEADER;
dflt = stdout;
break;
case CURLOPT_DEBUGFUNCTION:
cb = (int(*)())debug_cb;
dataopt = CURLOPT_DEBUGDATA;
if (!isvoid(x)) {
y = mkapp(x, argv[0]);
if (!y) return __ERROR;
}
break;
default:
return __FAIL;
}
#if 0
/* FIXME: This is supposed to reset the callbacks to the default
behaviour, but that doesn't work yet (libcurl segfaults), so we
disable it for now. */
if (isvoid(x)) {
cb = NULL;
x = dflt;
}
#endif
curl->code = curl_easy_setopt(curl->handle, option, cb);
if (curl->code == CURLE_OK) {
curl->code = curl_easy_setopt(curl->handle, dataopt, x);
if (curl->code != CURLE_OK)
curl_easy_setopt(curl->handle, option, NULL);
}
if (curl->code == CURLE_OK && !add_cache(curl, option, y)) {
curl_easy_setopt(curl->handle, option, NULL);
curl_easy_setopt(curl->handle, dataopt, dflt);
return __ERROR;
}
} else if (option >= CURLOPTTYPE_OBJECTPOINT &&
/* wrong classification in curl.h? */
option != CURLOPT_FILETIME)
switch (option) {
case CURLOPT_HTTPHEADER:
case CURLOPT_QUOTE:
case CURLOPT_POSTQUOTE:
{
/* string lists */
expr x = argv[2], hd, tl;
char *val;
struct curl_slist *sl = NULL;
while (iscons(x, &hd, &tl) && isstr(hd, &val)) {
struct curl_slist *sl1 = curl_slist_append(sl, val);
if (!sl1) {
if (sl) curl_slist_free_all(sl);
return __ERROR;
}
x = tl;
}
if (!isnil(x)) {
if (sl) curl_slist_free_all(sl);
return __FAIL;
}
curl->code = curl_easy_setopt(curl->handle, option, sl);
if (curl->code != CURLE_OK) {
if (sl) curl_slist_free_all(sl);
} else {
switch (option) {
case CURLOPT_HTTPHEADER:
if (curl->header) curl_slist_free_all(curl->header);
curl->header = NULL;
break;
case CURLOPT_QUOTE:
if (curl->quote) curl_slist_free_all(curl->quote);
curl->quote = NULL;
break;
case CURLOPT_POSTQUOTE:
if (curl->postquote) curl_slist_free_all(curl->postquote);
curl->postquote = NULL;
break;
}
if (!add_cache(curl, option, argv[2])) {
curl_easy_setopt(curl->handle, option, NULL);
if (sl) curl_slist_free_all(sl);
return __ERROR;
}
switch (option) {
case CURLOPT_HTTPHEADER:
curl->header = sl;
break;
case CURLOPT_QUOTE:
curl->quote = sl;
break;
case CURLOPT_POSTQUOTE:
curl->postquote = sl;
break;
}
}
}
break;
case CURLOPT_HTTPPOST:
{
/* list of HTTP POST sections */
expr x = argv[2], hd, tl, *xv;
int i, k, n;
struct curl_httppost *post = NULL, *last = NULL;
#ifdef FORMDEBUG
printf("entering\n");
#endif
while (iscons(x, &hd, &tl) && istuple(hd, &n, &xv) && n >= 3) {
char *name, *data = NULL, *datafile = NULL, *filename = NULL,
*content_type = NULL;
bstr_t *bdata = NULL;
long tag;
struct curl_forms forms[100];
#ifdef FORMDEBUG
printf("processing form section\n");
#endif
if (!isstr(xv[0], &name) || !isint(xv[1], &tag)) {
if (post) curl_formfree(post);
return __FAIL;
}
switch (tag) {
case CURLFORM_PTRCONTENTS:
#ifdef FORMDEBUG
printf("got CURLFORM_PTRCONTENTS (%s)\n", name);
#endif
if (!isstr(xv[2], &data) &&
!isobj(xv[2], type(ByteStr), (void**)&bdata) ||
n > 4 || n == 4 && !isstr(xv[3], &content_type)) {
if (post) curl_formfree(post);
return __FAIL;
}
i = 0;
forms[i].option = CURLFORM_PTRCONTENTS;
if (data) {
forms[i].value = data;
i++;
forms[i].option = CURLFORM_CONTENTSLENGTH;
forms[i].value = (void*)strlen(data);
} else {
forms[i].value = bdata->v;
i++;
forms[i].option = CURLFORM_CONTENTSLENGTH;
forms[i].value = (void*)bdata->size;
}
i++;
if (content_type) {
forms[i].option = CURLFORM_CONTENTTYPE;
forms[i].value = content_type;
i++;
}
forms[i].option = CURLFORM_END;
break;
case CURLFORM_FILECONTENT:
#ifdef FORMDEBUG
printf("got CURLFORM_FILECONTENT (%s)\n", name);
#endif
if (!isstr(xv[2], &datafile) ||
n > 4 || n == 4 && !isstr(xv[3], &content_type)) {
if (post) curl_formfree(post);
return __FAIL;
}
i = 0;
forms[i].option = CURLFORM_FILECONTENT;
forms[i].value = datafile;
i++;
if (content_type) {
forms[i].option = CURLFORM_CONTENTTYPE;
forms[i].value = content_type;
i++;
}
forms[i].option = CURLFORM_END;
break;
default:
i = 0; k = 1;
while (k < n && isint(xv[k], &tag) &&
(tag == CURLFORM_FILE && isstr(xv[k+1], &datafile) ||
tag == CURLFORM_BUFFER &&
(isstr(xv[k+1], &data) ||
isobj(xv[k+1], type(ByteStr), (void**)&bdata)) &&
isstr(xv[k+2], &filename))) {
if (i+5 >= 100) {
if (post) curl_formfree(post);
return __ERROR;
}
#ifdef FORMDEBUG
printf("processing part at %d (%s)\n", k, name);
#endif
if (tag == CURLFORM_FILE) {
#ifdef FORMDEBUG
printf("got CURLFORM_FILE (%s)\n", name);
#endif
k += 2;
if (k < n && isstr(xv[k], &filename)) {
k++;
if (k < n && isstr(xv[k], &content_type)) k++;
}
forms[i].option = CURLFORM_FILE;
forms[i].value = datafile;
i++;
if (filename) {
forms[i].option = CURLFORM_FILENAME;
forms[i].value = filename;
i++;
}
if (content_type) {
forms[i].option = CURLFORM_CONTENTTYPE;
forms[i].value = content_type;
i++;
}
} else {
#ifdef FORMDEBUG
printf("got CURLFORM_BUFFER (%s)\n", name);
#endif
k += 3;
if (k < n && isstr(xv[k], &content_type)) k++;
forms[i].option = CURLFORM_BUFFER;
forms[i].value = filename;
i++;
forms[i].option = CURLFORM_BUFFERPTR;
if (data) {
forms[i].value = data;
i++;
forms[i].option = CURLFORM_BUFFERLENGTH;
forms[i].value = (void*)strlen(data);
} else {
forms[i].value = bdata->v;
i++;
forms[i].option = CURLFORM_BUFFERLENGTH;
forms[i].value = (void*)bdata->size;
}
i++;
if (content_type) {
forms[i].option = CURLFORM_CONTENTTYPE;
forms[i].value = content_type;
i++;
}
}
data = datafile = filename = content_type = NULL;
bdata = NULL;
}
if (k < n) {
if (post) curl_formfree(post);
return __FAIL;
}
forms[i].option = CURLFORM_END;
}
#ifdef FORMDEBUG
printf("adding section (%s)\n", name);
#endif
if (curl_formadd(&post, &last,
CURLFORM_PTRNAME, name,
CURLFORM_ARRAY, forms,
CURLFORM_END)) {
if (post) curl_formfree(post);
return __ERROR;
}
x = tl;
}
if (!isnil(x)) {
if (post) curl_formfree(post);
return __FAIL;
}
#ifdef FORMDEBUG
printf("setting CURLOPT_HTTPPOST option\n");
#endif
curl->code = curl_easy_setopt(curl->handle, option, post);
if (curl->code != CURLE_OK) {
if (post) curl_formfree(post);
} else {
if (curl->post) curl_formfree(curl->post);
curl->post = NULL;
if (!add_cache(curl, option, argv[2])) {
curl_easy_setopt(curl->handle, option, NULL);
if (post) curl_formfree(post);
return __ERROR;
}
curl->post = post;
}
#ifdef FORMDEBUG
printf("done\n");
#endif
}
break;
default:
{
/* string value */
char *val;
if (isvoid(argv[2]))
val = NULL;
else if (!isstr(argv[2], &val))
return __FAIL;
curl->code = curl_easy_setopt(curl->handle, option, val);
if (curl->code == CURLE_OK && !add_cache(curl, option, argv[2])) {
curl_easy_setopt(curl->handle, option, NULL);
return __ERROR;
}
}
break;
}
else {
/* Bool/Int value */
int flag;
long val;
if (isbool(argv[2], &flag))
val = flag;
else if (!isint(argv[2], &val))
return __FAIL;
curl->code = curl_easy_setopt(curl->handle, option, val);
}
if (curl->code != CURLE_OK)
return mkerr(curl);
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(curl,curl_perform,argc,argv)
{
curl_t *curl;
if (argc == 1 && isobj(argv[0], type(Curl), (void**)&curl) &&
curl->handle) {
release_lock();
/* FIXME: we need to handle Ctrl-C here */
curl->code = curl_easy_perform(curl->handle);
acquire_lock();
if (curl->code != CURLE_OK)
return mkerr(curl);
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(curl,curl_getinfo,argc,argv)
{
curl_t *curl;
long info;
if (argc == 2 && isobj(argv[0], type(Curl), (void**)&curl) &&
curl->handle && isint(argv[1], &info) &&
(info&CURLINFO_MASK) > 0 && (info&CURLINFO_MASK) < CURLINFO_LASTONE) {
char *s;
long i;
double f;
switch (info&CURLINFO_TYPEMASK) {
case CURLINFO_STRING:
curl->code = curl_easy_getinfo(curl->handle, info, &s);
break;
case CURLINFO_LONG:
curl->code = curl_easy_getinfo(curl->handle, info, &i);
break;
case CURLINFO_DOUBLE:
curl->code = curl_easy_getinfo(curl->handle, info, &f);
break;
default: return __FAIL;
}
if (curl->code != CURLE_OK)
return mkerr(curl);
else {
expr x;
switch (info&CURLINFO_TYPEMASK) {
case CURLINFO_STRING:
x = mkstr(strdup(s));
break;
case CURLINFO_LONG:
x = mkint(i);
break;
case CURLINFO_DOUBLE:
x = mkfloat(f);
break;
}
return x;
}
} else
return __FAIL;
}
FUNCTION(curl,curl_version,argc,argv)
{
if (argc == 0) {
char *s = curl_version();
if (s)
return mkstr(strdup(s));
else
return __FAIL;
} else
return __FAIL;
}
/* curl_free doesn't seem to be available in libcurl < 7.10.0 */
#if LIBCURL_VERSION_NUM < 0x070a00
#define curl_free free
#endif
FUNCTION(curl,curl_escape,argc,argv)
{
char *s;
if (argc == 1 && isstr(argv[0], &s)) {
char *t = curl_escape(s, 0);
if (t) {
s = strdup(t);
curl_free(t);
return mkstr(s);
} else
return __FAIL;
} else
return __FAIL;
}
FUNCTION(curl,curl_unescape,argc,argv)
{
char *s;
if (argc == 1 && isstr(argv[0], &s)) {
char *t = curl_unescape(s, 0);
if (t) {
s = strdup(t);
curl_free(t);
return mkstr(s);
} else
return __FAIL;
} else
return __FAIL;
}
syntax highlighted by Code2HTML, v. 0.9.1