2 * - by Richard W.M. Jones <rich@annexia.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * $Id: pthr_cgi.c,v 1.6 2003/02/02 18:05:30 rich Exp $
39 #include "pthr_http.h"
42 static int post_max = -1;
44 static void parse_qs (cgi, const char *qs);
45 static void insert_param (cgi, char *name, char *value);
49 pool pool; /* Pool for allocations. */
50 shash params; /* Parameters (hash of string -> vector). */
54 cgi_get_post_max (void)
60 cgi_set_post_max (int new_post_max)
62 return post_max = new_post_max;
66 new_cgi (struct pool *pool, http_request h, io_handle io)
68 cgi c = pmalloc (pool, sizeof *c);
71 c->params = new_shash (pool, vector);
73 if (http_request_method (h) != HTTP_METHOD_POST)
75 const char *qs = http_request_query_string (h);
79 else /* Method POST. */
81 const char *content_length_s =
82 http_request_get_header (h, "Content-Length");
83 const char *content_type = http_request_get_header (h, "Content-Type");
84 int content_length = -1;
85 const char std_type[] = "application/x-www-form-urlencoded";
86 struct pool *tmp = new_subpool (pool);
89 if (content_length_s &&
90 sscanf (content_length_s, "%d", &content_length) != 1)
91 return 0; /* Error in field format. */
93 /* Content length too long? */
94 if (post_max >= 0 && content_length >= 0 && content_length > post_max)
95 return 0; /* Content too long. */
97 /* Check content type. If missing, assume it defaults to the
98 * standard application/x-www-form-urlencoded.
101 strncasecmp (content_type, std_type, strlen (std_type)) != 0)
102 return 0; /* Unexpected/unknown content type. */
104 /* Read the content, either to the end of input of else to the
105 * expected length. Note that Netscape 4 sends an extra CRLF
106 * after the POST data with is explicitly forbidden (see:
107 * RFC 2616, section 4.1). We ignore these next time around when
108 * we are reading the next request (see code in new_http_request).
110 if (content_length >= 0)
112 content = pmalloc (tmp, content_length + 1);
113 if (io_fread (content, 1, content_length, io) < content_length)
116 content[content_length] = '\0';
123 content = pstrdup (tmp, "");
125 while ((r = io_fread (t, 1, 1024, io)) > 0)
128 if (post_max >= 0 && n > post_max)
129 return 0; /* Content too long. */
132 content = pstrcat (tmp, content, t);
136 parse_qs (c, content);
145 parse_qs (cgi c, const char *qs)
149 static char *one = "1";
151 if (qs && qs[0] != '\0')
153 /* Parse the query string. */
154 v = pstrcsplit (c->pool, qs, '&');
156 for (i = 0; i < vector_size (v); ++i)
160 vector_get (v, i, param);
161 t = strchr (param, '=');
163 /* No '=' char found? Assume it's a parameter of the form name=1. */
164 if (!t) { insert_param (c, param, one); continue; }
166 /* Split the string on the '=' character and set name and value. */
168 insert_param (c, param, t+1);
174 insert_param (cgi c, char *name, char *value)
179 if (!shash_get (c->params, name, v))
180 v = new_vector (c->pool, char *);
182 s = cgi_unescape (c->pool, value);
183 vector_push_back (v, s);
184 shash_insert (c->params, name, v);
190 return shash_keys (c->params);
194 cgi_param (cgi c, const char *name)
199 if (!shash_get (c->params, name, v))
202 vector_get (v, 0, s);
207 cgi_param_list (cgi c, const char *name)
211 if (!shash_get (c->params, name, v))
218 cgi_erase (cgi c, const char *name)
220 return shash_erase (c->params, name);
224 copy_cgi (pool npool, cgi c)
226 const char *key, *value;
228 vector keys, values, v;
230 cgi nc = pmalloc (npool, sizeof *nc);
233 nc->params = new_shash (npool, vector);
235 keys = shash_keys_in_pool (c->params, npool);
237 for (i = 0; i < vector_size (keys); ++i)
239 vector_get (keys, i, key);
240 values = cgi_param_list (c, key);
242 for (j = 0; j < vector_size (values); ++j)
244 vector_get (values, j, value);
245 value = pstrdup (npool, value);
247 if (!shash_get (nc->params, key, v))
248 v = new_vector (npool, char *);
250 vector_push_back (v, value);
251 shash_insert (nc->params, key, v);
259 cgi_escape (pool pool, const char *str)
262 int len = strlen (str);
265 static const char hexdigits[] = "0123456789abcdef";
267 /* Work out how long the escaped string will be. Escaped strings
270 for (i = 0; i < len; ++i)
272 if (isalnum ((int) str[i]) ||
273 str[i] == ',' || str[i] == '-' || str[i] == ' ')
279 new_str = pmalloc (pool, new_len + 1);
281 /* Escape the string. */
282 for (i = 0, j = 0; i < len; ++i)
284 if (isalnum ((int) str[i]) ||
285 str[i] == ',' || str[i] == '-')
286 new_str[j++] = str[i];
287 else if (str[i] == ' ')
292 new_str[j++] = hexdigits [(str[i] >> 4) & 0xf];
293 new_str[j++] = hexdigits [str[i] & 0xf];
305 if (c >= '0' && c <= '9') return c - '0';
306 else if (c >= 'a' && c <= 'f') return c - 'a' + 10;
307 else if (c >= 'A' && c <= 'F') return c - 'A' + 10;
312 cgi_unescape (pool pool, const char *str)
315 int len = strlen (str);
318 /* Unescaped strings always get smaller. */
319 new_str = pmalloc (pool, len + 1);
321 for (i = 0, j = 0; i < len; ++i)
327 int a = hex_to_int (str[i+1]);
328 int b = hex_to_int (str[i+2]);
330 if (a >= 0 && b >= 0)
332 new_str[j++] = a << 4 | b;
336 new_str[j++] = str[i];
339 new_str[j++] = str[i];
341 else if (str[i] == '+')
344 new_str[j++] = str[i];