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