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