Take care of null termination
[infodrom/cgilib] / cgi.c
1 /*
2     cgi.c - Some simple routines for CGI programming
3     Copyright (c) 1996-9  Martin Schulze <joey@infodrom.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 as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
18  */
19
20 /*
21     Sat Aug 24 10:39:31 MET 1999: Martin Schulze <joey@infodrom.org>
22         Added cgiGetVariables(), corrected multiple values code
23
24     Tue Aug 17 13:13:00 CEST 1999: Martin Schulze <joey@infodrom.org>
25         Changed s_cgi to contain cookies as well.  Added proper support for
26         HTTP Cookies.
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <cgi.h>
35
36 int cgiDebugLevel = 0;
37 int cgiDebugStderr = 1;
38 char *cgiHeaderString = NULL;
39 char *cgiType = NULL;
40
41 extern s_cookie **cgiReadCookies();
42
43 int cgiSetHeader (char *name, char *value)
44 {
45     char *cp, *vp, *pivot;
46     int len;
47
48     if (!name || !strlen (name) || !value || !strlen (value))
49         return 0;
50     
51     for (cp=name;*cp && *cp!=' ' && *cp!='\r' && *cp!='\n' && *cp!=':';cp++);
52     for (vp=value;*vp && *vp!='\r' && *vp!='\n';vp++);
53
54     if (cgiHeaderString) {
55         len = (strlen (cgiHeaderString) + cp-name + vp-value + 5) * sizeof (char);
56         if ((pivot = (char *)realloc (cgiHeaderString,len)) == NULL)
57             return 0;
58         cgiHeaderString = pivot;
59         pivot += strlen (cgiHeaderString);
60     } else {
61         len = (cp-name + vp-value + 5) * sizeof (char);
62         if ((cgiHeaderString = (char *)malloc (len)) == NULL)
63             return 0;
64         pivot = cgiHeaderString;
65     }
66     strncpy (pivot, name, cp-name);
67     strncat (pivot, ": ", 2);
68     strncat (pivot, value, vp-value);
69     strncat (pivot, "\r\n", 2);
70
71     return 1;
72 }
73
74 int cgiSetType (char *type)
75 {
76     int len;
77     char *cp;
78
79     if (!type || !strlen (type))
80         return 0;
81     if (cgiType)
82         free (cgiType);
83
84     for (cp=type;*cp && *cp!='\r' && *cp!='\n';cp++);
85
86     len = (cp-type+1) * sizeof (char);
87     if ((cgiType = (char *)malloc (len+20)) == NULL)
88         return 0;
89     memset (cgiType, 0, len);
90     strncpy (cgiType, type, cp-type);
91     
92     return 1;
93 }
94
95 void cgiHeader ()
96 {
97     if (cgiType)
98         printf ("Content-type: %s\r\n", cgiType);
99     else
100         printf ("Content-type: text/html\r\n");
101     if (cgiHeaderString)
102         printf ("%s", cgiHeaderString);
103     printf ("\r\n");
104 }
105
106 void cgiDebug (int level, int where)
107 {
108     if (level > 0)
109         cgiDebugLevel = level;
110     else
111         cgiDebugLevel = 0;
112     if (where)
113         cgiDebugStderr = 0;
114     else
115         cgiDebugStderr = 1;
116 }
117
118 char *cgiDecodeString (char *text)
119 {
120     char *cp, *xp;
121
122     for (cp=text,xp=text; *cp; cp++) {
123         if (*cp == '%') {
124             if (strchr("0123456789ABCDEFabcdef", *(cp+1))
125                 && strchr("0123456789ABCDEFabcdef", *(cp+2))) {
126                 if (islower(*(cp+1)))
127                     *(cp+1) = toupper(*(cp+1));
128                 if (islower(*(cp+2)))
129                     *(cp+2) = toupper(*(cp+2));
130                 *(xp) = (*(cp+1) >= 'A' ? *(cp+1) - 'A' + 10 : *(cp+1) - '0' ) * 16
131                     + (*(cp+2) >= 'A' ? *(cp+2) - 'A' + 10 : *(cp+2) - '0');
132                 xp++;cp+=2;
133             }
134         } else {
135             *(xp++) = *cp;
136         }
137     }
138     memset(xp, 0, cp-xp);
139     return text;
140 }
141
142 /*  cgiReadVariables()
143  *
144  *  Read from stdin if no string is provided via CGI.  Variables that
145  *  doesn't have a value associated with it doesn't get stored.
146  */
147 s_var **cgiReadVariables ()
148 {
149     int length;
150     char *line = NULL;
151     int numargs;
152     char *cp, *ip, *esp, *sptr;
153     s_var **result;
154     int i, k, len;
155     char tmp[101];
156
157     cp = getenv("REQUEST_METHOD");
158     ip = getenv("CONTENT_LENGTH");
159
160     if (cp && !strcmp(cp, "POST")) {
161         if (ip) {
162             length = atoi(ip);
163             if ((line = (char *)malloc (length+2)) == NULL)
164                 return NULL;
165             fgets(line, length+1, stdin);
166         } else
167             return NULL;
168     } else if (cp && !strcmp(cp, "GET")) {
169         esp = getenv("QUERY_STRING");
170         if (esp && strlen(esp)) {
171             if ((line = (char *)malloc (strlen(esp)+2)) == NULL)
172                 return NULL;
173             sprintf (line, "%s", esp);
174         } else
175             return NULL;
176     } else {
177         length = 0;
178         printf ("(offline mode: enter name=value pairs on standard input)\n");
179         memset (tmp, 0, sizeof(tmp));
180         while((cp = fgets (tmp, 100, stdin)) != NULL) {
181             if (strlen(tmp)) {
182                 if (tmp[strlen(tmp)-1] == '\n')
183                     tmp[strlen(tmp)-1] = '&';
184                 if (length) {
185                     length += strlen(tmp);
186                     len = (length+1) * sizeof(char);
187                     if ((line = (char *)realloc (line, len)) == NULL)
188                         return NULL;
189                     strcat (line, tmp);
190                 } else {
191                     length = strlen(tmp);
192                     len = (length+1) * sizeof(char);
193                     if ((line = (char *)malloc (len)) == NULL)
194                         return NULL;
195                     memset (line, 0, len);
196                     strcpy (line, tmp);
197                 }
198             }
199             memset (tmp, 0, sizeof(tmp));
200         }
201         if (!line)
202             return NULL;
203         if (line[strlen(line)-1] == '&')
204             line[strlen(line)-1] = '\0';
205     }
206
207     /*
208      *  From now on all cgi variables are stored in the variable line
209      *  and look like  foo=bar&foobar=barfoo&foofoo=
210      */
211
212     if (cgiDebugLevel > 0) {
213         if (cgiDebugStderr)
214             fprintf (stderr, "Received cgi input: %s\n", line);
215         else
216             printf ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n", line);
217     }
218
219     for (cp=line; *cp; cp++)
220         if (*cp == '+')
221             *cp = ' ';
222
223     if (strlen(line)) {
224         for (numargs=1,cp=line; *cp; cp++)
225             if (*cp == '&' || *cp == ';' ) numargs++;
226     } else
227         numargs = 0;
228     if (cgiDebugLevel > 0) {
229         if (cgiDebugStderr)
230             fprintf (stderr, "%d cgi variables found.\n", numargs);
231         else
232             printf ("%d cgi variables found.<br>\n", numargs);
233     }
234
235     len = (numargs+1) * sizeof(s_var *);
236     if ((result = (s_var **)malloc (len)) == NULL)
237         return NULL;
238     memset (result, 0, len);
239
240     cp = line;
241     i=0;
242     while (*cp) {
243         if ((ip = (char *)strchr(cp, '&')) != NULL) {
244             *ip = '\0';
245         } else if ((ip = (char *)strchr(cp, ';')) != NULL) {
246             *ip = '\0';
247         } else
248             ip = cp + strlen(cp);
249
250         if ((esp=(char *)strchr(cp, '=')) == NULL) {
251             cp = ++ip;
252             continue;
253         }
254
255         if (!strlen(esp)) {
256             cp = ++ip;
257             continue;
258         }
259
260         if (i<numargs) {
261
262             /* try to find out if there's already such a variable */
263             for (k=0; k<i && (strncmp (result[k]->name,cp, esp-cp) || !(strlen (result[k]->name) == esp-cp)); k++);
264
265             if (k == i) {       /* No such variable yet */
266                 if ((result[i] = (s_var *)malloc(sizeof(s_var))) == NULL)
267                     return NULL;
268                 if ((result[i]->name = (char *)malloc((esp-cp+1) * sizeof(char))) == NULL)
269                     return NULL;
270                 memset (result[i]->name, 0, esp-cp+1);
271                 strncpy(result[i]->name, cp, esp-cp);
272                 cp = ++esp;
273                 if ((result[i]->value = (char *)malloc((ip-esp+1) * sizeof(char))) == NULL)
274                     return NULL;
275                 memset (result[i]->value, 0, ip-esp+1);
276                 strncpy(result[i]->value, cp, ip-esp);
277                 result[i]->value = cgiDecodeString(result[i]->value);
278                 if (cgiDebugLevel) {
279                     if (cgiDebugStderr)
280                         fprintf (stderr, "%s: %s\n", result[i]->name, result[i]->value);
281                     else
282                         printf ("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n", result[i]->name, result[i]->value);
283                 }
284                 i++;
285             } else {    /* There is already such a name, suppose a mutiple field */
286                 cp = ++esp;
287                 len = (strlen(result[k]->value)+(ip-esp)+2) * sizeof (char);
288                 if ((sptr = (char *)malloc(len)) == NULL)
289                     return NULL;
290                 memset (sptr, 0, len);
291                 sprintf (sptr, "%s\n", result[k]->value);
292                 strncat(sptr, cp, ip-esp);
293                 free(result[k]->value);
294                 result[k]->value = cgiDecodeString (sptr);
295             }
296         }
297         cp = ++ip;
298     }
299     return result;
300 }
301
302 /*  cgiInit()
303  *
304  *  Read from stdin if no string is provided via CGI.  Variables that
305  *  doesn't have a value associated with it doesn't get stored.
306  */
307 s_cgi *cgiInit()
308 {
309     s_cgi *res;
310     s_var **vars;
311     s_cookie **cookies;
312
313     vars = cgiReadVariables ();
314     cookies = cgiReadCookies ();
315
316     if (!vars && !cookies)
317         return NULL;
318
319     if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
320         return NULL;
321     res->vars = vars;
322     res->cookies = cookies;
323
324     return res;
325 }
326
327 char *cgiGetValue (s_cgi *parms, const char *name)
328 {
329     int i;
330
331     if (!parms || !parms->vars)
332         return NULL;
333     for (i=0;parms->vars[i]; i++)
334         if (!strcmp(name,parms->vars[i]->name)) {
335             if (cgiDebugLevel > 0) {
336                 if (cgiDebugStderr)
337                     fprintf (stderr, "%s found as %s\n", name, parms->vars[i]->value);
338                 else
339                     printf ("%s found as %s<br>\n", name, parms->vars[i]->value);
340             }
341             if (strlen(parms->vars[i]->value) > 0)
342                 return parms->vars[i]->value;
343             else
344                 return NULL;
345         }
346     if (cgiDebugLevel) {
347         if (cgiDebugStderr)
348             fprintf (stderr, "%s not found\n", name);
349         else
350             printf ("%s not found<br>\n", name);
351     }
352     return NULL;
353 }
354
355 char **cgiGetVariables (s_cgi *parms)
356 {
357     int i;
358     char **res = NULL;
359     int len;
360
361     if (!parms || !parms->vars)
362         return NULL;
363     for (i=0;parms->vars[i]; i++);
364     len = sizeof (char *) * ++i;
365     if ((res = (char **)malloc (len)) == NULL)
366         return NULL;
367     memset (res, 0, len);
368     for (i=0;parms->vars[i]; i++) {
369         len = strlen (parms->vars[i]->name) +1;
370         if ((res[i] = (char *)malloc (len)) == NULL)
371             return NULL;
372         memset (res[i], 0, len);
373         strcpy (res[i], parms->vars[i]->name);
374     }
375     return res;
376 }
377
378 void cgiRedirect (const char *url)
379 {
380     if (url && strlen(url)) {
381         printf ("Content-type: text/html\r\nContent-length: %d\r\n", 77+(strlen(url)*2));
382         printf ("Status: 302 Temporal Relocation\r\n");
383         printf ("Location: %s\r\n\r\n", url);
384         printf ("<html>\n<body>\nThe page has been moved to <a href=\"%s\">%s</a>\n</body>\n</html>\n", url, url);
385     }
386 }
387
388 void cgiFreeList (char **list)
389 {
390     int i;
391
392     for (i=0; list[i] != NULL; i++)
393         free (list[i]);
394         free (list);
395 }
396
397 void cgiFree (s_cgi *parms)
398 {
399     int i;
400
401     if (!parms)
402         return;
403     if (parms->vars) {
404         for (i=0;parms->vars[i]; i++) {
405             if (parms->vars[i]->name)
406                 free (parms->vars[i]->name);
407             if (parms->vars[i]->value)
408                 free (parms->vars[i]->value);
409             free (parms->vars[i]);
410         }
411         free (parms->vars);
412     }
413     if (parms->cookies) {
414         if (parms->cookies[0]->version)
415             free (parms->cookies[0]->version);
416         for (i=0;parms->cookies[i]; i++) {
417             if (parms->cookies[i]->name)
418                 free (parms->cookies[i]->name);
419             if (parms->cookies[i]->value)
420                 free (parms->cookies[i]->value);
421             if (parms->cookies[i]->path)
422                 free (parms->cookies[i]->path);
423             if (parms->cookies[i]->domain)
424                 free (parms->cookies[i]->domain);
425             free (parms->cookies[i]);
426         }
427         free (parms->cookies);
428     }
429     free (parms);
430
431     if (cgiHeaderString) {
432         free (cgiHeaderString);
433         cgiHeaderString = NULL;
434     }
435     if (cgiType) {
436         free (cgiType);
437         cgiType = NULL;
438     }
439 }
440
441 /*
442  * Local variables:
443  *  c-indent-level: 4
444  *  c-basic-offset: 4
445  *  tab-width: 8
446  * End:
447  */