0952ffbe32c0162b42aba7774408f86447549998
[m6w6/ext-http] / http_headers_api.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2006, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18
19 #include "php_http.h"
20
21 #include "ext/standard/url.h"
22 #include "ext/standard/php_string.h"
23
24 #include "php_http_api.h"
25 #include "php_http_headers_api.h"
26
27 #ifndef HTTP_DBG_NEG
28 # define HTTP_DBG_NEG 0
29 #endif
30
31 /* {{{ */
32 PHP_MINIT_FUNCTION(http_headers)
33 {
34 HTTP_LONG_CONSTANT("HTTP_REDIRECT", HTTP_REDIRECT);
35 HTTP_LONG_CONSTANT("HTTP_REDIRECT_PERM", HTTP_REDIRECT_PERM);
36 HTTP_LONG_CONSTANT("HTTP_REDIRECT_POST", HTTP_REDIRECT_POST);
37 HTTP_LONG_CONSTANT("HTTP_REDIRECT_TEMP", HTTP_REDIRECT_TEMP);
38
39 return SUCCESS;
40 }
41 /* }}} */
42
43 /* {{{ static int http_sort_q(const void *, const void *) */
44 static int http_sort_q(const void *a, const void *b TSRMLS_DC)
45 {
46 Bucket *f, *s;
47 zval result, *first, *second;
48
49 f = *((Bucket **) a);
50 s = *((Bucket **) b);
51
52 first = *((zval **) f->pData);
53 second= *((zval **) s->pData);
54
55 if (numeric_compare_function(&result, first, second TSRMLS_CC) != SUCCESS) {
56 return 0;
57 }
58 return (Z_LVAL(result) > 0 ? -1 : (Z_LVAL(result) < 0 ? 1 : 0));
59 }
60 /* }}} */
61
62 /* {{{ char *http_negotiate_language_func */
63 char *_http_negotiate_language_func(const char *test, double *quality, HashTable *supported TSRMLS_DC)
64 {
65 zval **value;
66 HashPosition pos;
67 const char *dash_test;
68
69 FOREACH_HASH_VAL(pos, supported, value) {
70 #if HTTP_DBG_NEG
71 fprintf(stderr, "strcasecmp('%s', '%s')\n", Z_STRVAL_PP(value), test);
72 #endif
73 if (!strcasecmp(Z_STRVAL_PP(value), test)) {
74 return Z_STRVAL_PP(value);
75 }
76 }
77
78 /* no distinct match found, so try primaries */
79 if ((dash_test = strchr(test, '-'))) {
80 FOREACH_HASH_VAL(pos, supported, value) {
81 int len = dash_test - test;
82 #if HTTP_DBG_NEG
83 fprintf(stderr, "strncasecmp('%s', '%s', %d)\n", Z_STRVAL_PP(value), test, len);
84 #endif
85 if ( (!strncasecmp(Z_STRVAL_PP(value), test, len)) &&
86 ( (Z_STRVAL_PP(value)[len] == '\0') ||
87 (Z_STRVAL_PP(value)[len] == '-'))) {
88 *quality *= .9;
89 return Z_STRVAL_PP(value);
90 }
91 }
92 }
93
94 return NULL;
95 }
96 /* }}} */
97
98 /* {{{ char *http_negotiate_default_func */
99 char *_http_negotiate_default_func(const char *test, double *quality, HashTable *supported TSRMLS_DC)
100 {
101 zval **value;
102 HashPosition pos;
103
104 FOREACH_HASH_VAL(pos, supported, value) {
105 #if HTTP_DBG_NEG
106 fprintf(stderr, "strcasecmp('%s', '%s')\n", Z_STRVAL_PP(value), test);
107 #endif
108 if (!strcasecmp(Z_STRVAL_PP(value), test)) {
109 return Z_STRVAL_PP(value);
110 }
111 }
112
113 return NULL;
114 }
115 /* }}} */
116
117 /* {{{ HashTable *http_negotiate_q(const char *, HashTable *, negotiate_func_t) */
118 PHP_HTTP_API HashTable *_http_negotiate_q(const char *header, HashTable *supported, negotiate_func_t neg TSRMLS_DC)
119 {
120 zval *accept;
121 HashTable *result = NULL;
122
123 #if HTTP_DBG_NEG
124 fprintf(stderr, "Reading header %s: ", header);
125 #endif
126 HTTP_GSC(accept, header, NULL);
127 #if HTTP_DBG_NEG
128 fprintf(stderr, "%s\n", Z_STRVAL_P(accept));
129 #endif
130
131 if (Z_STRLEN_P(accept)) {
132 zval ex_arr, ex_del;
133
134 INIT_PZVAL(&ex_del);
135 INIT_PZVAL(&ex_arr);
136 ZVAL_STRINGL(&ex_del, ",", 1, 0);
137 array_init(&ex_arr);
138
139 php_explode(&ex_del, accept, &ex_arr, -1);
140
141 if (zend_hash_num_elements(Z_ARRVAL(ex_arr)) > 0) {
142 int i = 0;
143 HashPosition pos;
144 zval **entry, array;
145
146 INIT_PZVAL(&array);
147 array_init(&array);
148
149 FOREACH_HASH_VAL(pos, Z_ARRVAL(ex_arr), entry) {
150 int ident_len;
151 double quality;
152 char *selected, *identifier, *freeme;
153 const char *separator;
154
155 #if HTTP_DBG_NEG
156 fprintf(stderr, "Checking %s\n", Z_STRVAL_PP(entry));
157 #endif
158
159 if ((separator = strchr(Z_STRVAL_PP(entry), ';'))) {
160 const char *ptr = separator;
161
162 while (*++ptr && !isdigit(*ptr) && '.' != *ptr);
163
164 quality = atof(ptr);
165 identifier = estrndup(Z_STRVAL_PP(entry), ident_len = separator - Z_STRVAL_PP(entry));
166 } else {
167 quality = 1000.0 - i++;
168 identifier = estrndup(Z_STRVAL_PP(entry), ident_len = Z_STRLEN_PP(entry));
169 }
170 freeme = identifier;
171
172 while (isspace(*identifier)) {
173 ++identifier;
174 --ident_len;
175 }
176 while (ident_len && isspace(identifier[ident_len - 1])) {
177 identifier[--ident_len] = '\0';
178 }
179
180 if ((selected = neg(identifier, &quality, supported TSRMLS_CC))) {
181 /* don't overwrite previously set with higher quality */
182 if (!zend_hash_exists(Z_ARRVAL(array), selected, strlen(selected) + 1)) {
183 add_assoc_double(&array, selected, quality);
184 }
185 }
186
187 efree(freeme);
188 }
189
190 result = Z_ARRVAL(array);
191 zend_hash_sort(result, zend_qsort, http_sort_q, 0 TSRMLS_CC);
192 }
193
194 zval_dtor(&ex_arr);
195 }
196
197 return result;
198 }
199 /* }}} */
200
201 /* {{{ http_range_status http_get_request_ranges(HashTable *ranges, size_t) */
202 PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, size_t length TSRMLS_DC)
203 {
204 zval *zrange;
205 char *range, c;
206 long begin = -1, end = -1, *ptr;
207
208 HTTP_GSC(zrange, "HTTP_RANGE", RANGE_NO);
209 range = Z_STRVAL_P(zrange);
210
211 if (strncmp(range, "bytes=", sizeof("bytes=") - 1)) {
212 return RANGE_NO;
213 }
214
215 ptr = &begin;
216 range += sizeof("bytes=") - 1;
217
218 do {
219 switch (c = *(range++))
220 {
221 case '0':
222 /* allow 000... - shall we? */
223 if (*ptr != -10) {
224 *ptr *= 10;
225 }
226 break;
227
228 case '1': case '2': case '3':
229 case '4': case '5': case '6':
230 case '7': case '8': case '9':
231 /*
232 * If the value of the pointer is already set (non-negative)
233 * then multiply its value by ten and add the current value,
234 * else initialise the pointers value with the current value
235 * --
236 * This let us recognize empty fields when validating the
237 * ranges, i.e. a "-10" for begin and "12345" for the end
238 * was the following range request: "Range: bytes=0-12345";
239 * While a "-1" for begin and "12345" for the end would
240 * have been: "Range: bytes=-12345".
241 */
242 if (*ptr > 0) {
243 *ptr *= 10;
244 *ptr += c - '0';
245 } else {
246 *ptr = c - '0';
247 }
248 break;
249
250 case '-':
251 ptr = &end;
252 break;
253
254 case ' ':
255 /* IE - ignore for now */
256 break;
257
258 case 0:
259 case ',':
260
261 if (length) {
262 /* validate ranges */
263 switch (begin)
264 {
265 /* "0-12345" */
266 case -10:
267 /* "0-" */
268 if (end == -1) {
269 return RANGE_NO;
270 }
271 /* "0-0" or overflow */
272 if (end == -10 || length <= (size_t) end) {
273 return RANGE_ERR;
274 }
275 begin = 0;
276 break;
277
278 /* "-12345" */
279 case -1:
280 /* "-", "-0" or overflow */
281 if (end == -1 || end == -10 || length <= (size_t) end) {
282 return RANGE_ERR;
283 }
284 begin = length - end;
285 end = length - 1;
286 break;
287
288 /* "12345-(xxx)" */
289 default:
290 switch (end)
291 {
292 /* "12345-0" */
293 case -10:
294 return RANGE_ERR;
295 break;
296
297 /* "12345-" */
298 case -1:
299 if (length <= (size_t) begin) {
300 return RANGE_ERR;
301 }
302 end = length - 1;
303 break;
304
305 /* "12345-67890" */
306 default:
307 if ( (length <= (size_t) begin) ||
308 (length <= (size_t) end) ||
309 (end < begin)) {
310 return RANGE_ERR;
311 }
312 break;
313 }
314 break;
315 }
316 }
317 {
318 zval *zentry;
319 MAKE_STD_ZVAL(zentry);
320 array_init(zentry);
321 add_index_long(zentry, 0, begin);
322 add_index_long(zentry, 1, end);
323 zend_hash_next_index_insert(ranges, &zentry, sizeof(zval *), NULL);
324
325 begin = -1;
326 end = -1;
327 ptr = &begin;
328 }
329 break;
330
331 default:
332 return RANGE_NO;
333 break;
334 }
335 } while (c != 0);
336
337 return RANGE_OK;
338 }
339 /* }}} */
340
341 /* {{{ STATUS http_parse_headers(char *, HashTable *, zend_bool) */
342 PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *headers, zend_bool prettify,
343 http_info_callback callback_func, void **callback_data TSRMLS_DC)
344 {
345 const char *colon = NULL, *line = NULL, *begin = header;
346 const char *body = http_locate_body(header);
347 size_t header_len;
348 zval array;
349
350 INIT_ZARR(array, headers);
351
352 if (body) {
353 header_len = body - header;
354 } else {
355 header_len = strlen(header) + 1;
356 }
357 line = header;
358
359 if (header_len) do {
360 int value_len = 0;
361 switch (*line++)
362 {
363 case ':':
364 if (!colon) {
365 colon = line - 1;
366 }
367 break;
368
369 case 0:
370 --value_len; /* we don't have CR so value length is one char less */
371 case '\n':
372 if ((!*(line - 1)) || ((*line != ' ') && (*line != '\t'))) {
373 http_info i;
374
375 /* response/request line */
376 if (SUCCESS == http_info_parse(header, &i)) {
377 callback_func(callback_data, &headers, &i TSRMLS_CC);
378 http_info_dtor(&i);
379 Z_ARRVAL(array) = headers;
380 } else
381
382 /* "header: value" pair */
383 if (colon) {
384
385 /* skip empty key */
386 if (header != colon) {
387 zval **previous = NULL;
388 char *value;
389 int keylen = colon - header;
390 char *key = estrndup(header, keylen);
391
392 if (prettify) {
393 key = pretty_key(key, keylen, 1, 1);
394 }
395
396 value_len += line - colon - 1;
397
398 /* skip leading ws */
399 while (isspace(*(++colon))) --value_len;
400 /* skip trailing ws */
401 while (isspace(colon[value_len - 1])) --value_len;
402
403 if (value_len > 0) {
404 value = estrndup(colon, value_len);
405 } else {
406 value = estrdup("");
407 value_len = 0;
408 }
409
410 /* if we already have got such a header make an array of those */
411 if (SUCCESS == zend_hash_find(headers, key, keylen + 1, (void **) &previous)) {
412 /* convert to array */
413 if (Z_TYPE_PP(previous) != IS_ARRAY) {
414 convert_to_array(*previous);
415 }
416 add_next_index_stringl(*previous, value, value_len, 0);
417 } else {
418 add_assoc_stringl(&array, key, value, value_len, 0);
419 }
420 efree(key);
421 }
422 }
423 colon = NULL;
424 value_len = 0;
425 header += line - header;
426 }
427 break;
428 }
429 } while (header_len > (size_t) (line - begin));
430
431 return SUCCESS;
432 }
433 /* }}} */
434
435 /* {{{ void http_get_request_headers_ex(HashTable *, zend_bool) */
436 PHP_HTTP_API void _http_get_request_headers_ex(HashTable *headers, zend_bool prettify TSRMLS_DC)
437 {
438 char *key = NULL;
439 ulong idx = 0;
440 uint keylen = 0;
441 zval array, **hsv;
442 HashPosition pos;
443
444 Z_ARRVAL(array) = headers;
445
446 if (SUCCESS == zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &hsv)) {
447 FOREACH_KEYLEN(pos, *hsv, key, keylen, idx) {
448 if (key && keylen > 6 && !strncmp(key, "HTTP_", 5)) {
449 zval **header, *orig;
450
451 key += 5;
452 keylen -= 6;
453 if (prettify) {
454 key = pretty_key(estrndup(key, keylen), keylen, 1, 1);
455 }
456
457 zend_hash_get_current_data_ex(Z_ARRVAL_PP(hsv), (void **) &header, &pos);
458
459 orig = *header;
460 convert_to_string_ex(header);
461 add_assoc_stringl(&array, key, Z_STRVAL_PP(header), Z_STRLEN_PP(header), 1);
462 if (orig != *header) {
463 zval_ptr_dtor(header);
464 }
465
466 if (prettify) {
467 efree(key);
468 }
469
470 key = NULL;
471 keylen = 0;
472 }
473 }
474 }
475 }
476 /* }}} */
477
478 /* {{{ zend_bool http_match_request_header(char *, char *) */
479 PHP_HTTP_API zend_bool _http_match_request_header_ex(const char *header, const char *value, zend_bool match_case TSRMLS_DC)
480 {
481 char *name;
482 uint name_len = strlen(header);
483 zend_bool result = 0;
484 HashTable headers;
485 zval **data;
486
487 name = pretty_key(estrndup(header, name_len), name_len, 1, 1);
488 zend_hash_init(&headers, 0, NULL, ZVAL_PTR_DTOR, 0);
489 http_get_request_headers_ex(&headers, 1);
490
491 if (SUCCESS == zend_hash_find(&headers, name, name_len+1, (void **) &data)) {
492 result = (match_case ? strcmp(Z_STRVAL_PP(data), value) : strcasecmp(Z_STRVAL_PP(data), value)) ? 0 : 1;
493 }
494 zend_hash_destroy(&headers);
495 efree(name);
496
497 return result;
498 }
499 /* }}} */
500
501
502 /*
503 * Local variables:
504 * tab-width: 4
505 * c-basic-offset: 4
506 * End:
507 * vim600: noet sw=4 ts=4 fdm=marker
508 * vim<600: noet sw=4 ts=4
509 */
510