diff options
Diffstat (limited to 'libs/sgi-webuci/src/cgi.c')
-rw-r--r-- | libs/sgi-webuci/src/cgi.c | 530 |
1 files changed, 530 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; +} + |