/******************************************************************************
* Author: Alexey Melnichuk <mimir@newmail.ru>
*
* Copyright (C) 2014 Alexey Melnichuk <mimir@newmail.ru>
*
* Licensed according to the included 'LICENSE' document
*
* This file is part of lua-lcurl library.
******************************************************************************/
#include "lcurl.h"
#include "lcutils.h"
#include "lcerror.h"
#define LCURL_STORAGE_SLIST 1
#define LCURL_STORAGE_KV 2
int lcurl_storage_init(lua_State *L){
lua_newtable(L);
return luaL_ref(L, LCURL_LUA_REGISTRY);
}
void lcurl_storage_preserve_value(lua_State *L, int storage, int i){
assert(i > 0 && i <= lua_gettop(L));
luaL_checkany(L, i);
lua_rawgeti(L, LCURL_LUA_REGISTRY, storage);
lua_pushvalue(L, i); lua_pushboolean(L, 1); lua_rawset(L, -3);
lua_pop(L, 1);
}
void lcurl_storage_remove_value(lua_State *L, int storage, int i){
assert(i > 0 && i <= lua_gettop(L));
luaL_checkany(L, i);
lua_rawgeti(L, LCURL_LUA_REGISTRY, storage);
lua_pushvalue(L, i); lua_pushnil(L); lua_rawset(L, -3);
lua_pop(L, 1);
}
static void lcurl_storage_ensure_t(lua_State *L, int t){
lua_rawgeti(L, -1, t);
if(!lua_istable(L, -1)){
lua_pop(L, 1);
lua_newtable(L);
lua_pushvalue(L, -1);
lua_rawseti(L, -3, t);
}
}
int lcurl_storage_preserve_slist(lua_State *L, int storage, struct curl_slist * list){
int r;
lua_rawgeti(L, LCURL_LUA_REGISTRY, storage);
lcurl_storage_ensure_t(L, LCURL_STORAGE_SLIST);
lua_pushlightuserdata(L, list);
r = luaL_ref(L, -2);
lua_pop(L, 2);
return r;
}
void lcurl_storage_preserve_iv(lua_State *L, int storage, int i, int v){
v = lua_absindex(L, v);
lua_rawgeti(L, LCURL_LUA_REGISTRY, storage);
lcurl_storage_ensure_t(L, LCURL_STORAGE_KV);
lua_pushvalue(L, v);
lua_rawseti(L, -2, i);
lua_pop(L, 2);
}
void lcurl_storage_remove_i(lua_State *L, int storage, int i){
lua_rawgeti(L, LCURL_LUA_REGISTRY, storage);
lua_rawgeti(L, -1, LCURL_STORAGE_KV);
if(lua_istable(L, -1)){
lua_pushnil(L);
lua_rawseti(L, -2, i);
}
lua_pop(L, 2);
}
void lcurl_storage_get_i(lua_State *L, int storage, int i){
lua_rawgeti(L, LCURL_LUA_REGISTRY, storage);
lua_rawgeti(L, -1, LCURL_STORAGE_KV);
if(lua_istable(L, -1)){
lua_rawgeti(L, -1, i);
lua_remove(L, -2);
}
lua_remove(L, -2);
}
struct curl_slist* lcurl_storage_remove_slist(lua_State *L, int storage, int idx){
struct curl_slist* list = NULL;
assert(idx != LUA_NOREF);
lua_rawgeti(L, LCURL_LUA_REGISTRY, storage);
lua_rawgeti(L, -1, LCURL_STORAGE_SLIST); // list storage
if(lua_istable(L, -1)){
lua_rawgeti(L, -1, idx);
list = (curl_slist*)lua_touserdata(L, -1);
assert(list);
luaL_unref(L, -2, idx);
lua_pop(L, 1);
}
lua_pop(L, 2);
return list;
}
int lcurl_storage_free(lua_State *L, int storage){
lua_rawgeti(L, LCURL_LUA_REGISTRY, storage);
lua_rawgeti(L, -1, LCURL_STORAGE_SLIST); // list storage
if(lua_istable(L, -1)){
lua_pushnil(L);
while(lua_next(L, -2) != 0){
struct curl_slist * list = (curl_slist*)lua_touserdata(L, -1);
curl_slist_free_all(list);
lua_pushvalue(L, -2); lua_pushnil(L);
lua_rawset(L, -5);
lua_pop(L, 1);
}
}
lua_pop(L, 1);
luaL_unref(L, LCURL_LUA_REGISTRY, storage);
return LUA_NOREF;
}
struct curl_slist* lcurl_util_array_to_slist(lua_State *L, int t){
struct curl_slist *list = NULL;
int i, n = lua_rawlen(L, t);
assert(lua_type(L, t) == LUA_TTABLE);
for(i = 1; i <= n; ++i){
lua_rawgeti(L, t, i);
list = curl_slist_append(list, lua_tostring(L, -1));
lua_pop(L, 1);
}
return list;
}
struct curl_slist* lcurl_util_to_slist(lua_State *L, int t){
if(lua_type(L, t) == LUA_TTABLE){
return lcurl_util_array_to_slist(L, t);
}
return 0;
}
void lcurl_util_slist_set(lua_State *L, int t, struct curl_slist* list){
int i;
t = lua_absindex(L, t);
for(i = 0;list;list = list->next){
lua_pushstring(L, list->data);
lua_rawseti(L, t, ++i);
}
}
void lcurl_util_slist_to_table(lua_State *L, struct curl_slist* list){
lua_newtable(L);
lcurl_util_slist_set(L, -1, list);
}
void lcurl_util_set_const(lua_State *L, const lcurl_const_t *reg){
const lcurl_const_t *p;
for(p = reg; p->name; ++p){
lua_pushstring(L, p->name);
lua_pushnumber(L, p->value);
lua_settable(L, -3);
}
}
int lcurl_set_callback(lua_State *L, lcurl_callback_t *c, int i, const char *method){
int top = lua_gettop(L);
i = lua_absindex(L, i);
luaL_argcheck(L, !lua_isnoneornil(L, i), i, "no function present");
luaL_argcheck(L, (top < (i + 2)), i + 2, "no arguments expected");
assert((top == i)||(top == (i + 1)));
if(c->ud_ref != LUA_NOREF){
luaL_unref(L, LCURL_LUA_REGISTRY, c->ud_ref);
c->ud_ref = LUA_NOREF;
}
if(c->cb_ref != LUA_NOREF){
luaL_unref(L, LCURL_LUA_REGISTRY, c->cb_ref);
c->cb_ref = LUA_NOREF;
}
if(lutil_is_null(L, i)){
if(top == (i + 1)){
// Do we can just ignore this?
luaL_argcheck(L,
lua_isnoneornil(L, i + 1) || lutil_is_null(L, i + 1)
,i + 1, "no context allowed when set callback to null"
);
}
lua_pop(L, top - i + 1);
return 1;
}
if(lua_gettop(L) == (i + 1)){// function + context
c->ud_ref = luaL_ref(L, LCURL_LUA_REGISTRY);
c->cb_ref = luaL_ref(L, LCURL_LUA_REGISTRY);
assert(top == (2 + lua_gettop(L)));
return 1;
}
assert(top == i);
if(lua_isfunction(L, i)){ // function
c->cb_ref = luaL_ref(L, LCURL_LUA_REGISTRY);
assert(top == (1 + lua_gettop(L)));
return 1;
}
if(lua_isuserdata(L, i) || lua_istable(L, i)){ // object
lua_getfield(L, i, method);
luaL_argcheck(L, lua_isfunction(L, -1), 2, "method not found in object");
c->cb_ref = luaL_ref(L, LCURL_LUA_REGISTRY);
c->ud_ref = luaL_ref(L, LCURL_LUA_REGISTRY);
assert(top == (1 + lua_gettop(L)));
return 1;
}
lua_pushliteral(L, "invalid object type");
return lua_error(L);
}
int lcurl_util_push_cb(lua_State *L, lcurl_callback_t *c){
assert(c->cb_ref != LUA_NOREF);
lua_rawgeti(L, LCURL_LUA_REGISTRY, c->cb_ref);
if(c->ud_ref != LUA_NOREF){
lua_rawgeti(L, LCURL_LUA_REGISTRY, c->ud_ref);
return 2;
}
return 1;
}
int lcurl_util_new_weak_table(lua_State*L, const char *mode){
int top = lua_gettop(L);
lua_newtable(L);
lua_newtable(L);
lua_pushstring(L, mode);
lua_setfield(L, -2, "__mode");
lua_setmetatable(L,-2);
assert((top+1) == lua_gettop(L));
return 1;
}
int lcurl_util_pcall_method(lua_State *L, const char *name, int nargs, int nresults, int errfunc){
int obj_index = -nargs - 1;
lua_getfield(L, obj_index, name);
lua_insert(L, obj_index - 1);
return lua_pcall(L, nargs + 1, nresults, errfunc);
}
static void lcurl_utils_pcall_close(lua_State *L, int obj){
int top = lua_gettop(L);
lua_pushvalue(L, obj);
lcurl_util_pcall_method(L, "close", 0, 0, 0);
lua_settop(L, top);
}
int lcurl_utils_apply_options(lua_State *L, int opt, int obj, int do_close,
int error_mode, int error_type, int error_code
){
int top = lua_gettop(L);
opt = lua_absindex(L, opt);
obj = lua_absindex(L, obj);
lua_pushnil(L);
while(lua_next(L, opt) != 0){
int n;
assert(lua_gettop(L) == (top + 2));
if(lua_type(L, -2) == LUA_TNUMBER){ /* [curl.OPT_URL] = "http://localhost" */
lua_pushvalue(L, -2);
lua_insert(L, -2); /*Stack : opt, obj, k, k, v */
lua_pushliteral(L, "setopt"); /*Stack : opt, obj, k, k, v, "setopt" */
n = 2;
}
else if(lua_type(L, -2) == LUA_TSTRING){ /* url = "http://localhost" */
lua_pushliteral(L, "setopt_"); lua_pushvalue(L, -3); lua_concat(L, 2);
/*Stack : opt, obj, k, v, "setopt_XXX" */
n = 1;
}
else{
lua_pop(L, 1);
continue;
}
/*Stack : opt, obj, k,[ k,] v, `setoptXXX` */
lua_gettable(L, obj); /* get e["settop_XXX]*/
if(lua_isnil(L, -1)){ /* unknown option */
if(do_close) lcurl_utils_pcall_close(L, obj);
lua_settop(L, top);
return lcurl_fail_ex(L, error_mode, error_type, error_code);
}
lua_insert(L, -n-1); /*Stack : opt, obj, k, setoptXXX, [ k,] v */
lua_pushvalue(L, obj); /*Stack : opt, obj, k, setoptXXX, [ k,] v, obj */
lua_insert(L, -n-1); /*Stack : opt, obj, k, setoptXXX, obj, [ k,] v */
if(lua_pcall(L, n+1, 2, 0)){
if(do_close) lcurl_utils_pcall_close(L, obj);
return lua_error(L);
}
if(lua_isnil(L, -2)){
if(do_close) lcurl_utils_pcall_close(L, obj);
lua_settop(L, top);
return 2;
}
/*Stack : opt, obj, k, ok, nil*/
lua_pop(L, 2);
assert(lua_gettop(L) == (top+1));
}
assert(lua_gettop(L) == top);
return 0;
}
void lcurl_stack_dump (lua_State *L){
int i = 1, top = lua_gettop(L);
fprintf(stderr, " ---------------- Stack Dump ----------------\n" );
while( i <= top ) {
int t = lua_type(L, i);
switch (t) {
case LUA_TSTRING:
fprintf(stderr, "%d(%d):`%s'\n", i, i - top - 1, lua_tostring(L, i));
break;
case LUA_TBOOLEAN:
fprintf(stderr, "%d(%d): %s\n", i, i - top - 1,lua_toboolean(L, i) ? "true" : "false");
break;
case LUA_TNUMBER:
fprintf(stderr, "%d(%d): %g\n", i, i - top - 1, lua_tonumber(L, i));
break;
default:
lua_getglobal(L, "tostring");
lua_pushvalue(L, i);
lua_call(L, 1, 1);
fprintf(stderr, "%d(%d): %s(%s)\n", i, i - top - 1, lua_typename(L, t), lua_tostring(L, -1));
lua_pop(L, 1);
break;
}
i++;
}
fprintf(stderr, " ------------ Stack Dump Finished ------------\n" );
}
curl_socket_t lcurl_opt_os_socket(lua_State *L, int idx, curl_socket_t def) {
if (lua_islightuserdata(L, idx))
return (long)lua_touserdata(L, idx);
return (curl_socket_t)lutil_optint64(L, idx, def);
}
void lcurl_push_os_socket(lua_State *L, curl_socket_t fd) {
#if !defined(_WIN32)
lutil_pushint64(L, fd);
#else /*_WIN32*/
/* Assumes that compiler can optimize constant conditions. MSVC do this. */
/*On Lua 5.3 lua_Integer type can be represented exactly*/
#if LUA_VERSION_NUM >= 503
if (sizeof(curl_socket_t) <= sizeof(lua_Integer)) {
lua_pushinteger(L, (lua_Integer)fd);
return;
}
#endif
#if defined(LUA_NUMBER_DOUBLE) || defined(LUA_NUMBER_FLOAT)
/*! @todo test DBL_MANT_DIG, FLT_MANT_DIG */
if (sizeof(lua_Number) == 8) { /*we have 53 bits for integer*/
if ((sizeof(curl_socket_t) <= 6)) {
lua_pushnumber(L, (lua_Number)fd);
return;
}
if(((UINT_PTR)fd & 0x1FFFFFFFFFFFFF) == (UINT_PTR)fd)
lua_pushnumber(L, (lua_Number)fd);
else
lua_pushlightuserdata(L, (void*)fd);
return;
}
if (sizeof(lua_Number) == 4) { /*we have 24 bits for integer*/
if (((UINT_PTR)fd & 0xFFFFFF) == (UINT_PTR)fd)
lua_pushnumber(L, (lua_Number)fd);
else
lua_pushlightuserdata(L, (void*)fd);
return;
}
#endif
lutil_pushint64(L, fd);
if (lcurl_opt_os_socket(L, -1, 0) != fd)
lua_pushlightuserdata(L, (void*)fd);
#endif /*_WIN32*/
}