blob: 5adb128153e4ed42042f261ff470af95ca8ce68a [file] [log] [blame]
/*
* Lua safe functions
*
* Copyright 2015-2016 Thierry Fournier <tfournier@arpalert.org>
*
* 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 <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#include <common/time.h>
#include <types/hlua.h>
/* 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;
}