/* * lucihttp - HTTP utility library - Lua binding * * Copyright 2018 Jo-Philipp Wich * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include /* * multipart parser binding * ------------------------------------------------------------------------- */ struct lh_L_mpart { lua_State *L; int callback; void *parser; }; static bool lh_L_mpart_cb(struct lh_mpart *p, enum lh_mpart_callback_type type, const char *buf, size_t len, void *priv) { struct lh_L_mpart *pu = priv; bool rv = false; if (pu->callback != -1) { /* function itself */ lua_rawgeti(pu->L, LUA_REGISTRYINDEX, pu->callback); /* arg #1: callback type */ lua_pushnumber(pu->L, type); /* arg #2: buffer data or nil */ if (buf) lua_pushlstring(pu->L, buf, len); else lua_pushnil(pu->L); /* arg #3: buffer length */ lua_pushnumber(pu->L, len); /* call, expect one boolean return */ lua_call(pu->L, 3, 1); /* fetch result */ rv = lua_toboolean(pu->L, -1); /* pop result */ lua_pop(pu->L, 1); } return rv; } static int lh_L_mpart_new(lua_State *L) { const char *boundary_header = luaL_optstring(L, 1, NULL); struct lh_L_mpart *pu; struct lh_mpart *p; size_t limit; p = lh_mpart_new(NULL); limit = luaL_optnumber(L, 3, 0); if (!p) { lua_pushnil(L); lua_pushstring(L, "Out of memory"); return 2; } if (!boundary_header || !lh_mpart_parse_boundary(p, boundary_header, NULL)) { lh_mpart_free(p); lua_pushnil(L); lua_pushstring(L, "Invalid boundary value"); return 2; } pu = lua_newuserdata(L, sizeof(*pu)); if (!pu) { lh_mpart_free(p); return 0; } luaL_getmetatable(L, LUCIHTTP_MPART_META); lua_setmetatable(L, -2); if (limit) lh_mpart_set_size_limit(p, limit); lh_mpart_set_callback(p, lh_L_mpart_cb, pu); if (lua_type(L, 2) == LUA_TFUNCTION) { lua_pushvalue(L, 2); pu->callback = luaL_ref(L, LUA_REGISTRYINDEX); } else { pu->callback = -1; } pu->L = L; pu->parser = p; return 1; } static int lh_L_mpart_parse(lua_State *L) { size_t len = 0; struct lh_L_mpart *pu = luaL_checkudata(L, 1, LUCIHTTP_MPART_META); const char *buf = luaL_optlstring(L, 2, NULL, &len); if (pu->parser) lua_pushboolean(L, lh_mpart_parse(pu->parser, buf, len)); else lua_pushnil(L); return 1; } static int lh_L_mpart__gc(lua_State *L) { struct lh_L_mpart *pu = luaL_checkudata(L, 1, LUCIHTTP_MPART_META); if (pu && pu->parser) { lh_mpart_free(pu->parser); pu->parser = NULL; } return 0; } /* * urlencoded parser binding * ------------------------------------------------------------------------- */ struct lh_L_urldec { lua_State *L; int callback; struct lh_urldec *parser; }; static bool lh_L_urldec_cb(struct lh_urldec *p, enum lh_urldec_callback_type type, const char *buf, size_t len, void *priv) { struct lh_L_urldec *pu = priv; bool rv = false; if (pu->callback != -1) { /* function itself */ lua_rawgeti(pu->L, LUA_REGISTRYINDEX, pu->callback); /* arg #1: callback type */ lua_pushnumber(pu->L, type); /* arg #2: buffer data or nil */ if (buf) lua_pushlstring(pu->L, buf, len); else lua_pushnil(pu->L); /* arg #3: buffer length */ lua_pushnumber(pu->L, len); /* call, expect one boolean return */ lua_call(pu->L, 3, 1); /* fetch result */ rv = lua_toboolean(pu->L, -1); /* pop result */ lua_pop(pu->L, 1); } return rv; } static int lh_L_urldec_new(lua_State *L) { struct lh_L_urldec *pu; struct lh_urldec *p; size_t limit; p = lh_urldec_new(NULL); limit = luaL_optnumber(L, 2, 0); if (!p) { lua_pushnil(L); lua_pushstring(L, "Out of memory"); return 2; } pu = lua_newuserdata(L, sizeof(*pu)); if (!pu) { lh_urldec_free(p); return 0; } luaL_getmetatable(L, LUCIHTTP_URLDEC_META); lua_setmetatable(L, -2); if (limit) lh_urldec_set_size_limit(p, limit); lh_urldec_set_callback(p, lh_L_urldec_cb, pu); if (lua_type(L, 1) == LUA_TFUNCTION) { lua_pushvalue(L, 1); pu->callback = luaL_ref(L, LUA_REGISTRYINDEX); } else { pu->callback = -1; } pu->L = L; pu->parser = p; return 1; } static int lh_L_urldec_parse(lua_State *L) { size_t len = 0; struct lh_L_urldec *pu = luaL_checkudata(L, 1, LUCIHTTP_URLDEC_META); const char *buf = luaL_optlstring(L, 2, NULL, &len); if (pu->parser) lua_pushboolean(L, lh_urldec_parse(pu->parser, buf, len)); else lua_pushnil(L); return 1; } static int lh_L_urldec__gc(lua_State *L) { struct lh_L_urldec *pu = luaL_checkudata(L, 1, LUCIHTTP_URLDEC_META); if (pu && pu->parser) { lh_urldec_free(pu->parser); pu->parser = NULL; } return 0; } /* * utility function bindings * ------------------------------------------------------------------------- */ static int lh_L_urlencode(lua_State *L) { size_t len; char *encoded; const char *str = luaL_optlstring(L, 1, NULL, &len); unsigned int flags = luaL_optnumber(L, 2, 0); if (!str) return 0; encoded = lh_urlencode(str, len, &len, flags); if (!encoded) { lua_pushnil(L); return 1; } lua_pushlstring(L, encoded, len); free(encoded); return 1; } static int lh_L_urldecode(lua_State *L) { size_t len; char *decoded; const char *str = luaL_optlstring(L, 1, NULL, &len); unsigned int flags = luaL_optnumber(L, 2, 0); if (!str) return 0; decoded = lh_urldecode(str, len, &len, flags); if (!decoded) { lua_pushnil(L); return 1; } lua_pushlstring(L, decoded, len); free(decoded); return 1; } static int lh_L_header_attribute(lua_State *L) { size_t len; char *value; const char *hval = luaL_optlstring(L, 1, NULL, &len); const char *attr = luaL_optstring(L, 2, NULL); if (!hval) return 0; value = lh_header_attribute(hval, len, attr, &len); if (!value) { lua_pushnil(L); return 1; } lua_pushlstring(L, value, len); return 1; } /* * module tables * ------------------------------------------------------------------------- */ static const luaL_reg R_mpart[] = { { "parse", lh_L_mpart_parse }, { "__gc", lh_L_mpart__gc }, { } }; static const luaL_reg R_urldec[] = { { "parse", lh_L_urldec_parse }, { "__gc", lh_L_urldec__gc }, { } }; static const luaL_reg R[] = { { "multipart_parser", lh_L_mpart_new }, { "urlencoded_parser", lh_L_urldec_new }, { "urlencode", lh_L_urlencode }, { "urldecode", lh_L_urldecode }, { "header_attribute", lh_L_header_attribute }, { } }; LUALIB_API int luaopen_lucihttp(lua_State *L) { luaL_newmetatable(L, LUCIHTTP_MPART_META); luaL_register(L, NULL, R_mpart); lua_pushnumber(L, LH_MP_CB_BODY_BEGIN); lua_setfield(L, -2, "BODY_BEGIN"); lua_pushnumber(L, LH_MP_CB_PART_INIT); lua_setfield(L, -2, "PART_INIT"); lua_pushnumber(L, LH_MP_CB_HEADER_NAME); lua_setfield(L, -2, "HEADER_NAME"); lua_pushnumber(L, LH_MP_CB_HEADER_VALUE); lua_setfield(L, -2, "HEADER_VALUE"); lua_pushnumber(L, LH_MP_CB_PART_BEGIN); lua_setfield(L, -2, "PART_BEGIN"); lua_pushnumber(L, LH_MP_CB_PART_DATA); lua_setfield(L, -2, "PART_DATA"); lua_pushnumber(L, LH_MP_CB_PART_END); lua_setfield(L, -2, "PART_END"); lua_pushnumber(L, LH_MP_CB_BODY_END); lua_setfield(L, -2, "BODY_END"); lua_pushnumber(L, LH_MP_CB_EOF); lua_setfield(L, -2, "EOF"); lua_pushnumber(L, LH_MP_CB_ERROR); lua_setfield(L, -2, "ERROR"); lua_pushvalue(L, -1); lua_setfield(L, -1, "__index"); luaL_newmetatable(L, LUCIHTTP_URLDEC_META); luaL_register(L, NULL, R_urldec); lua_pushnumber(L, LH_UD_CB_TUPLE); lua_setfield(L, -2, "TUPLE"); lua_pushnumber(L, LH_UD_CB_NAME); lua_setfield(L, -2, "NAME"); lua_pushnumber(L, LH_UD_CB_VALUE); lua_setfield(L, -2, "VALUE"); lua_pushnumber(L, LH_UD_CB_EOF); lua_setfield(L, -2, "EOF"); lua_pushnumber(L, LH_UD_CB_ERROR); lua_setfield(L, -2, "ERROR"); lua_pushvalue(L, -1); lua_setfield(L, -1, "__index"); luaL_newmetatable(L, LUCIHTTP_META); luaL_register(L, LUCIHTTP_META, R); lua_pushnumber(L, LH_URLENCODE_FULL); lua_setfield(L, -2, "ENCODE_FULL"); lua_pushnumber(L, LH_URLENCODE_IF_NEEDED); lua_setfield(L, -2, "ENCODE_IF_NEEDED"); lua_pushnumber(L, LH_URLENCODE_SPACE_PLUS); lua_setfield(L, -2, "ENCODE_SPACE_PLUS"); lua_pushnumber(L, LH_URLDECODE_STRICT); lua_setfield(L, -2, "DECODE_STRICT"); lua_pushnumber(L, LH_URLDECODE_IF_NEEDED); lua_setfield(L, -2, "DECODE_IF_NEEDED"); lua_pushnumber(L, LH_URLDECODE_KEEP_PLUS); lua_setfield(L, -2, "DECODE_KEEP_PLUS"); lua_pushnumber(L, LH_URLDECODE_PLUS); lua_setfield(L, -2, "DECODE_PLUS"); lua_pushvalue(L, -1); lua_setfield(L, -1, "__index"); return 1; };