flm01/openwrt/packages/luci/libs/sgi-webuci/src/cgi.c

531 lines
12 KiB
C

/*
* 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;
}