From b46fc0ad2599aeada78dbc4ddfc9ac732ef5a12c Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Mon, 25 Aug 2014 13:12:45 +0500 Subject: [PATCH] Init commit --- README.md | 60 +++++ doc/lcurl.ldoc | 145 ++++++++++++ msvc/lcurl.sln | 20 ++ msvc/lcurl.vcproj | 240 +++++++++++++++++++ src/l52util.c | 147 ++++++++++++ src/l52util.h | 55 +++++ src/lceasy.c | 569 ++++++++++++++++++++++++++++++++++++++++++++++ src/lceasy.h | 10 + src/lcerror.c | 320 ++++++++++++++++++++++++++ src/lcerror.h | 23 ++ src/lchttppost.c | 393 ++++++++++++++++++++++++++++++++ src/lchttppost.h | 22 ++ src/lcinfoeasy.h | 44 ++++ src/lcopteasy.h | 159 +++++++++++++ src/lcurl.c | 73 ++++++ src/lcurl.h | 16 ++ src/lcutils.c | 101 ++++++++ src/lcutils.h | 24 ++ 18 files changed, 2421 insertions(+) create mode 100644 README.md create mode 100644 doc/lcurl.ldoc create mode 100644 msvc/lcurl.sln create mode 100644 msvc/lcurl.vcproj create mode 100644 src/l52util.c create mode 100644 src/l52util.h create mode 100644 src/lceasy.c create mode 100644 src/lceasy.h create mode 100644 src/lcerror.c create mode 100644 src/lcerror.h create mode 100644 src/lchttppost.c create mode 100644 src/lchttppost.h create mode 100644 src/lcinfoeasy.h create mode 100644 src/lcopteasy.h create mode 100644 src/lcurl.c create mode 100644 src/lcurl.h create mode 100644 src/lcutils.c create mode 100644 src/lcutils.h diff --git a/README.md b/README.md new file mode 100644 index 0000000..944bb6d --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# Lua binding to [libcurl](http://curl.haxx.se/libcurl) + + + +## Usage + +```Lua +-- HTTP Get +curl:easy() + :setopt_url('http://httpbin.org/get') + :setopt_httpheader{ + "X-Test-Header1: Header-Data1", + "X-Test-Header2: Header-Data2", + } + :setopt_writefunction(io.stderr) + :perform() +:close() +``` + +```Lua +-- HTTP Post +curl:easy() + :setopt_url('http://posttestserver.com/post.php') + :setopt_writefunction(io.write) + :setopt_httppost(curl.httppost() + :add_content("test_content", "some data", { + "MyHeader: SomeValue" + }) + :add_buffer("test_file", "filename", "text data", "text/plain", { + "Description: my file description" + }) + :add_file("test_file2", "BuildLog.htm", "application/octet-stream", { + "Description: my file description" + }) + ) + :perform() +:close() +``` + +```Lua +-- FTP Upload +local function get_bin_by(str,n) + local pos = 1 - n + return function() + pos = pos + n + return (str:sub(pos,pos+n-1)) + end +end + +curl:easy() + :setopt_url("ftp://moteus:123456@127.0.0.1/test.dat") + :setopt_upload(true) + :setopt_readfunction( + get_bin_by(("0123456789"):rep(4), 9) + ) + :perform() +:close() + +``` + diff --git a/doc/lcurl.ldoc b/doc/lcurl.ldoc new file mode 100644 index 0000000..27745fe --- /dev/null +++ b/doc/lcurl.ldoc @@ -0,0 +1,145 @@ +--- +-- @module lcurl + + +--- Create HTTP multipart object. +-- +-- @treturn[1] httppost new curl HTTP Post object context +function httppost() end + + +--- HTTP multipart/formdata object +-- @type httppost +-- +do + +--- Add new part to form. +-- +-- @tparam string name provide the name of this part +-- @tparam string content actual data to send +-- @tparam[opt] string type provides the content-type for this part +-- @tparam[opt] table headers specifies extra headers for the form POST section +-- @return[1] self +function httppost:add_content() end + +--- Add new part to form. +-- +-- @tparam string name provide the name of this part +-- @tparam string filename provides the filename field in the content header +-- @tparam string content actual data to send +-- @tparam[opt] string type provides the content-type for this part +-- @tparam[opt] table headers specifies extra headers for the form POST section +-- @return[1] self +function httppost:add_buffer() end + +--- Add new part to form. +-- +-- @tparam string name provide the name of this part +-- @tparam string path path to file that contain actual data to send +-- @tparam[opt] string type provides the content-type for this part +-- @tparam[opt] string filename provides the filename field in the content header. +-- By default it is basename of path. +-- @tparam[opt] table headers specifies extra headers for the form POST section +-- @return[1] self +function httppost:add_file() end + +--- Serialize multipart/formdata HTTP POST chain. +-- +-- @return[1] string serialized data +-- +-- @usage print(post:get()) +-- +function httppost:get() end + +--- Serialize multipart/formdata HTTP POST chain. +-- +-- Writer function can return true or number of written bytes. +-- Also if function does not return anything is considered as success. +-- +-- @tparam function writer +-- @param[opt] context writer context +-- @return[1] self +-- +-- @usage +-- t = {} +-- post:get(table.insert, t) +-- print(table.concat(t)) +-- +function httppost:get() end + +--- Serialize multipart/formdata HTTP POST chain. +-- +-- This call same as httppost:get(writer.write, writer) +-- +-- @tparam object writer +-- @return[1] self +-- +-- @usage +-- f = io.open(...) +-- post:get(f) +-- +function httppost:get() end + +--- Free multipart/formdata. +-- +function httppost:free() end + +end + +--- Easy curl object +-- @type easy +-- +do + +--- Set writer function. +-- +-- @tparam function writer +-- @param[opt] context writer context +-- @return[1] self +-- +function easy:setopt_writefunction() end + +--- Set writer function. +-- +-- This call same as easy:set_writefunction(writer.write, writer) +-- +-- @tparam object writer +-- @return[1] self +-- +function easy:setopt_writefunction() end + +--- Set reader function. +-- +-- @tparam function reader +-- @param[opt] context reader context +-- @return[1] self +-- +function easy:setopt_readfunction() end + +--- Set reader function. +-- +-- This call same as easy:set_readfunction(reader.read, reader) +-- +-- @tparam object reader +-- @return[1] self +-- +function easy:setopt_readfunction() end + +--- Set HTTP multipart/formdata +-- +-- @tparam httppost data +-- @return[1] self +function easy:setopt_httppost() end + +--- Set HTTP multipart/formdata +-- +-- @tparam string data +-- @tparam[opt=#data] number length +-- @return[1] self +function easy:setopt_postfields() end + +--- End easy session +-- +function easy:close() end + +end diff --git a/msvc/lcurl.sln b/msvc/lcurl.sln new file mode 100644 index 0000000..fb537a0 --- /dev/null +++ b/msvc/lcurl.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lcurl", "lcurl.vcproj", "{200D5C0C-8123-48F6-9EA0-CF0E6A6943F2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {200D5C0C-8123-48F6-9EA0-CF0E6A6943F2}.Debug|Win32.ActiveCfg = Debug|Win32 + {200D5C0C-8123-48F6-9EA0-CF0E6A6943F2}.Debug|Win32.Build.0 = Debug|Win32 + {200D5C0C-8123-48F6-9EA0-CF0E6A6943F2}.Release|Win32.ActiveCfg = Release|Win32 + {200D5C0C-8123-48F6-9EA0-CF0E6A6943F2}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/msvc/lcurl.vcproj b/msvc/lcurl.vcproj new file mode 100644 index 0000000..67c68c8 --- /dev/null +++ b/msvc/lcurl.vcproj @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/l52util.c b/src/l52util.c new file mode 100644 index 0000000..2b569e3 --- /dev/null +++ b/src/l52util.c @@ -0,0 +1,147 @@ +#include "l52util.h" + +#include +#include + +#if LUA_VERSION_NUM >= 502 + +int luaL_typerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, + luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); +} + +#ifndef luaL_register + +void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l){ + if(libname) lua_newtable(L); + luaL_setfuncs(L, l, 0); +} + +#endif + +#else + +void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup){ + luaL_checkstack(L, nup, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + int i; + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_setfield(L, -(nup + 2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + +void lua_rawgetp(lua_State *L, int index, const void *p){ + index = lua_absindex(L, index); + lua_pushlightuserdata(L, (void *)p); + lua_rawget(L, index); +} + +void lua_rawsetp (lua_State *L, int index, const void *p){ + index = lua_absindex(L, index); + lua_pushlightuserdata(L, (void *)p); + lua_insert(L, -2); + lua_rawset(L, index); +} + +int luaL_getmetafield (lua_State *L, int obj, const char *event) { + if (!lua_getmetatable(L, obj)) /* no metatable? */ + return 0; + lua_pushstring(L, event); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); /* remove metatable and metafield */ + return 0; + } + else { + lua_remove(L, -2); /* remove only metatable */ + return 1; + } +} + +int luaL_callmeta (lua_State *L, int obj, const char *event) { + obj = lua_absindex(L, obj); + if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ + return 0; + lua_pushvalue(L, obj); + lua_call(L, 1, 1); + return 1; +} + +#endif + +int lutil_newmetatablep (lua_State *L, const void *p) { + lua_rawgetp(L, LUA_REGISTRYINDEX, p); + if (!lua_isnil(L, -1)) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + + lua_newtable(L); /* create metatable */ + lua_pushvalue(L, -1); /* duplicate metatable to set*/ + lua_rawsetp(L, LUA_REGISTRYINDEX, p); + + return 1; +} + +void lutil_getmetatablep (lua_State *L, const void *p) { + lua_rawgetp(L, LUA_REGISTRYINDEX, p); +} + +void lutil_setmetatablep (lua_State *L, const void *p) { + lutil_getmetatablep(L, p); + assert(lua_istable(L,-1)); + lua_setmetatable (L, -2); +} + +int lutil_isudatap (lua_State *L, int ud, const void *p) { + if (lua_isuserdata(L, ud)){ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + int res; + lutil_getmetatablep(L,p); /* get correct metatable */ + res = lua_rawequal(L, -1, -2); /* does it have the correct mt? */ + lua_pop(L, 2); /* remove both metatables */ + return res; + } + } + return 0; +} + +void *lutil_checkudatap (lua_State *L, int ud, const void *p) { + void *up = lua_touserdata(L, ud); + if (up != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + lutil_getmetatablep(L,p); /* get correct metatable */ + if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ + lua_pop(L, 2); /* remove both metatables */ + return up; + } + } + } + luaL_typerror(L, ud, p); /* else error */ + return NULL; /* to avoid warnings */ +} + +int lutil_createmetap (lua_State *L, const void *p, const luaL_Reg *methods, int nup) { + if (!lutil_newmetatablep(L, p)){ + lua_insert(L, -1 - nup); /* move mt prior upvalues */ + return 0; + } + + lua_insert(L, -1 - nup); /* move mt prior upvalues */ + luaL_setfuncs (L, methods, nup); /* define methods */ + lua_pushliteral (L, "__index"); /* define metamethods */ + lua_pushvalue (L, -2); + lua_settable (L, -3); + + return 1; +} + +void *lutil_newudatap_impl(lua_State *L, size_t size, const void *p){ + void *obj = lua_newuserdata (L, size); + memset(obj, 0, size); + lutil_setmetatablep(L, p); + return obj; +} diff --git a/src/l52util.h b/src/l52util.h new file mode 100644 index 0000000..91c89a6 --- /dev/null +++ b/src/l52util.h @@ -0,0 +1,55 @@ +#ifndef _L52UTIL_H_ +#define _L52UTIL_H_ + +#include "lua.h" +#include "lauxlib.h" + +#if LUA_VERSION_NUM >= 502 // lua 5.2 + +// lua_rawgetp +// lua_rawsetp +// luaL_setfuncs +// lua_absindex +#ifndef lua_objlen + +#define lua_objlen lua_rawlen + +#endif + +int luaL_typerror (lua_State *L, int narg, const char *tname); + +#ifndef luaL_register + +void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l); + +#endif + +#else // lua 5.1 + +// functions form lua 5.2 + +# define lua_absindex(L, i) (((i)>0)?(i):((i)<=LUA_REGISTRYINDEX?(i):(lua_gettop(L)+(i)+1))) +# define lua_rawlen lua_objlen + +void lua_rawgetp (lua_State *L, int index, const void *p); +void lua_rawsetp (lua_State *L, int index, const void *p); +void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); + +int luaL_getmetafield (lua_State *L, int obj, const char *event); +int luaL_callmeta (lua_State *L, int obj, const char *event); + +#endif + +int lutil_newmetatablep (lua_State *L, const void *p); +void lutil_getmetatablep (lua_State *L, const void *p); +void lutil_setmetatablep (lua_State *L, const void *p); + +#define lutil_newudatap(L, TTYPE, TNAME) (TTYPE *)lutil_newudatap_impl(L, sizeof(TTYPE), TNAME) +int lutil_isudatap (lua_State *L, int ud, const void *p); +void *lutil_checkudatap (lua_State *L, int ud, const void *p); +int lutil_createmetap (lua_State *L, const void *p, const luaL_Reg *methods, int nup); + +void *lutil_newudatap_impl (lua_State *L, size_t size, const void *p); + +#endif + diff --git a/src/lceasy.c b/src/lceasy.c new file mode 100644 index 0000000..fc83d78 --- /dev/null +++ b/src/lceasy.c @@ -0,0 +1,569 @@ +#include "lcurl.h" +#include "lceasy.h" +#include "lcerror.h" +#include "lcutils.h" +#include "lchttppost.h" + +#define LCURL_EASY_NAME LCURL_PREFIX" Easy" +static const char *LCURL_EASY = LCURL_EASY_NAME; + +typedef struct lcurl_callback_tag{ + int cb_ref; + int ud_ref; +}lcurl_callback_t; + +typedef struct lcurl_read_buffer_tag{ + int ref; + int off; +}lcurl_read_buffer_t; + + +#define LCURL_LST_INDEX(N) LCURL_##N##_LIST, +#define LCURL_STR_INDEX(N) +#define LCURL_LNG_INDEX(N) +#define OPT_ENTRY(L, N, T, S) LCURL_##T##_INDEX(N) + +enum { + LCURL_LISY_DUMMY = -1, + + #include"lcopteasy.h" + + LCURL_LIST_COUNT, +}; + +#undef LCURL_LST_INDEX +#undef LCURL_STR_INDEX +#undef LCURL_LNG_INDEX +#undef OPT_ENTRY + +typedef struct lcurl_easy_tag{ + lua_State *L; + CURL *curl; + int storage; + int lists[LCURL_LIST_COUNT]; + int err_mode; + lcurl_callback_t wr; + lcurl_callback_t rd; + lcurl_read_buffer_t rbuffer; + +}lcurl_easy_t; + +//{ + +int lcurl_easy_create(lua_State *L, int error_mode){ + lcurl_easy_t *p = lutil_newudatap(L, lcurl_easy_t, LCURL_EASY); + int i; + p->L = L; + p->curl = curl_easy_init(); + if(!p->curl) return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, CURLE_FAILED_INIT); + p->storage = lcurl_storage_init(L); + p->err_mode = error_mode; + p->wr.cb_ref = p->wr.ud_ref = LUA_NOREF; + p->rd.cb_ref = p->rd.ud_ref = LUA_NOREF; + p->rbuffer.ref = LUA_NOREF; + for(i = 0; i < LCURL_LIST_COUNT; ++i){ + p->lists[i] = LUA_NOREF; + } + return 1; +} + +static lcurl_easy_t *lcurl_geteasy_at(lua_State *L, int i){ + lcurl_easy_t *p = (lcurl_easy_t *)lutil_checkudatap (L, i, LCURL_EASY); + luaL_argcheck (L, p != NULL, 1, LCURL_PREFIX"HTTPPost object expected"); + return p; +} + +#define lcurl_geteasy(L) lcurl_geteasy_at((L),1) + +static int lcurl_easy_cleanup(lua_State *L){ + lcurl_easy_t *p = lcurl_geteasy(L); + if(p->curl){ + curl_easy_cleanup(p->curl); + p->curl = NULL; + } + + if(p->storage != LUA_NOREF){ + lcurl_storage_free(L, p->storage); + p->storage = LUA_NOREF; + } + + return 0; +} + +static int lcurl_easy_perform(lua_State *L){ + lcurl_easy_t *p = lcurl_geteasy(L); + CURLcode code; + lua_settop(L, 1); + + assert(p->rbuffer.ref == LUA_NOREF); + + code = curl_easy_perform(p->curl); + + if(p->rbuffer.ref != LUA_NOREF){ + luaL_unref(L, LCURL_LUA_REGISTRY, p->rbuffer.ref); + p->rbuffer.ref = LUA_NOREF; + } + + if(code == CURLE_OK){ + lua_settop(L, 1); + return 1; + } + + if(code == CURLE_WRITE_ERROR){ + /* error from callback*/ + if(lua_gettop(L) > 1){ + if(lua_isstring(L, 2)) return lua_error(L); + return lua_gettop(L) - 1; + } + } + + if(code == CURLE_ABORTED_BY_CALLBACK){ + if(lua_gettop(L) > 1){ + if(lua_isstring(L, 2)) return lua_error(L); + return lua_gettop(L) - 1; + } + } + + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); +} + +//{ OPTIONS + +static int lcurl_opt_set_long_(lua_State *L, int opt){ + lcurl_easy_t *p = lcurl_geteasy(L); + long val; CURLcode code; + + if(lua_isboolean(L, 2)) val = lua_toboolean(L, 2); + else val = luaL_checklong(L, 2); + + code = curl_easy_setopt(p->curl, opt, val); + if(code != CURLE_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); + } + lua_settop(L, 1); + return 1; +} + +static int lcurl_opt_set_string_(lua_State *L, int opt, int store){ + lcurl_easy_t *p = lcurl_geteasy(L); + const char *val = luaL_checkstring(L, 2); + CURLcode code = curl_easy_setopt(p->curl, opt, val); + if(code != CURLE_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); + } + if(store)lcurl_storage_preserve_value(L, p->storage, 2); + lua_settop(L, 1); + return 1; +} + +static int lcurl_opt_set_slist_(lua_State *L, int opt, int list_no){ + lcurl_easy_t *p = lcurl_geteasy(L); + struct curl_slist *list = lcurl_util_to_slist(L, 2); + CURLcode code; + int ref = p->lists[list_no]; + + luaL_argcheck(L, list, 2, "array expected"); + + if(ref != LUA_NOREF){ + struct curl_slist *tmp = lcurl_storage_remove_slist(L, p->storage, ref); + curl_slist_free_all(tmp); + p->lists[list_no] = LUA_NOREF; + } + + code = curl_easy_setopt(p->curl, opt, list); + + if(code != CURLE_OK){ + curl_slist_free_all(list); + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); + } + + p->lists[list_no] = lcurl_storage_preserve_slist(L, p->storage, list); + lua_settop(L, 1); + return 1; +} + +#define LCURL_STR_OPT(N, S) static int lcurl_easy_set_##N(lua_State *L){\ + return lcurl_opt_set_string_(L, CURLOPT_##N, (S)); \ +} + +#define LCURL_LST_OPT(N, S) static int lcurl_easy_set_##N(lua_State *L){\ + return lcurl_opt_set_slist_(L, CURLOPT_##N, LCURL_##N##_LIST);\ +} + +#define LCURL_LNG_OPT(N, S) static int lcurl_easy_set_##N(lua_State *L){\ + return lcurl_opt_set_long_(L, CURLOPT_##N);\ +} + +#define OPT_ENTRY(L, N, T, S) LCURL_##T##_OPT(N, S) + +#include "lcopteasy.h" + +#undef OPT_ENTRY + +static int lcurl_easy_set_POSTFIELDS(lua_State *L){ + lcurl_easy_t *p = lcurl_geteasy(L); + size_t len; const char *val = luaL_checklstring(L, 2, &len); + CURLcode code; + if(lua_isnumber(L, 3)){ + size_t n = (size_t)lua_tonumber(L, 3); + luaL_argcheck(L, len <= n, 3, "data length too big"); + len = n; + } + code = curl_easy_setopt(p->curl, CURLOPT_POSTFIELDS, val); + if(code != CURLE_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); + } + lcurl_storage_preserve_value(L, p->storage, 2); + code = curl_easy_setopt(p->curl, CURLOPT_POSTFIELDSIZE, (long)len); + if(code != CURLE_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); + } + lua_settop(L, 1); + return 1; +} + +#undef LCURL_STR_OPT +#undef LCURL_LST_OPT +#undef LCURL_LNG_OPT + +static int lcurl_easy_set_HTTPPOST(lua_State *L){ + lcurl_easy_t *p = lcurl_geteasy(L); + lcurl_hpost_t *post = lcurl_gethpost_at(L, 2); + CURLcode code = curl_easy_setopt(p->curl, CURLOPT_HTTPPOST, post->post); + if(code != CURLE_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); + } + + lcurl_storage_preserve_value(L, p->storage, 2); + + lua_settop(L, 1); + return 1; +} + +//} + +//{ info + +static int lcurl_info_get_long_(lua_State *L, int opt){ + lcurl_easy_t *p = lcurl_geteasy(L); + long val; CURLcode code; + + code = curl_easy_getinfo(p->curl, opt, &val); + if(code != CURLE_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); + } + + lua_pushnumber(L, val); + return 1; +} + +static int lcurl_info_get_double_(lua_State *L, int opt){ + lcurl_easy_t *p = lcurl_geteasy(L); + double val; CURLcode code; + + code = curl_easy_getinfo(p->curl, opt, &val); + if(code != CURLE_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); + } + + lua_pushnumber(L, val); + return 1; +} + +static int lcurl_info_get_string_(lua_State *L, int opt){ + lcurl_easy_t *p = lcurl_geteasy(L); + char *val; CURLcode code; + + code = curl_easy_getinfo(p->curl, opt, &val); + if(code != CURLE_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); + } + + lua_pushstring(L, val); + return 1; +} + +static int lcurl_info_get_slist_(lua_State *L, int opt){ + lcurl_easy_t *p = lcurl_geteasy(L); + struct curl_slist *val; CURLcode code; + + code = curl_easy_getinfo(p->curl, opt, &val); + if(code != CURLE_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_EASY, code); + } + + lcurl_util_slist_to_table(L, val); + curl_slist_free_all(val); + + return 1; +} + +#define LCURL_STR_INFO(N, S) static int lcurl_easy_get_##N(lua_State *L){\ + return lcurl_info_get_string_(L, CURLINFO_##N); \ +} + +#define LCURL_LST_INFO(N, S) static int lcurl_easy_get_##N(lua_State *L){\ + return lcurl_info_get_slist_(L, CURLINFO_##N);\ +} + +#define LCURL_LNG_INFO(N, S) static int lcurl_easy_get_##N(lua_State *L){\ + return lcurl_info_get_long_(L, CURLINFO_##N);\ +} + +#define LCURL_DBL_INFO(N, S) static int lcurl_easy_get_##N(lua_State *L){\ + return lcurl_info_get_double_(L, CURLINFO_##N);\ +} + +#define OPT_ENTRY(L, N, T, S) LCURL_##T##_INFO(N, S) + +#include "lcinfoeasy.h" + +#undef OPT_ENTRY + +#undef LCURL_STR_INFO +#undef LCURL_LST_INFO +#undef LCURL_LNG_INFO +#undef LCURL_DBL_INFO + +//} + +//{ CallBack + +static 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; +} + +//{ Writer + +static int lcurl_write_callback(char *ptr, size_t size, size_t nmemb, void *arg){ + lcurl_easy_t *p = arg; + lua_State *L = p->L; + + size_t ret = size * nmemb; + int top = lua_gettop(L); + int n = lcurl_util_push_cb(L, &p->wr); + + lua_pushlstring(L, ptr, ret); + if(lua_pcall(L, n, LUA_MULTRET, 0)) return 0; + + if(lua_gettop(L) > top){ + if(lua_isnil(L, top + 1)) return 0; + if(lua_isboolean(L, top + 1)){ + if(!lua_toboolean(L, top + 1)) ret = 0; + } + else ret = (size_t)lua_tonumber(L, top + 1); + } + + lua_settop(L, top); + return ret; +} + +static int lcurl_easy_set_WRITEFUNCTION(lua_State *L){ + lcurl_easy_t *p = lcurl_geteasy(L); + lcurl_callback_t *c = &p->wr; + + 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(lua_gettop(L) >= 3){// writer + context + lua_settop(L, 3); + luaL_argcheck(L, !lua_isnil(L, 2), 2, "no writer present"); + c->ud_ref = luaL_ref(L, LCURL_LUA_REGISTRY); + c->cb_ref = luaL_ref(L, LCURL_LUA_REGISTRY); + assert(1 == lua_gettop(L)); + return 1; + } + + lua_settop(L, 2); + + if(lua_isnoneornil(L, 2)){ + lua_pop(L, 1); + assert(1 == lua_gettop(L)); + + curl_easy_setopt(p->curl, CURLOPT_WRITEDATA, 0); + curl_easy_setopt(p->curl, CURLOPT_WRITEFUNCTION, 0); + + return 1; + } + + curl_easy_setopt(p->curl, CURLOPT_WRITEDATA, p); + curl_easy_setopt(p->curl, CURLOPT_WRITEFUNCTION, lcurl_write_callback); + + if(lua_isfunction(L, 2)){ + c->cb_ref = luaL_ref(L, LCURL_LUA_REGISTRY); + assert(1 == lua_gettop(L)); + return 1; + } + + if(lua_isuserdata(L, 2) || lua_istable(L, 2)){ + lua_getfield(L, 2, "write"); + luaL_argcheck(L, lua_isfunction(L, -1), 2, "write method not found in object"); + c->cb_ref = luaL_ref(L, LCURL_LUA_REGISTRY); + c->ud_ref = luaL_ref(L, LCURL_LUA_REGISTRY); + assert(1 == lua_gettop(L)); + return 1; + } + + lua_pushliteral(L, "invalid writer type"); + return lua_error(L); +} + +//} + +//{ Reader + +static int lcurl_read_callback(char *buffer, size_t size, size_t nitems, void *arg){ + lcurl_easy_t *p = arg; + lua_State *L = p->L; + + const char *data; size_t data_size; + + size_t ret = size * nitems; + int n, top = lua_gettop(L); + + if(p->rbuffer.ref != LUA_NOREF){ + lua_rawgeti(L, LCURL_LUA_REGISTRY, p->rbuffer.ref); + data = luaL_checklstring(L, -1, &data_size); + lua_pop(L, 1); + + data = data + p->rbuffer.off; + data_size -= p->rbuffer.off; + + if(data_size > ret){ + data_size = ret; + memcpy(buffer, data, data_size); + p->rbuffer.off += data_size; + } + else{ + memcpy(buffer, data, data_size); + luaL_unref(L, LCURL_LUA_REGISTRY, p->rbuffer.ref); + p->rbuffer.ref = LUA_NOREF; + } + + lua_settop(L, top); + return data_size; + } + + // buffer is clean + assert(p->rbuffer.ref == LUA_NOREF); + + n = lcurl_util_push_cb(L, &p->rd); + lua_pushnumber(L, ret); + if(lua_pcall(L, n, LUA_MULTRET, 0)) return CURL_READFUNC_ABORT; + + if(lua_isnoneornil(L, top + 1)) return CURL_READFUNC_ABORT; + data = lua_tolstring(L, -1, &data_size); + if(!data) return CURL_READFUNC_ABORT; + + if(data_size > ret){ + data_size = ret; + p->rbuffer.ref = luaL_ref(L, LCURL_LUA_REGISTRY); + p->rbuffer.off = data_size; + } + memcpy(buffer, data, data_size); + + lua_settop(L, top); + return data_size; +} + +static int lcurl_easy_set_READFUNCTION(lua_State *L){ + lcurl_easy_t *p = lcurl_geteasy(L); + lcurl_callback_t *c = &p->rd; + + 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(lua_gettop(L) >= 3){// writer + context + lua_settop(L, 3); + luaL_argcheck(L, !lua_isnil(L, 2), 2, "no writer present"); + c->ud_ref = luaL_ref(L, LCURL_LUA_REGISTRY); + c->cb_ref = luaL_ref(L, LCURL_LUA_REGISTRY); + assert(1 == lua_gettop(L)); + return 1; + } + + lua_settop(L, 2); + + if(lua_isnoneornil(L, 2)){ + lua_pop(L, 1); + assert(1 == lua_gettop(L)); + + curl_easy_setopt(p->curl, CURLOPT_READDATA, 0); + curl_easy_setopt(p->curl, CURLOPT_READFUNCTION, 0); + + return 1; + } + + curl_easy_setopt(p->curl, CURLOPT_READDATA, p); + curl_easy_setopt(p->curl, CURLOPT_READFUNCTION, lcurl_read_callback); + + if(lua_isfunction(L, 2)){ + c->cb_ref = luaL_ref(L, LCURL_LUA_REGISTRY); + assert(1 == lua_gettop(L)); + return 1; + } + + if(lua_isuserdata(L, 2) || lua_istable(L, 2)){ + lua_getfield(L, 2, "read"); + luaL_argcheck(L, lua_isfunction(L, -1), 2, "read method not found in object"); + c->cb_ref = luaL_ref(L, LCURL_LUA_REGISTRY); + c->ud_ref = luaL_ref(L, LCURL_LUA_REGISTRY); + assert(1 == lua_gettop(L)); + return 1; + } + + lua_pushliteral(L, "invalid reader type"); + return lua_error(L); +} + +//} + +//} + +//} + + +#define OPT_ENTRY(L, N, T, S) { "setopt_"#L, lcurl_easy_set_##N }, +static const struct luaL_Reg lcurl_easy_methods[] = { + + #include "lcopteasy.h" + + OPT_ENTRY(httppost, HTTPPOST, TTT, 0) + OPT_ENTRY(writefunction, WRITEFUNCTION, TTT, 0) + OPT_ENTRY(readfunction, READFUNCTION, TTT, 0) + + {"getinfo_effective_url", lcurl_easy_get_EFFECTIVE_URL }, + + {"perform", lcurl_easy_perform }, + {"close", lcurl_easy_cleanup }, + {"__gc", lcurl_easy_cleanup }, + + {NULL,NULL} +}; +#undef OPT_ENTRY + +void lcurl_easy_initlib(lua_State *L, int nup){ + if(!lutil_createmetap(L, LCURL_EASY, lcurl_easy_methods, nup)) + lua_pop(L, nup); + lua_pop(L, 1); +} \ No newline at end of file diff --git a/src/lceasy.h b/src/lceasy.h new file mode 100644 index 0000000..7ce09fa --- /dev/null +++ b/src/lceasy.h @@ -0,0 +1,10 @@ +#ifndef _LCEASY_H_ +#define _LCEASY_H_ + +#include "lcurl.h" + +int lcurl_easy_create(lua_State *L, int error_mode); + +void lcurl_easy_initlib(lua_State *L, int nup); + +#endif diff --git a/src/lcerror.c b/src/lcerror.c new file mode 100644 index 0000000..80e65e3 --- /dev/null +++ b/src/lcerror.c @@ -0,0 +1,320 @@ +/* + Author: Alexey Melnichuk + + Copyright (C) 2013-2014 Alexey Melnichuk + + Licensed according to the included 'LICENCE' document + + This file is part of lua-lcurl library. + */ + +#include "lcurl.h" +#include "lcerror.h" +#include + +#define LCURL_ERROR_NAME LCURL_PREFIX" Error" +static const char *LCURL_ERROR = LCURL_ERROR_NAME; + +typedef struct lcurl_error_tag{ + int tp; + int no; +}lcurl_error_t; + +//{ + +static const char* lcurl_err_easy_mnemo(int err){ +#define RETURN_IF(E) case CURLE_##E: return #E; + + switch (err){ + RETURN_IF ( OK ) + RETURN_IF ( UNSUPPORTED_PROTOCOL ) + RETURN_IF ( FAILED_INIT ) + RETURN_IF ( URL_MALFORMAT ) + RETURN_IF ( NOT_BUILT_IN ) + RETURN_IF ( COULDNT_RESOLVE_PROXY ) + RETURN_IF ( COULDNT_RESOLVE_HOST ) + RETURN_IF ( COULDNT_CONNECT ) + RETURN_IF ( FTP_WEIRD_SERVER_REPLY ) + RETURN_IF ( REMOTE_ACCESS_DENIED ) + RETURN_IF ( FTP_ACCEPT_FAILED ) + RETURN_IF ( FTP_WEIRD_PASS_REPLY ) + RETURN_IF ( FTP_ACCEPT_TIMEOUT ) + RETURN_IF ( FTP_WEIRD_PASV_REPLY ) + RETURN_IF ( FTP_WEIRD_227_FORMAT ) + RETURN_IF ( FTP_CANT_GET_HOST ) + RETURN_IF ( OBSOLETE16 ) + RETURN_IF ( FTP_COULDNT_SET_TYPE ) + RETURN_IF ( PARTIAL_FILE ) + RETURN_IF ( FTP_COULDNT_RETR_FILE ) + RETURN_IF ( OBSOLETE20 ) + RETURN_IF ( QUOTE_ERROR ) + RETURN_IF ( HTTP_RETURNED_ERROR ) + RETURN_IF ( WRITE_ERROR ) + RETURN_IF ( OBSOLETE24 ) + RETURN_IF ( UPLOAD_FAILED ) + RETURN_IF ( READ_ERROR ) + RETURN_IF ( OUT_OF_MEMORY ) + RETURN_IF ( OPERATION_TIMEDOUT ) + RETURN_IF ( OBSOLETE29 ) + RETURN_IF ( FTP_PORT_FAILED ) + RETURN_IF ( FTP_COULDNT_USE_REST ) + RETURN_IF ( OBSOLETE32 ) + RETURN_IF ( RANGE_ERROR ) + RETURN_IF ( HTTP_POST_ERROR ) + RETURN_IF ( SSL_CONNECT_ERROR ) + RETURN_IF ( BAD_DOWNLOAD_RESUME ) + RETURN_IF ( FILE_COULDNT_READ_FILE ) + RETURN_IF ( LDAP_CANNOT_BIND ) + RETURN_IF ( LDAP_SEARCH_FAILED ) + RETURN_IF ( OBSOLETE40 ) + RETURN_IF ( FUNCTION_NOT_FOUND ) + RETURN_IF ( ABORTED_BY_CALLBACK ) + RETURN_IF ( BAD_FUNCTION_ARGUMENT ) + RETURN_IF ( OBSOLETE44 ) + RETURN_IF ( INTERFACE_FAILED ) + RETURN_IF ( OBSOLETE46 ) + RETURN_IF ( TOO_MANY_REDIRECTS ) + RETURN_IF ( UNKNOWN_OPTION ) + RETURN_IF ( TELNET_OPTION_SYNTAX ) + RETURN_IF ( OBSOLETE50 ) + RETURN_IF ( PEER_FAILED_VERIFICATION ) + RETURN_IF ( GOT_NOTHING ) + RETURN_IF ( SSL_ENGINE_NOTFOUND ) + RETURN_IF ( SSL_ENGINE_SETFAILED ) + RETURN_IF ( SEND_ERROR ) + RETURN_IF ( RECV_ERROR ) + RETURN_IF ( OBSOLETE57 ) + RETURN_IF ( SSL_CERTPROBLEM ) + RETURN_IF ( SSL_CIPHER ) + RETURN_IF ( SSL_CACERT ) + RETURN_IF ( BAD_CONTENT_ENCODING ) + RETURN_IF ( LDAP_INVALID_URL ) + RETURN_IF ( FILESIZE_EXCEEDED ) + RETURN_IF ( USE_SSL_FAILED ) + RETURN_IF ( SEND_FAIL_REWIND ) + RETURN_IF ( SSL_ENGINE_INITFAILED ) + RETURN_IF ( LOGIN_DENIED ) + RETURN_IF ( TFTP_NOTFOUND ) + RETURN_IF ( TFTP_PERM ) + RETURN_IF ( REMOTE_DISK_FULL ) + RETURN_IF ( TFTP_ILLEGAL ) + RETURN_IF ( TFTP_UNKNOWNID ) + RETURN_IF ( REMOTE_FILE_EXISTS ) + RETURN_IF ( TFTP_NOSUCHUSER ) + RETURN_IF ( CONV_FAILED ) + RETURN_IF ( CONV_REQD ) + RETURN_IF ( SSL_CACERT_BADFILE ) + RETURN_IF ( REMOTE_FILE_NOT_FOUND ) + RETURN_IF ( SSH ) + RETURN_IF ( SSL_SHUTDOWN_FAILED ) + RETURN_IF ( AGAIN ) + RETURN_IF ( SSL_CRL_BADFILE ) + RETURN_IF ( SSL_ISSUER_ERROR ) + RETURN_IF ( FTP_PRET_FAILED ) + RETURN_IF ( RTSP_CSEQ_ERROR ) + RETURN_IF ( RTSP_SESSION_ERROR ) + RETURN_IF ( FTP_BAD_FILE_LIST ) + RETURN_IF ( CHUNK_FAILED ) + RETURN_IF ( NO_CONNECTION_AVAILABLE ) + } + return "UNKNOWN"; + +#undef RETURN_IF +} + +static const char* lcurl_err_multi_mnemo(int err){ +#define RETURN_IF(E) case CURLM_##E: return #E; + + switch (err){ + RETURN_IF ( OK ) + RETURN_IF ( CALL_MULTI_PERFORM ) + RETURN_IF ( BAD_HANDLE ) + RETURN_IF ( BAD_EASY_HANDLE ) + RETURN_IF ( OUT_OF_MEMORY ) + RETURN_IF ( INTERNAL_ERROR ) + RETURN_IF ( BAD_SOCKET ) + RETURN_IF ( UNKNOWN_OPTION ) + RETURN_IF ( ADDED_ALREADY ) + } + return "UNKNOWN"; + +#undef RETURN_IF +} + +static const char* lcurl_err_share_mnemo(int err){ +#define RETURN_IF(E) case CURLSHE_##E: return #E; + + switch (err){ + RETURN_IF ( OK ) + RETURN_IF ( BAD_OPTION ) + RETURN_IF ( IN_USE ) + RETURN_IF ( INVALID ) + RETURN_IF ( NOMEM ) + RETURN_IF ( NOT_BUILT_IN ) + } + return "UNKNOWN"; + +#undef RETURN_IF +} + +static const char* lcurl_err_form_mnemo(int err){ +#define RETURN_IF(E) case CURL_FORMADD_##E: return #E; + + switch (err){ + RETURN_IF ( OK ) + RETURN_IF ( MEMORY ) + RETURN_IF ( OPTION_TWICE ) + RETURN_IF ( NULL ) + RETURN_IF ( UNKNOWN_OPTION ) + RETURN_IF ( INCOMPLETE ) + RETURN_IF ( ILLEGAL_ARRAY ) + RETURN_IF ( DISABLED ) + } + return "UNKNOWN"; + +#undef RETURN_IF +} + +static const char* _lcurl_err_mnemo(int tp, int err){ + switch(tp){ + case LCURL_ERROR_EASY : return lcurl_err_easy_mnemo (err); + case LCURL_ERROR_MULTI: return lcurl_err_multi_mnemo(err); + case LCURL_ERROR_SHARE: return lcurl_err_share_mnemo(err); + case LCURL_ERROR_FORM : return lcurl_err_form_mnemo (err); + } + assert(0); + return ""; +} + +static const char* _lcurl_err_msg(int tp, int err){ + switch(tp){ + case LCURL_ERROR_EASY : return curl_easy_strerror (err); + case LCURL_ERROR_MULTI: return curl_multi_strerror(err); + case LCURL_ERROR_SHARE: return curl_share_strerror(err); + case LCURL_ERROR_FORM : return lcurl_err_form_mnemo(err); + } + assert(0); + return ""; +} + +static void _lcurl_err_pushstring(lua_State *L, int tp, int err){ + lua_pushfstring(L, "[%s] %s (%d)", + _lcurl_err_mnemo(tp, err), + _lcurl_err_msg(tp, err), + err + ); +} + +//} + +//{ + +int lcurl_error_create(lua_State *L, int error_type, int no){ + lcurl_error_t *err = lutil_newudatap(L, lcurl_error_t, LCURL_ERROR); + + assert( + (error_type == LCURL_ERROR_EASY ) || + (error_type == LCURL_ERROR_MULTI) || + (error_type == LCURL_ERROR_SHARE) || + (error_type == LCURL_ERROR_FORM ) || + 0 + ); + + err->tp = error_type; + err->no = no; + return 1; +} + +static lcurl_error_t *lcurl_geterror_at(lua_State *L, int i){ + lcurl_error_t *err = (lcurl_error_t *)lutil_checkudatap (L, i, LCURL_ERROR); + luaL_argcheck (L, err != NULL, 1, LCURL_PREFIX"error object expected"); + return err; +} + +#define lcurl_geterror(L) lcurl_geterror_at((L),1) + +static int lcurl_err_no(lua_State *L){ + lcurl_error_t *err = lcurl_geterror(L); + lua_pushinteger(L, err->no); + return 1; +} + +static int lcurl_err_msg(lua_State *L){ + lcurl_error_t *err = lcurl_geterror(L); + lua_pushstring(L, _lcurl_err_msg(err->tp, err->no)); + return 1; +} + +static int lcurl_err_mnemo(lua_State *L){ + lcurl_error_t *err = lcurl_geterror(L); + lua_pushstring(L, _lcurl_err_mnemo(err->tp, err->no)); + return 1; +} + +static int lcurl_err_tostring(lua_State *L){ + lcurl_error_t *err = lcurl_geterror(L); + _lcurl_err_pushstring(L, err->tp, err->no); + return 1; +} + +static int lcurl_err_equal(lua_State *L){ + lcurl_error_t *lhs = lcurl_geterror_at(L, 1); + lcurl_error_t *rhs = lcurl_geterror_at(L, 2); + lua_pushboolean(L, ((lhs->no == rhs->no)&&(lhs->tp == rhs->tp))?1:0); + return 1; +} + +//} + +//{ + +int lcurl_fail_ex(lua_State *L, int mode, int error_type, int code){ + if(mode == LCURL_ERROR_RETURN){ + lua_pushnil(L); + lcurl_error_create(L, error_type, code); + return 2; + } + +#if LUA_VERSION_NUM >= 502 // lua 5.2 + lcurl_error_create(L, error_type, code); +#else + _lcurl_err_pushstring(L, error_type, code); +#endif + + assert(LCURL_ERROR_RAISE == mode); + + return lua_error(L); +} + +int lcurl_fail(lua_State *L, int error_type, int code){ + return lcurl_fail_ex(L, LCURL_ERROR_RETURN, error_type, code); +} + +//} + +int lcurl_error_new(lua_State *L){ + int tp = luaL_checkint(L, 1); + int no = luaL_checkint(L, 2); + + //! @todo checks error type value + + lcurl_error_create(L, tp, no); + return 1; +} + +static const struct luaL_Reg lcurl_err_methods[] = { + {"no", lcurl_err_no }, + {"msg", lcurl_err_msg }, + {"name", lcurl_err_mnemo }, + {"mnemo", lcurl_err_mnemo }, + {"__tostring", lcurl_err_tostring }, + {"__eq", lcurl_err_equal }, + + {NULL,NULL} +}; + +void lcurl_error_initlib(lua_State *L, int nup){ + if(!lutil_createmetap(L, LCURL_ERROR, lcurl_err_methods, nup)) + lua_pop(L, nup); + lua_pop(L, 1); +} diff --git a/src/lcerror.h b/src/lcerror.h new file mode 100644 index 0000000..7a8d42c --- /dev/null +++ b/src/lcerror.h @@ -0,0 +1,23 @@ +#ifndef _LCERROR_H_ +#define _LCERROR_H_ + +#include "lcurl.h" + +#define LCURL_ERROR_CURL 1 +#define LCURL_ERROR_EASY 1 +#define LCURL_ERROR_MULTI 2 +#define LCURL_ERROR_SHARE 3 +#define LCURL_ERROR_FORM 4 + +#define LCURL_ERROR_RETURN 1 +#define LCURL_ERROR_RAISE 2 + +int lcurl_fail(lua_State *L, int error_type, int code); + +int lcurl_fail_ex(lua_State *L, int mode, int error_type, int code); + +int lcurl_error_new(lua_State *L); + +void lcurl_error_initlib(lua_State *L, int nup); + +#endif diff --git a/src/lchttppost.c b/src/lchttppost.c new file mode 100644 index 0000000..5baea7a --- /dev/null +++ b/src/lchttppost.c @@ -0,0 +1,393 @@ +#include "lcurl.h" +#include "lchttppost.h" +#include "lcerror.h" +#include "lcutils.h" + +#define LCURL_HTTPPOST_NAME LCURL_PREFIX" HTTPPost" +static const char *LCURL_HTTPPOST = LCURL_HTTPPOST_NAME; + +//{ + +int lcurl_hpost_create(lua_State *L, int error_mode){ + lcurl_hpost_t *p = lutil_newudatap(L, lcurl_hpost_t, LCURL_HTTPPOST); + p->post = p->last = 0; + p->storage = lcurl_storage_init(L); + p->err_mode = error_mode; + + return 1; +} + +lcurl_hpost_t *lcurl_gethpost_at(lua_State *L, int i){ + lcurl_hpost_t *p = (lcurl_hpost_t *)lutil_checkudatap (L, i, LCURL_HTTPPOST); + luaL_argcheck (L, p != NULL, 1, LCURL_PREFIX"HTTPPost object expected"); + return p; +} + +static int lcurl_hpost_add_content(lua_State *L){ + // add_buffer(name, data, [type,] [headers]) + lcurl_hpost_t *p = lcurl_gethpost(L); + size_t name_len; const char *name = luaL_checklstring(L, 2, &name_len); + size_t cont_len; const char *cont = luaL_checklstring(L, 3, &cont_len); + const char *type = lua_tostring(L, 4); + struct curl_slist *list = lcurl_util_to_slist(L, type?5:4); + struct curl_forms forms[3]; + CURLFORMcode code; + + int i = 0; + if(type){ forms[i].option = CURLFORM_CONTENTTYPE; forms[i++].value = type; } + if(list){ forms[i].option = CURLFORM_CONTENTHEADER; forms[i++].value = (char*)list; } + forms[i].option = CURLFORM_END; + + code = curl_formadd(&p->post, &p->last, + CURLFORM_PTRNAME, name, CURLFORM_NAMELENGTH, name_len, + CURLFORM_PTRCONTENTS, cont, CURLFORM_CONTENTSLENGTH, cont_len, + CURLFORM_ARRAY, forms, + CURLFORM_END); + + if(code != CURL_FORMADD_OK){ + if(list) curl_slist_free_all(list); + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_FORM, code); + } + + lcurl_storage_preserve_value(L, p->storage, 2); + lcurl_storage_preserve_value(L, p->storage, 3); + if(list) lcurl_storage_preserve_slist (L, p->storage, list); + + lua_settop(L, 1); + return 1; +} + +static int lcurl_hpost_add_buffer(lua_State *L){ + // add_buffer(name, filename, data, [type,] [headers]) + lcurl_hpost_t *p = lcurl_gethpost(L); + size_t name_len; const char *name = luaL_checklstring(L, 2, &name_len); + const char *buff = luaL_checkstring(L, 3); + size_t cont_len; const char *cont = luaL_checklstring(L, 4, &cont_len); + const char *type = lua_tostring(L, 5); + struct curl_slist *list = lcurl_util_to_slist(L, type?6:5); + struct curl_forms forms[3]; + CURLFORMcode code; + + int i = 0; + if(type){ forms[i].option = CURLFORM_CONTENTTYPE; forms[i++].value = type; } + if(list){ forms[i].option = CURLFORM_CONTENTHEADER; forms[i++].value = (char*)list; } + forms[i].option = CURLFORM_END; + + code = curl_formadd(&p->post, &p->last, + CURLFORM_PTRNAME, name, CURLFORM_NAMELENGTH, name_len, + CURLFORM_BUFFER, buff, + CURLFORM_BUFFERPTR, cont, CURLFORM_BUFFERLENGTH, cont_len, + CURLFORM_ARRAY, forms, + CURLFORM_END); + + if(code != CURL_FORMADD_OK){ + if(list) curl_slist_free_all(list); + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_FORM, code); + } + + lcurl_storage_preserve_value(L, p->storage, 2); + lcurl_storage_preserve_value(L, p->storage, 4); + if(list) lcurl_storage_preserve_slist (L, p->storage, list); + + lua_settop(L, 1); + return 1; +} + +static int lcurl_hpost_add_file(lua_State *L){ + // add_file(name, path, [type, [fname,]] [headers]) + // add_file("Picture", "c:\\image.jpg") + // add_file("Picture", "c:\\image.jpg", "image/jpeg") + // add_file("Picture", "c:\\image.jpg", "image/jpeg", {"XDescript: my image"}) + // add_file("Picture", "c:\\image.jpg", "image/jpeg", "avatar.jpeg", {"XDescript: my image"}) + // add_file("Picture", "c:\\image.jpg", nil, "avatar.jpeg", {"XDescript: my image"}) + + int top = lua_gettop(L); + lcurl_hpost_t *p = lcurl_gethpost(L); + size_t name_len; const char *name = luaL_checklstring(L, 2, &name_len); + const char *path = luaL_checkstring(L, 3); + const char *type = 0, *fname = 0; + struct curl_slist *list; + struct curl_forms forms[3]; + CURLFORMcode code; + int i = 0; + + if(top == 4){ /* name, path, type | headers */ + if(lua_istable(L, 4)) + list = lcurl_util_to_slist(L, 4); + else + type = lua_tostring(L, 4); + } + else if(top > 4){ /* name, path, type, fname | [fname, headers] */ + type = lua_tostring(L, 4); + if(top == 5){ /* name, path, type, fname | headers */ + if(lua_istable(L, 5)) + list = lcurl_util_to_slist(L, 5); + else + fname = lua_tostring(L, 5); + } + else{ /* name, path, type, fname, headers */ + fname = lua_tostring(L, 5); + list = lcurl_util_to_slist(L, 6); + } + } + + if(type){ forms[i].option = CURLFORM_CONTENTTYPE; forms[i++].value = type; } + if(list){ forms[i].option = CURLFORM_CONTENTHEADER; forms[i++].value = (char*)list; } + forms[i].option = CURLFORM_END; + + code = curl_formadd(&p->post, &p->last, + CURLFORM_PTRNAME, name, CURLFORM_NAMELENGTH, name_len, + CURLFORM_FILE, path, + CURLFORM_ARRAY, forms, + CURLFORM_END); + + if(code != CURL_FORMADD_OK){ + if(list) curl_slist_free_all(list); + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_FORM, code); + } + + lcurl_storage_preserve_value(L, p->storage, 2); + if(list) lcurl_storage_preserve_slist (L, p->storage, list); + + lua_settop(L, 1); + return 1; +} + +static int lcurl_hpost_add_files(lua_State *L){ + lcurl_hpost_t *p = lcurl_gethpost(L); + size_t name_len; const char *name = luaL_checklstring(L, 2, &name_len); + int i; int opt_count = 0; + int arr_count = lua_rawlen(L, 3); + struct curl_forms *forms; + CURLFORMcode code; + + lua_settop(L, 3); + if(lua_type(L, -1) != LUA_TTABLE){ + //! @fixme use library specific error codes + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_FORM, CURL_FORMADD_ILLEGAL_ARRAY); + } + + for(i = 1; i <= arr_count; ++i){ + int n; + lua_rawgeti(L, 3, i); + + if((lua_type(L, -1) != LUA_TTABLE) && (lua_type(L, -1) != LUA_TSTRING)){ + //! @fixme use library specific error codes + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_FORM, CURL_FORMADD_ILLEGAL_ARRAY); + } + + n = (lua_type(L, -1) == LUA_TSTRING) ? 1: lua_rawlen(L, -1); + if(n == 1) opt_count += 1; // name + else if(n == 2) opt_count += 2; // name and type + else if(n == 3) opt_count += 3; // name, type and filename + else{ + //! @fixme use library specific error codes + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_FORM, CURL_FORMADD_ILLEGAL_ARRAY); + } + + lua_pop(L, 1); + } + + if(opt_count == 0){ + lua_settop(L, 1); + return 1; + } + + forms = calloc(opt_count + 1, sizeof(struct curl_forms)); + if(!forms){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_FORM, CURL_FORMADD_MEMORY); + } + forms[opt_count].option = CURLFORM_END; + + opt_count = 0; + for(i = 1; i <= arr_count; ++i){ + int n; + + lua_rawgeti(L, 3, i); + if (lua_type(L, -1) == LUA_TSTRING){ + forms[opt_count].option = CURLFORM_FILE; forms[opt_count++].value = luaL_checkstring(L, -1); + } + else{ + n = lua_rawlen(L, -1); + lua_rawgeti(L, -1, 1); + forms[opt_count].option = CURLFORM_FILE; forms[opt_count++].value = luaL_checkstring(L, -1); + lua_pop(L, 1); + if(n > 1){ + lua_rawgeti(L, -1, 2); + forms[opt_count].option = CURLFORM_CONTENTTYPE; forms[opt_count++].value = luaL_checkstring(L, -1); + lua_pop(L, 1); + } + if(n > 2){ + lua_rawgeti(L, -1, 3); + forms[opt_count].option = CURLFORM_FILENAME; forms[opt_count++].value = luaL_checkstring(L, -1); + lua_pop(L, 1); + } + } + + lua_pop(L, 1); + } + + code = curl_formadd(&p->post, &p->last, + CURLFORM_PTRNAME, name, CURLFORM_NAMELENGTH, name_len, + CURLFORM_ARRAY, forms, + CURLFORM_END); + + free(forms); + + if(code != CURL_FORMADD_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_FORM, code); + } + + lua_settop(L, 1); + return 1; +} + +static size_t lcurl_hpost_getter_by_buffer(void *arg, const char *buf, size_t len){ + luaL_Buffer *b = arg; + luaL_addlstring(b, buf, len); + return len; +} + +static size_t call_writer(lua_State *L, int fn, int ctx, const char *buf, size_t len){ + int top = lua_gettop(L); + int n = 1; // number of args + lua_Number ret = (lua_Number)len; + + lua_pushvalue(L, fn); + if(ctx){ + lua_pushvalue(L, ctx); + n += 1; + } + lua_pushlstring(L, buf, len); + + if(lua_pcall(L, n, LUA_MULTRET, 0)) return 0; + + if(lua_gettop(L) > top){ + if(lua_isnil(L, top + 1)) return 0; + if(lua_isboolean(L, top + 1)){ + if(!lua_toboolean(L, top + 1)) ret = 0; + } + else ret = lua_tonumber(L, top + 1); + } + lua_settop(L, top); + + return (size_t)ret; +} + +static size_t lcurl_hpost_getter_by_callback1(void *arg, const char *buf, size_t len){ + lua_State *L = arg; + assert(2 == lua_gettop(L)); + return call_writer(L, 2, 0, buf, len); +} + +static size_t lcurl_hpost_getter_by_callback2(void *arg, const char *buf, size_t len){ + lua_State *L = arg; + assert(3 == lua_gettop(L)); + return call_writer(L, 2, 3, buf, len); +} + +static int lcurl_hpost_get(lua_State *L){ + // get() + // get(fn [, ctx]) + // get(object) + lcurl_hpost_t *p = lcurl_gethpost(L); + CURLcode code; + int top; + + if(lua_isnoneornil(L, 2)){ + luaL_Buffer b; + luaL_buffinit(L, &b); + + code = curl_formget(p->post, &b, lcurl_hpost_getter_by_buffer); + if(code != CURLE_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_CURL, code); + } + + luaL_pushresult(&b); + return 1; + } + + if(lua_isfunction(L, 2)){ + if(lua_gettop(L) == 2){ + top = 2; + code = curl_formget(p->post, L, lcurl_hpost_getter_by_callback1); + } + else{ + top = 3; + lua_settop(L, 3); + code = curl_formget(p->post, L, lcurl_hpost_getter_by_callback2); + } + } + else if(lua_isuserdata(L, 2) || lua_istable(L, 2)){ + lua_settop(L, 2); + lua_getfield(L, 2, "write"); + luaL_argcheck(L, lua_isfunction(L, -1), 2, "write method not found in object"); + assert(3 == lua_gettop(L)); + lua_insert(L, -2); + top = 3; + code = curl_formget(p->post, L, lcurl_hpost_getter_by_callback2); + } + else{ + lua_pushliteral(L, "invalid writer type"); + return lua_error(L); + } + + if((CURLcode)-1 == code){ + if(((lua_gettop(L) == top+1))&&(lua_isstring(L, -1))){ + return lua_error(L); + } + return lua_gettop(L) - top; + } + + if(code != CURLE_OK){ + return lcurl_fail_ex(L, p->err_mode, LCURL_ERROR_CURL, code); + } + + lua_settop(L, 1); + return 1; +} + +static int lcurl_hpost_storage(lua_State *L){ + lcurl_hpost_t *p = lcurl_gethpost(L); + lua_rawgeti(L, LCURL_LUA_REGISTRY, p->storage); + return 1; +} + +static int lcurl_hpost_free(lua_State *L){ + lcurl_hpost_t *p = lcurl_gethpost(L); + if(p->post){ + curl_formfree(p->post); + p->post = p->last = 0; + } + + if(p->storage != LUA_NOREF){ + lcurl_storage_free(L, p->storage); + p->storage = LUA_NOREF; + } + + return 0; +} + +//} + +static const struct luaL_Reg lcurl_hpost_methods[] = { + {"add_content", lcurl_hpost_add_content }, + {"add_buffer", lcurl_hpost_add_buffer }, + {"add_file", lcurl_hpost_add_file }, + + {"add_files", lcurl_hpost_add_files }, + + {"storage", lcurl_hpost_storage }, + {"get", lcurl_hpost_get }, + {"free", lcurl_hpost_free }, + {"__gc", lcurl_hpost_free }, + + {NULL,NULL} +}; + +void lcurl_hpost_initlib(lua_State *L, int nup){ + if(!lutil_createmetap(L, LCURL_HTTPPOST, lcurl_hpost_methods, nup)) + lua_pop(L, nup); + lua_pop(L, 1); +} + diff --git a/src/lchttppost.h b/src/lchttppost.h new file mode 100644 index 0000000..6039884 --- /dev/null +++ b/src/lchttppost.h @@ -0,0 +1,22 @@ +#ifndef _LCHTTPPOST_H_ +#define _LCHTTPPOST_H_ + +#include "lcurl.h" + +typedef struct lcurl_hpost_tag{ + struct curl_httppost *post; + struct curl_httppost *last; + int storage; + int err_mode; +}lcurl_hpost_t; + +int lcurl_hpost_create(lua_State *L, int error_mode); + +void lcurl_hpost_initlib(lua_State *L, int nup); + +lcurl_hpost_t *lcurl_gethpost_at(lua_State *L, int i); + +#define lcurl_gethpost(L) lcurl_gethpost_at((L),1) + + +#endif diff --git a/src/lcinfoeasy.h b/src/lcinfoeasy.h new file mode 100644 index 0000000..575ff32 --- /dev/null +++ b/src/lcinfoeasy.h @@ -0,0 +1,44 @@ +OPT_ENTRY( effective_url, EFFECTIVE_URL, STR, 0) +OPT_ENTRY( response_code, RESPONSE_CODE, LNG, 0) +OPT_ENTRY( http_connectcode, HTTP_CONNECTCODE, LNG, 0) +OPT_ENTRY( filetime, FILETIME, LNG, 0) +OPT_ENTRY( total_time, TOTAL_TIME, DBL, 0) +OPT_ENTRY( namelookup_time, NAMELOOKUP_TIME, DBL, 0) +OPT_ENTRY( connect_time, CONNECT_TIME, DBL, 0) +OPT_ENTRY( appconnect_time, APPCONNECT_TIME, DBL, 0) +OPT_ENTRY( pretransfer_time, PRETRANSFER_TIME, DBL, 0) +OPT_ENTRY( starttransfer_time, STARTTRANSFER_TIME, DBL, 0) +OPT_ENTRY( redirect_time, REDIRECT_TIME, DBL, 0) +OPT_ENTRY( redirect_count, REDIRECT_COUNT, LNG, 0) +OPT_ENTRY( redirect_url, REDIRECT_URL, STR, 0) +OPT_ENTRY( size_upload, SIZE_UPLOAD, DBL, 0) +OPT_ENTRY( size_download, SIZE_DOWNLOAD, DBL, 0) +OPT_ENTRY( speed_download, SPEED_DOWNLOAD, DBL, 0) +OPT_ENTRY( speed_upload, SPEED_UPLOAD, DBL, 0) +OPT_ENTRY( header_size, HEADER_SIZE, LNG, 0) +OPT_ENTRY( request_size, REQUEST_SIZE, LNG, 0) +OPT_ENTRY( ssl_verifyresult, SSL_VERIFYRESULT, LNG, 0) +OPT_ENTRY( ssl_engines, SSL_ENGINES, LST, 0) +OPT_ENTRY( content_length_download, CONTENT_LENGTH_DOWNLOAD, DBL, 0) +OPT_ENTRY( content_length_upload, CONTENT_LENGTH_UPLOAD, DBL, 0) +OPT_ENTRY( content_type, CONTENT_TYPE, STR, 0) +OPT_ENTRY( httpauth_avail, HTTPAUTH_AVAIL, LNG, 0) +OPT_ENTRY( proxyauth_avail, PROXYAUTH_AVAIL, LNG, 0) +OPT_ENTRY( os_errno, OS_ERRNO, LNG, 0) +OPT_ENTRY( num_connects, NUM_CONNECTS, LNG, 0) +OPT_ENTRY( primary_ip, PRIMARY_IP, STR, 0) +OPT_ENTRY( primary_port, PRIMARY_PORT, LNG, 0) +OPT_ENTRY( local_ip, LOCAL_IP, STR, 0) +OPT_ENTRY( local_port, LOCAL_PORT, LNG, 0) +OPT_ENTRY( cookielist, COOKIELIST, LST, 0) +OPT_ENTRY( lastsocket, LASTSOCKET, LNG, 0) +OPT_ENTRY( ftp_entry_path, FTP_ENTRY_PATH, STR, 0) +OPT_ENTRY( condition_unmet, CONDITION_UNMET, LNG, 0) +OPT_ENTRY( rtsp_session_id, RTSP_SESSION_ID, STR, 0) +OPT_ENTRY( rtsp_client_cseq, RTSP_CLIENT_CSEQ, LNG, 0) +OPT_ENTRY( rtsp_server_cseq, RTSP_SERVER_CSEQ, LNG, 0) +OPT_ENTRY( rtsp_cseq_recv, RTSP_CSEQ_RECV, LNG, 0) + +// OPT_ENTRY( PRIVATE, void ) +// OPT_ENTRY( CERTINFO, struct curl_certinfo * +// OPT_ENTRY( TLS_SESSION, struct curl_tlssessioninfo * diff --git a/src/lcopteasy.h b/src/lcopteasy.h new file mode 100644 index 0000000..f70366c --- /dev/null +++ b/src/lcopteasy.h @@ -0,0 +1,159 @@ +#undef INTERFACE +#undef BUFFERSIZE +#undef TCP_NODELAY +#undef TCP_KEEPALIVE + +/* Before version 7.17.0, strings were not copied. + Instead the user was forced keep them available + until libcurl no longer needed them. +*/ + +#ifndef LCURL_STORE_STRING +# define LCURL_STORE_STRING 0 +#endif + +OPT_ENTRY( verbose, VERBOSE, LNG, 0 ) +OPT_ENTRY( header, HEADER, LNG, 0 ) +OPT_ENTRY( noprogress, NOPROGRESS, LNG, 0 ) +OPT_ENTRY( nosignal, NOSIGNAL, LNG, 0 ) +OPT_ENTRY( wildcardmatch, WILDCARDMATCH, LNG, 0 ) + +OPT_ENTRY( url, URL, STR, LCURL_STORE_STRING ) +OPT_ENTRY( failonerror, FAILONERROR, LNG, 0 ) + +OPT_ENTRY( protocols, PROTOCOLS, LNG, 0 ) +OPT_ENTRY( redir_protocols, REDIR_PROTOCOLS, LNG, 0 ) +OPT_ENTRY( proxy, PROXY, STR, LCURL_STORE_STRING ) +OPT_ENTRY( proxyport, PROXYPORT, LNG, 0 ) +OPT_ENTRY( proxytype, PROXYTYPE, LNG, 0 ) +OPT_ENTRY( noproxy, NOPROXY, STR, LCURL_STORE_STRING ) +OPT_ENTRY( httpproxytunnel, HTTPPROXYTUNNEL, LNG, 0 ) +OPT_ENTRY( socks5_gssapi_service, SOCKS5_GSSAPI_SERVICE, STR, LCURL_STORE_STRING ) +OPT_ENTRY( socks5_gssapi_nec, SOCKS5_GSSAPI_NEC, LNG, 0 ) +OPT_ENTRY( interface, INTERFACE, STR, LCURL_STORE_STRING ) +OPT_ENTRY( localport, LOCALPORT, LNG, 0 ) +OPT_ENTRY( localportrange, LOCALPORTRANGE, LNG, 0 ) +OPT_ENTRY( dns_cache_timeout, DNS_CACHE_TIMEOUT, LNG, 0 ) +OPT_ENTRY( dns_use_global_cache, DNS_USE_GLOBAL_CACHE, LNG, 0 ) +OPT_ENTRY( buffersize, BUFFERSIZE, LNG, 0 ) +OPT_ENTRY( port, PORT, LNG, 0 ) +OPT_ENTRY( tcp_nodelay, TCP_NODELAY, LNG, 0 ) +OPT_ENTRY( address_scope, ADDRESS_SCOPE, LNG, 0 ) +OPT_ENTRY( tcp_keepalive, TCP_KEEPALIVE, LNG, 0 ) +OPT_ENTRY( tcp_keepidle, TCP_KEEPIDLE, LNG, 0 ) +OPT_ENTRY( tcp_keepintvl, TCP_KEEPINTVL, LNG, 0 ) + +OPT_ENTRY( netrc, NETRC, LNG, 0 ) +OPT_ENTRY( netrc_file, NETRC_FILE, STR, LCURL_STORE_STRING ) +OPT_ENTRY( userpwd, USERPWD, STR, LCURL_STORE_STRING ) +OPT_ENTRY( proxyuserpwd, PROXYUSERPWD, STR, LCURL_STORE_STRING ) +OPT_ENTRY( username, USERNAME, STR, LCURL_STORE_STRING ) +OPT_ENTRY( password, PASSWORD, STR, LCURL_STORE_STRING ) +OPT_ENTRY( login_options, LOGIN_OPTIONS, STR, LCURL_STORE_STRING ) +OPT_ENTRY( proxyusername, PROXYUSERNAME, STR, LCURL_STORE_STRING ) +OPT_ENTRY( proxypassword, PROXYPASSWORD, STR, LCURL_STORE_STRING ) +OPT_ENTRY( httpauth, HTTPAUTH, STR, LCURL_STORE_STRING ) +OPT_ENTRY( tlsauth_username, TLSAUTH_USERNAME, STR, LCURL_STORE_STRING ) +OPT_ENTRY( tlsauth_password, TLSAUTH_PASSWORD, STR, LCURL_STORE_STRING ) +OPT_ENTRY( proxyauth, PROXYAUTH, LNG, 0 ) +OPT_ENTRY( sasl_ir, SASL_IR, LNG, 0 ) +OPT_ENTRY( xoauth2_bearer, XOAUTH2_BEARER, STR, LCURL_STORE_STRING ) + +OPT_ENTRY( autoreferer, AUTOREFERER, LNG, 0 ) +OPT_ENTRY( accept_encoding, ACCEPT_ENCODING, STR, LCURL_STORE_STRING ) +OPT_ENTRY( transfer_encoding, TRANSFER_ENCODING, LNG, 0 ) +OPT_ENTRY( followlocation, FOLLOWLOCATION, LNG, 0 ) +OPT_ENTRY( unrestricted_auth, UNRESTRICTED_AUTH, LNG, 0 ) +OPT_ENTRY( maxredirs, MAXREDIRS, LNG, 0 ) +OPT_ENTRY( postredir, POSTREDIR, LNG, 0 ) +OPT_ENTRY( put, PUT, LNG, 0 ) +OPT_ENTRY( post, POST, LNG, 0 ) +OPT_ENTRY( referer, REFERER, STR, LCURL_STORE_STRING ) +OPT_ENTRY( useragent, USERAGENT, STR, LCURL_STORE_STRING ) +OPT_ENTRY( headeropt, HEADEROPT, LNG, 0 ) +OPT_ENTRY( httpheader, HTTPHEADER, LST, 0 ) +OPT_ENTRY( proxyheader, PROXYHEADER, LST, 0 ) +OPT_ENTRY( http200aliases, HTTP200ALIASES, LST, 0 ) +OPT_ENTRY( cookie, COOKIE, STR, LCURL_STORE_STRING ) +OPT_ENTRY( cookiefile, COOKIEFILE, STR, LCURL_STORE_STRING ) +OPT_ENTRY( cookiejar, COOKIEJAR, STR, LCURL_STORE_STRING ) +OPT_ENTRY( cookiesession, COOKIESESSION, LNG, 0 ) +OPT_ENTRY( cookielist, COOKIELIST, STR, LCURL_STORE_STRING ) +OPT_ENTRY( httpget, HTTPGET, LNG, 0 ) +OPT_ENTRY( http_version, HTTP_VERSION, LNG, 0 ) +OPT_ENTRY( ignore_content_length, IGNORE_CONTENT_LENGTH, LNG, 0 ) +OPT_ENTRY( http_content_decoding, HTTP_CONTENT_DECODING, LNG, 0 ) +OPT_ENTRY( http_transfer_decoding, HTTP_TRANSFER_DECODING, LNG, 0 ) +OPT_ENTRY( expect_100_timeout_ms, EXPECT_100_TIMEOUT_MS, LNG, 0 ) + +OPT_ENTRY( mail_from, MAIL_FROM, STR, LCURL_STORE_STRING ) +OPT_ENTRY( mail_rcpt, MAIL_RCPT, STR, LCURL_STORE_STRING ) +OPT_ENTRY( mail_auth, MAIL_AUTH, STR, LCURL_STORE_STRING ) + +OPT_ENTRY( tftp_blksize, TFTP_BLKSIZE, LNG, 0 ) + +OPT_ENTRY( ftpport, FTPPORT, STR, LCURL_STORE_STRING ) +OPT_ENTRY( quote, QUOTE, LST, 0 ) +OPT_ENTRY( postquote, POSTQUOTE, LST, 0 ) +OPT_ENTRY( prequote, PREQUOTE, STR, LCURL_STORE_STRING ) +OPT_ENTRY( dirlistonly, DIRLISTONLY, LNG, 0 ) +OPT_ENTRY( append, APPEND, LNG, 0 ) +OPT_ENTRY( ftp_use_eprt, FTP_USE_EPRT, LNG, 0 ) +OPT_ENTRY( ftp_use_epsv, FTP_USE_EPSV, LNG, 0 ) +OPT_ENTRY( ftp_use_pret, FTP_USE_PRET, LNG, 0 ) +OPT_ENTRY( ftp_create_missing_dirs, FTP_CREATE_MISSING_DIRS, LNG, 0 ) +OPT_ENTRY( ftp_response_timeout, FTP_RESPONSE_TIMEOUT, LNG, 0 ) +OPT_ENTRY( ftp_alternative_to_user, FTP_ALTERNATIVE_TO_USER, STR, LCURL_STORE_STRING ) +OPT_ENTRY( ftp_skip_pasv_ip, FTP_SKIP_PASV_IP, LNG, 0 ) +OPT_ENTRY( ftpsslauth, FTPSSLAUTH, LNG, 0 ) +OPT_ENTRY( ftp_ssl_ccc, FTP_SSL_CCC, LNG, 0 ) +OPT_ENTRY( ftp_account, FTP_ACCOUNT, STR, LCURL_STORE_STRING ) +OPT_ENTRY( ftp_filemethod, FTP_FILEMETHOD, LNG, 0 ) + +OPT_ENTRY( transfertext, TRANSFERTEXT, LNG, 0 ) +OPT_ENTRY( proxy_transfer_mode, PROXY_TRANSFER_MODE, LNG, 0 ) +OPT_ENTRY( crlf, CRLF, LNG, 0 ) +OPT_ENTRY( range, RANGE, STR, LCURL_STORE_STRING ) +OPT_ENTRY( resume_from, RESUME_FROM, LNG, 0 ) +OPT_ENTRY( resume_from_large, RESUME_FROM_LARGE, LNG, 0 ) +OPT_ENTRY( customrequest, CUSTOMREQUEST, STR, LCURL_STORE_STRING ) +OPT_ENTRY( filetime, FILETIME, LNG, 0 ) +OPT_ENTRY( nobody, NOBODY, LNG, 0 ) +OPT_ENTRY( infilesize, INFILESIZE, LNG, 0 ) +OPT_ENTRY( infilesize_large, INFILESIZE_LARGE, LNG, 0 ) +OPT_ENTRY( upload, UPLOAD, LNG, 0 ) +OPT_ENTRY( maxfilesize, MAXFILESIZE, LNG, 0 ) +OPT_ENTRY( maxfilesize_large, MAXFILESIZE_LARGE, LNG, 0 ) +OPT_ENTRY( timecondition, TIMECONDITION, LNG, 0 ) +OPT_ENTRY( timevalue, TIMEVALUE, LNG, 0 ) + +OPT_ENTRY( timeout, TIMEOUT, LNG, 0 ) +OPT_ENTRY( timeout_ms, TIMEOUT_MS, LNG, 0 ) +OPT_ENTRY( low_speed_limit, LOW_SPEED_LIMIT, LNG, 0 ) +OPT_ENTRY( low_speed_time, LOW_SPEED_TIME, LNG, 0 ) +OPT_ENTRY( max_send_speed_large, MAX_SEND_SPEED_LARGE, LNG, 0 ) +OPT_ENTRY( max_recv_speed_large, MAX_RECV_SPEED_LARGE, LNG, 0 ) +OPT_ENTRY( maxconnects, MAXCONNECTS, LNG, 0 ) +OPT_ENTRY( fresh_connect, FRESH_CONNECT, LNG, 0 ) +OPT_ENTRY( forbid_reuse, FORBID_REUSE, LNG, 0 ) +OPT_ENTRY( connecttimeout, CONNECTTIMEOUT, LNG, 0 ) +OPT_ENTRY( connecttimeout_ms, CONNECTTIMEOUT_MS, LNG, 0 ) +OPT_ENTRY( ipresolve, IPRESOLVE, LNG, 0 ) +OPT_ENTRY( connect_only, CONNECT_ONLY, LNG, 0 ) +OPT_ENTRY( use_ssl, USE_SSL, LNG, 0 ) +OPT_ENTRY( resolve, RESOLVE, LST, 0 ) +OPT_ENTRY( dns_interface, DNS_INTERFACE, STR, LCURL_STORE_STRING ) +OPT_ENTRY( dns_local_ip4, DNS_LOCAL_IP4, STR, LCURL_STORE_STRING ) +OPT_ENTRY( dns_local_ip6, DNS_LOCAL_IP6, STR, LCURL_STORE_STRING ) +OPT_ENTRY( accepttimeout_ms, ACCEPTTIMEOUT_MS, LNG, 0 ) + +OPT_ENTRY( ssh_auth_types, SSH_AUTH_TYPES, LNG, 0) +OPT_ENTRY( ssh_host_public_key_md5, SSH_HOST_PUBLIC_KEY_MD5, STR, 0) +OPT_ENTRY( ssh_public_keyfile, SSH_PUBLIC_KEYFILE, STR, 0) +OPT_ENTRY( ssh_private_keyfile, SSH_PRIVATE_KEYFILE, STR, 0) +OPT_ENTRY( ssh_knownhosts, SSH_KNOWNHOSTS, STR, 0) + +OPT_ENTRY( new_file_perms, NEW_FILE_PERMS, LNG, 0) +OPT_ENTRY( new_directory_perms, NEW_DIRECTORY_PERMS, LNG, 0) + +OPT_ENTRY( telnetoptions, TELNETOPTIONS, LST, 0) diff --git a/src/lcurl.c b/src/lcurl.c new file mode 100644 index 0000000..0ad2dcc --- /dev/null +++ b/src/lcurl.c @@ -0,0 +1,73 @@ +#include "lcurl.h" +#include "lceasy.h" +#include "lcerror.h" +#include "lchttppost.h" + + +static int lcurl_easy_new_safe(lua_State *L){ + return lcurl_easy_create(L, LCURL_ERROR_RETURN); +} + +static int lcurl_hpost_new_safe(lua_State *L){ + return lcurl_hpost_create(L, LCURL_ERROR_RETURN); +} + +static int lcurl_easy_new(lua_State *L){ + return lcurl_easy_create(L, LCURL_ERROR_RAISE); +} + +static int lcurl_hpost_new(lua_State *L){ + return lcurl_hpost_create(L, LCURL_ERROR_RAISE); +} + +static const struct luaL_Reg lcurl_functions[] = { + {"error", lcurl_error_new }, + {"httppost", lcurl_hpost_new }, + {"easy", lcurl_easy_new }, + + {NULL,NULL} +}; + +static const struct luaL_Reg lcurl_functions_safe[] = { + {"error", lcurl_error_new }, + {"httppost", lcurl_hpost_new_safe }, + {"easy", lcurl_easy_new_safe }, + + {NULL,NULL} +}; + +static volatile int LCURL_INIT = 0; + +static const char* LCURL_REGISTRY = "LCURL Registry"; + +static int luaopen_lcurl_(lua_State *L, const struct luaL_Reg *func){ + if(!LCURL_INIT){ + curl_global_init(CURL_GLOBAL_DEFAULT); + LCURL_INIT = 1; + } + + lua_rawgetp(L, LUA_REGISTRYINDEX, LCURL_REGISTRY); + if(!lua_istable(L, -1)){ /* registry */ + lua_pop(L, 1); + lua_newtable(L); + } + lua_newtable(L); /* library */ + + lua_pushvalue(L, -2); luaL_setfuncs(L, func, 1); + lua_pushvalue(L, -2); lcurl_error_initlib(L, 1); + lua_pushvalue(L, -2); lcurl_hpost_initlib(L, 1); + lua_pushvalue(L, -2); lcurl_easy_initlib (L, 1); + + lua_pushvalue(L, -2); lua_rawsetp(L, LUA_REGISTRYINDEX, LCURL_REGISTRY); + + lua_remove(L, -2); /* registry */ + + return 1; +} + +__declspec(dllexport) +int luaopen_lcurl(lua_State *L){ return luaopen_lcurl_(L, lcurl_functions); } + +__declspec(dllexport) +int luaopen_lcurl_safe(lua_State *L){ return luaopen_lcurl_(L, lcurl_functions_safe); } + diff --git a/src/lcurl.h b/src/lcurl.h new file mode 100644 index 0000000..a13162b --- /dev/null +++ b/src/lcurl.h @@ -0,0 +1,16 @@ +#ifndef _LCURL_H_ +#define _LCURL_H_ + +#include "l52util.h" +#include "curl/curl.h" +#include "curl/easy.h" +#include "curl/multi.h" + +#include + +#define LCURL_PREFIX "LcURL" + +#define LCURL_LUA_REGISTRY lua_upvalueindex(1) + + +#endif diff --git a/src/lcutils.c b/src/lcutils.c new file mode 100644 index 0000000..b05f941 --- /dev/null +++ b/src/lcutils.c @@ -0,0 +1,101 @@ +#include "lcurl.h" +#include "lcutils.h" +#include "lcerror.h" + +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); + 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); +} + + +int lcurl_storage_preserve_slist(lua_State *L, int storage, struct curl_slist * list){ + int r; + lua_rawgeti(L, LCURL_LUA_REGISTRY, storage); + lua_rawgeti(L, -1, 1); // list storage + if(!lua_istable(L, -1)){ + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, -3, 1); + } + lua_pushlightuserdata(L, list); + r = luaL_ref(L, -2); + lua_pop(L, 2); + return r; +} + +struct curl_slist* lcurl_storage_remove_slist(lua_State *L, int storage, int idx){ + struct curl_slist* list; + assert(idx != LUA_NOREF); + lua_rawgeti(L, LCURL_LUA_REGISTRY, storage); + lua_rawgeti(L, -1, 1); // list storage + lua_rawgeti(L, -1, idx); + list = lua_touserdata(L, -1); + assert(list); + luaL_unref(L, -2, idx); + lua_pop(L, 3); + return list; +} + + +void lcurl_storage_free(lua_State *L, int storage){ + lua_rawgeti(L, LCURL_LUA_REGISTRY, storage); + lua_rawgeti(L, -1, 1); // list storage + if(lua_istable(L, -1)){ + lua_pushnil(L); + while(lua_next(L, -2) != 0){ + struct curl_slist * list = 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); +} + +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); +} + + diff --git a/src/lcutils.h b/src/lcutils.h new file mode 100644 index 0000000..19ec427 --- /dev/null +++ b/src/lcutils.h @@ -0,0 +1,24 @@ +#ifndef _LCUTILS_H_ +#define _LCUTILS_H_ + +#include "lcurl.h" + +int lcurl_storage_init(lua_State *L); + +void lcurl_storage_preserve_value(lua_State *L, int storage, int i); + +int lcurl_storage_preserve_slist(lua_State *L, int storage, struct curl_slist * list); + +struct curl_slist* lcurl_storage_remove_slist(lua_State *L, int storage, int idx); + +void lcurl_storage_free(lua_State *L, int storage); + +struct curl_slist* lcurl_util_array_to_slist(lua_State *L, int t); + +struct curl_slist* lcurl_util_to_slist(lua_State *L, int t); + +void lcurl_util_slist_set(lua_State *L, int t, struct curl_slist* list); + +void lcurl_util_slist_to_table(lua_State *L, struct curl_slist* list); + +#endif