2 some simple CGI helper routines
3 Copyright (C) Andrew Tridgell 1997-2001
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.
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.
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.
24 inplace handling of + and % escapes in http variables
26 static void unescape(char *p)
31 if (*p == '+') *p = ' ';
32 if (*p == '%' && sscanf(p+1, "%02x", &v) == 1) {
34 memcpy(p+1, p+3, strlen(p+3)+1);
41 read one line from a file, allocating space as need be
42 adjust length on return
44 static char *grab_line(FILE *f, int *length)
55 if (len == 0) len = 1024;
57 ret2 = (char *)realloc(ret, len);
58 if (!ret2) return ret;
70 if (c == '\r') continue;
72 if (strchr("\n&", c)) break;
83 add a name/value pair to the list of cgi variables
85 static void put(struct cgi_state *cgi, const char *name, const char *value)
90 if (!name || !value) return;
91 var = malloc(sizeof(*var));
92 var->next = cgi->variables;
94 /* trim leading spaces */
95 while (*name && (*name == '+' || *name == ' ')) name++;
97 var->name = strdup(name);
98 var->value = strdup(value);
100 unescape(var->value);
102 /* trim trailing spaces */
103 len = strlen(var->value);
104 while (len && isspace(var->value[len-1])) {
105 var->value[len-1] = 0;
109 cgi->variables = var;
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.
117 static void load_variables(struct cgi_state *cgi)
124 len = cgi->content_length;
126 if (len > 0 && cgi->request_post) {
127 while (len && (line=grab_line(f, &len))) {
128 p = strchr(line,'=');
137 if ((s=cgi->query_string)) {
139 for (tok=strtok_r(s,"&;", &pp);tok;tok=strtok_r(NULL,"&;", &pp)) {
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.
157 static const char *get(struct cgi_state *cgi, const char *name)
161 for (var = cgi->variables; var; var = var->next) {
162 if (strcmp(var->name, name) == 0) {
169 /* set a variable in the cgi template from a cgi variable */
170 static void setvar(struct cgi_state *cgi, const char *name)
172 cgi->tmpl->put(cgi->tmpl, name, cgi->get(cgi, name));
177 tell a browser about a fatal error in the http processing
179 static void http_error(struct cgi_state *cgi,
180 const char *err, const char *header, const char *info)
182 if (!cgi->got_request) {
183 /* damn browsers don't like getting cut off before they give a request */
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) {
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);
198 send a http header based on file extension
200 static enum MIME_TYPE http_header(struct cgi_state *cgi, const char *filename)
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},
215 printf("HTTP/1.0 200 OK\r\nConnection: close\r\n");
217 for (i=0; mime_types[i].pattern; i++) {
218 if (fnmatch(mime_types[i].pattern, filename, 0) == 0) break;
220 printf("Content-Type: %s\r\n\r\n", mime_types[i].mime_type);
221 return mime_types[i].type;
226 handle a file download
228 static void download(struct cgi_state *cgi, const char *path)
230 enum MIME_TYPE mtype;
234 mtype = cgi->http_header(cgi, path);
236 if (mtype == MIME_TYPE_TEXT_HTML) {
237 cgi->tmpl->process(cgi->tmpl, path);
241 m = map_file(path, &size);
243 fwrite(m, 1, size, stdout);
249 read and parse the http request
251 static int setup(struct cgi_state *cgi)
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");
272 } else if (strncasecmp(line,"Content-Length: ", 16)==0) {
273 cgi->content_length = atoi(&line[16]);
275 /* ignore all other requests! */
279 cgi->http_error(cgi, "400 Bad Request", "",
280 "You must specify a GET or POST request");
284 if ((p = strchr(url,' ')) || (p=strchr(url,'\t'))) {
287 while (*url && strchr("\r\n",url[strlen(url)-1])) {
288 url[strlen(url)-1] = 0;
291 /* anything following a ? in the URL is part of the query string */
292 if ((p=strchr(url,'?'))) {
293 cgi->query_string = p+1;
301 while (*cgi->pathinfo == '/') cgi->pathinfo++;
307 destroy a open cgi state
309 static void destroy(struct cgi_state *cgi)
311 cgi->tmpl->destroy(cgi->tmpl);
313 while (cgi->variables) {
314 struct cgi_var *var = cgi->variables;
317 cgi->variables = cgi->variables->next;
322 memset(cgi, 0, sizeof(cgi));
326 static struct cgi_state cgi_base = {
340 struct cgi_state *cgi_init(void)
342 struct cgi_state *cgi;
344 cgi = malloc(sizeof(*cgi));
345 memcpy(cgi, &cgi_base, sizeof(*cgi));
347 cgi->tmpl = template_init();