2 +--------------------------------------------------------------------+
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-2007, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
15 #define HTTP_WANT_SAPI
18 #include "ext/standard/url.h"
19 #include "ext/standard/php_string.h"
21 #include "php_http_api.h"
22 #include "php_http_headers_api.h"
25 # define HTTP_DBG_NEG 0
28 /* {{{ static void http_grab_response_headers(void *, void *) */
29 static void http_grab_response_headers(void *data
, void *arg TSRMLS_DC
)
31 phpstr_appendl(PHPSTR(arg
), ((sapi_header_struct
*)data
)->header
);
32 phpstr_appends(PHPSTR(arg
), HTTP_CRLF
);
36 /* {{{ static int http_sort_q(const void *, const void *) */
37 static int http_sort_q(const void *a
, const void *b TSRMLS_DC
)
40 zval result
, *first
, *second
;
45 first
= *((zval
**) f
->pData
);
46 second
= *((zval
**) s
->pData
);
48 if (numeric_compare_function(&result
, first
, second TSRMLS_CC
) != SUCCESS
) {
51 return (Z_LVAL(result
) > 0 ? -1 : (Z_LVAL(result
) < 0 ? 1 : 0));
55 /* {{{ char *http_negotiate_language_func */
56 char *_http_negotiate_language_func(const char *test
, double *quality
, HashTable
*supported TSRMLS_DC
)
60 const char *dash_test
;
62 FOREACH_HASH_VAL(pos
, supported
, value
) {
64 fprintf(stderr
, "strcasecmp('%s', '%s')\n", Z_STRVAL_PP(value
), test
);
66 if (!strcasecmp(Z_STRVAL_PP(value
), test
)) {
67 return Z_STRVAL_PP(value
);
71 /* no distinct match found, so try primaries */
72 if ((dash_test
= strchr(test
, '-'))) {
73 FOREACH_HASH_VAL(pos
, supported
, value
) {
74 int len
= dash_test
- test
;
76 fprintf(stderr
, "strncasecmp('%s', '%s', %d)\n", Z_STRVAL_PP(value
), test
, len
);
78 if ( (!strncasecmp(Z_STRVAL_PP(value
), test
, len
)) &&
79 ( (Z_STRVAL_PP(value
)[len
] == '\0') ||
80 (Z_STRVAL_PP(value
)[len
] == '-'))) {
82 return Z_STRVAL_PP(value
);
91 /* {{{ char *http_negotiate_default_func */
92 char *_http_negotiate_default_func(const char *test
, double *quality
, HashTable
*supported TSRMLS_DC
)
97 FOREACH_HASH_VAL(pos
, supported
, value
) {
99 fprintf(stderr
, "strcasecmp('%s', '%s')\n", Z_STRVAL_PP(value
), test
);
101 if (!strcasecmp(Z_STRVAL_PP(value
), test
)) {
102 return Z_STRVAL_PP(value
);
110 /* {{{ HashTable *http_negotiate_q(const char *, HashTable *, negotiate_func_t) */
111 PHP_HTTP_API HashTable
*_http_negotiate_q(const char *header
, HashTable
*supported
, negotiate_func_t neg TSRMLS_DC
)
114 HashTable
*result
= NULL
;
117 fprintf(stderr
, "Reading header %s: ", header
);
119 if (!(accept
= http_get_server_var(header
, 1))) {
123 fprintf(stderr
, "%s\n", Z_STRVAL_P(accept
));
126 if (Z_STRLEN_P(accept
)) {
131 ZVAL_STRINGL(&ex_del
, ",", 1, 0);
134 php_explode(&ex_del
, accept
, &ex_arr
, -1);
136 if (zend_hash_num_elements(Z_ARRVAL(ex_arr
)) > 0) {
144 FOREACH_HASH_VAL(pos
, Z_ARRVAL(ex_arr
), entry
) {
147 char *selected
, *identifier
, *freeme
;
148 const char *separator
;
151 fprintf(stderr
, "Checking %s\n", Z_STRVAL_PP(entry
));
154 if ((separator
= strchr(Z_STRVAL_PP(entry
), ';'))) {
155 const char *ptr
= separator
;
157 while (*++ptr
&& !HTTP_IS_CTYPE(digit
, *ptr
) && '.' != *ptr
);
159 quality
= zend_strtod(ptr
, NULL
);
160 identifier
= estrndup(Z_STRVAL_PP(entry
), ident_len
= separator
- Z_STRVAL_PP(entry
));
162 quality
= 1000.0 - i
++;
163 identifier
= estrndup(Z_STRVAL_PP(entry
), ident_len
= Z_STRLEN_PP(entry
));
167 while (HTTP_IS_CTYPE(space
, *identifier
)) {
171 while (ident_len
&& HTTP_IS_CTYPE(space
, identifier
[ident_len
- 1])) {
172 identifier
[--ident_len
] = '\0';
175 if ((selected
= neg(identifier
, &quality
, supported TSRMLS_CC
))) {
176 /* don't overwrite previously set with higher quality */
177 if (!zend_hash_exists(Z_ARRVAL(array
), selected
, strlen(selected
) + 1)) {
178 add_assoc_double(&array
, selected
, quality
);
185 result
= Z_ARRVAL(array
);
186 zend_hash_sort(result
, zend_qsort
, http_sort_q
, 0 TSRMLS_CC
);
196 /* {{{ http_range_status http_get_request_ranges(HashTable *ranges, size_t) */
197 PHP_HTTP_API http_range_status
_http_get_request_ranges(HashTable
*ranges
, size_t length TSRMLS_DC
)
201 long begin
= -1, end
= -1, *ptr
;
203 if ( !(zrange
= http_get_server_var("HTTP_RANGE", 1)) ||
204 (size_t) Z_STRLEN_P(zrange
) < lenof("bytes=") || strncmp(Z_STRVAL_P(zrange
), "bytes=", lenof("bytes="))) {
207 range
= Z_STRVAL_P(zrange
) + lenof("bytes=");
211 switch (c
= *(range
++)) {
213 /* allow 000... - shall we? */
219 case '1': case '2': case '3':
220 case '4': case '5': case '6':
221 case '7': case '8': case '9':
223 * If the value of the pointer is already set (non-negative)
224 * then multiply its value by ten and add the current value,
225 * else initialise the pointers value with the current value
227 * This let us recognize empty fields when validating the
228 * ranges, i.e. a "-10" for begin and "12345" for the end
229 * was the following range request: "Range: bytes=0-12345";
230 * While a "-1" for begin and "12345" for the end would
231 * have been: "Range: bytes=-12345".
252 /* validate ranges */
267 if (length
<= (size_t) end
) {
277 /* "-", "-0" or overflow */
278 if (end
== -1 || end
== -10 || length
<= (size_t) end
) {
281 begin
= length
- end
;
294 if (length
<= (size_t) begin
) {
302 if ( (length
<= (size_t) begin
) ||
303 (length
<= (size_t) end
) ||
314 MAKE_STD_ZVAL(zentry
);
316 add_index_long(zentry
, 0, begin
);
317 add_index_long(zentry
, 1, end
);
318 zend_hash_next_index_insert(ranges
, &zentry
, sizeof(zval
*), NULL
);
335 /* {{{ STATUS http_parse_headers(char *, HashTable *, zend_bool) */
336 PHP_HTTP_API STATUS
_http_parse_headers_ex(const char *header
, HashTable
*headers
, zend_bool prettify
,
337 http_info_callback callback_func
, void **callback_data TSRMLS_DC
)
339 const char *colon
= NULL
, *line
= NULL
;
342 INIT_ZARR(array
, headers
);
344 /* skip leading ws */
345 while (HTTP_IS_CTYPE(space
, *header
)) ++header
;
348 #define MORE_HEADERS (*(line-1) && !(*(line-1) == '\n' && (*line == '\n' || *line == '\r')))
360 --value_len
; /* we don't have CR so value length is one char less */
362 if ((!*(line
- 1)) || ((*line
!= ' ') && (*line
!= '\t'))) {
365 if (SUCCESS
== http_info_parse(header
, &i
)) {
366 /* response/request line */
367 callback_func(callback_data
, &headers
, &i TSRMLS_CC
);
369 Z_ARRVAL(array
) = headers
;
371 /* "header: value" pair */
372 if (header
!= colon
) {
373 int keylen
= colon
- header
;
374 const char *key
= header
;
376 /* skip leading ws */
377 while (keylen
&& HTTP_IS_CTYPE(space
, *key
)) --keylen
, ++key
;
378 /* skip trailing ws */
379 while (keylen
&& HTTP_IS_CTYPE(space
, key
[keylen
- 1])) --keylen
;
382 zval
**previous
= NULL
;
384 char *keydup
= estrndup(key
, keylen
);
387 keydup
= pretty_key(keydup
, keylen
, 1, 1);
390 value_len
+= line
- colon
- 1;
392 /* skip leading ws */
393 while (HTTP_IS_CTYPE(space
, *(++colon
))) --value_len
;
394 /* skip trailing ws */
395 while (HTTP_IS_CTYPE(space
, colon
[value_len
- 1])) --value_len
;
398 value
= estrndup(colon
, value_len
);
404 /* if we already have got such a header make an array of those */
405 if (SUCCESS
== zend_hash_find(headers
, keydup
, keylen
+ 1, (void *) &previous
)) {
406 /* convert to array */
407 if (Z_TYPE_PP(previous
) != IS_ARRAY
) {
408 convert_to_array(*previous
);
410 add_next_index_stringl(*previous
, value
, value_len
, 0);
412 add_assoc_stringl(&array
, keydup
, value
, value_len
, 0);
416 /* empty key (" : ...") */
420 /* empty key (": ...") */
423 } else if (MORE_HEADERS
) {
424 /* a line without a colon */
429 header
+= line
- header
;
433 } while (MORE_HEADERS
);
439 /* {{{ void http_get_request_headers(HashTable *) */
440 PHP_HTTP_API
void _http_get_request_headers(HashTable
*headers TSRMLS_DC
)
442 HashKey key
= initHashKey(0);
443 zval
**hsv
, **header
;
446 if (!HTTP_G
->request
.headers
) {
447 ALLOC_HASHTABLE(HTTP_G
->request
.headers
);
448 zend_hash_init(HTTP_G
->request
.headers
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
451 zend_is_auto_global("_SERVER", lenof("_SERVER") TSRMLS_CC
);
454 if (SUCCESS
== zend_hash_find(&EG(symbol_table
), "_SERVER", sizeof("_SERVER"), (void *) &hsv
) && Z_TYPE_PP(hsv
) == IS_ARRAY
) {
455 FOREACH_KEY(pos
, *hsv
, key
) {
456 if (key
.type
== HASH_KEY_IS_STRING
&& key
.len
> 6 && !strncmp(key
.str
, "HTTP_", 5)) {
458 key
.str
= pretty_key(estrndup(key
.str
+ 5, key
.len
- 1), key
.len
- 1, 1, 1);
460 zend_hash_get_current_data_ex(Z_ARRVAL_PP(hsv
), (void *) &header
, &pos
);
461 ZVAL_ADDREF(*header
);
462 zend_hash_add(HTTP_G
->request
.headers
, key
.str
, key
.len
, (void *) header
, sizeof(zval
*), NULL
);
471 zend_hash_copy(headers
, HTTP_G
->request
.headers
, (copy_ctor_func_t
) zval_add_ref
, NULL
, sizeof(zval
*));
476 /* {{{ STATUS http_get_response_headers(HashTable *) */
477 PHP_HTTP_API STATUS
_http_get_response_headers(HashTable
*headers_ht TSRMLS_DC
)
482 phpstr_init(&headers
);
483 zend_llist_apply_with_argument(&SG(sapi_headers
).headers
, http_grab_response_headers
, &headers TSRMLS_CC
);
484 phpstr_fix(&headers
);
486 status
= http_parse_headers_ex(PHPSTR_VAL(&headers
), headers_ht
, 1);
487 phpstr_dtor(&headers
);
493 /* {{{ zend_bool http_match_request_header(char *, char *) */
494 PHP_HTTP_API zend_bool
_http_match_request_header_ex(const char *header
, const char *value
, zend_bool match_case TSRMLS_DC
)
497 uint name_len
= strlen(header
);
498 zend_bool result
= 0;
499 zval
**data
, *zvalue
;
501 http_get_request_headers(NULL
);
502 name
= pretty_key(estrndup(header
, name_len
), name_len
, 1, 1);
503 if (SUCCESS
== zend_hash_find(HTTP_G
->request
.headers
, name
, name_len
+1, (void *) &data
)) {
504 zvalue
= zval_copy(IS_STRING
, *data
);
505 result
= (match_case
? strcmp(Z_STRVAL_P(zvalue
), value
) : strcasecmp(Z_STRVAL_P(zvalue
), value
)) ? 0 : 1;
520 * vim600: noet sw=4 ts=4 fdm=marker
521 * vim<600: noet sw=4 ts=4