summaryrefslogtreecommitdiffhomepage
path: root/libs/sgi-webuci/src
diff options
context:
space:
mode:
Diffstat (limited to 'libs/sgi-webuci/src')
-rw-r--r--libs/sgi-webuci/src/cgi.c530
-rw-r--r--libs/sgi-webuci/src/luci.c216
2 files changed, 746 insertions, 0 deletions
diff --git a/libs/sgi-webuci/src/cgi.c b/libs/sgi-webuci/src/cgi.c
new file mode 100644
index 000000000..f8bf4045e
--- /dev/null
+++ b/libs/sgi-webuci/src/cgi.c
@@ -0,0 +1,530 @@
+/*
+ * CGI routines for luci
+ * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Based on code from cgilib:
+ *
+ * cgi.c - Some simple routines for CGI programming
+ * Copyright (c) 1996-9,2007,8 Martin Schulze <joey@infodrom.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <strings.h>
+#include <ctype.h>
+#include <lauxlib.h>
+
+#define BUFSIZE 128
+
+static char *
+cgiGetLine (FILE *stream)
+{
+ static char *line = NULL;
+ static size_t size = 0;
+ char buf[BUFSIZE];
+ char *cp;
+
+ if (!line) {
+ if ((line = (char *)malloc (BUFSIZE)) == NULL)
+ return NULL;
+ size = BUFSIZE;
+ }
+ line[0] = '\0';
+
+ while (!feof (stream)) {
+ if ((cp = fgets (buf, sizeof (buf), stream)) == NULL)
+ return NULL;
+
+ if (strlen(line)+strlen(buf)+1 > size) {
+ if ((cp = (char *)realloc (line, size + BUFSIZE)) == NULL)
+ return line;
+ size += BUFSIZE;
+ line = cp;
+ }
+
+ strcat (line, buf);
+ if (line[strlen(line)-1] == '\n') {
+ line[strlen(line)-1] = '\0';
+ if (line[strlen(line)-1] == '\r')
+ line[strlen(line)-1] = '\0';
+ return line;
+ }
+ }
+
+ return NULL;
+}
+
+
+static const char *
+luci_getenv(lua_State *L, const char *name)
+{
+ const char *ret;
+
+ lua_getfield(L, lua_upvalueindex(2), name);
+ ret = lua_tostring(L, -1);
+ lua_pop(L, 1);
+ return ret;
+}
+
+static void
+luci_setvar(lua_State *L, const char *name, const char *value, bool append)
+{
+ /* Check if there is an existing value already */
+ lua_getfield(L, lua_upvalueindex(1), name);
+ if (lua_isnil(L, -1)) {
+ /* nope, we're safe - add a new one */
+ lua_pushstring(L, value);
+ lua_setfield(L, lua_upvalueindex(1), name);
+ } else if (lua_istable(L, -1) && append) {
+ /* it's a table already, but appending is requested
+ * take the last element and append the new string to it */
+ int tlast = lua_objlen(L, -1);
+ lua_rawgeti(L, -1, tlast);
+ lua_pushstring(L, value);
+ lua_pushstring(L, "\n");
+ lua_concat(L, 3);
+ lua_rawseti(L, -2, tlast);
+ } else if (lua_istable(L, -1)) {
+ /* it's a table, which means we already have two
+ * or more entries, add the next one */
+
+ int tnext = lua_objlen(L, -1) + 1; /* next entry */
+
+ lua_pushstring(L, value);
+ luaL_setn(L, -2, tnext);
+ lua_rawseti(L, -2, tnext);
+ } else if (lua_isstring(L, -1) && append) {
+ /* append the new string to the existing variable */
+ lua_pushstring(L, value);
+ lua_pushstring(L, "\n");
+ lua_concat(L, 3);
+ lua_setfield(L, lua_upvalueindex(1), name);
+ } else if (lua_isstring(L, -1)) {
+ /* we're trying to add a variable that already has
+ * a string value. convert the string value to a
+ * table and add our new value to the table as well
+ */
+ lua_createtable(L, 2, 0);
+ lua_pushvalue(L, -2); /* copy of the initial string value */
+ lua_rawseti(L, -2, 1);
+
+ lua_pushstring(L, value);
+ lua_rawseti(L, -2, 2);
+ lua_setfield(L, lua_upvalueindex(1), name);
+ } else {
+ luaL_error(L, "Invalid table entry type for index '%s'", name);
+ }
+}
+
+char *cgiDecodeString (char *text)
+{
+ char *cp, *xp;
+
+ for (cp=text,xp=text; *cp; cp++) {
+ if (*cp == '%') {
+ if (strchr("0123456789ABCDEFabcdef", *(cp+1))
+ && strchr("0123456789ABCDEFabcdef", *(cp+2))) {
+ if (islower(*(cp+1)))
+ *(cp+1) = toupper(*(cp+1));
+ if (islower(*(cp+2)))
+ *(cp+2) = toupper(*(cp+2));
+ *(xp) = (*(cp+1) >= 'A' ? *(cp+1) - 'A' + 10 : *(cp+1) - '0' ) * 16
+ + (*(cp+2) >= 'A' ? *(cp+2) - 'A' + 10 : *(cp+2) - '0');
+ xp++;cp+=2;
+ }
+ } else {
+ *(xp++) = *cp;
+ }
+ }
+ memset(xp, 0, cp-xp);
+ return text;
+}
+
+#if 0
+/* cgiReadFile()
+ *
+ * Read and save a file fro a multipart request
+ */
+#include <errno.h>
+char *cgiReadFile (FILE *stream, char *boundary)
+{
+ char *crlfboundary, *buf;
+ size_t boundarylen;
+ int c;
+ unsigned int pivot;
+ char *cp;
+ char template[]= "/tmp/cgilibXXXXXX";
+ FILE *tmpfile;
+ int fd;
+
+ boundarylen = strlen(boundary)+3;
+ if ((crlfboundary = (char *)malloc (boundarylen)) == NULL)
+ return NULL;
+ sprintf (crlfboundary, "\r\n%s", boundary);
+
+ if ((buf = (char *)malloc (boundarylen)) == NULL) {
+ free (crlfboundary);
+ return NULL;
+ }
+ memset (buf, 0, boundarylen);
+ pivot = 0;
+
+ if ((fd = mkstemp (template)) == -1) {
+ free (crlfboundary);
+ free (buf);
+ return NULL;
+ }
+
+ if ((tmpfile = fdopen (fd, "w")) == NULL) {
+ free (crlfboundary);
+ free (buf);
+ unlink (template);
+ return NULL;
+ }
+
+ while (!feof (stream)) {
+ c = fgetc (stream);
+
+ if (c == 0) {
+ if (strlen (buf)) {
+ for (cp=buf; *cp; cp++)
+ putc (*cp, tmpfile);
+ memset (buf, 0, boundarylen);
+ pivot = 0;
+ }
+ putc (c, tmpfile);
+ continue;
+ }
+
+ if (strlen (buf)) {
+ if (crlfboundary[pivot+1] == c) {
+ buf[++pivot] = c;
+
+ if (strlen (buf) == strlen (crlfboundary))
+ break;
+ else
+ continue;
+ } else {
+ for (cp=buf; *cp; cp++)
+ putc (*cp, tmpfile);
+ memset (buf, 0, boundarylen);
+ pivot = 0;
+ }
+ }
+
+ if (crlfboundary[0] == c) {
+ buf[0] = c;
+ } else {
+ fputc (c, tmpfile);
+ }
+ }
+
+ if (!feof (stream))
+ fgets (buf, boundarylen, stream);
+
+ fclose (tmpfile);
+
+ free (crlfboundary);
+ free (buf);
+
+ return strdup (template);
+}
+#endif
+
+/*
+ * Decode multipart/form-data
+ */
+#define MULTIPART_DELTA 5
+void luci_parse_multipart (lua_State *L, char *boundary)
+{
+ char *line;
+ char *cp, *xp;
+ char *name = NULL, *type = NULL;
+ char *fname = NULL;
+ int header = 1;
+ bool append = false;
+
+ while ((line = cgiGetLine (stdin)) != NULL) {
+ if (!strncmp (line, boundary, strlen(boundary))) {
+ header = 1;
+ if (name)
+ free(name);
+ if (type)
+ free(type);
+ name = NULL;
+ type = NULL;
+ append = false;
+ } else if (header && !name && !strncasecmp (line, "Content-Disposition: form-data; ", 32)) {
+ if ((cp = strstr (line, "name=\"")) == NULL)
+ continue;
+ cp += 6;
+ if ((xp = strchr (cp, '\"')) == NULL)
+ continue;
+ name = malloc(xp-cp + 1);
+ strncpy(name, cp, xp-cp);
+ name[xp-cp] = 0;
+ cgiDecodeString (name);
+
+ if ((cp = strstr (line, "filename=\"")) == NULL)
+ continue;
+ cp += 10;
+ if ((xp = strchr (cp, '\"')) == NULL)
+ continue;
+ fname = malloc(xp-cp + 1);
+ strncpy(fname, cp, xp-cp);
+ fname[xp-cp] = 0;
+ cgiDecodeString (fname);
+ } else if (header && !type && !strncasecmp (line, "Content-Type: ", 14)) {
+ cp = line + 14;
+ type = strdup (cp);
+ } else if (header) {
+ if (!strlen(line)) {
+ header = 0;
+
+ if (fname) {
+#if 0
+ header = 1;
+ tmpfile = cgiReadFile (stdin, boundary);
+
+ if (!tmpfile) {
+ free (name);
+ free (fname);
+ if (type)
+ free (type);
+ name = fname = type = NULL;
+ }
+
+ cgiDebugOutput (2, "Wrote %s (%s) to file: %s", name, fname, tmpfile);
+
+ if (!strlen (fname)) {
+ cgiDebugOutput (3, "Found empty filename, removing");
+ unlink (tmpfile);
+ free (tmpfile);
+ free (name);
+ free (fname);
+ if (type)
+ free (type);
+ name = fname = type = NULL;
+ } else {
+ if ((file = (s_file *)malloc (sizeof (s_file))) == NULL) {
+ cgiDebugOutput (3, "malloc failed, ignoring %s=%s", name, fname);
+ unlink (tmpfile);
+ free (tmpfile);
+ free (name);
+ free (fname);
+ if (type)
+ free (type);
+ name = fname = type = NULL;
+ continue;
+ }
+
+ file->name = name;
+ file->type = type;
+ file->tmpfile = tmpfile;
+ if ((cp = rindex (fname, '/')) == NULL)
+ file->filename = fname;
+ else {
+ file->filename = strdup (++cp);
+ free (fname);
+ }
+ name = type = fname = NULL;
+
+ if (!files) {
+ if ((files = (s_file **)malloc(2*sizeof (s_file *))) == NULL) {
+ cgiDebugOutput (3, "malloc failed, ignoring %s=%s", name, fname);
+ unlink (tmpfile);
+ free (tmpfile);
+ free (name);
+ name = NULL;
+ if (type) {
+ free (type);
+ type = NULL;
+ }
+ free (file->filename);
+ free (file);
+ continue;
+ }
+ memset (files, 0, 2*sizeof (s_file *));
+ index = 0;
+ } else {
+ for (index=0; files[index]; index++);
+ if ((tmpf = (s_file **)realloc(files, (index+2)*sizeof (s_file *))) == NULL) {
+ cgiDebugOutput (3, "realloc failed, ignoring %s=%s", name, fname);
+ unlink (tmpfile);
+ free (tmpfile);
+ free (name);
+ if (type)
+ free (type);
+ free (file->filename);
+ free (file);
+ name = type = fname = NULL;
+ continue;
+ }
+ files = tmpf;
+ memset (files + index, 0, 2*sizeof (s_file *));
+ }
+ files[index] = file;
+ }
+#else
+ free(fname);
+ fname = NULL;
+#endif
+ }
+ }
+ } else {
+ if (!name)
+ return;
+
+ cgiDecodeString(line);
+ luci_setvar(L, name, line, append);
+ if (!append) /* beginning of variable contents */
+ append = true;
+ }
+ }
+}
+
+/* parse the request header and store variables
+ * in the array supplied as function argument 1 on the stack
+ */
+int luci_parse_header (lua_State *L)
+{
+ int length;
+ char *line = NULL;
+ int numargs;
+ char *cp = NULL, *ip = NULL, *esp = NULL;
+ const char *ct, *il;
+ int i;
+
+ if (!lua_istable(L, lua_upvalueindex(1)))
+ luaL_error(L, "Invalid argument");
+
+ if (!lua_istable(L, lua_upvalueindex(2)))
+ luaL_error(L, "Invalid argument");
+
+ ct = luci_getenv(L, "content_type");
+ if (ct) {
+ ct = cp = strdup(ct);
+ }
+ if (cp && strstr(cp, "multipart/form-data") && strstr(cp, "boundary=")) {
+ cp = strstr(cp, "boundary=") + strlen ("boundary=") - 2;
+ *cp = *(cp+1) = '-';
+ luci_parse_multipart(L, cp);
+ free((char *) ct);
+ return 0;
+ }
+ free((char *) ct);
+
+ ct = luci_getenv(L, "request_method");
+ il = luci_getenv(L, "content_length");
+
+ if (!ct) {
+ fprintf(stderr, "no request method!\n");
+ return 0;
+ }
+
+ if (!strcmp(ct, "POST")) {
+ if (il) {
+ length = atoi(il);
+ if (length <= 0)
+ return 0;
+ line = (char *)malloc (length+2);
+ if (line)
+ fgets(line, length+1, stdin);
+ }
+ } else if (!strcmp(ct, "GET")) {
+ ct = luci_getenv(L, "query_string");
+ if (ct)
+ esp = strdup(ct);
+ if (esp && strlen(esp)) {
+ line = (char *)malloc (strlen(esp)+2);
+ if (line)
+ strcpy (line, esp);
+ }
+ free(esp);
+ }
+
+ if (!line)
+ return 0;
+
+ /*
+ * From now on all cgi variables are stored in the variable line
+ * and look like foo=bar&foobar=barfoo&foofoo=
+ */
+ for (cp=line; *cp; cp++)
+ if (*cp == '+')
+ *cp = ' ';
+
+ if (strlen(line)) {
+ for (numargs=1,cp=line; *cp; cp++)
+ if (*cp == '&' || *cp == ';' ) numargs++;
+ } else
+ numargs = 0;
+
+ cp = line;
+ i=0;
+ while (*cp) {
+ char *name;
+ char *value;
+
+ if ((ip = (char *)strchr(cp, '&')) != NULL) {
+ *ip = '\0';
+ } else if ((ip = (char *)strchr(cp, ';')) != NULL) {
+ *ip = '\0';
+ } else
+ ip = cp + strlen(cp);
+
+ if ((esp=(char *)strchr(cp, '=')) == NULL)
+ goto skip;
+
+ if (!strlen(esp))
+ goto skip;
+
+ if (i >= numargs)
+ goto skip;
+
+ esp[0] = 0;
+ name = cp;
+ cgiDecodeString (name);
+
+ cp = ++esp;
+ value = cp;
+ cgiDecodeString (value);
+
+ luci_setvar(L, name, value, false);
+skip:
+ cp = ++ip;
+ }
+ free(line);
+ return 0;
+}
+
diff --git a/libs/sgi-webuci/src/luci.c b/libs/sgi-webuci/src/luci.c
new file mode 100644
index 000000000..d65fc20b6
--- /dev/null
+++ b/libs/sgi-webuci/src/luci.c
@@ -0,0 +1,216 @@
+/*
+ * luci
+ * Copyright (C) 2008 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <boa-plugin.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#define LUAMAIN "luci.lua"
+
+static lua_State *L = NULL;
+
+extern int luci_parse_header (lua_State *L);
+
+static int luci_init(struct httpd_plugin *p)
+{
+ char *path = NULL;
+ int ret = 0;
+
+ L = luaL_newstate();
+ if (!L)
+ goto error;
+
+ luaL_openlibs(L);
+
+ path = malloc(strlen(p->dir) + sizeof(LUAMAIN) + 2);
+ strcpy(path, p->dir);
+ strcat(path, "/" LUAMAIN);
+
+ ret = luaL_dofile(L, path);
+
+ lua_getfield(L, LUA_GLOBALSINDEX, "luci-plugin");
+ do {
+ if (!lua_istable(L, -1)) {
+ ret = 1;
+ break;
+ }
+
+ lua_getfield(L, -1, "init");
+ if (!lua_isfunction(L, -1))
+ break;
+
+ lua_pushstring(L, p->dir);
+ ret = lua_pcall(L, 1, 0, 0);
+ } while (0);
+ free(path);
+
+ if (ret != 0)
+ goto error;
+
+ return 1;
+
+error:
+ fprintf(stderr, "Error: ");
+ if (L) {
+ const char *s = lua_tostring(L, -1);
+ if (!s)
+ s = "unknown error";
+ fprintf(stderr, "%s\n", s);
+ lua_close(L);
+ } else {
+ fprintf(stderr, "Out of memory!\n");
+ }
+ return 0;
+}
+
+static void pushvar(char *name, char *val)
+{
+ if (!val)
+ return;
+
+ lua_pushstring(L, val);
+ lua_setfield(L, -2, name);
+}
+
+static int luci_pcall(lua_State *L, char *func, int narg)
+{
+ int ret;
+
+ ret = lua_pcall(L, narg, narg, 0);
+ if (ret) {
+ const char *s = lua_tostring(L, -1);
+ if (s)
+ fprintf(stderr, "Error running %s: %s\n", func, s);
+ return ret;
+ }
+ if (!narg)
+ return ret;
+
+ ret = lua_isnumber(L, -1);
+ if (!ret)
+ goto done;
+
+ ret = lua_tonumber(L, -1);
+
+done:
+ lua_pop(L, 1);
+ return ret;
+}
+
+static int luci_prepare_req(struct httpd_plugin *p, struct http_context *ctx)
+{
+ int ret;
+
+ lua_getglobal(L, "luci-plugin");
+ lua_getfield(L, -1, "prepare_req");
+
+ ret = lua_isfunction(L, -1);
+ if (!ret)
+ goto done;
+
+ lua_pushstring(L, ctx->uri);
+
+ ret = luci_pcall(L, "prepare_req", 1);
+
+done:
+ lua_pop(L, 1);
+ return ret;
+}
+
+static int luci_handle_req(struct httpd_plugin *p, struct http_context *ctx)
+{
+ int ret;
+
+ lua_newtable(L); /* new table for the http context */
+
+ /* convert http_context data structure to lua table */
+#define PUSH(x) pushvar(#x, ctx->x)
+ PUSH(request_method);
+ PUSH(server_addr);
+ PUSH(server_proto);
+ PUSH(query_string);
+ PUSH(remote_addr);
+ lua_pushinteger(L, ctx->remote_port);
+ lua_setfield(L, -2, "remote_port");
+ PUSH(content_type);
+ PUSH(content_length);
+ PUSH(http_accept);
+#undef PUSH
+
+ if (!strncmp(ctx->uri, p->prefix, strlen(p->prefix)))
+ pushvar("uri", ctx->uri + strlen(p->prefix));
+ else
+ pushvar("uri", ctx->uri);
+
+
+ /* make sure the global 'luci' table is prepared */
+ lua_getglobal(L, "luci-plugin");
+ if (!lua_istable(L, -1))
+ return 0;
+
+ lua_getfield(L, -1, "init_req");
+ if (!lua_isfunction(L, -1)) {
+ /* ignore error */
+ lua_pop(L, 1);
+ } else {
+ lua_pushvalue(L, -3);
+ luci_pcall(L, "init_req", 1);
+ }
+
+ /* storage space for cgi variables */
+ lua_newtable(L);
+ lua_pushvalue(L, -1); /* copy for setfield */
+ lua_setfield(L, -3, "vars");
+
+ lua_pushvalue(L, -3); /* the http context table */
+
+ /*
+ * make luci_parse_header a closure
+ * argument 1: the luci.vars table
+ * argument 2: the http context table
+ */
+ lua_pushcclosure(L, luci_parse_header, 2);
+ ret = luci_pcall(L, "parse_header", 0);
+
+ lua_getfield(L, -1, "handle_req");
+ ret = lua_isfunction(L, -1);
+ if (!ret)
+ goto done;
+
+ lua_pushvalue(L, -3);
+ ret = luci_pcall(L, "handle_req", 1);
+
+ /* pop the luci and http context tables */
+done:
+ lua_pop(L, 2);
+ return ret;
+}
+
+static void luci_unload(struct httpd_plugin *p)
+{
+ lua_close(L);
+}
+
+HTTPD_PLUGIN {
+ .prefix = "/luci/",
+ .init = luci_init,
+ .done = luci_unload,
+ .prepare_req = luci_prepare_req,
+ .handle_req = luci_handle_req,
+};