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 +----------------------------------------------------------------------+
24 #include "ext/standard/url.h"
25 #include "ext/standard/head.h"
28 #include "php_http_std_defs.h"
29 #include "php_http_api.h"
30 #include "php_http_headers_api.h"
31 #include "php_http_send_api.h"
34 # include "zend_exceptions.h"
35 # include "php_http_exception_object.h"
40 #ifdef HTTP_HAVE_MAGIC
41 # if defined(PHP_WIN32) && !defined(USE_MAGIC_DLL) && !defined(USE_MAGIC_STATIC)
42 # define USE_MAGIC_STATIC
47 ZEND_EXTERN_MODULE_GLOBALS(http
);
49 /* char *pretty_key(char *, size_t, zend_bool, zend_bool) */
50 char *_http_pretty_key(char *key
, size_t key_len
, zend_bool uctitle
, zend_bool xhyphen
)
55 if (wasalpha
= isalpha((int) key
[0])) {
56 key
[0] = (char) (uctitle
? toupper((int) key
[0]) : tolower((int) key
[0]));
58 for (i
= 1; i
< key_len
; i
++) {
59 if (isalpha((int) key
[i
])) {
60 key
[i
] = (char) (((!wasalpha
) && uctitle
) ? toupper((int) key
[i
]) : tolower((int) key
[i
]));
63 if (xhyphen
&& (key
[i
] == '_')) {
75 void _http_key_list_default_decoder(const char *encoded
, size_t encoded_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
77 *decoded
= estrndup(encoded
, encoded_len
);
78 *decoded_len
= (size_t) php_url_decode(*decoded
, encoded_len
);
83 STATUS
_http_parse_key_list(const char *list
, HashTable
*items
, char separator
, http_key_list_decode_t decode
, zend_bool first_entry_is_name_value_pair TSRMLS_DC
)
85 const char *key
= list
, *val
= NULL
;
86 int vallen
= 0, keylen
= 0, done
= 0;
89 INIT_ZARR(array
, items
);
91 if (!(val
= strchr(list
, '='))) {
95 #define HTTP_KEYLIST_VAL(array, k, str, len) \
100 decode(str, len, &decoded, &decoded_len TSRMLS_CC); \
103 decoded = estrndup(str, decoded_len); \
105 add_assoc_stringl(array, k, decoded, decoded_len, 0); \
107 #define HTTP_KEYLIST_FIXKEY() \
109 while (isspace(*key)) ++key; \
110 keylen = val - key; \
111 while (isspace(key[keylen - 1])) --keylen; \
113 #define HTTP_KEYLIST_FIXVAL() \
116 while (isspace(*val)) ++val; \
117 vallen = key - val; \
118 while (isspace(val[vallen - 1])) --vallen; \
121 HTTP_KEYLIST_FIXKEY();
123 if (first_entry_is_name_value_pair
) {
124 HTTP_KEYLIST_VAL(&array
, "name", key
, keylen
);
126 /* just one name=value */
127 if (!(key
= strchr(val
, separator
))) {
128 key
= val
+ strlen(val
);
129 HTTP_KEYLIST_FIXVAL();
130 HTTP_KEYLIST_VAL(&array
, "value", val
, vallen
);
133 /* additional info appended */
135 HTTP_KEYLIST_FIXVAL();
136 HTTP_KEYLIST_VAL(&array
, "value", val
, vallen
);
143 if (!(val
= strchr(key
, '='))) {
147 /* start at 0 if first_entry_is_name_value_pair==0 */
148 if (zend_hash_num_elements(items
)) {
152 HTTP_KEYLIST_FIXKEY();
153 keydup
= estrndup(key
, keylen
);
154 if (!(key
= strchr(val
, separator
))) {
156 key
= val
+ strlen(val
);
158 HTTP_KEYLIST_FIXVAL();
159 HTTP_KEYLIST_VAL(&array
, keydup
, val
, vallen
);
167 /* {{{ void http_error(long, long, char*) */
168 void _http_error_ex(long type TSRMLS_DC
, long code
, const char *format
, ...)
172 va_start(args
, format
);
174 if ((type
== E_THROW
) || (PG(error_handling
) == EH_THROW
)) {
177 vspprintf(&message
, 0, format
, args
);
178 zend_throw_exception(http_exception_get_for_code(code
), message
, code TSRMLS_CC
);
181 php_verror(NULL
, "", type
, format
, args TSRMLS_CC
);
186 /* {{{ void http_log(char *, char *, char *) */
187 void _http_log_ex(char *file
, const char *ident
, const char *message TSRMLS_DC
)
194 strftime(datetime
, sizeof(datetime
), "%Y-%m-%d %H:%M:%S", php_localtime_r(&now
, &nowtm
));
196 #define HTTP_LOG_WRITE(file, type, msg) \
197 if (file && strlen(file)) { \
198 php_stream *log = php_stream_open_wrapper(file, "ab", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); \
201 php_stream_printf(log TSRMLS_CC, "%s\t[%s]\t%s\t<%s>%s", datetime, type, msg, SG(request_info).request_uri, PHP_EOL); \
202 php_stream_close(log); \
207 HTTP_LOG_WRITE(file
, ident
, message
);
208 HTTP_LOG_WRITE(HTTP_G(log
).composite
, ident
, message
);
212 /* {{{ STATUS http_exit(int, char*, char*) */
213 STATUS
_http_exit_ex(int status
, char *header
, char *body
, zend_bool send_header TSRMLS_DC
)
215 if (status
|| send_header
) {
216 if (SUCCESS
!= http_send_status_header(status
, send_header
? header
: NULL
)) {
217 http_error_ex(HE_WARNING
, HTTP_E_HEADER
, "Failed to exit with status/header: %d - %s", status
, header
? header
: "");
224 if (php_header(TSRMLS_C
) && body
) {
225 PHPWRITE(body
, strlen(body
));
230 case 301: http_log(HTTP_G(log
).redirect
, "301-REDIRECT", header
); break;
231 case 302: http_log(HTTP_G(log
).redirect
, "302-REDIRECT", header
); break;
232 case 303: http_log(HTTP_G(log
).redirect
, "303-REDIRECT", header
); break;
233 case 307: http_log(HTTP_G(log
).redirect
, "307-REDIRECT", header
); break;
234 case 304: http_log(HTTP_G(log
).cache
, "304-CACHE", header
); break;
235 case 405: http_log(HTTP_G(log
).allowed_methods
, "405-ALLOWED", header
); break;
236 default: http_log(NULL
, header
, body
); break;
248 /* {{{ STATUS http_check_method(char *) */
249 STATUS
_http_check_method_ex(const char *method
, const char *methods
)
253 if ( (found
= strstr(methods
, method
)) &&
254 (found
== method
|| !isalpha(found
[-1])) &&
255 (!isalpha(found
[strlen(method
) + 1]))) {
262 /* {{{ zval *http_get_server_var_ex(char *, size_t) */
263 PHP_HTTP_API zval
*_http_get_server_var_ex(const char *key
, size_t key_size
, zend_bool check TSRMLS_DC
)
268 if (SUCCESS
!= zend_hash_find(&EG(symbol_table
), "_SERVER", sizeof("_SERVER"), (void **) &hsv
)) {
271 if (SUCCESS
!= zend_hash_find(Z_ARRVAL_PP(hsv
), (char *) key
, key_size
, (void **) &var
)) {
274 if (check
&& !(Z_STRVAL_PP(var
) && Z_STRLEN_PP(var
))) {
281 /* {{{ STATUS http_get_request_body(char **, size_t *) */
282 PHP_HTTP_API STATUS
_http_get_request_body_ex(char **body
, size_t *length
, zend_bool dup TSRMLS_DC
)
287 if (SG(request_info
).raw_post_data
) {
288 *length
= SG(request_info
).raw_post_data_length
;
289 *body
= (char *) (dup
? estrndup(SG(request_info
).raw_post_data
, *length
) : SG(request_info
).raw_post_data
);
296 /* {{{ char *http_chunked_decode(char *, size_t, char **, size_t *) */
297 PHP_HTTP_API
const char *_http_chunked_decode(const char *encoded
, size_t encoded_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
304 *decoded
= ecalloc(1, encoded_len
);
308 while ((rest
= encoded
+ encoded_len
- e_ptr
) > 0) {
310 int EOL_len
= 0, eol_mismatch
= 0;
313 chunk_len
= strtol(e_ptr
, &n_ptr
, 16);
316 * - we could not read in chunk size
317 * - we got a negative chunk size
318 * - chunk size is greater then remaining size
319 * - chunk size is not followed by (CR)LF|NUL
321 if ( (n_ptr
== e_ptr
) || (chunk_len
< 0) || (chunk_len
> rest
) ||
322 (*n_ptr
&& (eol_mismatch
= (n_ptr
!= http_locate_eol(e_ptr
, &EOL_len
))))) {
323 /* don't fail on apperently not encoded data */
324 if (e_ptr
== encoded
) {
325 memcpy(*decoded
, encoded
, encoded_len
);
326 *decoded_len
= encoded_len
;
327 return encoded
+ encoded_len
;
332 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Invalid character (expected 0x0D 0x0A; got: 0x%X 0x%X)", *n_ptr
, *(n_ptr
+ 1));
334 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Invalid character (expected 0x0A; got: 0x%X)", *n_ptr
);
337 char *error
= estrndup(n_ptr
, strcspn(n_ptr
, "\r\n "));
338 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Invalid chunk size: '%s' at pos %d", error
, n_ptr
- encoded
);
347 /* reached the end */
352 memcpy(d_ptr
, e_ptr
+= EOL_len
, chunk_len
);
354 e_ptr
+= chunk_len
+ EOL_len
;
355 *decoded_len
+= chunk_len
;
362 /* {{{ char *http_guess_content_type(char *magic_file, long magic_mode, void *data, size_t size, http_send_mode mode) */
363 PHP_HTTP_API
char *_http_guess_content_type(const char *magicfile
, long magicmode
, void *data_ptr
, size_t data_len
, http_send_mode data_mode TSRMLS_DC
)
367 #ifdef HTTP_HAVE_MAGIC
368 /* magic_load() fails if MAGIC_MIME is set because it
369 cowardly adds .mime to the file name */
370 struct magic_set
*magic
= magic_open(magicmode
&~ MAGIC_MIME
);
373 http_error_ex(HE_WARNING
, HTTP_E_INVALID_PARAM
, "Invalid magic mode: %ld", magicmode
);
374 } else if (-1 == magic_load(magic
, magicfile
)) {
375 http_error_ex(HE_WARNING
, HTTP_E_RUNTIME
, "Failed to load magic database '%s' (%s)", magicfile
, magic_error(magic
));
377 const char *ctype
= NULL
;
379 magic_setflags(magic
, magicmode
);
388 b_len
= php_stream_copy_to_mem(data_ptr
, &buffer
, 65536, 0);
389 ctype
= magic_buffer(magic
, buffer
, b_len
);
395 ctype
= magic_buffer(magic
, data_ptr
, data_len
);
399 ctype
= magic_file(magic
, data_ptr
);
406 http_error_ex(HE_WARNING
, HTTP_E_RUNTIME
, "Failed to guess Content-Type: %s", magic_error(magic
));
413 http_error(HE_WARNING
, HTTP_E_RUNTIME
, "Cannot guess Content-Type; libmagic not available");
424 * vim600: noet sw=4 ts=4 fdm=marker
425 * vim<600: noet sw=4 ts=4