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