don't show errors in capacity
[tridge/junkcode.git] / tserver / cgi.c
1 /* 
2    some simple CGI helper routines
3    Copyright (C) Andrew Tridgell 1997-2001
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., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20
21 #include "includes.h"
22
23 /* 
24    inplace handling of + and % escapes in http variables 
25 */
26 static void unescape(char *p)
27 {
28         unsigned v;
29
30         while (*p) {
31                 if (*p == '+') *p = ' ';
32                 if (*p == '%' && sscanf(p+1, "%02x", &v) == 1) {
33                         *p = (char)v;
34                         memcpy(p+1, p+3, strlen(p+3)+1);
35                 }
36                 p++;
37         }
38 }
39
40 /*
41   read one line from a file, allocating space as need be
42   adjust length on return
43 */
44 static char *grab_line(FILE *f, int *length)
45 {
46         char *ret = NULL;
47         int i = 0;
48         int len = 0;
49
50         while (*length) {
51                 int c;
52         
53                 if (i == len) {
54                         char *ret2;
55                         if (len == 0) len = 1024;
56                         else len *= 2;
57                         ret2 = (char *)realloc(ret, len);
58                         if (!ret2) return ret;
59                         ret = ret2;
60                 }
61         
62                 c = fgetc(f);
63                 (*length)--;
64
65                 if (c == EOF) {
66                         (*length) = 0;
67                         break;
68                 }
69                 
70                 if (c == '\r') continue;
71
72                 if (strchr("\n&", c)) break;
73
74                 ret[i++] = c;
75         }
76         
77
78         ret[i] = 0;
79         return ret;
80 }
81
82 /*
83   add a name/value pair to the list of cgi variables 
84 */
85 static void put(struct cgi_state *cgi, const char *name, const char *value)
86 {
87         struct cgi_var *var;
88         int len;
89
90         if (!name || !value) return;
91         var = malloc(sizeof(*var));
92         var->next = cgi->variables;
93         
94         /* trim leading spaces */
95         while (*name && (*name == '+' || *name == ' ')) name++;
96
97         var->name = strdup(name);
98         var->value = strdup(value);
99         unescape(var->name);
100         unescape(var->value);
101
102         /* trim trailing spaces */
103         len = strlen(var->value);
104         while (len && isspace(var->value[len-1])) {
105                 var->value[len-1] = 0;
106                 len--;
107         }
108
109         cgi->variables = var;
110 }
111
112
113 /*
114   load all the variables passed to the CGI program. May have multiple variables
115   with the same name and the same or different values. 
116 */
117 static void load_variables(struct cgi_state *cgi)
118 {
119         char *line;
120         char *p, *s, *tok;
121         int len;
122         FILE *f = stdin;
123
124         len = cgi->content_length;
125
126         if (len > 0 && cgi->request_post) {
127                 while (len && (line=grab_line(f, &len))) {
128                         p = strchr(line,'=');
129                         if (p) {
130                                 *p = 0;
131                                 put(cgi, line, p+1);
132                         }
133                         free(line);
134                 }
135         }
136
137         if ((s=cgi->query_string)) {
138                 char *pp;
139                 for (tok=strtok_r(s,"&;", &pp);tok;tok=strtok_r(NULL,"&;", &pp)) {
140                         p = strchr(tok,'=');
141                         if (p) {
142                                 *p = 0;
143                                 put(cgi, tok, p+1);
144                         }
145                 }
146         }
147 }
148
149
150 /*
151   find a variable passed via CGI
152   Doesn't quite do what you think in the case of POST text variables, because
153   if they exist they might have a value of "" or even " ", depending on the 
154   browser. Also doesn't allow for variables[] containing multiple variables
155   with the same name and the same or different values.
156 */
157 static const char *get(struct cgi_state *cgi, const char *name)
158 {
159         struct cgi_var *var;
160
161         for (var = cgi->variables; var; var = var->next) {
162                 if (strcmp(var->name, name) == 0) {
163                         return var->value;
164                 }
165         }
166         return NULL;
167 }
168
169 /* set a variable in the cgi template from a cgi variable */
170 static void setvar(struct cgi_state *cgi, const char *name)
171 {
172         cgi->tmpl->put(cgi->tmpl, name, cgi->get(cgi, name));
173 }
174
175
176 /*
177   tell a browser about a fatal error in the http processing
178 */
179 static void http_error(struct cgi_state *cgi, 
180                        const char *err, const char *header, const char *info)
181 {
182         if (!cgi->got_request) {
183                 /* damn browsers don't like getting cut off before they give a request */
184                 char line[1024];
185                 while (fgets(line, sizeof(line)-1, stdin)) {
186                         if (strncasecmp(line,"GET ", 4)==0 || 
187                             strncasecmp(line,"POST ", 5)==0 ||
188                             strncasecmp(line,"PUT ", 4)==0) {
189                                 break;
190                         }
191                 }
192         }
193
194         printf("HTTP/1.0 %s\r\n%sConnection: close\r\nContent-Type: text/html\r\n\r\n<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>%s</H1>%s<p></BODY></HTML>\r\n\r\n", err, header, err, err, info);
195 }
196
197 /*
198   send a http header based on file extension
199 */
200 static enum MIME_TYPE http_header(struct cgi_state *cgi, const char *filename)
201 {
202         int i;
203         static struct {
204                 char *pattern;
205                 char *mime_type;
206                 enum MIME_TYPE type;
207         } mime_types[] = {
208                 {"*.gif",  "image/gif",  MIME_TYPE_IMAGE_GIF},
209                 {"*.jpg",  "image/jpeg", MIME_TYPE_IMAGE_JPEG},
210                 {"*.txt",  "text/plain", MIME_TYPE_TEXT_PLAIN},
211                 {"*.html", "text/html",  MIME_TYPE_TEXT_HTML},
212                 {NULL,     "data",       MIME_TYPE_UNKNOWN},
213         };
214
215         printf("HTTP/1.0 200 OK\r\nConnection: close\r\n");
216
217         for (i=0; mime_types[i].pattern; i++) {
218                 if (fnmatch(mime_types[i].pattern, filename, 0) == 0) break;
219         }
220         printf("Content-Type: %s\r\n\r\n", mime_types[i].mime_type);
221         return mime_types[i].type;
222 }
223
224
225 /*
226   handle a file download
227 */
228 static void download(struct cgi_state *cgi, const char *path)
229 {
230         enum MIME_TYPE mtype;
231         size_t size;
232         void *m;
233
234         mtype = cgi->http_header(cgi, path);
235
236         if (mtype == MIME_TYPE_TEXT_HTML) {
237                 cgi->tmpl->process(cgi->tmpl, path);
238                 return;
239         }
240
241         m = map_file(path, &size);
242         if (m) {
243                 fwrite(m, 1, size, stdout);
244                 unmap_file(m, size);
245         }
246 }
247
248 /*
249   read and parse the http request
250  */
251 static int setup(struct cgi_state *cgi)
252 {
253         char line[1024];
254         char *url=NULL;
255         char *p;
256
257         /* we are a mini-web server. We need to read the request from stdin */
258         while (fgets(line, sizeof(line)-1, stdin)) {
259                 if (line[0] == '\r' || line[0] == '\n') break;
260                 if (strncasecmp(line,"GET ", 4)==0) {
261                         cgi->got_request = 1;
262                         url = strdup(&line[4]);
263                 } else if (strncasecmp(line,"POST ", 5)==0) {
264                         cgi->got_request = 1;
265                         cgi->request_post = 1;
266                         url = strdup(&line[5]);
267                 } else if (strncasecmp(line,"PUT ", 4)==0) {
268                         cgi->got_request = 1;
269                         cgi->http_error(cgi, "400 Bad Request", "",
270                                         "This server does not accept PUT requests");
271                         return -1;
272                 } else if (strncasecmp(line,"Content-Length: ", 16)==0) {
273                         cgi->content_length = atoi(&line[16]);
274                 }
275                 /* ignore all other requests! */
276         }
277
278         if (!url) {
279                 cgi->http_error(cgi, "400 Bad Request", "",
280                                 "You must specify a GET or POST request");
281         }
282
283         /* trim the URL */
284         if ((p = strchr(url,' ')) || (p=strchr(url,'\t'))) {
285                 *p = 0;
286         }
287         while (*url && strchr("\r\n",url[strlen(url)-1])) {
288                 url[strlen(url)-1] = 0;
289         }
290
291         /* anything following a ? in the URL is part of the query string */
292         if ((p=strchr(url,'?'))) {
293                 cgi->query_string = p+1;
294                 *p = 0;
295         }
296
297         cgi->url = url;
298
299         cgi->pathinfo = url;
300
301         while (*cgi->pathinfo == '/') cgi->pathinfo++;
302
303         return 0;
304 }
305
306 /*
307   destroy a open cgi state
308 */
309 static void destroy(struct cgi_state *cgi)
310 {
311         cgi->tmpl->destroy(cgi->tmpl);
312
313         while (cgi->variables) {
314                 struct cgi_var *var = cgi->variables;
315                 free(var->name);
316                 free(var->value);
317                 cgi->variables = cgi->variables->next;
318                 free(var);
319         }
320
321         free(cgi->url);
322         memset(cgi, 0, sizeof(cgi));
323         free(cgi);
324 }
325
326 static struct cgi_state cgi_base = {
327         /* methods */
328         setup,
329         destroy,
330         http_header,
331         load_variables,
332         get,
333         setvar,
334         http_error,
335         download,
336         
337         /* rest are zero */
338 };
339
340 struct cgi_state *cgi_init(void)
341 {
342         struct cgi_state *cgi;
343
344         cgi = malloc(sizeof(*cgi));
345         memcpy(cgi, &cgi_base, sizeof(*cgi));
346
347         cgi->tmpl = template_init();
348
349         return cgi;
350 }