Squashed commit of the following:
[project/luci.git] / libs / sgi-webuci / src / cgi.c
1 /*
2 * CGI routines for luci
3 * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
4
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.
8 *
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.
13 */
14
15 /*
16 * Based on code from cgilib:
17 *
18 * cgi.c - Some simple routines for CGI programming
19 * Copyright (c) 1996-9,2007,8 Martin Schulze <joey@infodrom.org>
20 *
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.
25 *
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.
30 *
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.
34 */
35
36 #define _GNU_SOURCE 1
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <string.h>
42 #include <stdbool.h>
43 #include <strings.h>
44 #include <ctype.h>
45 #include <lauxlib.h>
46
47 #define BUFSIZE 128
48
49 static char *
50 cgiGetLine (FILE *stream)
51 {
52 static char *line = NULL;
53 static size_t size = 0;
54 char buf[BUFSIZE];
55 char *cp;
56
57 if (!line) {
58 if ((line = (char *)malloc (BUFSIZE)) == NULL)
59 return NULL;
60 size = BUFSIZE;
61 }
62 line[0] = '\0';
63
64 while (!feof (stream)) {
65 if ((cp = fgets (buf, sizeof (buf), stream)) == NULL)
66 return NULL;
67
68 if (strlen(line)+strlen(buf)+1 > size) {
69 if ((cp = (char *)realloc (line, size + BUFSIZE)) == NULL)
70 return line;
71 size += BUFSIZE;
72 line = cp;
73 }
74
75 strcat (line, buf);
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';
80 return line;
81 }
82 }
83
84 return NULL;
85 }
86
87
88 static const char *
89 luci_getenv(lua_State *L, const char *name)
90 {
91 const char *ret;
92
93 lua_getfield(L, lua_upvalueindex(2), name);
94 ret = lua_tostring(L, -1);
95 lua_pop(L, 1);
96 return ret;
97 }
98
99 static void
100 luci_setvar(lua_State *L, const char *name, const char *value, bool append)
101 {
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");
115 lua_concat(L, 3);
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 */
120
121 int tnext = lua_objlen(L, -1) + 1; /* next entry */
122
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");
130 lua_concat(L, 3);
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
136 */
137 lua_createtable(L, 2, 0);
138 lua_pushvalue(L, -2); /* copy of the initial string value */
139 lua_rawseti(L, -2, 1);
140
141 lua_pushstring(L, value);
142 lua_rawseti(L, -2, 2);
143 lua_setfield(L, lua_upvalueindex(1), name);
144 } else {
145 luaL_error(L, "Invalid table entry type for index '%s'", name);
146 }
147 }
148
149 char *cgiDecodeString (char *text)
150 {
151 char *cp, *xp;
152
153 for (cp=text,xp=text; *cp; cp++) {
154 if (*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');
163 xp++;cp+=2;
164 }
165 } else {
166 *(xp++) = *cp;
167 }
168 }
169 memset(xp, 0, cp-xp);
170 return text;
171 }
172
173 #if 0
174 /* cgiReadFile()
175 *
176 * Read and save a file fro a multipart request
177 */
178 #include <errno.h>
179 char *cgiReadFile (FILE *stream, char *boundary)
180 {
181 char *crlfboundary, *buf;
182 size_t boundarylen;
183 int c;
184 unsigned int pivot;
185 char *cp;
186 char template[]= "/tmp/cgilibXXXXXX";
187 FILE *tmpfile;
188 int fd;
189
190 boundarylen = strlen(boundary)+3;
191 if ((crlfboundary = (char *)malloc (boundarylen)) == NULL)
192 return NULL;
193 sprintf (crlfboundary, "\r\n%s", boundary);
194
195 if ((buf = (char *)malloc (boundarylen)) == NULL) {
196 free (crlfboundary);
197 return NULL;
198 }
199 memset (buf, 0, boundarylen);
200 pivot = 0;
201
202 if ((fd = mkstemp (template)) == -1) {
203 free (crlfboundary);
204 free (buf);
205 return NULL;
206 }
207
208 if ((tmpfile = fdopen (fd, "w")) == NULL) {
209 free (crlfboundary);
210 free (buf);
211 unlink (template);
212 return NULL;
213 }
214
215 while (!feof (stream)) {
216 c = fgetc (stream);
217
218 if (c == 0) {
219 if (strlen (buf)) {
220 for (cp=buf; *cp; cp++)
221 putc (*cp, tmpfile);
222 memset (buf, 0, boundarylen);
223 pivot = 0;
224 }
225 putc (c, tmpfile);
226 continue;
227 }
228
229 if (strlen (buf)) {
230 if (crlfboundary[pivot+1] == c) {
231 buf[++pivot] = c;
232
233 if (strlen (buf) == strlen (crlfboundary))
234 break;
235 else
236 continue;
237 } else {
238 for (cp=buf; *cp; cp++)
239 putc (*cp, tmpfile);
240 memset (buf, 0, boundarylen);
241 pivot = 0;
242 }
243 }
244
245 if (crlfboundary[0] == c) {
246 buf[0] = c;
247 } else {
248 fputc (c, tmpfile);
249 }
250 }
251
252 if (!feof (stream))
253 fgets (buf, boundarylen, stream);
254
255 fclose (tmpfile);
256
257 free (crlfboundary);
258 free (buf);
259
260 return strdup (template);
261 }
262 #endif
263
264 /*
265 * Decode multipart/form-data
266 */
267 #define MULTIPART_DELTA 5
268 void luci_parse_multipart (lua_State *L, char *boundary)
269 {
270 char *line;
271 char *cp, *xp;
272 char *name = NULL, *type = NULL;
273 char *fname = NULL;
274 int header = 1;
275 bool append = false;
276
277 while ((line = cgiGetLine (stdin)) != NULL) {
278 if (!strncmp (line, boundary, strlen(boundary))) {
279 header = 1;
280 if (name)
281 free(name);
282 if (type)
283 free(type);
284 name = NULL;
285 type = NULL;
286 append = false;
287 } else if (header && !name && !strncasecmp (line, "Content-Disposition: form-data; ", 32)) {
288 if ((cp = strstr (line, "name=\"")) == NULL)
289 continue;
290 cp += 6;
291 if ((xp = strchr (cp, '\"')) == NULL)
292 continue;
293 name = malloc(xp-cp + 1);
294 strncpy(name, cp, xp-cp);
295 name[xp-cp] = 0;
296 cgiDecodeString (name);
297
298 if ((cp = strstr (line, "filename=\"")) == NULL)
299 continue;
300 cp += 10;
301 if ((xp = strchr (cp, '\"')) == NULL)
302 continue;
303 fname = malloc(xp-cp + 1);
304 strncpy(fname, cp, xp-cp);
305 fname[xp-cp] = 0;
306 cgiDecodeString (fname);
307 } else if (header && !type && !strncasecmp (line, "Content-Type: ", 14)) {
308 cp = line + 14;
309 type = strdup (cp);
310 } else if (header) {
311 if (!strlen(line)) {
312 header = 0;
313
314 if (fname) {
315 #if 0
316 header = 1;
317 tmpfile = cgiReadFile (stdin, boundary);
318
319 if (!tmpfile) {
320 free (name);
321 free (fname);
322 if (type)
323 free (type);
324 name = fname = type = NULL;
325 }
326
327 cgiDebugOutput (2, "Wrote %s (%s) to file: %s", name, fname, tmpfile);
328
329 if (!strlen (fname)) {
330 cgiDebugOutput (3, "Found empty filename, removing");
331 unlink (tmpfile);
332 free (tmpfile);
333 free (name);
334 free (fname);
335 if (type)
336 free (type);
337 name = fname = type = NULL;
338 } else {
339 if ((file = (s_file *)malloc (sizeof (s_file))) == NULL) {
340 cgiDebugOutput (3, "malloc failed, ignoring %s=%s", name, fname);
341 unlink (tmpfile);
342 free (tmpfile);
343 free (name);
344 free (fname);
345 if (type)
346 free (type);
347 name = fname = type = NULL;
348 continue;
349 }
350
351 file->name = name;
352 file->type = type;
353 file->tmpfile = tmpfile;
354 if ((cp = rindex (fname, '/')) == NULL)
355 file->filename = fname;
356 else {
357 file->filename = strdup (++cp);
358 free (fname);
359 }
360 name = type = fname = NULL;
361
362 if (!files) {
363 if ((files = (s_file **)malloc(2*sizeof (s_file *))) == NULL) {
364 cgiDebugOutput (3, "malloc failed, ignoring %s=%s", name, fname);
365 unlink (tmpfile);
366 free (tmpfile);
367 free (name);
368 name = NULL;
369 if (type) {
370 free (type);
371 type = NULL;
372 }
373 free (file->filename);
374 free (file);
375 continue;
376 }
377 memset (files, 0, 2*sizeof (s_file *));
378 index = 0;
379 } else {
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);
383 unlink (tmpfile);
384 free (tmpfile);
385 free (name);
386 if (type)
387 free (type);
388 free (file->filename);
389 free (file);
390 name = type = fname = NULL;
391 continue;
392 }
393 files = tmpf;
394 memset (files + index, 0, 2*sizeof (s_file *));
395 }
396 files[index] = file;
397 }
398 #else
399 free(fname);
400 fname = NULL;
401 #endif
402 }
403 }
404 } else {
405 if (!name)
406 return;
407
408 cgiDecodeString(line);
409 luci_setvar(L, name, line, append);
410 if (!append) /* beginning of variable contents */
411 append = true;
412 }
413 }
414 }
415
416 /* parse the request header and store variables
417 * in the array supplied as function argument 1 on the stack
418 */
419 int luci_parse_header (lua_State *L)
420 {
421 int length;
422 char *line = NULL;
423 int numargs;
424 char *cp = NULL, *ip = NULL, *esp = NULL;
425 const char *ct, *il;
426 int i;
427
428 if (!lua_istable(L, lua_upvalueindex(1)))
429 luaL_error(L, "Invalid argument");
430
431 if (!lua_istable(L, lua_upvalueindex(2)))
432 luaL_error(L, "Invalid argument");
433
434 ct = luci_getenv(L, "content_type");
435 if (ct) {
436 ct = cp = strdup(ct);
437 }
438 if (cp && strstr(cp, "multipart/form-data") && strstr(cp, "boundary=")) {
439 cp = strstr(cp, "boundary=") + strlen ("boundary=") - 2;
440 *cp = *(cp+1) = '-';
441 luci_parse_multipart(L, cp);
442 free((char *) ct);
443 return 0;
444 }
445 free((char *) ct);
446
447 ct = luci_getenv(L, "request_method");
448 il = luci_getenv(L, "content_length");
449
450 if (!ct) {
451 fprintf(stderr, "no request method!\n");
452 return 0;
453 }
454
455 if (!strcmp(ct, "POST")) {
456 if (il) {
457 length = atoi(il);
458 if (length <= 0)
459 return 0;
460 line = (char *)malloc (length+2);
461 if (line)
462 fgets(line, length+1, stdin);
463 }
464 } else if (!strcmp(ct, "GET")) {
465 ct = luci_getenv(L, "query_string");
466 if (ct)
467 esp = strdup(ct);
468 if (esp && strlen(esp)) {
469 line = (char *)malloc (strlen(esp)+2);
470 if (line)
471 strcpy (line, esp);
472 }
473 free(esp);
474 }
475
476 if (!line)
477 return 0;
478
479 /*
480 * From now on all cgi variables are stored in the variable line
481 * and look like foo=bar&foobar=barfoo&foofoo=
482 */
483 for (cp=line; *cp; cp++)
484 if (*cp == '+')
485 *cp = ' ';
486
487 if (strlen(line)) {
488 for (numargs=1,cp=line; *cp; cp++)
489 if (*cp == '&' || *cp == ';' ) numargs++;
490 } else
491 numargs = 0;
492
493 cp = line;
494 i=0;
495 while (*cp) {
496 char *name;
497 char *value;
498
499 if ((ip = (char *)strchr(cp, '&')) != NULL) {
500 *ip = '\0';
501 } else if ((ip = (char *)strchr(cp, ';')) != NULL) {
502 *ip = '\0';
503 } else
504 ip = cp + strlen(cp);
505
506 if ((esp=(char *)strchr(cp, '=')) == NULL)
507 goto skip;
508
509 if (!strlen(esp))
510 goto skip;
511
512 if (i >= numargs)
513 goto skip;
514
515 esp[0] = 0;
516 name = cp;
517 cgiDecodeString (name);
518
519 cp = ++esp;
520 value = cp;
521 cgiDecodeString (value);
522
523 luci_setvar(L, name, value, false);
524 skip:
525 cp = ++ip;
526 }
527 free(line);
528 return 0;
529 }
530