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