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-2006, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
15 #define HTTP_WANT_SAPI
18 #include "php_output.h"
19 #include "ext/standard/url.h"
21 #include "php_http_api.h"
22 #include "php_http_send_api.h"
25 # include "php_http_exception_object.h"
28 PHP_MINIT_FUNCTION(http_support
)
30 HTTP_LONG_CONSTANT("HTTP_SUPPORT", HTTP_SUPPORT
);
31 HTTP_LONG_CONSTANT("HTTP_SUPPORT_REQUESTS", HTTP_SUPPORT_REQUESTS
);
32 HTTP_LONG_CONSTANT("HTTP_SUPPORT_MAGICMIME", HTTP_SUPPORT_MAGICMIME
);
33 HTTP_LONG_CONSTANT("HTTP_SUPPORT_ENCODINGS", HTTP_SUPPORT_ENCODINGS
);
34 HTTP_LONG_CONSTANT("HTTP_SUPPORT_SSLREQUESTS", HTTP_SUPPORT_SSLREQUESTS
);
39 PHP_HTTP_API
long _http_support(long feature
)
41 long support
= HTTP_SUPPORT
;
44 support
|= HTTP_SUPPORT_REQUESTS
;
46 support
|= HTTP_SUPPORT_SSLREQUESTS
;
49 #ifdef HTTP_HAVE_MAGIC
50 support
|= HTTP_SUPPORT_MAGICMIME
;
53 support
|= HTTP_SUPPORT_ENCODINGS
;
57 return (feature
== (support
& feature
));
62 /* char *pretty_key(char *, size_t, zend_bool, zend_bool) */
63 char *_http_pretty_key(char *key
, size_t key_len
, zend_bool uctitle
, zend_bool xhyphen
)
68 if ((wasalpha
= isalpha((int) key
[0]))) {
69 key
[0] = (char) (uctitle
? toupper((int) key
[0]) : tolower((int) key
[0]));
71 for (i
= 1; i
< key_len
; i
++) {
72 if (isalpha((int) key
[i
])) {
73 key
[i
] = (char) (((!wasalpha
) && uctitle
) ? toupper((int) key
[i
]) : tolower((int) key
[i
]));
76 if (xhyphen
&& (key
[i
] == '_')) {
88 void _http_key_list_default_decoder(const char *encoded
, size_t encoded_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
90 *decoded
= estrndup(encoded
, encoded_len
);
91 *decoded_len
= (size_t) php_url_decode(*decoded
, encoded_len
);
96 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
)
98 char *str
= estrdup(list
), *key
= str
, *val
= NULL
;
99 int vallen
= 0, keylen
= 0, done
= 0;
102 INIT_ZARR(array
, items
);
104 if (!(val
= strchr(str
, '='))) {
109 #define HTTP_KEYLIST_VAL(array, k, str, len) \
112 size_t decoded_len; \
114 decode(str, len, &decoded, &decoded_len TSRMLS_CC); \
117 decoded = estrndup(str, decoded_len); \
119 add_assoc_stringl(array, k, decoded, decoded_len, 0); \
121 #define HTTP_KEYLIST_FIXKEY() \
123 while (isspace(*key)) ++key; \
124 keylen = val - key; \
125 while (isspace(key[keylen - 1])) --keylen; \
127 #define HTTP_KEYLIST_FIXVAL() \
130 while (isspace(*val)) ++val; \
131 vallen = key - val; \
132 while (isspace(val[vallen - 1])) --vallen; \
133 if (val[0] == '"' && val[vallen - 1] == '"') { \
137 for (i = 0; i < vallen; ++i) { \
138 if (val[i] == '\\' && val[i+1] == '"' && (!i || val[i-1] != '\\')) { \
139 memmove(&val[i], &val[i+1], vallen - i); \
146 HTTP_KEYLIST_FIXKEY();
148 if (first_entry_is_name_value_pair
) {
149 HTTP_KEYLIST_VAL(&array
, "name", key
, keylen
);
151 /* just one name=value */
152 if (!(key
= strchr(val
, separator
))) {
153 key
= val
+ strlen(val
);
154 HTTP_KEYLIST_FIXVAL();
155 HTTP_KEYLIST_VAL(&array
, "value", val
, vallen
);
159 /* additional info appended */
161 HTTP_KEYLIST_FIXVAL();
162 HTTP_KEYLIST_VAL(&array
, "value", val
, vallen
);
169 if (!(val
= strchr(key
, '='))) {
173 /* start at 0 if first_entry_is_name_value_pair==0 */
174 if (zend_hash_num_elements(items
)) {
178 HTTP_KEYLIST_FIXKEY();
179 keydup
= estrndup(key
, keylen
);
180 if (!(key
= strchr(val
, separator
))) {
182 key
= val
+ strlen(val
);
184 HTTP_KEYLIST_FIXVAL();
185 HTTP_KEYLIST_VAL(&array
, keydup
, val
, vallen
);
194 /* {{{ void http_error(long, long, char*) */
195 void _http_error_ex(long type TSRMLS_DC
, long code
, const char *format
, ...)
199 va_start(args
, format
);
201 if ((type
== E_THROW
) || (PG(error_handling
) == EH_THROW
)) {
204 vspprintf(&message
, 0, format
, args
);
205 zend_throw_exception(http_exception_get_for_code(code
), message
, code TSRMLS_CC
);
209 php_verror(NULL
, "", type
, format
, args TSRMLS_CC
);
214 /* {{{ void http_log(char *, char *, char *) */
215 void _http_log_ex(char *file
, const char *ident
, const char *message TSRMLS_DC
)
219 char datetime
[20] = {0};
221 now
= HTTP_GET_REQUEST_TIME();
222 strftime(datetime
, sizeof(datetime
), "%Y-%m-%d %H:%M:%S", php_localtime_r(&now
, &nowtm
));
224 #define HTTP_LOG_WRITE(file, type, msg) \
225 if (file && *file) { \
226 php_stream *log = php_stream_open_wrapper(file, "ab", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); \
229 php_stream_printf(log TSRMLS_CC, "%s\t[%s]\t%s\t<%s>%s", datetime, type, msg, SG(request_info).request_uri, PHP_EOL); \
230 php_stream_close(log); \
235 HTTP_LOG_WRITE(file
, ident
, message
);
236 HTTP_LOG_WRITE(HTTP_G
->log
.composite
, ident
, message
);
240 static void http_ob_blackhole(char *output
, uint output_len
, char **handled_output
, uint
*handled_output_len
, int mode TSRMLS_DC
)
242 *handled_output
= ecalloc(1,1);
243 *handled_output_len
= 0;
246 /* {{{ STATUS http_exit(int, char*, char*) */
247 STATUS
_http_exit_ex(int status
, char *header
, char *body
, zend_bool send_header TSRMLS_DC
)
249 if ( (send_header
&& (SUCCESS
!= http_send_status_header(status
, header
))) ||
250 (!send_header
&& status
&& (SUCCESS
!= http_send_status(status
)))) {
251 http_error_ex(HE_WARNING
, HTTP_E_HEADER
, "Failed to exit with status/header: %d - %s", status
, header
? header
: "");
257 php_end_ob_buffers(0 TSRMLS_CC
);
258 if ((SUCCESS
== sapi_send_headers(TSRMLS_C
)) && body
) {
259 PHPWRITE(body
, strlen(body
));
264 case 301: http_log(HTTP_G
->log
.redirect
, "301-REDIRECT", header
); break;
265 case 302: http_log(HTTP_G
->log
.redirect
, "302-REDIRECT", header
); break;
266 case 303: http_log(HTTP_G
->log
.redirect
, "303-REDIRECT", header
); break;
267 case 305: http_log(HTTP_G
->log
.redirect
, "305-REDIRECT", header
); break;
268 case 307: http_log(HTTP_G
->log
.redirect
, "307-REDIRECT", header
); break;
269 case 304: http_log(HTTP_G
->log
.cache
, "304-CACHE", header
); break;
270 case 405: http_log(HTTP_G
->log
.allowed_methods
, "405-ALLOWED", header
); break;
271 default: http_log(NULL
, header
, body
); break;
277 if (HTTP_G
->force_exit
) {
280 php_ob_set_internal_handler(http_ob_blackhole
, 4096, "blackhole", 0 TSRMLS_CC
);
287 /* {{{ STATUS http_check_method(char *) */
288 STATUS
_http_check_method_ex(const char *method
, const char *methods
)
292 if ( (found
= strstr(methods
, method
)) &&
293 (found
== method
|| !isalpha(found
[-1])) &&
294 (strlen(found
) >= strlen(method
) && !isalpha(found
[strlen(method
)]))) {
301 /* {{{ zval *http_get_server_var_ex(char *, size_t) */
302 PHP_HTTP_API zval
*_http_get_server_var_ex(const char *key
, size_t key_size
, zend_bool check TSRMLS_DC
)
307 if ((SUCCESS
!= zend_hash_find(&EG(symbol_table
), "_SERVER", sizeof("_SERVER"), (void **) &hsv
)) || (Z_TYPE_PP(hsv
) != IS_ARRAY
)) {
310 if ((SUCCESS
!= zend_hash_find(Z_ARRVAL_PP(hsv
), (char *) key
, key_size
, (void **) &var
)) || (Z_TYPE_PP(var
) != IS_STRING
)) {
313 if (check
&& !(Z_STRVAL_PP(var
) && Z_STRLEN_PP(var
))) {
320 /* {{{ STATUS http_get_request_body(char **, size_t *) */
321 PHP_HTTP_API STATUS
_http_get_request_body_ex(char **body
, size_t *length
, zend_bool dup TSRMLS_DC
)
326 if (SG(request_info
).raw_post_data
) {
327 *length
= SG(request_info
).raw_post_data_length
;
328 *body
= SG(request_info
).raw_post_data
;
331 *body
= estrndup(*body
, *length
);
334 } else if (sapi_module
.read_post
&& !HTTP_G
->read_post_data
) {
338 HTTP_G
->read_post_data
= 1;
340 while (0 < (len
= sapi_module
.read_post(buf
, sizeof(buf
) TSRMLS_CC
))) {
341 *body
= erealloc(*body
, *length
+ len
+ 1);
342 memcpy(*body
+ *length
, buf
, len
);
344 (*body
)[*length
] = '\0';
347 /* check for error */
354 SG(request_info
).raw_post_data
= *body
;
355 SG(request_info
).raw_post_data_length
= *length
;
358 *body
= estrndup(*body
, *length
);
367 /* {{{ php_stream *_http_get_request_body_stream(void) */
368 PHP_HTTP_API php_stream
*_http_get_request_body_stream(TSRMLS_D
)
370 php_stream
*s
= NULL
;
372 if (SG(request_info
).raw_post_data
) {
373 s
= php_stream_open_wrapper("php://input", "rb", 0, NULL
);
374 } else if (sapi_module
.read_post
&& !HTTP_G
->read_post_data
) {
375 HTTP_G
->read_post_data
= 1;
377 if ((s
= php_stream_temp_new())) {
381 while (0 < (len
= sapi_module
.read_post(buf
, sizeof(buf
) TSRMLS_CC
))) {
382 php_stream_write(s
, buf
, len
);
389 php_stream_rewind(s
);
403 * vim600: noet sw=4 ts=4 fdm=marker
404 * vim<600: noet sw=4 ts=4