/******************************************************************************
cURL_Lua, Lua bindings for cURL
******************************************************************************
Released under the GNU/GPL license or MIT license (at your option),
no warranties.
Author:
- Enrico Tassi <gareuselesinge@users.sourceforge.net>
status:
- binds from 7.9.5 to 7.11.2
changelog:
- first public release
todo:
- WRITE_CB,READ_CB,DEBUG_CB must be identifyed by a unique pointer, but
using CURL* + OFFSET may not be the case... think a bit more
- CURLINFO ???
******************************************************************************
$Id: curl_lua.c,v 1.26 2006/01/13 21:46:54 gareuselesinge Exp $
******************************************************************************/
#include <lua.h>
#include <lauxlib.h>
#include <curl/curl.h>
#include <curl/easy.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "luabind.h"
#include "compat-5.1.h"
#define CURL_EASY_META_NAME "curleasy.type"
/* think more if this means unicity... maybe store in the bag some pointers */
/* this need that a has size > bigger_offset */
#define CURL_WRITECB_OFF(a) ((void*)(((char*)a)+0))
#define CURL_READCB_OFF(a) ((void*)(((char*)a)+1))
#define CURL_HEADCB_OFF(a) ((void*)(((char*)a)+2))
/* to check the curl version on the fly, this is a v >= LIBCURL_VERSION */
#define CURL_NEWER(M,m,p) ((p + (m << 8) + (M << 16)) <= LIBCURL_VERSION_NUM)
#define CURL_OLDER(M,m,p) ((p + (m << 8) + (M << 16)) > LIBCURL_VERSION_NUM)
/* some compatibility for name aliases */
#ifndef CURLOPT_WRITEDATA
#define CURLOPT_WRITEDATA CURLOPT_FILE
#endif
#ifndef CURLOPT_READDATA
#define CURLOPT_READDATA CURLOPT_INFILE
#endif
#ifndef CURLOPT_HEADERDATA
#define CURLOPT_HEADERDATA CURLOPT_WRITEHEADER
#endif
/* strings put in the bag, vectorialized for faster/shorter access */
#define STR_CAINFO 0
#define STR_COOKIE 1
#define STR_COOKIEFILE 2
#define STR_COOKIEJAR 3
#define STR_CUSTOMREQUEST 4
#define STR_EGDSOCKET 5
#define STR_FTPPORT 6
#define STR_INTERFACE 7
#define STR_KRB4LEVEL 8
#define STR_POSTFIELDS 9
#define STR_PROXY 10
#define STR_PROXYUSERPWD 11
#define STR_RANDOM_FILE 12
#define STR_RANGE 13
#define STR_REFERER 14
#define STR_SSLCERT 15
#define STR_SSLCERTPASSWD 16
#define STR_SSLCERTTYPE 17
#define STR_SSLENGINE 18
#define STR_SSLKEY 19
#define STR_SSLKEYTYPE 20
#define STR_SSL_CIPHER_LIST 21
#define STR_URL 22
#define STR_USERAGENT 23
#define STR_USERPWD 24
#define STR_LAST STR_USERPWD
#if CURL_NEWER(7,9,8)
#define STR_CAPATH 25
#undef STR_LAST
#define STR_LAST STR_CAPATH
#endif
#if CURL_NEWER(7,10,0)
#define STR_ENCODING 26
#undef STR_LAST
#define STR_LAST STR_ENCODING
#endif
#if CURL_NEWER(7,10,3)
#define STR_PRIVATE 27
#undef STR_LAST
#define STR_LAST STR_PRIVATE
#endif
#if CURL_NEWER(7,11,0)
#define STR_NETRC_FILE 28
#undef STR_LAST
#define STR_LAST STR_NETRC_FILE
#endif
#define STR_SIZE (STR_LAST + 1)
/******************************************************************************
* we need to keep with us the CURL handler plus some buffers
*
*/
struct L_curl_bag {
CURL* handler;
char* strings[STR_SIZE];
char err[CURL_ERROR_SIZE];
struct curl_slist * headlist;
#if CURL_NEWER(7,9,6)
struct curl_httppost *post;
#endif
};
/******************************************************************************
* table created with this script:
*
* cat /usr/include/curl/curl.h | grep "^ *CINIT(" | \
* sed "s/CINIT(/{\"OPT_/" | sed -r "s/, +/\",CURLOPTTYPE_/" | \
* sed "s/, / + /" | sed "s/),/},/" > curlopt.h
*
*/
static const struct L_const curl_easy_c [] = {
#include "curlopt.h"
{NULL,0}
};
/******************************************************************************
* table created with this script:
*
* cat /usr/include/curl/curl.h | grep "^ *CURL_NETRC_[A-Z]*," | \
* cut -f 1 -d "," | \
* awk '{print "{\"" $1 "\", (int)" $1 "}," }' | \
* sed "s/CURL_//" > curl_netrcopt.h
*/
#if CURL_NEWER(7,9,8)
static const struct L_const curl_easy_netrc_c [] = {
#include "curl_netrcopt.h"
{NULL,0}
};
#endif
/******************************************************************************
* table created with this script:
*
* cat /usr/include/curl/curl.h | grep "CURLAUTH_" | \
* sed "s/#define *CURL/{\"/" | sed "s/ *\/\*.*\*\///" | \
* sed "s/ /\",/" | sed "s/$$/},/" > curl_authopt.h
*/
#if CURL_NEWER(7,10,6)
static const struct L_const curl_easy_auth_c [] = {
#include "curl_authopt.h"
{NULL,0}
};
#endif
/******************************************************************************
* table created by hand:
*
*/
static const struct L_const curl_easy_httpver_c [] = {
{"HTTP_VERSION_NONE",CURL_HTTP_VERSION_NONE},
{"HTTP_VERSION_1_0",CURL_HTTP_VERSION_1_0},
{"HTTP_VERSION_1_1",CURL_HTTP_VERSION_1_1},
{NULL,0}
};
/******************************************************************************
* table created by hand:
*
*/
#if CURL_NEWER(7,11,0)
static const struct L_const curl_easy_ftpssl_c [] = {
{"FTPSSL_NONE",CURLFTPSSL_NONE},
{"FTPSSL_TRY",CURLFTPSSL_TRY},
{"FTPSSL_CONTROL",CURLFTPSSL_CONTROL},
{"FTPSSL_ALL",CURLFTPSSL_ALL},
{NULL,0}
};
#endif
/******************************************************************************
* table created by hand:
*
*/
static const struct L_const curl_easy_closepolicy_c [] = {
{"CLOSEPOLICY_LEAST_RECENTLY_USED",CURLCLOSEPOLICY_LEAST_RECENTLY_USED},
{"CLOSEPOLICY_OLDEST",CURLCLOSEPOLICY_OLDEST},
{NULL,0}
};
/******************************************************************************
* table created by hand:
*
*/
#if CURL_NEWER(7,10,8)
static const struct L_const curl_easy_ipresolve_c [] = {
{"IPRESOLVE_WHATEVER",CURL_IPRESOLVE_WHATEVER},
{"IPRESOLVE_V4",CURL_IPRESOLVE_V4},
{"IPRESOLVE_V6",CURL_IPRESOLVE_V6},
{NULL,0}
};
#endif
/******************************************************************************
* table created by hand:
*
*/
#if CURL_NEWER(7,10,0)
static const struct L_const curl_easy_proxytype_c [] = {
{"PROXY_HTTP",CURLPROXY_HTTP},
{"PROXY_SOCKS5",CURLPROXY_SOCKS5},
{NULL,0}
};
#endif
/******************************************************************************
* table created with this script:
*
* cat /usr/include/curl/curl.h | grep "^ *CFINIT" | \
* grep -v "CFINIT(NOTHING)" | sed "s/CFINIT(//" | \
* sed "s/),/ ,/" | \
* awk '{print "{\"FORM_" $1 "\",CURLFORM_" $1 "}," }' > \
* curl_form.h
*/
static const struct L_const curl_easy_form_c [] = {
#include "curl_form.h"
{NULL,0}
};
/******************************************************************************
* checks and returns a CURL* handler from the first position in the stack
*
*/
static CURL* L_checkcurleasy(lua_State*L)
{
void* tmp = luaL_checkudata(L,1,CURL_EASY_META_NAME);
luaL_argcheck(L,tmp != NULL,1,"curleasy expected");
return ((struct L_curl_bag*)tmp)->handler;
}
/******************************************************************************
* checks and returns the userdata
*
*/
static struct L_curl_bag* L_checkcurluserdata(lua_State*L)
{
void* tmp = luaL_checkudata(L,1,CURL_EASY_META_NAME);
luaL_argcheck(L,tmp != NULL,1,"curleasy expected");
return ((struct L_curl_bag*)tmp);
}
/******************************************************************************
* maps a curl option to the right bag.strings[] element
*
*/
static unsigned int L_CURLoption2vect(lua_State*L,CURLoption opt){
switch (opt) {
case CURLOPT_CAINFO: return STR_CAINFO;
case CURLOPT_COOKIE: return STR_COOKIE;
case CURLOPT_COOKIEFILE: return STR_COOKIEFILE;
case CURLOPT_COOKIEJAR: return STR_COOKIEJAR;
case CURLOPT_CUSTOMREQUEST: return STR_CUSTOMREQUEST;
case CURLOPT_EGDSOCKET: return STR_EGDSOCKET;
case CURLOPT_FTPPORT: return STR_FTPPORT;
case CURLOPT_INTERFACE: return STR_INTERFACE;
case CURLOPT_KRB4LEVEL: return STR_KRB4LEVEL;
case CURLOPT_POSTFIELDS: return STR_POSTFIELDS;
case CURLOPT_PROXY: return STR_PROXY;
case CURLOPT_PROXYUSERPWD: return STR_PROXYUSERPWD;
case CURLOPT_RANDOM_FILE: return STR_RANDOM_FILE;
case CURLOPT_RANGE: return STR_RANGE;
case CURLOPT_REFERER: return STR_REFERER;
case CURLOPT_SSLCERT: return STR_SSLCERT;
case CURLOPT_SSLCERTPASSWD: return STR_SSLCERTPASSWD;
case CURLOPT_SSLCERTTYPE: return STR_SSLCERTTYPE;
case CURLOPT_SSLENGINE: return STR_SSLENGINE;
case CURLOPT_SSLKEY: return STR_SSLKEY;
case CURLOPT_SSLKEYTYPE: return STR_SSLKEYTYPE;
case CURLOPT_SSL_CIPHER_LIST: return STR_SSL_CIPHER_LIST;
case CURLOPT_URL: return STR_URL;
case CURLOPT_USERAGENT: return STR_USERAGENT;
case CURLOPT_USERPWD: return STR_USERPWD;
#if CURL_NEWER(7,9,8)
case CURLOPT_CAPATH: return STR_CAPATH;
#endif
#if CURL_NEWER(7,10,0)
case CURLOPT_ENCODING: return STR_ENCODING;
#endif
#if CURL_NEWER(7,10,3)
case CURLOPT_PRIVATE: return STR_PRIVATE;
#endif
#if CURL_NEWER(7,11,0)
case CURLOPT_NETRC_FILE: return STR_NETRC_FILE;
#endif
default: L_error(L,"Unsupported string in bag");
}
return 0;
}
/******************************************************************************
* checks and returns a string field from the first position in the stack
*
*/
static char** L_checkcurlstring(lua_State*L,CURLoption opt)
{
struct L_curl_bag* tmp = (struct L_curl_bag*)
luaL_checkudata(L,1,CURL_EASY_META_NAME);
luaL_argcheck(L,tmp != NULL,1,"curleasy expected");
return &(tmp->strings[L_CURLoption2vect(L,opt)]);
}
/******************************************************************************
* checks and returns the header slist
*
*/
static struct curl_slist ** L_checkcurlheadlist(lua_State*L)
{
struct L_curl_bag* tmp = (struct L_curl_bag*)
luaL_checkudata(L,1,CURL_EASY_META_NAME);
luaL_argcheck(L,tmp != NULL,1,"curleasy expected");
return &(tmp->headlist);
}
/******************************************************************************
* checks and returns the err field from the first position in the stack
*
*/
static char* L_checkcurlerr(lua_State*L)
{
void* tmp = luaL_checkudata(L,1,CURL_EASY_META_NAME);
luaL_argcheck(L,tmp != NULL,1,"curleasy expected");
return ((struct L_curl_bag*)tmp)->err;
}
/******************************************************************************
* checks and returns the post field from the first position in the stack
*
*/
#if CURL_NEWER(7,9,6)
static struct curl_httppost **L_checkcurlpost(lua_State*L){
void* tmp = luaL_checkudata(L,1,CURL_EASY_META_NAME);
luaL_argcheck(L,tmp != NULL,1,"curleasy expected");
return &((struct L_curl_bag*)tmp)->post;
}
#endif
/******************************************************************************
* checks if c is_in t and returns it
*
*/
static long L_checkconst(lua_State* L,
const struct L_const* t,const char* t_nam, int c){
int i,found;
long int con;
if( lua_type(L,c) != LUA_TNUMBER)
L_error(L,"Expecting a %s value, got %s",t_nam,
lua_typename(L,lua_type(L,c)));
con = (long int)lua_tonumber(L,c);
for ( i = 0,found = 0 ; t[i].name != NULL ; i++){
if( t[i].value == con){
found = 1;
break;
}
}
if(found == 1)
return con;
else {
L_error(L,"Expecting a %s value, got something else",t_nam);
}
return -1;
}
/******************************************************************************
* checks if c is <= \sigma t and returns it
*
*/
static long int L_checkconst_mask(lua_State* L,
const struct L_const* t,const char* t_nam, int c){
int con;
if( lua_type(L,c) != LUA_TNUMBER )
L_error(L,"Expecting a %s value, got nothing",t_nam);
//FIXME: think a check
con = (long int)lua_tonumber(L,c);
return con;
}
/******************************************************************************
* checks, builds and return a string list
*
*/
static struct curl_slist * L_checkslist(lua_State* L,int tab_index) {
struct curl_slist * sl = NULL;
/* since we manipulate the stack we want tab_index in absolute */
if ( tab_index < 0 )
tab_index = lua_gettop(L) + 1 + tab_index;
/* a slist must be a LUA table */
luaL_argcheck(L,lua_istable(L,tab_index),tab_index,"expecting a table");
/* create the slist */
sl = NULL;
/* traverse the table */
lua_pushnil(L);
while( lua_next(L,tab_index) != 0 ){
const char * val;
/* now we have: ...old_stack... | key:int | val:string */
if ( lua_type(L,-1) != LUA_TSTRING) {
curl_slist_free_all(sl);
L_error(L,"this table must be a string list");
}
if ( lua_type(L,-2) != LUA_TNUMBER ) {
curl_slist_free_all(sl);
L_error(L,"this table is a list, keys must be unused");
}
/* get the string */
val = lua_tostring(L,-1);
/* pop val */
lua_pop(L,1);
/* store it in the list */
sl = curl_slist_append(sl,val);
}
return sl;
}
/******************************************************************************
* curl_easy_perform
*
*/
static int luacurl_easy_perform(lua_State* L) {
CURL* c = L_checkcurleasy(L);
CURLcode rc = curl_easy_perform(c);
L_checknarg(L,1,"perform wants 1 argument (self)");
if ( rc == CURLE_OK ) {
lua_pushnumber(L,(lua_Number)rc);
lua_pushnil(L);
}else{
lua_pushnumber(L,(lua_Number)rc);
lua_pushstring(L,L_checkcurlerr(L));
}
return 2;
}
/******************************************************************************
* curl write callback
*
*/
static size_t L_callback_writedata(
void *ptr,size_t size,size_t nmemb,void *stream){
lua_State* L = (lua_State*)stream;
size_t rc;
size_t dimension = size * nmemb;
L_checknarg(L,1,"we expect the callback to have a curl handler on the stack");
/* find the lua closure */
/* XXX we assume the c:perform() leaves c on the stack */
lua_pushlightuserdata(L,CURL_WRITECB_OFF(L_checkcurleasy(L)));
lua_rawget(L,LUA_REGISTRYINDEX);
/* call it */
lua_pushlstring(L,(const char *)ptr,dimension);
lua_pushnumber(L,dimension);
lua_call(L,2,2);
L_checknarg(L,3,"we expect the callback to return 2 arguments");
if (lua_type(L,-2) != LUA_TNUMBER)
L_error(L,"head_cb must return: (number,errror_message or nil) but the "
"first one is not a number");
rc = (size_t)lua_tonumber(L,-2);
/*
if( rc != dimension ) {
if ( lua_type(L,-1) == LUA_TSTRING)
L_error(L,"write_cb returned %d that is not the expected %d"
", error message: '%s'",rc,dimension,lua_tostring(L,-1));
else
L_error(L,"write_cb returned %d that is not the expected %d"
", no error message",rc,dimension);
}
*/
lua_pop(L,2);
return rc;
}
/******************************************************************************
* curl write header callback
*
*/
static size_t L_callback_writehead(
void *ptr,size_t size,size_t nmemb,void *stream){
lua_State* L = (lua_State*)stream;
size_t rc;
size_t dimension = size * nmemb;
L_checknarg(L,1,"we expect the callback to have a curl handler on the stack");
/* find the lua closure */
/* XXX we assume the c:perform() leaves c on the stack */
lua_pushlightuserdata(L,CURL_HEADCB_OFF(L_checkcurleasy(L)));
lua_rawget(L,LUA_REGISTRYINDEX);
/* call it */
lua_pushlstring(L,(const char *)ptr,dimension);
lua_pushnumber(L,dimension);
lua_call(L,2,2);
L_checknarg(L,3,"we expect the callback to return 2 arguments");
if (lua_type(L,-2) != LUA_TNUMBER)
L_error(L,"head_cb must return: (number,errror_message or nil) but the "
"first one is not a number");
rc = (size_t)lua_tonumber(L,-2);
/*
if( rc != dimension ) {
if ( lua_type(L,-1) == LUA_TSTRING)
L_error(L,"head_cb returned %d that is not the expected %d"
", error message: '%s'",rc,dimension,lua_tostring(L,-1));
else
L_error(L,"head_cb returned %d that is not the expected %d"
", no error message",rc,dimension);
}
*/
lua_pop(L,2);
return rc;
}
/******************************************************************************
* curl read callback
*
*/
static size_t L_callback_readdata(
void *ptr,size_t size,size_t nmemb,void *stream){
lua_State* L = (lua_State*)stream;
size_t rc;
size_t dimension = size * nmemb;
L_checknarg(L,1,"we expect the callback to have a curl handler on the stack");
/* find the lua closure */
/* XXX we assume the c:perform() leaves c on the stack */
lua_pushlightuserdata(L,CURL_READCB_OFF(L_checkcurleasy(L)));
lua_rawget(L,LUA_REGISTRYINDEX);
/* call it */
lua_pushnumber(L,dimension);
lua_call(L,1,2);
L_checknarg(L,3,"we expect the callback to return 2 arguments");
if (lua_type(L,-2) != LUA_TNUMBER)
L_error(L,"read_cb must return: (number,errror_message or nil) but the "
"first one is not a number");
rc = (size_t)lua_tonumber(L,-2);
if(rc != 0) {
/* we have data to send */
if ( rc > dimension )
L_error(L,"read_rc must return a size <= than the number "
"that received in input");
if ( lua_type(L,-1) != LUA_TSTRING)
L_error(L,"read_cb must return a string as the second "
"value, not a %s",lua_typename(L,lua_type(L,-1)));
if ( rc != lua_strlen(L,-1) )
L_error(L,"read_cb must return a size and a string, "
"and the size must be the string size");
memcpy(ptr,lua_tostring(L,-1),rc);
}
lua_pop(L,2);
return rc;
}
/******************************************************************************
* CURLOPT_HTTPPOST parser
*
*/
#if CURL_NEWER(7,9,8)
static CURLcode L_httppost(CURL* c,CURLoption opt,lua_State* L){
/* we assume we hve stack: || c | opt | table
*
* table is a table of tables since we assume the function is called:
* c:setopt(curl.OPT_HTTPPOST,{
* {curl.FORM_COPYNAME,"name1",
* curl.FORM_COPYCONTENTS,"data1",
* curl.FORM_CONTENTTYPE,"Content-type: text/plain",
* curl.FORM_END},
* {curl.FORM_COPYNAME,"name2",
* curl.FORM_COPYCONTENTS,"data2",
* curl.FORM_CONTENTTYPE,"Content-type: text/plain",
* curl.FORM_END}
* })
*
*/
struct curl_httppost *post = NULL, *last = NULL;
#if CURL_NEWER(7,9,8)
CURLFORMcode rc = CURL_FORMADD_OK;
#else
int rc = CURL_FORMADD_OK;
#endif
CURLcode rc_opt = CURLE_OK;
/* check for the table */
if( ! lua_istable(L,3) )
L_error(L,"expecting a table, got %s",lua_typename(L,lua_type(L,3)));
/* outer loop to travers the table list */
lua_pushnil(L);
while( lua_next(L,3) != 0 ){
/* now we have: ...old_stack... | key:int | val:table
* and we traverse the internal table
*/
int forms_size = L_tablesize(L,5)/2+1;
struct curl_forms forms[forms_size]; /* not ANSI */
int position = 0;
lua_pushnil(L);
while( lua_next(L,5) != 0 ){
CURLformoption o = (CURLformoption)
L_checkconst(L,curl_easy_form_c,"CURLformoption",7);
switch(o){
case CURLFORM_BUFFER:
case CURLFORM_BUFFERPTR:
case CURLFORM_FILENAME:
case CURLFORM_CONTENTTYPE: /* sould be ok */
case CURLFORM_FILE:
case CURLFORM_FILECONTENT:
case CURLFORM_PTRCONTENTS:
case CURLFORM_COPYCONTENTS:
case CURLFORM_PTRNAME:
case CURLFORM_COPYNAME:{
forms[position].option = o;
lua_pop(L,1);
if(lua_next(L,5) == 0)
L_error(L,
"incomplete FORM, value missed");
forms[position].value = luaL_checkstring(L,7);
}break;
case CURLFORM_BUFFERLENGTH:{
forms[position].option = o;
lua_pop(L,1);
if(lua_next(L,5) == 0)
L_error(L,
"incomplete FORM, value missed");
forms[position].value = (char*)
#ifdef __UWORD_TYPE
(__UWORD_TYPE)
#endif
//not sure this makes sense
luaL_checkint(L,7);
}break;
case CURLFORM_CONTENTHEADER:{
/* we need a damned bag here! */ L_error(L,"not implemented, use "
"CURLFORM_CONTENTTYPE instead");
}break;
case CURLFORM_END:{
forms[position].option = o;
}break;
case CURLFORM_ARRAY:{
L_error(L,"You can't use CURLFORM_ARRAY");
}break;
default:{
L_error(L,"invalid CURLFORM_");
}break;
}
position++;
lua_pop(L,1);
}
if ( (position<forms_size && forms[position].option != CURLFORM_END) ||
(position==forms_size && forms[position-1].option != CURLFORM_END))
L_error(L,"You must end a form with CURLFORM_END");
rc = curl_formadd(&post,&last,CURLFORM_ARRAY,forms,CURLFORM_END);
if( rc != CURL_FORMADD_OK) {
char* desc = NULL;
switch(rc){
case CURL_FORMADD_MEMORY:
desc="the FormInfo allocation fails";break;
case CURL_FORMADD_OPTION_TWICE:
desc="one option is given twice for one Form";
break;
case CURL_FORMADD_NULL:
desc="a null pointer was given for a char";
break;
case CURL_FORMADD_UNKNOWN_OPTION:
desc="an unknown option was used";break;
case CURL_FORMADD_INCOMPLETE:
desc="some FormInfo is not complete (or error)";
break;
case CURL_FORMADD_ILLEGAL_ARRAY:
desc="an illegal option is used in an "
"array (internal)";
break;
default: desc = "Unknown error";break;
}
L_error(L,"Invalid form '%s'",desc);
}
lua_pop(L,1);
}
rc_opt = curl_easy_setopt(c,opt,post);
if( *L_checkcurlpost(L) != NULL)
curl_formfree(*L_checkcurlpost(L));
*L_checkcurlpost(L) = post;
return rc_opt;
}
#endif
/******************************************************************************
* curl_easy_setopt
*
*/
static int luacurl_easy_setopt(lua_State* L) {
CURL* c = L_checkcurleasy(L);
CURLoption opt = (CURLoption)L_checkconst(L,curl_easy_c,"CURLoption",2);
CURLcode rc = CURLE_OK;
L_checknarg(L,3,"setopt wants 3 argument (self,opt,val)");
switch(opt) {
/* long */
case CURLOPT_SSLENGINE_DEFAULT:
case CURLOPT_SSLVERSION:
case CURLOPT_SSL_VERIFYPEER:
case CURLOPT_SSL_VERIFYHOST:
case CURLOPT_TRANSFERTEXT:
case CURLOPT_CRLF:
case CURLOPT_RESUME_FROM:
case CURLOPT_FILETIME:
case CURLOPT_NOBODY:
case CURLOPT_INFILESIZE:
case CURLOPT_UPLOAD:
#if CURL_NEWER(7,10,8)
case CURLOPT_MAXFILESIZE:
#endif
case CURLOPT_TIMECONDITION:
case CURLOPT_TIMEVALUE:
case CURLOPT_TIMEOUT:
case CURLOPT_LOW_SPEED_LIMIT:
case CURLOPT_LOW_SPEED_TIME:
case CURLOPT_MAXCONNECTS:
case CURLOPT_FRESH_CONNECT:
case CURLOPT_FORBID_REUSE:
case CURLOPT_CONNECTTIMEOUT:
case CURLOPT_FTPLISTONLY:
case CURLOPT_FTPAPPEND:
case CURLOPT_HEADER:
case CURLOPT_NOPROGRESS:
#if CURL_NEWER(7,10,0)
case CURLOPT_NOSIGNAL:
case CURLOPT_BUFFERSIZE:
#endif
case CURLOPT_FAILONERROR:
case CURLOPT_PROXYPORT:
case CURLOPT_HTTPPROXYTUNNEL:
case CURLOPT_DNS_CACHE_TIMEOUT:
case CURLOPT_DNS_USE_GLOBAL_CACHE:
case CURLOPT_PORT:
#if CURL_NEWER(7,11,2)
case CURLOPT_TCP_NODELAY:
#endif
case CURLOPT_AUTOREFERER:
case CURLOPT_FOLLOWLOCATION:
#if CURL_NEWER(7,10,4)
case CURLOPT_UNRESTRICTED_AUTH:
#endif
case CURLOPT_MAXREDIRS:
case CURLOPT_PUT:
case CURLOPT_POST:
case CURLOPT_POSTFIELDSIZE:
#if CURL_NEWER(7,11,1)
case CURLOPT_POSTFIELDSIZE_LARGE:
#endif
#if CURL_NEWER(7,9,7)
case CURLOPT_COOKIESESSION:
#endif
case CURLOPT_HTTPGET:
case CURLOPT_VERBOSE:
#if CURL_NEWER(7,10,7)
case CURLOPT_FTP_CREATE_MISSING_DIRS:
#endif
#if CURL_NEWER(7,10,8)
case CURLOPT_FTP_RESPONSE_TIMEOUT:
#endif
#if CURL_NEWER(7,10,5)
case CURLOPT_FTP_USE_EPRT:
#endif
case CURLOPT_FTP_USE_EPSV:{
long par = luaL_checklong(L,3);
rc = curl_easy_setopt(c,opt,par);
}break;
#if CURL_NEWER(7,11,1)
/* curl_off_t */
case CURLOPT_RESUME_FROM_LARGE:
case CURLOPT_INFILESIZE_LARGE:
case CURLOPT_MAXFILESIZE_LARGE:{
curl_off_t o = (curl_off_t)luaL_checknumber(L,3);
rc = curl_easy_setopt(c,opt,o);
}break;
#endif
/* char* */
case CURLOPT_ERRORBUFFER:{
/* not used since the lua perform returns it */
L_error(L,"not used, lua returns the error string "
"as the second arg if something fails");
}break;
case CURLOPT_SSLCERT:
case CURLOPT_SSLCERTTYPE:
case CURLOPT_SSLCERTPASSWD: /* alias CURLOPT_SSLKEYPASSWD */
case CURLOPT_SSLKEY:
case CURLOPT_SSLKEYTYPE:
case CURLOPT_SSLENGINE:
case CURLOPT_CAINFO:
#if CURL_NEWER(7,9,8)
case CURLOPT_CAPATH:
#endif
case CURLOPT_RANDOM_FILE:
case CURLOPT_EGDSOCKET:
case CURLOPT_SSL_CIPHER_LIST:
case CURLOPT_KRB4LEVEL:
#if CURL_NEWER(7,10,3)
case CURLOPT_PRIVATE:
#endif
case CURLOPT_RANGE:
case CURLOPT_CUSTOMREQUEST:
case CURLOPT_FTPPORT:
case CURLOPT_PROXY:
case CURLOPT_INTERFACE:
#if CURL_NEWER(7,11,0)
case CURLOPT_NETRC_FILE:
#endif
case CURLOPT_USERPWD:
case CURLOPT_PROXYUSERPWD:
#if CURL_NEWER(7,10,0)
case CURLOPT_ENCODING:
#endif
case CURLOPT_POSTFIELDS:
case CURLOPT_REFERER:
case CURLOPT_USERAGENT:
case CURLOPT_COOKIE:
case CURLOPT_COOKIEFILE:
case CURLOPT_COOKIEJAR:
case CURLOPT_URL: {
const char* str = luaL_checkstring(L,3);
char **u = L_checkcurlstring(L,opt);
free(*u);
*u = ((str == NULL) ? NULL : strdup(str));
rc = curl_easy_setopt(c,opt,*u);
}break;
/* function ? think more how many type we need here ? */
#if CURL_NEWER(7,9,6)
case CURLOPT_DEBUGFUNCTION:{
L_error(L,"FIX: Not implemented");
}break;
#endif
#if CURL_NEWER(7,10,6)
case CURLOPT_SSL_CTX_FUNCTION:
#endif
case CURLOPT_PROGRESSFUNCTION:{
L_error(L,"Not implemented");
}break;
case CURLOPT_READFUNCTION:{
/* we expect a function */
if ( ! lua_isfunction(L,3) )
L_error(L,"Expecting a function");
/* we store it somewere, maybe in the registry */
lua_pushlightuserdata(L,CURL_READCB_OFF(c));
lua_pushvalue(L,-2);
lua_rawset(L,LUA_REGISTRYINDEX);
/* we save the registry key in the C function bag */
rc = curl_easy_setopt(c,CURLOPT_READDATA,L);
/* check for errors */
if( rc != CURLE_OK) {
L_error(L,"%s",L_checkcurlerr(L));
}
/* we attach the function to a C function that calls it */
rc = curl_easy_setopt(c,
CURLOPT_READFUNCTION,L_callback_readdata);
}break;
case CURLOPT_HEADERFUNCTION:{
/* we expect a function */
if ( ! lua_isfunction(L,3) )
L_error(L,"Expecting a function");
/* we store it somewere, maybe in the registry */
lua_pushlightuserdata(L,CURL_HEADCB_OFF(c));
lua_pushvalue(L,-2);
lua_rawset(L,LUA_REGISTRYINDEX);
/* we save the registry key in the C function bag */
rc = curl_easy_setopt(c,CURLOPT_WRITEHEADER,L);
/* check for errors */
if( rc != CURLE_OK) {
L_error(L,"%s",L_checkcurlerr(L));
}
/* we attach the function to a C function that calls it */
rc = curl_easy_setopt(c,
CURLOPT_HEADERFUNCTION,L_callback_writehead);
}break;
case CURLOPT_WRITEFUNCTION:{
/* we expect a function */
if ( ! lua_isfunction(L,3) )
L_error(L,"Expecting a function");
/* we store it somewere, maybe in the registry */
lua_pushlightuserdata(L,CURL_WRITECB_OFF(c));
lua_pushvalue(L,-2);
lua_rawset(L,LUA_REGISTRYINDEX);
/* we save the registry key in the C function bag */
rc = curl_easy_setopt(c,CURLOPT_WRITEDATA,L);
/* check for errors */
if( rc != CURLE_OK) {
L_error(L,"%s",L_checkcurlerr(L));
}
/* we attach the function to a C function that calls it */
rc = curl_easy_setopt(c,
CURLOPT_WRITEFUNCTION,L_callback_writedata);
}break;
/* void* */
#if CURL_NEWER(7,10,6)
case CURLOPT_SSL_CTX_DATA:
#endif
#if CURL_NEWER(7,9,6)
case CURLOPT_DEBUGDATA:
#endif
case CURLOPT_WRITEHEADER:
case CURLOPT_PROGRESSDATA:
case CURLOPT_READDATA:
case CURLOPT_WRITEDATA:{
L_error(L,"This option must not be used,"
"use Lua's lexical scoping closure instead");
}break;
/* FILE* */
case CURLOPT_STDERR:{
L_error(L,"Not implemented");
/* it is not hard to put a FILE* in L_curl_bag and open a
new FILE* on request made by file name and not FILE*...
but not really useful I think */
}break;
/* constants */
case CURLOPT_NETRC:{
#if CURL_NEWER(7,9,8)
enum CURL_NETRC_OPTION o=(enum CURL_NETRC_OPTION)
L_checkconst(L,curl_easy_netrc_c,"CURL_NETRC_OPTION",3);
#else
long o = luaL_checklong(L,3);
#endif
rc = curl_easy_setopt(c,opt,o);
}break;
#if CURL_NEWER(7,10,7)
case CURLOPT_PROXYAUTH:
#endif
#if CURL_NEWER(7,10,6)
case CURLOPT_HTTPAUTH:{
long int o= L_checkconst_mask(L,
curl_easy_auth_c,"CURLAUTH_*",3);
rc = curl_easy_setopt(c,opt,o);
}break;
#endif
case CURLOPT_HTTP_VERSION:{
long int o = L_checkconst(L,
curl_easy_httpver_c,"CURL_HTTP_VERSION_*",3);
rc = curl_easy_setopt(c,opt,o);
}break;
#if CURL_NEWER(7,11,0)
case CURLOPT_FTP_SSL:{
long int o = L_checkconst(L,
curl_easy_ftpssl_c,"CURLFTPSSL_*",3);
rc = curl_easy_setopt(c,opt,o);
}break;
#endif
case CURLOPT_CLOSEPOLICY:{
long int o = L_checkconst(L,
curl_easy_closepolicy_c,"CURLCLOSEPOLICY_*",3);
rc = curl_easy_setopt(c,opt,o);
}break;
#if CURL_NEWER(7,10,8)
case CURLOPT_IPRESOLVE:{
long int o = L_checkconst(L,
curl_easy_ipresolve_c,"CURL_IPRESOLVE_*",3);
rc = curl_easy_setopt(c,opt,o);
}break;
#endif
/* FIXME: not sure of this */
#if CURL_NEWER(7,10,0)
case CURLOPT_PROXYTYPE:{
long int o = L_checkconst(L,
curl_easy_proxytype_c,"CURLPROXY_*",3);
rc = curl_easy_setopt(c,opt,o);
}break;
#endif
/* slist */
#if CURL_NEWER(7,9,8)
case CURLOPT_HTTPPOST:{
rc = L_httppost(c,opt,L);
}break;
#endif
case CURLOPT_QUOTE:
case CURLOPT_POSTQUOTE:
#if CURL_NEWER(7,9,5)
case CURLOPT_PREQUOTE:
#endif
#if CURL_NEWER(7,10,3)
case CURLOPT_HTTP200ALIASES:
#endif
case CURLOPT_HTTPHEADER:{
struct curl_slist ** old=NULL;
struct curl_slist * sl = L_checkslist(L,3);
rc = curl_easy_setopt(c,opt,sl);
old = L_checkcurlheadlist(L);
curl_slist_free_all(*old);
*old = sl;
}break;
/* share handle */
#if CURL_NEWER(7,10,0)
case CURLOPT_SHARE:{
L_error(L,"not implemented");
}break;
#endif
/* deprecated */
#if CURL_OLDER(7,10,8)
case CURLOPT_PASSWDFUNCTION:
case CURLOPT_PASSWDDATA:
#endif
{
L_error(L,"deprecated and not implemented");
}break;
/* default */
default:{
L_error(L,"invalid CURLOPT_");
}break;
}
/* check for errors */
if( rc != CURLE_OK) {
L_error(L,"setopt: '%s'",L_checkcurlerr(L));
}
return 0;
}
/******************************************************************************
* curl_easy_init
*
*/
static int luacurl_easy_init(lua_State* L) {
CURL * tmp = NULL;
struct L_curl_bag* c = NULL;
CURLcode rc = CURLE_OK;
int i;
tmp = curl_easy_init();
if ( tmp == NULL) {
L_error(L,"curl_easy_init() returned NULL");
}
c = (struct L_curl_bag*)lua_newuserdata(L,sizeof(struct L_curl_bag));
luaL_getmetatable(L,CURL_EASY_META_NAME);
lua_setmetatable(L,-2);
c->handler = tmp;
for(i = 0 ; i < STR_SIZE ; i++)
c->strings[i] = NULL;
c->headlist = NULL;
#if CURL_NEWER(7,9,6)
c->post=NULL;
#endif
rc = curl_easy_setopt(tmp,CURLOPT_ERRORBUFFER,c->err);
/* check for errors */
if( rc != CURLE_OK) {
L_error(L,"unable to call CURLOPT_ERRORBUFFER (%d)",rc);
}
return 1;
}
/******************************************************************************
* curl_easy_cleanup
*
*/
static int luacurl_easy_cleanup(lua_State* L) {
struct L_curl_bag* c = L_checkcurluserdata(L);
int i;
curl_easy_cleanup(c->handler);
for(i = 0 ; i < STR_SIZE ; i++)
free(c->strings[i]);
curl_slist_free_all(c->headlist);
#if CURL_NEWER(7,9,6)
if(c->post != NULL)
curl_formfree(c->post);
#endif
lua_pushlightuserdata(L,CURL_WRITECB_OFF(c));
lua_pushnil(L);
lua_rawset(L,LUA_REGISTRYINDEX);
lua_pushlightuserdata(L,CURL_READCB_OFF(c));
lua_pushnil(L);
lua_rawset(L,LUA_REGISTRYINDEX);
lua_pushlightuserdata(L,CURL_HEADCB_OFF(c));
lua_pushnil(L);
lua_rawset(L,LUA_REGISTRYINDEX);
return 0;
}
/******************************************************************************
* curl_escape
*
*/
static int luacurl_escape(lua_State* L) {
const char* data = luaL_checkstring(L,1);
size_t len = lua_strlen(L,1);
char * tmp;
L_checknarg(L,1,"escape wants 1 argument (string)");
tmp = curl_escape(data,len);
lua_pushstring(L,tmp);
#if CURL_NEWER(7,10,0)
curl_free(tmp);
#else
free(tmp);
#endif
return 1;
}
/******************************************************************************
* curl_unescape
*
*/
static int luacurl_unescape(lua_State* L) {
const char* data = luaL_checkstring(L,1);
size_t len = lua_strlen(L,1);
char * tmp;
L_checknarg(L,1,"unescape wants 1 argument (string)");
tmp = curl_unescape(data,len);
lua_pushstring(L,tmp);
#if CURL_NEWER(7,10,0)
curl_free(tmp);
#else
free(tmp);
#endif
return 1;
}
/******************************************************************************
* curl_version
*
*/
static int luacurl_version(lua_State* L) {
L_checknarg(L,0,"version wants no arguments");
lua_pushstring(L,curl_version());
return 1;
}
/******************************************************************************
* curl_version_info
*
*/
#if CURL_NEWER(7,10,0)
static int luacurl_version_info(lua_State* L) {
curl_version_info_data *d = curl_version_info(CURLVERSION_NOW);
int i;
L_checknarg(L,0,"version_info wants no arguments");
lua_newtable(L);
lua_pushstring(L,"version");
lua_pushstring(L,d->version);
lua_settable(L,-3);
lua_pushstring(L,"version_num");
lua_pushnumber(L,d->version_num);
lua_settable(L,-3);
lua_pushstring(L,"host");
lua_pushstring(L,d->host);
lua_settable(L,-3);
lua_pushstring(L,"ssl_version");
lua_pushstring(L,d->ssl_version);
lua_settable(L,-3);
lua_pushstring(L,"ssl_version_num");
lua_pushnumber(L,d->ssl_version_num);
lua_settable(L,-3);
lua_pushstring(L,"features");
lua_pushnumber(L,d->features);
lua_settable(L,-3);
lua_pushstring(L,"libz_version");
lua_pushstring(L,d->libz_version);
lua_settable(L,-3);
lua_pushstring(L,"protocols");
lua_newtable(L);
for(i=0;d->protocols[i] != NULL;i++){
lua_pushnumber(L,i+1);
lua_pushstring(L,d->protocols[i]);
lua_settable(L,-3);
}
lua_pushstring(L,"n");
lua_pushnumber(L,i);
lua_settable(L,-3);
lua_settable(L,-3);
return 1;
}
#endif
/******************************************************************************
* curl.* functions
*
*/
static const struct luaL_reg curl_f [] = {
{"easy_init",luacurl_easy_init},
{"escape",luacurl_escape},
{"unescape",luacurl_unescape},
{"version",luacurl_version},
#if CURL_NEWER(7,10,0)
{"version_info",luacurl_version_info},
#endif
{NULL,NULL}
};
/******************************************************************************
* c:* functions
*
*/
static const struct luaL_reg curl_easy_m [] = {
{"setopt",luacurl_easy_setopt},
{"perform",luacurl_easy_perform},
{NULL,NULL}
};
/******************************************************************************
* open the luacurl library
* you need to call curl_global_init manually
*
*/
int luacurl_open(lua_State* L) {
luaL_newmetatable(L,CURL_EASY_META_NAME);
lua_pushstring(L,"__gc");
lua_pushcfunction(L,luacurl_easy_cleanup);
lua_settable(L,-3);
lua_pushstring(L,"__index");
lua_pushvalue(L,-2);
lua_settable(L,-3);
luaL_openlib(L,NULL,curl_easy_m,0);
luaL_openlib(L,"curl",curl_f,0);
L_openconst(L,curl_easy_c);
#if CURL_NEWER(7,9,8)
L_openconst(L,curl_easy_netrc_c);
#endif
#if CURL_NEWER(7,10,6)
L_openconst(L,curl_easy_auth_c);
#endif
L_openconst(L,curl_easy_httpver_c);
L_openconst(L,curl_easy_form_c);
#if CURL_NEWER(7,11,0)
L_openconst(L,curl_easy_ftpssl_c);
#endif
L_openconst(L,curl_easy_closepolicy_c);
#if CURL_NEWER(7,10,8)
L_openconst(L,curl_easy_ipresolve_c);
#endif
#if CURL_NEWER(7,10,0)
L_openconst(L,curl_easy_proxytype_c);
#endif
return 1;
}
/******************************************************************************
* opens the luacurl library and calls curl_global_init(CURL_GLOBAL_ALL)
* use this if you have not initialized cURL in the C code
*
*/
int luacurl_open_and_init(lua_State* L) {
curl_global_init(CURL_GLOBAL_ALL);
return luacurl_open(L);
}
syntax highlighted by Code2HTML, v. 0.9.1