/* * Lua safe functions * * Copyright 2015-2016 Thierry Fournier * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * * All the functions in this file runs with a Lua stack, and can * return with a longjmp. All of these function must be launched * in an environment able to catch a longjmp, otherwise a * critical error can be raised. */ #include #include #include #include #include /* Contains the class reference of the concat object. */ static int class_concat_ref; /* Return an object of the expected type, or throws an error. */ void *hlua_checkudata(lua_State *L, int ud, int class_ref) { void *p; int ret; /* Check if the stack entry is an array. */ if (!lua_istable(L, ud)) luaL_argerror(L, ud, NULL); /* pop the metatable of the referencecd object. */ if (!lua_getmetatable(L, ud)) luaL_argerror(L, ud, NULL); /* pop the expected metatable. */ lua_rawgeti(L, LUA_REGISTRYINDEX, class_ref); /* Check if the metadata have the expected type. */ ret = lua_rawequal(L, -1, -2); lua_pop(L, 2); if (!ret) luaL_argerror(L, ud, NULL); /* Push on the stack at the entry [0] of the table. */ lua_rawgeti(L, ud, 0); /* Check if this entry is userdata. */ p = lua_touserdata(L, -1); if (!p) luaL_argerror(L, ud, NULL); /* Remove the entry returned by lua_rawgeti(). */ lua_pop(L, 1); /* Return the associated struct. */ return p; } /* This function return the current date at epoch format in milliseconds. */ int hlua_now(lua_State *L) { lua_newtable(L); lua_pushstring(L, "sec"); lua_pushinteger(L, now.tv_sec); lua_rawset(L, -3); lua_pushstring(L, "usec"); lua_pushinteger(L, now.tv_usec); lua_rawset(L, -3); return 1; } /* This functions expects a Lua string as HTTP date, parse it and * returns an integer containing the epoch format of the date, or * nil if the parsing fails. */ static int hlua_parse_date(lua_State *L, int (*fcn)(const char *, int, struct tm*)) { const char *str; size_t len; struct tm tm; time_t time; str = luaL_checklstring(L, 1, &len); if (!fcn(str, len, &tm)) { lua_pushnil(L); return 1; } /* This function considers the content of the broken-down time * is exprimed in the UTC timezone. timegm don't care about * the gnu variable tm_gmtoff. If gmtoff is set, or if you know * the timezone from the broken-down time, it must be fixed * after the conversion. */ time = timegm(&tm); if (time == -1) { lua_pushnil(L); return 1; } lua_pushinteger(L, (int)time); return 1; } static int hlua_http_date(lua_State *L) { return hlua_parse_date(L, parse_http_date); } static int hlua_imf_date(lua_State *L) { return hlua_parse_date(L, parse_imf_date); } static int hlua_rfc850_date(lua_State *L) { return hlua_parse_date(L, parse_rfc850_date); } static int hlua_asctime_date(lua_State *L) { return hlua_parse_date(L, parse_asctime_date); } static void hlua_array_add_fcn(lua_State *L, const char *name, int (*function)(lua_State *L)) { lua_pushstring(L, name); lua_pushcclosure(L, function, 0); lua_rawset(L, -3); } static struct hlua_concat *hlua_check_concat(lua_State *L, int ud) { return (struct hlua_concat *)(hlua_checkudata(L, ud, class_concat_ref)); } static int hlua_concat_add(lua_State *L) { struct hlua_concat *b; char *buffer; char *new; const char *str; size_t l; /* First arg must be a concat object. */ b = hlua_check_concat(L, 1); /* Second arg must be a string. */ str = luaL_checklstring(L, 2, &l); /* Get the buffer. */ lua_rawgeti(L, 1, 1); buffer = lua_touserdata(L, -1); lua_pop(L, 1); /* Update the buffer size if it s required. The old buffer * is crushed by the new in the object array, so it will * be deleted by the GC. * Note that in the first loop, the "new" variable is only * used as a flag. */ new = NULL; while (b->size - b->len < l) { b->size += HLUA_CONCAT_BLOCSZ; new = buffer; } if (new) { new = lua_newuserdata(L, b->size); memcpy(new, buffer, b->len); lua_rawseti(L, 1, 1); buffer = new; } /* Copy string, and update metadata. */ memcpy(buffer + b->len, str, l); b->len += l; return 0; } static int hlua_concat_dump(lua_State *L) { struct hlua_concat *b; char *buffer; /* First arg must be a concat object. */ b = hlua_check_concat(L, 1); /* Get the buffer. */ lua_rawgeti(L, 1, 1); buffer = lua_touserdata(L, -1); lua_pop(L, 1); /* Push the soncatenated strng in the stack. */ lua_pushlstring(L, buffer, b->len); return 1; } int hlua_concat_new(lua_State *L) { struct hlua_concat *b; lua_newtable(L); b = (struct hlua_concat *)lua_newuserdata(L, sizeof(*b)); b->size = HLUA_CONCAT_BLOCSZ; b->len = 0; lua_rawseti(L, -2, 0); lua_newuserdata(L, HLUA_CONCAT_BLOCSZ); lua_rawseti(L, -2, 1); lua_rawgeti(L, LUA_REGISTRYINDEX, class_concat_ref); lua_setmetatable(L, -2); return 1; } static int concat_tostring(lua_State *L) { const void *ptr = lua_topointer(L, 1); lua_pushfstring(L, "Concat object: %p", ptr); return 1; } static int hlua_concat_init(lua_State *L) { /* Creates the buffered concat object. */ lua_newtable(L); lua_pushstring(L, "__tostring"); lua_pushcclosure(L, concat_tostring, 0); lua_settable(L, -3); lua_pushstring(L, "__index"); /* Creates the index entry. */ lua_newtable(L); /* The "__index" content. */ lua_pushstring(L, "add"); lua_pushcclosure(L, hlua_concat_add, 0); lua_settable(L, -3); lua_pushstring(L, "dump"); lua_pushcclosure(L, hlua_concat_dump, 0); lua_settable(L, -3); lua_settable(L, -3); /* Sets the __index entry. */ class_concat_ref = luaL_ref(L, LUA_REGISTRYINDEX); return 1; } int hlua_fcn_reg_core_fcn(lua_State *L) { if (!hlua_concat_init(L)) return 0; hlua_array_add_fcn(L, "now", hlua_now); hlua_array_add_fcn(L, "http_date", hlua_http_date); hlua_array_add_fcn(L, "imf_date", hlua_imf_date); hlua_array_add_fcn(L, "rfc850_date", hlua_rfc850_date); hlua_array_add_fcn(L, "asctime_date", hlua_asctime_date); hlua_array_add_fcn(L, "concat", hlua_concat_new); return 5; }