2 * CGI routines for luci
3 * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
16 * Based on code from cgilib:
18 * cgi.c - Some simple routines for CGI programming
19 * Copyright (c) 1996-9,2007,8 Martin Schulze <joey@infodrom.org>
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software Foundation
33 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
50 cgiGetLine (FILE *stream)
52 static char *line = NULL;
53 static size_t size = 0;
58 if ((line = (char *)malloc (BUFSIZE)) == NULL)
64 while (!feof (stream)) {
65 if ((cp = fgets (buf, sizeof (buf), stream)) == NULL)
68 if (strlen(line)+strlen(buf)+1 > size) {
69 if ((cp = (char *)realloc (line, size + BUFSIZE)) == NULL)
76 if (line[strlen(line)-1] == '\n') {
77 line[strlen(line)-1] = '\0';
78 if (line[strlen(line)-1] == '\r')
79 line[strlen(line)-1] = '\0';
89 luci_getenv(lua_State *L, const char *name)
93 lua_getfield(L, lua_upvalueindex(2), name);
94 ret = lua_tostring(L, -1);
100 luci_setvar(lua_State *L, const char *name, const char *value, bool append)
102 /* Check if there is an existing value already */
103 lua_getfield(L, lua_upvalueindex(1), name);
104 if (lua_isnil(L, -1)) {
105 /* nope, we're safe - add a new one */
106 lua_pushstring(L, value);
107 lua_setfield(L, lua_upvalueindex(1), name);
108 } else if (lua_istable(L, -1) && append) {
109 /* it's a table already, but appending is requested
110 * take the last element and append the new string to it */
111 int tlast = lua_objlen(L, -1);
112 lua_rawgeti(L, -1, tlast);
113 lua_pushstring(L, value);
114 lua_pushstring(L, "\n");
116 lua_rawseti(L, -2, tlast);
117 } else if (lua_istable(L, -1)) {
118 /* it's a table, which means we already have two
119 * or more entries, add the next one */
121 int tnext = lua_objlen(L, -1) + 1; /* next entry */
123 lua_pushstring(L, value);
124 luaL_setn(L, -2, tnext);
125 lua_rawseti(L, -2, tnext);
126 } else if (lua_isstring(L, -1) && append) {
127 /* append the new string to the existing variable */
128 lua_pushstring(L, value);
129 lua_pushstring(L, "\n");
131 lua_setfield(L, lua_upvalueindex(1), name);
132 } else if (lua_isstring(L, -1)) {
133 /* we're trying to add a variable that already has
134 * a string value. convert the string value to a
135 * table and add our new value to the table as well
137 lua_createtable(L, 2, 0);
138 lua_pushvalue(L, -2); /* copy of the initial string value */
139 lua_rawseti(L, -2, 1);
141 lua_pushstring(L, value);
142 lua_rawseti(L, -2, 2);
143 lua_setfield(L, lua_upvalueindex(1), name);
145 luaL_error(L, "Invalid table entry type for index '%s'", name);
149 char *cgiDecodeString (char *text)
153 for (cp=text,xp=text; *cp; cp++) {
155 if (strchr("0123456789ABCDEFabcdef", *(cp+1))
156 && strchr("0123456789ABCDEFabcdef", *(cp+2))) {
157 if (islower(*(cp+1)))
158 *(cp+1) = toupper(*(cp+1));
159 if (islower(*(cp+2)))
160 *(cp+2) = toupper(*(cp+2));
161 *(xp) = (*(cp+1) >= 'A' ? *(cp+1) - 'A' + 10 : *(cp+1) - '0' ) * 16
162 + (*(cp+2) >= 'A' ? *(cp+2) - 'A' + 10 : *(cp+2) - '0');
169 memset(xp, 0, cp-xp);
176 * Read and save a file fro a multipart request
179 char *cgiReadFile (FILE *stream, char *boundary)
181 char *crlfboundary, *buf;
186 char template[]= "/tmp/cgilibXXXXXX";
190 boundarylen = strlen(boundary)+3;
191 if ((crlfboundary = (char *)malloc (boundarylen)) == NULL)
193 sprintf (crlfboundary, "\r\n%s", boundary);
195 if ((buf = (char *)malloc (boundarylen)) == NULL) {
199 memset (buf, 0, boundarylen);
202 if ((fd = mkstemp (template)) == -1) {
208 if ((tmpfile = fdopen (fd, "w")) == NULL) {
215 while (!feof (stream)) {
220 for (cp=buf; *cp; cp++)
222 memset (buf, 0, boundarylen);
230 if (crlfboundary[pivot+1] == c) {
233 if (strlen (buf) == strlen (crlfboundary))
238 for (cp=buf; *cp; cp++)
240 memset (buf, 0, boundarylen);
245 if (crlfboundary[0] == c) {
253 fgets (buf, boundarylen, stream);
260 return strdup (template);
265 * Decode multipart/form-data
267 #define MULTIPART_DELTA 5
268 void luci_parse_multipart (lua_State *L, char *boundary)
272 char *name = NULL, *type = NULL;
277 while ((line = cgiGetLine (stdin)) != NULL) {
278 if (!strncmp (line, boundary, strlen(boundary))) {
287 } else if (header && !name && !strncasecmp (line, "Content-Disposition: form-data; ", 32)) {
288 if ((cp = strstr (line, "name=\"")) == NULL)
291 if ((xp = strchr (cp, '\"')) == NULL)
293 name = malloc(xp-cp + 1);
294 strncpy(name, cp, xp-cp);
296 cgiDecodeString (name);
298 if ((cp = strstr (line, "filename=\"")) == NULL)
301 if ((xp = strchr (cp, '\"')) == NULL)
303 fname = malloc(xp-cp + 1);
304 strncpy(fname, cp, xp-cp);
306 cgiDecodeString (fname);
307 } else if (header && !type && !strncasecmp (line, "Content-Type: ", 14)) {
317 tmpfile = cgiReadFile (stdin, boundary);
324 name = fname = type = NULL;
327 cgiDebugOutput (2, "Wrote %s (%s) to file: %s", name, fname, tmpfile);
329 if (!strlen (fname)) {
330 cgiDebugOutput (3, "Found empty filename, removing");
337 name = fname = type = NULL;
339 if ((file = (s_file *)malloc (sizeof (s_file))) == NULL) {
340 cgiDebugOutput (3, "malloc failed, ignoring %s=%s", name, fname);
347 name = fname = type = NULL;
353 file->tmpfile = tmpfile;
354 if ((cp = rindex (fname, '/')) == NULL)
355 file->filename = fname;
357 file->filename = strdup (++cp);
360 name = type = fname = NULL;
363 if ((files = (s_file **)malloc(2*sizeof (s_file *))) == NULL) {
364 cgiDebugOutput (3, "malloc failed, ignoring %s=%s", name, fname);
373 free (file->filename);
377 memset (files, 0, 2*sizeof (s_file *));
380 for (index=0; files[index]; index++);
381 if ((tmpf = (s_file **)realloc(files, (index+2)*sizeof (s_file *))) == NULL) {
382 cgiDebugOutput (3, "realloc failed, ignoring %s=%s", name, fname);
388 free (file->filename);
390 name = type = fname = NULL;
394 memset (files + index, 0, 2*sizeof (s_file *));
408 cgiDecodeString(line);
409 luci_setvar(L, name, line, append);
410 if (!append) /* beginning of variable contents */
416 /* parse the request header and store variables
417 * in the array supplied as function argument 1 on the stack
419 int luci_parse_header (lua_State *L)
424 char *cp = NULL, *ip = NULL, *esp = NULL;
428 if (!lua_istable(L, lua_upvalueindex(1)))
429 luaL_error(L, "Invalid argument");
431 if (!lua_istable(L, lua_upvalueindex(2)))
432 luaL_error(L, "Invalid argument");
434 ct = luci_getenv(L, "content_type");
436 ct = cp = strdup(ct);
438 if (cp && strstr(cp, "multipart/form-data") && strstr(cp, "boundary=")) {
439 cp = strstr(cp, "boundary=") + strlen ("boundary=") - 2;
441 luci_parse_multipart(L, cp);
447 ct = luci_getenv(L, "request_method");
448 il = luci_getenv(L, "content_length");
451 fprintf(stderr, "no request method!\n");
455 if (!strcmp(ct, "POST")) {
460 line = (char *)malloc (length+2);
462 fgets(line, length+1, stdin);
464 } else if (!strcmp(ct, "GET")) {
465 ct = luci_getenv(L, "query_string");
468 if (esp && strlen(esp)) {
469 line = (char *)malloc (strlen(esp)+2);
480 * From now on all cgi variables are stored in the variable line
481 * and look like foo=bar&foobar=barfoo&foofoo=
483 for (cp=line; *cp; cp++)
488 for (numargs=1,cp=line; *cp; cp++)
489 if (*cp == '&' || *cp == ';' ) numargs++;
499 if ((ip = (char *)strchr(cp, '&')) != NULL) {
501 } else if ((ip = (char *)strchr(cp, ';')) != NULL) {
504 ip = cp + strlen(cp);
506 if ((esp=(char *)strchr(cp, '=')) == NULL)
517 cgiDecodeString (name);
521 cgiDecodeString (value);
523 luci_setvar(L, name, value, false);