2 +----------------------------------------------------------------------+
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 +----------------------------------------------------------------------+
23 #include "ext/standard/php_string.h"
24 #include "ext/standard/url.h"
27 #include "php_http_std_defs.h"
28 #include "php_http_api.h"
29 #include "php_http_headers_api.h"
33 ZEND_EXTERN_MODULE_GLOBALS(http
);
35 /* {{{ static int http_sort_q(const void *, const void *) */
36 static int http_sort_q(const void *a
, const void *b TSRMLS_DC
)
39 zval result
, *first
, *second
;
44 first
= *((zval
**) f
->pData
);
45 second
= *((zval
**) s
->pData
);
47 if (numeric_compare_function(&result
, first
, second TSRMLS_CC
) != SUCCESS
) {
50 return (Z_LVAL(result
) > 0 ? -1 : (Z_LVAL(result
) < 0 ? 1 : 0));
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
)
57 zval
*zaccept
, zdelim
, zarray
, zentries
, **zentry
, **zsupp
;
58 char *q_ptr
= NULL
, *key
= NULL
;
63 HTTP_GSC(zaccept
, entry
, estrdup(def
));
66 array_init(&zentries
);
68 Z_STRVAL(zdelim
) = ",";
71 php_explode(&zdelim
, zaccept
, &zarray
, -1);
73 FOREACH_HASH_VAL(Z_ARRVAL(zarray
), zentry
) {
74 if (q_ptr
= strrchr(Z_STRVAL_PP(zentry
), ';')) {
75 qual
= strtod(q_ptr
+ 3, NULL
);
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
);
91 zend_hash_sort(Z_ARRVAL(zentries
), zend_qsort
, http_sort_q
, 0 TSRMLS_CC
);
93 FOREACH_HASH_KEY(Z_ARRVAL(zentries
), key
, idx
) {
100 zval_dtor(&zentries
);
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
)
111 long begin
= -1, end
= -1, *ptr
;
113 HTTP_GSC(zrange
, "HTTP_RANGE", RANGE_NO
);
114 range
= Z_STRVAL_P(zrange
);
116 if (strncmp(range
, "bytes=", sizeof("bytes=") - 1)) {
121 range
+= sizeof("bytes=") - 1;
124 switch (c
= *(range
++))
130 case '1': case '2': case '3':
131 case '4': case '5': case '6':
132 case '7': case '8': case '9':
134 * If the value of the pointer is already set (non-negative)
135 * then multiply its value by ten and add the current value,
136 * else initialise the pointers value with the current value
138 * This let us recognize empty fields when validating the
139 * ranges, i.e. a "-10" for begin and "12345" for the end
140 * was the following range request: "Range: bytes=0-12345";
141 * While a "-1" for begin and "12345" for the end would
142 * have been: "Range: bytes=-12345".
157 /* IE - ignore for now */
164 /* validate ranges */
173 /* "0-0" or overflow */
174 if (end
== -10 || length
<= (size_t) end
) {
182 /* "-", "-0" or overflow */
183 if (end
== -1 || end
== -10 || length
<= (size_t) end
) {
186 begin
= length
- end
;
201 if (length
<= (size_t) begin
) {
209 if ( (length
<= (size_t) begin
) ||
210 (length
<= (size_t) end
) ||
221 MAKE_STD_ZVAL(zentry
);
223 add_index_long(zentry
, 0, begin
);
224 add_index_long(zentry
, 1, end
);
225 zend_hash_next_index_insert(ranges
, &zentry
, sizeof(zval
*), NULL
);
243 /* {{{ STATUS http_parse_headers(char *, HashTable *, zend_bool) */
244 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
)
246 const char *colon
= NULL
, *line
= NULL
, *begin
= header
, *crlfcrlf
= NULL
;
250 Z_ARRVAL(array
) = headers
;
252 if (crlfcrlf
= strstr(header
, HTTP_CRLF HTTP_CRLF
)) {
253 header_len
= crlfcrlf
- header
+ lenof(HTTP_CRLF
);
255 header_len
= strlen(header
) + 1;
259 if (header_len
< 2 || !strchr(header
, ':')) {
260 http_error(HE_WARNING
, HTTP_E_MALFORMED_HEADERS
, "Cannot parse too short or malformed HTTP headers");
266 while (header_len
>= (size_t) (line
- begin
)) {
272 --value_len
; /* we don't have CR so value length is one char less */
274 if ((!(*line
- 1)) || ((*line
!= ' ') && (*line
!= '\t'))) {
275 /* response/request line */
276 if ( (!strncmp(header
, "HTTP/1.", lenof("HTTP/1."))) ||
277 (!strncmp(line
- lenof("HTTP/1.x" HTTP_CRLF
) + value_len
, "HTTP/1.", lenof("HTTP/1.")))) {
279 func(header
, &headers
, callback_data TSRMLS_CC
);
280 Z_ARRVAL(array
) = headers
;
284 /* "header: value" pair */
288 if (header
!= colon
) {
289 zval
**previous
= NULL
;
291 int keylen
= colon
- header
;
292 char *key
= estrndup(header
, keylen
);
295 key
= pretty_key(key
, keylen
, 1, 1);
298 value_len
+= line
- colon
- 1;
300 /* skip leading ws */
301 while (isspace(*(++colon
))) --value_len
;
302 /* skip trailing ws */
303 while (isspace(colon
[value_len
- 1])) --value_len
;
306 value
= estrndup(colon
, value_len
);
312 /* if we already have got such a header make an array of those */
313 if (SUCCESS
== zend_hash_find(headers
, key
, keylen
+ 1, (void **) &previous
)) {
314 /* convert to array */
315 if (Z_TYPE_PP(previous
) != IS_ARRAY
) {
316 convert_to_array(*previous
);
318 add_next_index_stringl(*previous
, value
, value_len
, 0);
320 add_assoc_stringl(&array
, key
, value
, value_len
, 0);
327 header
+= line
- header
;
342 PHP_HTTP_API
void _http_parse_headers_default_callback(const char *http_line
, HashTable
**headers
, void **cb_data TSRMLS_DC
)
347 Z_ARRVAL(array
) = *headers
;
349 if (crlf
= strstr(http_line
, HTTP_CRLF
)) {
350 line_length
= crlf
- http_line
;
352 line_length
= strlen(http_line
);
356 if (!strncmp(http_line
, "HTTP/1.", lenof("HTTP/1."))) {
357 char *status
= estrndup(http_line
+ lenof("HTTP/1.x "), line_length
- lenof("HTTP/1.x "));
358 add_assoc_stringl(&array
, "Response Status", status
, line_length
- lenof("HTTP/1.x "), 0);
361 if (!strncmp(http_line
+ line_length
- lenof("HTTP/1.x"), "HTTP/1.", lenof("HTTP/1."))) {
362 char *sep
= strchr(http_line
, ' ');
363 char *url
= estrndup(sep
+ 1, strstr(sep
, "HTTP/1.") - sep
+ 1 + 1);
364 char *met
= estrndup(http_line
, sep
- http_line
);
366 add_assoc_stringl(&array
, "Request Method", met
, sep
- http_line
, 0);
367 add_assoc_stringl(&array
, "Request Uri", url
, strstr(sep
, "HTTP/1.") - sep
+ 1 + 1, 0);
371 /* {{{ void http_get_request_headers_ex(HashTable *, zend_bool) */
372 PHP_HTTP_API
void _http_get_request_headers_ex(HashTable
*headers
, zend_bool prettify TSRMLS_DC
)
378 Z_ARRVAL(array
) = headers
;
380 FOREACH_HASH_KEY(HTTP_SERVER_VARS
, key
, idx
) {
381 if (key
&& !strncmp(key
, "HTTP_", 5)) {
386 key
= pretty_key(key
, strlen(key
), 1, 1);
389 zend_hash_get_current_data(HTTP_SERVER_VARS
, (void **) &header
);
390 add_assoc_stringl(&array
, key
, Z_STRVAL_PP(header
), Z_STRLEN_PP(header
), 1);
397 /* {{{ zend_bool http_match_request_header(char *, char *) */
398 PHP_HTTP_API zend_bool
_http_match_request_header_ex(const char *header
, const char *value
, zend_bool match_case TSRMLS_DC
)
400 char *name
, *key
= NULL
;
402 zend_bool result
= 0;
405 name
= pretty_key(estrdup(header
), strlen(header
), 1, 1);
406 zend_hash_init(&headers
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
407 http_get_request_headers_ex(&headers
, 1);
409 FOREACH_HASH_KEY(&headers
, key
, idx
) {
410 if (key
&& (!strcmp(key
, name
))) {
413 if (SUCCESS
== zend_hash_get_current_data(&headers
, (void **) &data
)) {
414 result
= (match_case
? strcmp(Z_STRVAL_PP(data
), value
) : strcasecmp(Z_STRVAL_PP(data
), value
)) ? 0 : 1;
420 zend_hash_destroy(&headers
);
433 * vim600: noet sw=4 ts=4 fdm=marker
434 * vim<600: noet sw=4 ts=4