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