Remove building with NOCRYPTO option
[minix3.git] / libexec / httpd / lua-bozo.c
blob972c130bce73d4019ca167de2c60a3f4de84f838
1 /* $NetBSD: lua-bozo.c,v 1.12 2015/07/04 22:39:23 christos Exp $ */
3 /*
4 * Copyright (c) 2013 Marc Balmer <marc@msys.ch>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer and
14 * dedication in the documentation and/or other materials provided
15 * with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
31 /* this code implements dynamic content generation using Lua for bozohttpd */
33 #ifndef NO_LUA_SUPPORT
35 #include <sys/param.h>
37 #include <lua.h>
38 #include <lauxlib.h>
39 #include <lualib.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
44 #include "bozohttpd.h"
46 /* Lua binding for bozohttp */
48 #if LUA_VERSION_NUM < 502
49 #define LUA_HTTPDLIBNAME "httpd"
50 #endif
52 #define FORM "application/x-www-form-urlencoded"
54 static int
55 lua_flush(lua_State *L)
57 bozohttpd_t *httpd;
59 lua_pushstring(L, "bozohttpd");
60 lua_gettable(L, LUA_REGISTRYINDEX);
61 httpd = lua_touserdata(L, -1);
62 lua_pop(L, 1);
64 bozo_flush(httpd, stdout);
65 return 0;
68 static int
69 lua_print(lua_State *L)
71 bozohttpd_t *httpd;
73 lua_pushstring(L, "bozohttpd");
74 lua_gettable(L, LUA_REGISTRYINDEX);
75 httpd = lua_touserdata(L, -1);
76 lua_pop(L, 1);
78 bozo_printf(httpd, "%s\r\n", lua_tostring(L, -1));
79 return 0;
82 static int
83 lua_read(lua_State *L)
85 bozohttpd_t *httpd;
86 int n, len;
87 char *data;
89 lua_pushstring(L, "bozohttpd");
90 lua_gettable(L, LUA_REGISTRYINDEX);
91 httpd = lua_touserdata(L, -1);
92 lua_pop(L, 1);
94 len = luaL_checkinteger(L, -1);
95 data = bozomalloc(httpd, len + 1);
96 n = bozo_read(httpd, STDIN_FILENO, data, len);
97 if (n >= 0) {
98 data[n] = '\0';
99 lua_pushstring(L, data);
100 } else
101 lua_pushnil(L);
102 free(data);
103 return 1;
106 static int
107 lua_register_handler(lua_State *L)
109 lua_state_map_t *map;
110 lua_handler_t *handler;
111 bozohttpd_t *httpd;
113 lua_pushstring(L, "lua_state_map");
114 lua_gettable(L, LUA_REGISTRYINDEX);
115 map = lua_touserdata(L, -1);
116 lua_pushstring(L, "bozohttpd");
117 lua_gettable(L, LUA_REGISTRYINDEX);
118 httpd = lua_touserdata(L, -1);
119 lua_pop(L, 2);
121 luaL_checkstring(L, 1);
122 luaL_checktype(L, 2, LUA_TFUNCTION);
124 handler = bozomalloc(httpd, sizeof(lua_handler_t));
126 handler->name = bozostrdup(httpd, lua_tostring(L, 1));
127 handler->ref = luaL_ref(L, LUA_REGISTRYINDEX);
128 SIMPLEQ_INSERT_TAIL(&map->handlers, handler, h_next);
129 httpd->process_lua = 1;
130 return 0;
133 static int
134 lua_write(lua_State *L)
136 bozohttpd_t *httpd;
137 const char *data;
139 lua_pushstring(L, "bozohttpd");
140 lua_gettable(L, LUA_REGISTRYINDEX);
141 httpd = lua_touserdata(L, -1);
142 lua_pop(L, 1);
144 data = luaL_checkstring(L, -1);
145 lua_pushinteger(L, bozo_write(httpd, STDIN_FILENO, data, strlen(data)));
146 return 1;
149 static int
150 luaopen_httpd(lua_State *L)
152 struct luaL_Reg functions[] = {
153 { "flush", lua_flush },
154 { "print", lua_print },
155 { "read", lua_read },
156 { "register_handler", lua_register_handler },
157 { "write", lua_write },
158 { NULL, NULL }
160 #if LUA_VERSION_NUM >= 502
161 luaL_newlib(L, functions);
162 #else
163 luaL_register(L, LUA_HTTPDLIBNAME, functions);
164 #endif
165 lua_pushstring(L, "httpd 1.0.0");
166 lua_setfield(L, -2, "_VERSION");
167 return 1;
170 #if LUA_VERSION_NUM < 502
171 static void
172 lua_openlib(lua_State *L, const char *name, lua_CFunction fn)
174 lua_pushcfunction(L, fn);
175 lua_pushstring(L, name);
176 lua_call(L, 1, 0);
178 #endif
180 /* bozohttpd integration */
181 void
182 bozo_add_lua_map(bozohttpd_t *httpd, const char *prefix, const char *script)
184 lua_state_map_t *map;
186 map = bozomalloc(httpd, sizeof(lua_state_map_t));
187 map->prefix = bozostrdup(httpd, prefix);
188 if (*script == '/')
189 map->script = bozostrdup(httpd, script);
190 else {
191 char cwd[MAXPATHLEN], *path;
193 getcwd(cwd, sizeof(cwd) - 1);
194 asprintf(&path, "%s/%s", cwd, script);
195 map->script = path;
197 map->L = luaL_newstate();
198 if (map->L == NULL)
199 bozo_err(httpd, 1, "can't create Lua state");
200 SIMPLEQ_INIT(&map->handlers);
202 #if LUA_VERSION_NUM >= 502
203 luaL_openlibs(map->L);
204 lua_getglobal(map->L, "package");
205 lua_getfield(map->L, -1, "preload");
206 lua_pushcfunction(map->L, luaopen_httpd);
207 lua_setfield(map->L, -2, "httpd");
208 lua_pop(map->L, 2);
209 #else
210 lua_openlib(map->L, "", luaopen_base);
211 lua_openlib(map->L, LUA_LOADLIBNAME, luaopen_package);
212 lua_openlib(map->L, LUA_TABLIBNAME, luaopen_table);
213 lua_openlib(map->L, LUA_STRLIBNAME, luaopen_string);
214 lua_openlib(map->L, LUA_MATHLIBNAME, luaopen_math);
215 lua_openlib(map->L, LUA_OSLIBNAME, luaopen_os);
216 lua_openlib(map->L, LUA_IOLIBNAME, luaopen_io);
217 lua_openlib(map->L, LUA_HTTPDLIBNAME, luaopen_httpd);
218 #endif
219 lua_pushstring(map->L, "lua_state_map");
220 lua_pushlightuserdata(map->L, map);
221 lua_settable(map->L, LUA_REGISTRYINDEX);
223 lua_pushstring(map->L, "bozohttpd");
224 lua_pushlightuserdata(map->L, httpd);
225 lua_settable(map->L, LUA_REGISTRYINDEX);
227 if (luaL_loadfile(map->L, script))
228 bozo_err(httpd, 1, "failed to load script %s: %s", script,
229 lua_tostring(map->L, -1));
230 if (lua_pcall(map->L, 0, 0, 0))
231 bozo_err(httpd, 1, "failed to execute script %s: %s", script,
232 lua_tostring(map->L, -1));
233 SIMPLEQ_INSERT_TAIL(&httpd->lua_states, map, s_next);
236 static void
237 lua_env(lua_State *L, const char *name, const char *value)
239 lua_pushstring(L, value);
240 lua_setfield(L, -2, name);
243 /* decode query string */
244 static void
245 lua_url_decode(lua_State *L, char *s)
247 char *v, *p, *val, *q;
248 char buf[3];
249 int c;
251 v = strchr(s, '=');
252 if (v == NULL)
253 return;
254 *v++ = '\0';
255 val = malloc(strlen(v) + 1);
256 if (val == NULL)
257 return;
259 for (p = v, q = val; *p; p++) {
260 switch (*p) {
261 case '%':
262 if (*(p + 1) == '\0' || *(p + 2) == '\0') {
263 free(val);
264 return;
266 buf[0] = *++p;
267 buf[1] = *++p;
268 buf[2] = '\0';
269 sscanf(buf, "%2x", &c);
270 *q++ = (char)c;
271 break;
272 case '+':
273 *q++ = ' ';
274 break;
275 default:
276 *q++ = *p;
279 *q = '\0';
280 lua_pushstring(L, val);
281 lua_setfield(L, -2, s);
282 free(val);
285 static void
286 lua_decode_query(lua_State *L, char *query)
288 char *s;
290 s = strtok(query, "&");
291 while (s) {
292 lua_url_decode(L, s);
293 s = strtok(NULL, "&");
298 bozo_process_lua(bozo_httpreq_t *request)
300 bozohttpd_t *httpd = request->hr_httpd;
301 lua_state_map_t *map;
302 lua_handler_t *hndlr;
303 int n, ret, length;
304 char date[40];
305 bozoheaders_t *headp;
306 char *s, *query, *uri, *file, *command, *info, *content;
307 const char *type, *clen;
308 char *prefix, *handler, *p;
309 int rv = 0;
311 if (!httpd->process_lua)
312 return 0;
314 info = NULL;
315 query = NULL;
316 prefix = NULL;
317 uri = request->hr_oldfile ? request->hr_oldfile : request->hr_file;
319 if (*uri == '/') {
320 file = bozostrdup(httpd, uri);
321 if (file == NULL)
322 goto out;
323 prefix = bozostrdup(httpd, &uri[1]);
324 } else {
325 if (asprintf(&file, "/%s", uri) < 0)
326 goto out;
327 prefix = bozostrdup(httpd, uri);
329 if (prefix == NULL)
330 goto out;
332 if (request->hr_query && request->hr_query[0])
333 query = bozostrdup(httpd, request->hr_query);
335 p = strchr(prefix, '/');
336 if (p == NULL)
337 goto out;
338 *p++ = '\0';
339 handler = p;
340 if (!*handler)
341 goto out;
342 p = strchr(handler, '/');
343 if (p != NULL)
344 *p++ = '\0';
346 command = file + 1;
347 if ((s = strchr(command, '/')) != NULL) {
348 info = bozostrdup(httpd, s);
349 *s = '\0';
352 type = request->hr_content_type;
353 clen = request->hr_content_length;
355 SIMPLEQ_FOREACH(map, &httpd->lua_states, s_next) {
356 if (strcmp(map->prefix, prefix))
357 continue;
359 SIMPLEQ_FOREACH(hndlr, &map->handlers, h_next) {
360 if (strcmp(hndlr->name, handler))
361 continue;
363 lua_rawgeti(map->L, LUA_REGISTRYINDEX, hndlr->ref);
365 /* Create the "environment" */
366 lua_newtable(map->L);
367 lua_env(map->L, "SERVER_NAME",
368 BOZOHOST(httpd, request));
369 lua_env(map->L, "GATEWAY_INTERFACE", "Luigi/1.0");
370 lua_env(map->L, "SERVER_PROTOCOL", request->hr_proto);
371 lua_env(map->L, "REQUEST_METHOD",
372 request->hr_methodstr);
373 lua_env(map->L, "SCRIPT_PREFIX", map->prefix);
374 lua_env(map->L, "SCRIPT_NAME", file);
375 lua_env(map->L, "HANDLER_NAME", hndlr->name);
376 lua_env(map->L, "SCRIPT_FILENAME", map->script);
377 lua_env(map->L, "SERVER_SOFTWARE",
378 httpd->server_software);
379 lua_env(map->L, "REQUEST_URI", uri);
380 lua_env(map->L, "DATE_GMT",
381 bozo_http_date(date, sizeof(date)));
382 if (query && *query)
383 lua_env(map->L, "QUERY_STRING", query);
384 if (info && *info)
385 lua_env(map->L, "PATH_INFO", info);
386 if (type && *type)
387 lua_env(map->L, "CONTENT_TYPE", type);
388 if (clen && *clen)
389 lua_env(map->L, "CONTENT_LENGTH", clen);
390 if (request->hr_serverport && *request->hr_serverport)
391 lua_env(map->L, "SERVER_PORT",
392 request->hr_serverport);
393 if (request->hr_remotehost && *request->hr_remotehost)
394 lua_env(map->L, "REMOTE_HOST",
395 request->hr_remotehost);
396 if (request->hr_remoteaddr && *request->hr_remoteaddr)
397 lua_env(map->L, "REMOTE_ADDR",
398 request->hr_remoteaddr);
400 /* Pass the headers in a separate table */
401 lua_newtable(map->L);
402 SIMPLEQ_FOREACH(headp, &request->hr_headers, h_next)
403 lua_env(map->L, headp->h_header,
404 headp->h_value);
406 /* Pass the query variables */
407 if ((query && *query) ||
408 (type && *type && !strcmp(type, FORM))) {
409 lua_newtable(map->L);
410 if (query && *query)
411 lua_decode_query(map->L, query);
412 if (type && *type && !strcmp(type, FORM)) {
413 if (clen && *clen && atol(clen) > 0) {
414 length = atol(clen);
415 content = bozomalloc(httpd,
416 length + 1);
417 n = bozo_read(httpd,
418 STDIN_FILENO, content,
419 length);
420 if (n >= 0) {
421 content[n] = '\0';
422 lua_decode_query(map->L,
423 content);
424 } else {
425 lua_pop(map->L, 1);
426 lua_pushnil(map->L);
428 free(content);
431 } else
432 lua_pushnil(map->L);
434 ret = lua_pcall(map->L, 3, 0, 0);
435 if (ret)
436 printf("<br>Lua error: %s\n",
437 lua_tostring(map->L, -1));
438 bozo_flush(httpd, stdout);
439 rv = 1;
440 goto out;
443 out:
444 free(prefix);
445 free(uri);
446 free(info);
447 free(query);
448 free(file);
449 return rv;
452 #endif /* NO_LUA_SUPPORT */