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