37#define O_RDONLY _O_RDONLY
46#ifdef HAVE_SYS_SOCKET_H
47#include <sys/socket.h>
53#include <netinet/in.h>
55#ifdef HAVE_ARPA_INET_H
60#if defined(PHP_WIN32) || defined(__riscos__)
70#define HTTP_HEADER_BLOCK_SIZE 1024
71#define HTTP_HEADER_MAX_LOCATION_SIZE 8182
72#define PHP_URL_REDIRECT_MAX 20
73#define HTTP_HEADER_USER_AGENT 1
74#define HTTP_HEADER_HOST 2
75#define HTTP_HEADER_AUTH 4
76#define HTTP_HEADER_FROM 8
77#define HTTP_HEADER_CONTENT_LENGTH 16
78#define HTTP_HEADER_TYPE 32
79#define HTTP_HEADER_CONNECTION 64
81#define HTTP_WRAPPER_HEADER_INIT 1
82#define HTTP_WRAPPER_REDIRECTED 2
83#define HTTP_WRAPPER_KEEP_METHOD 4
85static inline void strip_header(
char *header_bag,
char *lc_header_bag,
86 const char *lc_header_name)
88 char *lc_header_start =
strstr(lc_header_bag, lc_header_name);
90 && (lc_header_start == lc_header_bag || *(lc_header_start-1) ==
'\n')
92 char *header_start = header_bag + (lc_header_start - lc_header_bag);
93 char *lc_eol =
strchr(lc_header_start,
'\n');
96 char *eol = header_start + (lc_eol - lc_header_start);
97 size_t eollen =
strlen(lc_eol);
99 memmove(lc_header_start, lc_eol+1, eollen);
100 memmove(header_start, eol+1, eollen);
102 *lc_header_start =
'\0';
103 *header_start =
'\0';
108static bool check_has_header(
const char *headers,
const char *
header) {
109 const char *
s = headers;
111 if (
s == headers || (*(
s-1) ==
'\n' && *(
s-2) ==
'\r')) {
124 while (*
s ==
' ' || *
s ==
'\t')
s++;
126 while (*
p != 0 && *
p !=
':' && *
p !=
'\r' && *
p !=
'\n')
p++;
129 if (
p -
s ==
sizeof(
"Proxy-Authorization:") - 1 &&
131 "Proxy-Authorization:",
sizeof(
"Proxy-Authorization:") - 1) == 0) {
132 while (*
p != 0 && *
p !=
'\r' && *
p !=
'\n')
p++;
134 smart_str_appendl(
header,
"\r\n",
sizeof(
"\r\n")-1);
137 while (*
p != 0 && *
p !=
'\r' && *
p !=
'\n')
p++;
141 while (*
s ==
'\r' || *
s ==
'\n')
s++;
156static void php_stream_http_response_header_info_init(
164static bool php_stream_http_response_header_trim(
char *http_header_line,
165 size_t *http_header_line_length)
167 char *http_header_line_end = http_header_line + *http_header_line_length - 1;
168 while (http_header_line_end >= http_header_line &&
169 (*http_header_line_end ==
'\n' || *http_header_line_end ==
'\r')) {
170 http_header_line_end--;
179 bool space_trim = (*http_header_line_end ==
' ' || *http_header_line_end ==
'\t');
182 http_header_line_end--;
183 }
while (http_header_line_end >= http_header_line &&
184 (*http_header_line_end ==
' ' || *http_header_line_end ==
'\t'));
186 http_header_line_end++;
187 *http_header_line_end =
'\0';
188 *http_header_line_length = http_header_line_end - http_header_line;
198 zend_string *last_header_line_str,
char *header_line,
size_t *header_line_length,
199 int response_code,
zval *response_header,
202 char *last_header_line =
ZSTR_VAL(last_header_line_str);
203 size_t last_header_line_length =
ZSTR_LEN(last_header_line_str);
204 char *last_header_line_end =
ZSTR_VAL(last_header_line_str) +
ZSTR_LEN(last_header_line_str) - 1;
207 if (header_line && (*header_line !=
'\n' && *header_line !=
'\r')) {
209 if (php_stream_http_response_header_trim(header_line, header_line_length) &&
210 *header_line_length == 0) {
212 return last_header_line_str;
216 if (header_line && (*header_line ==
' ' || *header_line ==
'\t')) {
217 char *http_folded_header_line = header_line;
218 size_t http_folded_header_line_length = *header_line_length;
220 while (*http_folded_header_line ==
' ' || *http_folded_header_line ==
'\t') {
221 http_folded_header_line++;
222 http_folded_header_line_length--;
229 last_header_line, last_header_line_length,
231 http_folded_header_line, http_folded_header_line_length);
232 zend_string_efree(last_header_line_str);
233 last_header_line_str = extended_header_str;
235 return last_header_line_str;
240 char *last_header_value = memchr(last_header_line,
':', last_header_line_length);
241 if (last_header_value) {
243 char *last_header_name = last_header_line + 1;
244 while (last_header_name < last_header_value) {
245 if (*last_header_name ==
' ' || *last_header_name ==
'\t') {
246 header_info->
error =
true;
248 "HTTP invalid response format (space in header name)!");
249 zend_string_efree(last_header_line_str);
258 while (last_header_value < last_header_line_end
259 && (*last_header_value ==
' ' || *last_header_value ==
'\t')) {
264 header_info->
error =
true;
266 "HTTP invalid response format (no colon in header line)!");
267 zend_string_efree(last_header_line_str);
271 bool store_header =
true;
274 if (!
strncasecmp(last_header_line,
"Location:",
sizeof(
"Location:")-1)) {
278 }
else if (!((response_code >= 300 && response_code < 304)
279 || 307 == response_code || 308 == response_code)) {
286 size_t last_header_value_len =
strlen(last_header_value);
288 header_info->
error =
true;
290 "HTTP Location header size is over the limit of %d bytes",
292 zend_string_efree(last_header_line_str);
297 }
else if (header_info->
location_len <= last_header_value_len) {
301 memcpy(header_info->
location, last_header_value, last_header_value_len + 1);
302 }
else if (!
strncasecmp(last_header_line,
"Content-Type:",
sizeof(
"Content-Type:")-1)) {
304 }
else if (!
strncasecmp(last_header_line,
"Content-Length:",
sizeof(
"Content-Length:")-1)) {
306 const char *
ptr = last_header_value;
308 if (*
ptr >=
'0' && *
ptr <=
'9') {
319 !
strncasecmp(last_header_line,
"Transfer-Encoding:",
sizeof(
"Transfer-Encoding:")-1)
320 && !
strncasecmp(last_header_value,
"Chunked",
sizeof(
"Chunked")-1)
338 store_header =
false;
349 zend_string_efree(last_header_line_str);
366 zval *ua_zval =
NULL, *tmpzval =
NULL, ssl_proxy_peer_name;
368 char *http_header_line =
NULL;
372 size_t chunk_size = 0;
377 bool request_fulluri =
false, ignore_errors =
false;
378 struct timeval timeout;
379 char *user_headers =
NULL;
385 bool custom_request_method;
389 if (redirect_max < 1) {
395 if (resource ==
NULL) {
410 request_fulluri =
true;
413 transport_string = zend_string_copy(
Z_STR_P(tmpzval));
430 if (use_ssl && resource->
port == 0)
431 resource->
port = 443;
432 else if (resource->
port == 0)
440 transport_string = zend_string_copy(
Z_STR_P(tmpzval));
449 zend_string_release(transport_string);
454 double d = zval_get_double(tmpzval);
458 const double timeoutmax = (double)
LONG_MAX / 1000000.0;
461 if (d > timeoutmax) {
463 zend_string_release(transport_string);
468 timeout.tv_sec = (time_t) d;
469 timeout.tv_usec = (size_t) ((d - timeout.tv_sec) * 1000000);
471 timeout.tv_sec = (long) d;
472 timeout.tv_usec = (long) ((d - timeout.tv_sec) * 1000000);
476 timeout.tv_sec =
FG(default_socket_timeout);
478 timeout.tv_sec = (long)
FG(default_socket_timeout);
497 zend_string_release(transport_string);
499 if (stream && use_proxy && use_ssl) {
509 smart_str_appendl(&
header,
"CONNECT ",
sizeof(
"CONNECT ")-1);
511 smart_str_appendc(&
header,
':');
512 smart_str_append_unsigned(&
header, resource->
port);
513 smart_str_appendl(&
header,
" HTTP/1.0\r\n",
sizeof(
" HTTP/1.0\r\n")-1);
525 if (php_stream_handle_proxy_authorization_header(
s, &
header) ==
SUCCESS) {
532 if (php_stream_handle_proxy_authorization_header(
s, &
header) ==
SUCCESS) {
538 smart_str_appendl(&
header,
"\r\n",
sizeof(
"\r\n")-1);
552 if (header_line[0] ==
'\n' ||
553 header_line[0] ==
'\r' ||
554 header_line[0] ==
'\0') {
571 php_stream_http_response_header_info_init(&header_info);
590 redirect_max = (int)zval_get_long(tmpzval);
593 custom_request_method = 0;
598 if (!redirected || redirect_keep_method
602 custom_request_method = 1;
603 smart_str_append(&req_buf,
Z_STR_P(tmpzval));
604 smart_str_appendc(&req_buf,
' ');
609 if (!custom_request_method) {
610 smart_str_appends(&req_buf,
"GET ");
613 if (request_fulluri) {
615 smart_str_appends(&req_buf, path);
623 smart_str_appendc(&req_buf,
'/');
627 if (resource->
query) {
628 smart_str_appendc(&req_buf,
'?');
635 char *protocol_version;
636 spprintf(&protocol_version, 0,
"%.1F", zval_get_double(tmpzval));
638 smart_str_appends(&req_buf,
" HTTP/");
639 smart_str_appends(&req_buf, protocol_version);
640 smart_str_appends(&req_buf,
"\r\n");
641 efree(protocol_version);
643 smart_str_appends(&req_buf,
" HTTP/1.1\r\n");
655 smart_str_append(&tmpstr,
Z_STR_P(tmpheader));
656 smart_str_appendl(&tmpstr,
"\r\n",
sizeof(
"\r\n") - 1);
659 smart_str_0(&tmpstr);
663 smart_str_free(&tmpstr);
686 if (!header_init && !redirect_keep_method) {
688 strip_header(user_headers, t,
"content-length:");
689 strip_header(user_headers, t,
"content-type:");
692 if (check_has_header(t,
"user-agent:")) {
695 if (check_has_header(t,
"host:")) {
698 if (check_has_header(t,
"from:")) {
701 if (check_has_header(t,
"authorization:")) {
704 if (check_has_header(t,
"content-length:")) {
707 if (check_has_header(t,
"content-type:")) {
710 if (check_has_header(t,
"connection:")) {
715 if (use_proxy && use_ssl && (
s =
strstr(t,
"proxy-authorization:")) &&
716 (
s == t || *(
s-1) ==
'\n')) {
717 char *
p =
s +
sizeof(
"proxy-authorization:") - 1;
719 while (
s > t && (*(
s-1) ==
' ' || *(
s-1) ==
'\t'))
s--;
720 while (*
p != 0 && *
p !=
'\r' && *
p !=
'\n')
p++;
721 while (*
p ==
'\r' || *
p ==
'\n')
p++;
727 while (
s > t && (*(
s-1) ==
'\r' || *(
s-1) ==
'\n'))
s--;
728 user_headers[
s - t] = 0;
744 size_t scratch_len =
strlen(path) + 1;
745 char *scratch =
emalloc(scratch_len);
755 if (resource->
pass) {
760 stmp = php_base64_encode((
unsigned char*)scratch,
strlen(scratch));
762 smart_str_appends(&req_buf,
"Authorization: Basic ");
763 smart_str_appends(&req_buf,
ZSTR_VAL(stmp));
764 smart_str_appends(&req_buf,
"\r\n");
768 zend_string_free(stmp);
774 smart_str_appends(&req_buf,
"From: ");
775 smart_str_appends(&req_buf,
FG(from_address));
776 smart_str_appends(&req_buf,
"\r\n");
781 smart_str_appends(&req_buf,
"Host: ");
783 if ((use_ssl && resource->
port != 443 && resource->
port != 0) ||
784 (!use_ssl && resource->
port != 80 && resource->
port != 0)) {
785 smart_str_appendc(&req_buf,
':');
786 smart_str_append_unsigned(&req_buf, resource->
port);
788 smart_str_appends(&req_buf,
"\r\n");
798 smart_str_appends(&req_buf,
"Connection: close\r\n");
805 }
else if (
FG(user_agent)) {
806 ua_str =
FG(user_agent);
810#define _UA_HEADER "User-Agent: %s\r\n"
821 smart_str_appendl(&req_buf, ua, ua_len);
834 (header_init || redirect_keep_method) &&
840 smart_str_appends(&req_buf,
"Content-Length: ");
841 smart_str_append_unsigned(&req_buf,
Z_STRLEN_P(tmpzval));
842 smart_str_appends(&req_buf,
"\r\n");
846 smart_str_appends(&req_buf, user_headers);
847 smart_str_appends(&req_buf,
"\r\n");
852 if ((header_init || redirect_keep_method) &&
context &&
856 smart_str_appends(&req_buf,
"Content-Length: ");
857 smart_str_append_unsigned(&req_buf,
Z_STRLEN_P(tmpzval));
858 smart_str_appends(&req_buf,
"\r\n");
861 smart_str_appends(&req_buf,
"Content-Type: application/x-www-form-urlencoded\r\n");
864 smart_str_appends(&req_buf,
"\r\n");
867 smart_str_appends(&req_buf,
"\r\n");
884 if (tmp_line_len > 9) {
885 response_code = atoi(tmp_line + 9);
900 if (response_code >= 100 && response_code < 200 && response_code != 101) {
905 && ( tmp_line_len <
sizeof(
"HTTP/1") - 1 ||
strncasecmp(tmp_line,
"HTTP/1",
sizeof(
"HTTP/1") - 1) )
908 if (tmp_line_len > 9) {
909 response_code = atoi(tmp_line + 9);
917 if (response_code >= 200 && response_code < 400) {
920 switch(response_code) {
923 tmp_line, response_code);
931 tmp_line, response_code);
934 if (tmp_line_len >= 1 && tmp_line[tmp_line_len - 1] ==
'\n') {
936 if (tmp_line_len >= 1 &&tmp_line[tmp_line_len - 1] ==
'\r') {
956 size_t http_header_line_length;
958 if (http_header_line !=
NULL) {
959 efree(http_header_line);
963 if (*http_header_line ==
'\r') {
964 if (http_header_line[1] !=
'\n') {
968 "HTTP invalid header name (cannot start with CR character)!");
972 }
else if (*http_header_line ==
'\n') {
978 if (last_header_line_str !=
NULL) {
980 last_header_line_str = php_stream_http_response_headers_parse(wrapper, stream,
982 &http_header_line_length, response_code, response_header, &header_info);
995 if (*http_header_line ==
' ' || *http_header_line ==
'\t') {
999 "HTTP invalid response format (folding header at the start)!");
1003 php_stream_http_response_header_trim(http_header_line, &http_header_line_length);
1011 last_header_line_str = zend_string_init(http_header_line, http_header_line_length, 0);
1018 if (last_header_line_str !=
NULL) {
1019 php_stream_http_response_headers_parse(wrapper, stream,
context,
options,
1020 last_header_line_str,
NULL,
NULL, response_code, response_header, &header_info);
1041 char *new_path =
NULL;
1049 char *loc_path =
NULL;
1050 if (*header_info.
location !=
'/') {
1051 if (*(header_info.
location+1) !=
'\0' && resource->
path) {
1064 if (resource->
path &&
1078 if ((use_ssl && resource->
port != 443) || (!use_ssl && resource->
port != 80)) {
1099#define CHECK_FOR_CNTRL_CHARS(val) { \
1101 unsigned char *s, *e; \
1102 ZSTR_LEN(val) = php_url_decode(ZSTR_VAL(val), ZSTR_LEN(val)); \
1103 s = (unsigned char*)ZSTR_VAL(val); e = s + ZSTR_LEN(val); \
1105 if (iscntrl(*s)) { \
1106 php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); \
1115 if (
strncasecmp(new_path,
"http://",
sizeof(
"http://") - 1) ||
strncasecmp(new_path,
"https://",
sizeof(
"https://") - 1)) {
1121 if (response_code == 307 || response_code == 308) {
1127 stream = php_stream_url_wrap_http_ex(
1129 --redirect_max, new_flags, response_header
STREAMS_CC);
1137 smart_str_free(&req_buf);
1139 if (http_header_line) {
1140 efree(http_header_line);
1162 stream->
flags |= eol_detect;
1197 stream = php_stream_url_wrap_http_ex(
1205 "http_response_header",
sizeof(
"http_response_header")-1, &headers, 0)) {
1226 php_stream_http_stream_stat,
strrchr(string $haystack, string $needle, bool $before_needle=false)
strpbrk(string $string, string $characters)
header(string $header, bool $replace=true, int $response_code=0)
strstr(string $haystack, string $needle, bool $before_needle=false)
strchr(string $haystack, string $needle, bool $before_needle=false)
memset(ptr, 0, type->size)
PHPAPI const php_stream_wrapper php_stream_http_wrapper
#define HTTP_HEADER_USER_AGENT
#define HTTP_WRAPPER_REDIRECTED
#define CHECK_FOR_CNTRL_CHARS(val)
#define HTTP_HEADER_CONNECTION
#define HTTP_HEADER_CONTENT_LENGTH
#define HTTP_HEADER_MAX_LOCATION_SIZE
#define HTTP_HEADER_BLOCK_SIZE
#define PHP_URL_REDIRECT_MAX
struct _php_stream_http_response_header_info php_stream_http_response_header_info
#define HTTP_WRAPPER_KEEP_METHOD
php_stream * php_stream_url_wrap_http(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
#define HTTP_WRAPPER_HEADER_INIT
PHPAPI php_stream_filter * php_stream_filter_create(const char *filtername, zval *filterparams, uint8_t persistent)
PHPAPI void php_stream_filter_free(php_stream_filter *filter)
PHPAPI size_t php_url_decode(char *str, size_t len)
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
PHP_JSON_API size_t int options
#define php_stream_notify_progress_increment(context, dsofar, dmax)
#define php_stream_notify_file_size(context, file_size, xmsg, xcode)
PHPAPI php_stream_context * php_stream_context_set(php_stream *stream, php_stream_context *context)
#define php_stream_notify_info(context, code, xmsg, xcode)
#define PHP_STREAM_NOTIFY_AUTH_REQUIRED
#define PHP_STREAM_NOTIFY_MIME_TYPE_IS
#define PHP_STREAM_NOTIFY_REDIRECTED
#define PHP_STREAM_NOTIFY_FAILURE
#define php_stream_notify_progress_init(context, sofar, bmax)
PHPAPI zval * php_stream_context_get_option(php_stream_context *context, const char *wrappername, const char *optionname)
#define PHP_STREAM_NOTIFY_AUTH_RESULT
#define PHP_STREAM_NOTIFY_CONNECT
#define php_stream_notify_error(context, code, xmsg, xcode)
PHPAPI void php_stream_context_set_option(php_stream_context *context, const char *wrappername, const char *optionname, zval *optionvalue)
#define php_stream_filter_append(chain, filter)
#define php_stream_xport_create(name, namelen, options, flags, persistent_id, timeout, context, estr, ecode)
PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate)
@ STREAM_CRYPTO_METHOD_SSLv23_CLIENT
#define STREAM_XPORT_CONNECT
PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream)
#define STREAM_XPORT_CLIENT
struct _php_stream_wrapper_ops php_stream_wrapper_ops
struct _php_stream php_stream
struct _php_stream_context php_stream_context
#define php_stream_gets(stream, buf, maxlen)
struct _php_stream_filter php_stream_filter
#define php_stream_get_line(stream, buf, maxlen, retlen)
#define STREAM_ONLY_GET_HEADERS
#define php_stream_set_chunk_size(stream, size)
#define php_stream_eof(stream)
#define php_stream_open_wrapper_ex(path, mode, options, opened, context)
#define php_stream_close(stream)
#define PHP_STREAM_CONTEXT(stream)
#define php_stream_is_persistent(stream)
#define PHP_STREAM_OPTION_READ_TIMEOUT
#define PHP_STREAM_FLAG_DETECT_EOL
struct _php_stream_wrapper php_stream_wrapper
#define PHP_STREAM_FLAG_EOL_MAC
#define php_stream_set_option(stream, option, value, ptrvalue)
struct _php_stream_statbuf php_stream_statbuf
#define php_stream_write(stream, buf, count)
PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, int options, const char *fmt,...) PHP_ATTRIBUTE_FORMAT(printf
PHPAPI zend_string * php_trim(zend_string *str, const char *what, size_t what_len, int mode)
#define PHP_TIMEOUT_ULL_MAX
const php_stream_wrapper_ops * wops
php_stream_filter_chain readfilters
PHPAPI php_url * php_url_parse(char const *str)
PHPAPI void php_url_free(php_url *theurl)
ZEND_API zend_string * zend_strpprintf(size_t max_len, const char *format,...)
ZEND_API zend_result zend_set_local_var_str(const char *name, size_t len, zval *value, bool force)
#define ZVAL_STRINGL(z, s, l)
#define estrndup(s, length)
#define erealloc(ptr, size)
zend_string_release_ex(func->internal_function.function_name, 0)
#define strncasecmp(s1, s2, n)
ZEND_API zval *ZEND_FASTCALL zend_hash_next_index_insert(HashTable *ht, zval *pData)
#define ZEND_HASH_FOREACH_END()
#define ZEND_HASH_FOREACH_VAL(ht, _val)
#define ZEND_STRTOUL(s0, s1, base)
struct _zend_string zend_string
ZEND_API int ZEND_FASTCALL zend_binary_strcasecmp(const char *s1, size_t len1, const char *s2, size_t len2)
ZEND_API bool ZEND_FASTCALL zend_is_true(const zval *op)
ZEND_API void ZEND_FASTCALL zend_str_tolower(char *str, size_t length)
#define EXPECTED(condition)
#define UNEXPECTED(condition)
ZEND_API zend_string * zend_string_concat3(const char *str1, size_t str1_len, const char *str2, size_t str2_len, const char *str3, size_t str3_len)
#define ZSTR_IS_INTERNED(s)
#define ZSTR_INIT_LITERAL(s, persistent)
#define zend_string_equals_literal(str, literal)
#define zend_string_equals_literal_ci(str, c)
#define Z_STRVAL_P(zval_p)
#define Z_ISUNDEF_P(zval_p)
#define Z_ARRVAL_P(zval_p)
#define ZVAL_STR_COPY(z, s)
#define Z_STRLEN_P(zval_p)
#define ZVAL_NEW_STR(z, s)
ZEND_RESULT_CODE zend_result
ZEND_API void zval_ptr_dtor(zval *zval_ptr)