blob: 5adb128153e4ed42042f261ff470af95ca8ce68a [file] [log] [blame]
Thierry Fourniere726b142016-02-11 17:57:57 +01001/*
2 * Lua safe functions
3 *
4 * Copyright 2015-2016 Thierry Fournier <tfournier@arpalert.org>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 *
12 * All the functions in this file runs with a Lua stack, and can
Thierry Fournierfb0b5462016-01-21 09:28:58 +010013 * return with a longjmp. All of these function must be launched
14 * in an environment able to catch a longjmp, otherwise a
15 * critical error can be raised.
16 */
17#include <lauxlib.h>
18#include <lua.h>
19#include <lualib.h>
20
Thierry Fournierb1f46562016-01-21 09:46:15 +010021#include <common/time.h>
22
Thierry Fournier49d48422016-02-19 12:09:29 +010023#include <types/hlua.h>
24
Thierry Fournier1de16592016-01-27 09:49:07 +010025/* Contains the class reference of the concat object. */
26static int class_concat_ref;
27
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +010028/* Return an object of the expected type, or throws an error. */
29void *hlua_checkudata(lua_State *L, int ud, int class_ref)
30{
31 void *p;
Thierry Fournier53518272016-01-27 10:34:09 +010032 int ret;
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +010033
34 /* Check if the stack entry is an array. */
35 if (!lua_istable(L, ud))
Thierry Fournier53518272016-01-27 10:34:09 +010036 luaL_argerror(L, ud, NULL);
37
38 /* pop the metatable of the referencecd object. */
39 if (!lua_getmetatable(L, ud))
40 luaL_argerror(L, ud, NULL);
41
42 /* pop the expected metatable. */
43 lua_rawgeti(L, LUA_REGISTRYINDEX, class_ref);
44
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +010045 /* Check if the metadata have the expected type. */
Thierry Fournier53518272016-01-27 10:34:09 +010046 ret = lua_rawequal(L, -1, -2);
47 lua_pop(L, 2);
48 if (!ret)
49 luaL_argerror(L, ud, NULL);
50
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +010051 /* Push on the stack at the entry [0] of the table. */
52 lua_rawgeti(L, ud, 0);
Thierry Fournier53518272016-01-27 10:34:09 +010053
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +010054 /* Check if this entry is userdata. */
55 p = lua_touserdata(L, -1);
56 if (!p)
Thierry Fournier53518272016-01-27 10:34:09 +010057 luaL_argerror(L, ud, NULL);
58
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +010059 /* Remove the entry returned by lua_rawgeti(). */
60 lua_pop(L, 1);
Thierry Fournier53518272016-01-27 10:34:09 +010061
Thierry Fournier9e7e3ea2016-01-27 09:55:30 +010062 /* Return the associated struct. */
63 return p;
64}
65
Thierry Fournierb1f46562016-01-21 09:46:15 +010066/* This function return the current date at epoch format in milliseconds. */
67int hlua_now(lua_State *L)
68{
69 lua_newtable(L);
70 lua_pushstring(L, "sec");
71 lua_pushinteger(L, now.tv_sec);
72 lua_rawset(L, -3);
73 lua_pushstring(L, "usec");
74 lua_pushinteger(L, now.tv_usec);
75 lua_rawset(L, -3);
76 return 1;
77}
78
Thierry Fournier1550d5d2016-01-21 09:35:41 +010079/* This functions expects a Lua string as HTTP date, parse it and
80 * returns an integer containing the epoch format of the date, or
81 * nil if the parsing fails.
82 */
83static int hlua_parse_date(lua_State *L, int (*fcn)(const char *, int, struct tm*))
84{
85 const char *str;
86 size_t len;
87 struct tm tm;
88 time_t time;
89
90 str = luaL_checklstring(L, 1, &len);
91
92 if (!fcn(str, len, &tm)) {
93 lua_pushnil(L);
94 return 1;
95 }
96
97 /* This function considers the content of the broken-down time
98 * is exprimed in the UTC timezone. timegm don't care about
99 * the gnu variable tm_gmtoff. If gmtoff is set, or if you know
100 * the timezone from the broken-down time, it must be fixed
101 * after the conversion.
102 */
103 time = timegm(&tm);
104 if (time == -1) {
105 lua_pushnil(L);
106 return 1;
107 }
108
109 lua_pushinteger(L, (int)time);
110 return 1;
111}
112static int hlua_http_date(lua_State *L)
113{
114 return hlua_parse_date(L, parse_http_date);
115}
116static int hlua_imf_date(lua_State *L)
117{
118 return hlua_parse_date(L, parse_imf_date);
119}
120static int hlua_rfc850_date(lua_State *L)
121{
122 return hlua_parse_date(L, parse_rfc850_date);
123}
124static int hlua_asctime_date(lua_State *L)
125{
126 return hlua_parse_date(L, parse_asctime_date);
127}
128
Thierry Fournierfb0b5462016-01-21 09:28:58 +0100129static void hlua_array_add_fcn(lua_State *L, const char *name,
130 int (*function)(lua_State *L))
131{
132 lua_pushstring(L, name);
133 lua_pushcclosure(L, function, 0);
134 lua_rawset(L, -3);
135}
136
Thierry Fournier49d48422016-02-19 12:09:29 +0100137static struct hlua_concat *hlua_check_concat(lua_State *L, int ud)
Thierry Fournier1de16592016-01-27 09:49:07 +0100138{
Thierry Fournier49d48422016-02-19 12:09:29 +0100139 return (struct hlua_concat *)(hlua_checkudata(L, ud, class_concat_ref));
Thierry Fournier1de16592016-01-27 09:49:07 +0100140}
141
142static int hlua_concat_add(lua_State *L)
143{
Thierry Fournier49d48422016-02-19 12:09:29 +0100144 struct hlua_concat *b;
145 char *buffer;
146 char *new;
Thierry Fournier1de16592016-01-27 09:49:07 +0100147 const char *str;
148 size_t l;
149
150 /* First arg must be a concat object. */
151 b = hlua_check_concat(L, 1);
152
153 /* Second arg must be a string. */
154 str = luaL_checklstring(L, 2, &l);
155
Thierry Fournier49d48422016-02-19 12:09:29 +0100156 /* Get the buffer. */
157 lua_rawgeti(L, 1, 1);
158 buffer = lua_touserdata(L, -1);
159 lua_pop(L, 1);
160
161 /* Update the buffer size if it s required. The old buffer
162 * is crushed by the new in the object array, so it will
163 * be deleted by the GC.
164 * Note that in the first loop, the "new" variable is only
165 * used as a flag.
166 */
167 new = NULL;
168 while (b->size - b->len < l) {
169 b->size += HLUA_CONCAT_BLOCSZ;
170 new = buffer;
171 }
172 if (new) {
173 new = lua_newuserdata(L, b->size);
174 memcpy(new, buffer, b->len);
175 lua_rawseti(L, 1, 1);
176 buffer = new;
177 }
178
179 /* Copy string, and update metadata. */
180 memcpy(buffer + b->len, str, l);
181 b->len += l;
Thierry Fournier1de16592016-01-27 09:49:07 +0100182 return 0;
183}
184
185static int hlua_concat_dump(lua_State *L)
186{
Thierry Fournier49d48422016-02-19 12:09:29 +0100187 struct hlua_concat *b;
188 char *buffer;
Thierry Fournier1de16592016-01-27 09:49:07 +0100189
190 /* First arg must be a concat object. */
191 b = hlua_check_concat(L, 1);
192
Thierry Fournier49d48422016-02-19 12:09:29 +0100193 /* Get the buffer. */
194 lua_rawgeti(L, 1, 1);
195 buffer = lua_touserdata(L, -1);
196 lua_pop(L, 1);
197
Thierry Fournier1de16592016-01-27 09:49:07 +0100198 /* Push the soncatenated strng in the stack. */
Thierry Fournier49d48422016-02-19 12:09:29 +0100199 lua_pushlstring(L, buffer, b->len);
Thierry Fournier1de16592016-01-27 09:49:07 +0100200 return 1;
201}
202
203int hlua_concat_new(lua_State *L)
204{
Thierry Fournier49d48422016-02-19 12:09:29 +0100205 struct hlua_concat *b;
Thierry Fournier1de16592016-01-27 09:49:07 +0100206
207 lua_newtable(L);
Thierry Fournier49d48422016-02-19 12:09:29 +0100208 b = (struct hlua_concat *)lua_newuserdata(L, sizeof(*b));
209 b->size = HLUA_CONCAT_BLOCSZ;
210 b->len = 0;
Thierry Fournier1de16592016-01-27 09:49:07 +0100211 lua_rawseti(L, -2, 0);
Thierry Fournier49d48422016-02-19 12:09:29 +0100212 lua_newuserdata(L, HLUA_CONCAT_BLOCSZ);
213 lua_rawseti(L, -2, 1);
Thierry Fournier1de16592016-01-27 09:49:07 +0100214
215 lua_rawgeti(L, LUA_REGISTRYINDEX, class_concat_ref);
216 lua_setmetatable(L, -2);
217
Thierry Fournier1de16592016-01-27 09:49:07 +0100218 return 1;
219}
220
221static int concat_tostring(lua_State *L)
222{
223 const void *ptr = lua_topointer(L, 1);
224 lua_pushfstring(L, "Concat object: %p", ptr);
225 return 1;
226}
227
228static int hlua_concat_init(lua_State *L)
229{
230 /* Creates the buffered concat object. */
231 lua_newtable(L);
232
233 lua_pushstring(L, "__tostring");
234 lua_pushcclosure(L, concat_tostring, 0);
235 lua_settable(L, -3);
236
237 lua_pushstring(L, "__index"); /* Creates the index entry. */
238 lua_newtable(L); /* The "__index" content. */
239
240 lua_pushstring(L, "add");
241 lua_pushcclosure(L, hlua_concat_add, 0);
242 lua_settable(L, -3);
243
244 lua_pushstring(L, "dump");
245 lua_pushcclosure(L, hlua_concat_dump, 0);
246 lua_settable(L, -3);
247
248 lua_settable(L, -3); /* Sets the __index entry. */
249 class_concat_ref = luaL_ref(L, LUA_REGISTRYINDEX);
250
251 return 1;
252}
253
Thierry Fournierfb0b5462016-01-21 09:28:58 +0100254int hlua_fcn_reg_core_fcn(lua_State *L)
255{
Thierry Fournier1de16592016-01-27 09:49:07 +0100256 if (!hlua_concat_init(L))
257 return 0;
258
Thierry Fournierb1f46562016-01-21 09:46:15 +0100259 hlua_array_add_fcn(L, "now", hlua_now);
Thierry Fournier1550d5d2016-01-21 09:35:41 +0100260 hlua_array_add_fcn(L, "http_date", hlua_http_date);
261 hlua_array_add_fcn(L, "imf_date", hlua_imf_date);
262 hlua_array_add_fcn(L, "rfc850_date", hlua_rfc850_date);
263 hlua_array_add_fcn(L, "asctime_date", hlua_asctime_date);
Thierry Fournier1de16592016-01-27 09:49:07 +0100264 hlua_array_add_fcn(L, "concat", hlua_concat_new);
Thierry Fournier1550d5d2016-01-21 09:35:41 +0100265 return 5;
Thierry Fournierfb0b5462016-01-21 09:28:58 +0100266}