- use &EG(symbol_table)'s HTTP_SERVER_VARS instead of
[m6w6/ext-http] / http_headers_api.c
1 /*
2 +----------------------------------------------------------------------+
3 | PECL :: http |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
14 */
15
16 /* $Id$ */
17
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21 #include "php.h"
22
23 #include "ext/standard/php_string.h"
24 #include "ext/standard/url.h"
25
26 #include "php_http.h"
27 #include "php_http_std_defs.h"
28 #include "php_http_api.h"
29 #include "php_http_headers_api.h"
30
31 #include <ctype.h>
32
33 ZEND_EXTERN_MODULE_GLOBALS(http);
34
35 /* {{{ static int http_sort_q(const void *, const void *) */
36 static int http_sort_q(const void *a, const void *b TSRMLS_DC)
37 {
38 Bucket *f, *s;
39 zval result, *first, *second;
40
41 f = *((Bucket **) a);
42 s = *((Bucket **) b);
43
44 first = *((zval **) f->pData);
45 second= *((zval **) s->pData);
46
47 if (numeric_compare_function(&result, first, second TSRMLS_CC) != SUCCESS) {
48 return 0;
49 }
50 return (Z_LVAL(result) > 0 ? -1 : (Z_LVAL(result) < 0 ? 1 : 0));
51 }
52 /* }}} */
53
54 /* {{{ char *http_negotiate_q(char *, HashTable *, char *) */
55 PHP_HTTP_API char *_http_negotiate_q(const char *entry, const HashTable *supported, const char *def TSRMLS_DC)
56 {
57 zval *zaccept, zdelim, zarray, zentries, **zentry, **zsupp;
58 char *q_ptr = NULL, *key = NULL;
59 int i = 0;
60 ulong idx = 0;
61 double qual;
62
63 HTTP_GSC(zaccept, entry, estrdup(def));
64
65 array_init(&zarray);
66 array_init(&zentries);
67
68 Z_STRVAL(zdelim) = ",";
69 Z_STRLEN(zdelim) = 1;
70
71 php_explode(&zdelim, zaccept, &zarray, -1);
72
73 FOREACH_HASH_VAL(Z_ARRVAL(zarray), zentry) {
74 if (q_ptr = strrchr(Z_STRVAL_PP(zentry), ';')) {
75 qual = strtod(q_ptr + 3, NULL);
76 *q_ptr = 0;
77 q_ptr = NULL;
78 } else {
79 qual = 1000.0 - i++;
80 }
81 /* TODO: support primaries only, too */
82 FOREACH_HASH_VAL((HashTable *)supported, zsupp) {
83 if (!strcasecmp(Z_STRVAL_PP(zsupp), Z_STRVAL_PP(zentry))) {
84 add_assoc_double(&zentries, Z_STRVAL_PP(zsupp), qual);
85 break;
86 }
87 }
88 }
89 zval_dtor(&zarray);
90
91 zend_hash_sort(Z_ARRVAL(zentries), zend_qsort, http_sort_q, 0 TSRMLS_CC);
92
93 FOREACH_HASH_KEY(Z_ARRVAL(zentries), key, idx) {
94 if (key) {
95 key = estrdup(key);
96 zval_dtor(&zentries);
97 return key;
98 }
99 }
100 zval_dtor(&zentries);
101
102 return estrdup(def);
103 }
104 /* }}} */
105
106 /* {{{ http_range_status http_get_request_ranges(HashTable *ranges, size_t) */
107 PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, size_t length TSRMLS_DC)
108 {
109 zval *zrange;
110 char *range, c;
111 long begin = -1, end = -1, *ptr;
112
113 HTTP_GSC(zrange, "HTTP_RANGE", RANGE_NO);
114 range = Z_STRVAL_P(zrange);
115
116 if (strncmp(range, "bytes=", sizeof("bytes=") - 1)) {
117 return RANGE_NO;
118 }
119
120 ptr = &begin;
121 range += sizeof("bytes=") - 1;
122
123 do {
124 switch (c = *(range++))
125 {
126 case '0':
127 /* allow 000... - shall we? */
128 if (*ptr != -10) {
129 *ptr *= 10;
130 }
131 break;
132
133 case '1': case '2': case '3':
134 case '4': case '5': case '6':
135 case '7': case '8': case '9':
136 /*
137 * If the value of the pointer is already set (non-negative)
138 * then multiply its value by ten and add the current value,
139 * else initialise the pointers value with the current value
140 * --
141 * This let us recognize empty fields when validating the
142 * ranges, i.e. a "-10" for begin and "12345" for the end
143 * was the following range request: "Range: bytes=0-12345";
144 * While a "-1" for begin and "12345" for the end would
145 * have been: "Range: bytes=-12345".
146 */
147 if (*ptr > 0) {
148 *ptr *= 10;
149 *ptr += c - '0';
150 } else {
151 *ptr = c - '0';
152 }
153 break;
154
155 case '-':
156 ptr = &end;
157 break;
158
159 case ' ':
160 /* IE - ignore for now */
161 break;
162
163 case 0:
164 case ',':
165
166 if (length) {
167 /* validate ranges */
168 switch (begin)
169 {
170 /* "0-12345" */
171 case -10:
172 /* "0-" */
173 if (end == -1) {
174 return RANGE_NO;
175 }
176 /* "0-0" or overflow */
177 if (end == -10 || length <= (size_t) end) {
178 return RANGE_ERR;
179 }
180 begin = 0;
181 break;
182
183 /* "-12345" */
184 case -1:
185 /* "-", "-0" or overflow */
186 if (end == -1 || end == -10 || length <= (size_t) end) {
187 return RANGE_ERR;
188 }
189 begin = length - end;
190 end = length - 1;
191 break;
192
193 /* "12345-(xxx)" */
194 default:
195 switch (end)
196 {
197 /* "12345-0" */
198 case -10:
199 return RANGE_ERR;
200 break;
201
202 /* "12345-" */
203 case -1:
204 if (length <= (size_t) begin) {
205 return RANGE_ERR;
206 }
207 end = length - 1;
208 break;
209
210 /* "12345-67890" */
211 default:
212 if ( (length <= (size_t) begin) ||
213 (length <= (size_t) end) ||
214 (end < begin)) {
215 return RANGE_ERR;
216 }
217 break;
218 }
219 break;
220 }
221 }
222 {
223 zval *zentry;
224 MAKE_STD_ZVAL(zentry);
225 array_init(zentry);
226 add_index_long(zentry, 0, begin);
227 add_index_long(zentry, 1, end);
228 zend_hash_next_index_insert(ranges, &zentry, sizeof(zval *), NULL);
229
230 begin = -1;
231 end = -1;
232 ptr = &begin;
233 }
234 break;
235
236 default:
237 return RANGE_NO;
238 break;
239 }
240 } while (c != 0);
241
242 return RANGE_OK;
243 }
244 /* }}} */
245
246 /* {{{ STATUS http_parse_headers(char *, HashTable *, zend_bool) */
247 PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *headers, zend_bool prettify, http_parse_headers_callback_t func, void **callback_data TSRMLS_DC)
248 {
249 const char *colon = NULL, *line = NULL, *begin = header, *crlfcrlf = NULL;
250 size_t header_len;
251 zval array;
252
253 Z_ARRVAL(array) = headers;
254
255 if (crlfcrlf = strstr(header, HTTP_CRLF HTTP_CRLF)) {
256 header_len = crlfcrlf - header + lenof(HTTP_CRLF);
257 } else {
258 header_len = strlen(header) + 1;
259 }
260
261
262 if (header_len < 2 || !strchr(header, ':')) {
263 http_error(HE_WARNING, HTTP_E_MALFORMED_HEADERS, "Cannot parse too short or malformed HTTP headers");
264 return FAILURE;
265 }
266
267 line = header;
268
269 while (header_len >= (size_t) (line - begin)) {
270 int value_len = 0;
271
272 switch (*line++)
273 {
274 case 0:
275 --value_len; /* we don't have CR so value length is one char less */
276 case '\n':
277 if ((!(*line - 1)) || ((*line != ' ') && (*line != '\t'))) {
278 /* response/request line */
279 if ( (!strncmp(header, "HTTP/1.", lenof("HTTP/1."))) ||
280 (!strncmp(line - lenof("HTTP/1.x" HTTP_CRLF) + value_len, "HTTP/1.", lenof("HTTP/1.")))) {
281 if (func) {
282 func(header, &headers, callback_data TSRMLS_CC);
283 Z_ARRVAL(array) = headers;
284 }
285 } else
286
287 /* "header: value" pair */
288 if (colon) {
289
290 /* skip empty key */
291 if (header != colon) {
292 zval **previous = NULL;
293 char *value;
294 int keylen = colon - header;
295 char *key = estrndup(header, keylen);
296
297 if (prettify) {
298 key = pretty_key(key, keylen, 1, 1);
299 }
300
301 value_len += line - colon - 1;
302
303 /* skip leading ws */
304 while (isspace(*(++colon))) --value_len;
305 /* skip trailing ws */
306 while (isspace(colon[value_len - 1])) --value_len;
307
308 if (value_len > 0) {
309 value = estrndup(colon, value_len);
310 } else {
311 value = estrdup("");
312 value_len = 0;
313 }
314
315 /* if we already have got such a header make an array of those */
316 if (SUCCESS == zend_hash_find(headers, key, keylen + 1, (void **) &previous)) {
317 /* convert to array */
318 if (Z_TYPE_PP(previous) != IS_ARRAY) {
319 convert_to_array(*previous);
320 }
321 add_next_index_stringl(*previous, value, value_len, 0);
322 } else {
323 add_assoc_stringl(&array, key, value, value_len, 0);
324 }
325 efree(key);
326 }
327 }
328 colon = NULL;
329 value_len = 0;
330 header += line - header;
331 }
332 break;
333
334 case ':':
335 if (!colon) {
336 colon = line - 1;
337 }
338 break;
339 }
340 }
341 return SUCCESS;
342 }
343 /* }}} */
344
345 PHP_HTTP_API void _http_parse_headers_default_callback(const char *http_line, HashTable **headers, void **cb_data TSRMLS_DC)
346 {
347 zval array;
348 char *crlf = NULL;
349 size_t line_length;
350 Z_ARRVAL(array) = *headers;
351
352 if (crlf = strstr(http_line, HTTP_CRLF)) {
353 line_length = crlf - http_line;
354 } else {
355 line_length = strlen(http_line);
356 }
357
358 /* response */
359 if (!strncmp(http_line, "HTTP/1.", lenof("HTTP/1."))) {
360 char *status = estrndup(http_line + lenof("HTTP/1.x "), line_length - lenof("HTTP/1.x "));
361 add_assoc_stringl(&array, "Response Status", status, line_length - lenof("HTTP/1.x "), 0);
362 } else
363 /* request */
364 if (!strncmp(http_line + line_length - lenof("HTTP/1.x"), "HTTP/1.", lenof("HTTP/1."))) {
365 char *sep = strchr(http_line, ' ');
366 char *url = estrndup(sep + 1, strstr(sep, "HTTP/1.") - sep + 1 + 1);
367 char *met = estrndup(http_line, sep - http_line);
368
369 add_assoc_stringl(&array, "Request Method", met, sep - http_line, 0);
370 add_assoc_stringl(&array, "Request Uri", url, strstr(sep, "HTTP/1.") - sep + 1 + 1, 0);
371 }
372 }
373
374 /* {{{ void http_get_request_headers_ex(HashTable *, zend_bool) */
375 PHP_HTTP_API void _http_get_request_headers_ex(HashTable *headers, zend_bool prettify TSRMLS_DC)
376 {
377 char *key = NULL;
378 ulong idx = 0;
379 zval array, **hsv;
380
381 Z_ARRVAL(array) = headers;
382
383 if (SUCCESS == zend_hash_find(&EG(symbol_table), "HTTP_SERVER_VARS", sizeof("HTTP_SERVER_VARS"), (void **) &hsv)) {
384 FOREACH_KEY(*hsv, key, idx) {
385 if (key && !strncmp(key, "HTTP_", 5)) {
386 zval **header;
387
388 key += 5;
389 if (prettify) {
390 key = pretty_key(key, strlen(key), 1, 1);
391 }
392
393 zend_hash_get_current_data(Z_ARRVAL_PP(hsv), (void **) &header);
394 add_assoc_stringl(&array, key, Z_STRVAL_PP(header), Z_STRLEN_PP(header), 1);
395 key = NULL;
396 }
397 }
398 }
399 }
400 /* }}} */
401
402 /* {{{ zend_bool http_match_request_header(char *, char *) */
403 PHP_HTTP_API zend_bool _http_match_request_header_ex(const char *header, const char *value, zend_bool match_case TSRMLS_DC)
404 {
405 char *name, *key = NULL;
406 ulong idx;
407 zend_bool result = 0;
408 HashTable headers;
409
410 name = pretty_key(estrdup(header), strlen(header), 1, 1);
411 zend_hash_init(&headers, 0, NULL, ZVAL_PTR_DTOR, 0);
412 http_get_request_headers_ex(&headers, 1);
413
414 FOREACH_HASH_KEY(&headers, key, idx) {
415 if (key && (!strcmp(key, name))) {
416 zval **data;
417
418 if (SUCCESS == zend_hash_get_current_data(&headers, (void **) &data)) {
419 result = (match_case ? strcmp(Z_STRVAL_PP(data), value) : strcasecmp(Z_STRVAL_PP(data), value)) ? 0 : 1;
420 }
421 break;
422 }
423 }
424
425 zend_hash_destroy(&headers);
426 efree(name);
427
428 return result;
429 }
430 /* }}} */
431
432
433 /*
434 * Local variables:
435 * tab-width: 4
436 * c-basic-offset: 4
437 * End:
438 * vim600: noet sw=4 ts=4 fdm=marker
439 * vim<600: noet sw=4 ts=4
440 */
441