php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
http_fopen_wrapper.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Rasmus Lerdorf <rasmus@php.net> |
14 | Jim Winstead <jimw@php.net> |
15 | Hartmut Holzgraefe <hholzgra@php.net> |
16 | Wez Furlong <wez@thebrainroom.com> |
17 | Sara Golemon <pollita@php.net> |
18 +----------------------------------------------------------------------+
19 */
20
21#include "php.h"
22#include "php_globals.h"
23#include "php_streams.h"
24#include "php_network.h"
25#include "php_ini.h"
27#include "zend_smart_str.h"
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <errno.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <fcntl.h>
35
36#ifdef PHP_WIN32
37#define O_RDONLY _O_RDONLY
38#include "win32/param.h"
39#else
40#include <sys/param.h>
41#endif
42
43#include "php_standard.h"
44
45#include <sys/types.h>
46#ifdef HAVE_SYS_SOCKET_H
47#include <sys/socket.h>
48#endif
49
50#ifdef PHP_WIN32
51#include <winsock2.h>
52#else
53#include <netinet/in.h>
54#include <netdb.h>
55#ifdef HAVE_ARPA_INET_H
56#include <arpa/inet.h>
57#endif
58#endif
59
60#if defined(PHP_WIN32) || defined(__riscos__)
61#undef AF_UNIX
62#endif
63
64#if defined(AF_UNIX)
65#include <sys/un.h>
66#endif
67
68#include "php_fopen_wrappers.h"
69
70#define HTTP_HEADER_BLOCK_SIZE 1024
71#define HTTP_HEADER_MAX_LOCATION_SIZE 8182 /* 8192 - 10 (size of "Location: ") */
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
80
81#define HTTP_WRAPPER_HEADER_INIT 1
82#define HTTP_WRAPPER_REDIRECTED 2
83#define HTTP_WRAPPER_KEEP_METHOD 4
84
85static inline void strip_header(char *header_bag, char *lc_header_bag,
86 const char *lc_header_name)
87{
88 char *lc_header_start = strstr(lc_header_bag, lc_header_name);
89 if (lc_header_start
90 && (lc_header_start == lc_header_bag || *(lc_header_start-1) == '\n')
91 ) {
92 char *header_start = header_bag + (lc_header_start - lc_header_bag);
93 char *lc_eol = strchr(lc_header_start, '\n');
94
95 if (lc_eol) {
96 char *eol = header_start + (lc_eol - lc_header_start);
97 size_t eollen = strlen(lc_eol);
98
99 memmove(lc_header_start, lc_eol+1, eollen);
100 memmove(header_start, eol+1, eollen);
101 } else {
102 *lc_header_start = '\0';
103 *header_start = '\0';
104 }
105 }
106}
107
108static bool check_has_header(const char *headers, const char *header) {
109 const char *s = headers;
110 while ((s = strstr(s, header))) {
111 if (s == headers || (*(s-1) == '\n' && *(s-2) == '\r')) {
112 return 1;
113 }
114 s++;
115 }
116 return 0;
117}
118
119static zend_result php_stream_handle_proxy_authorization_header(const char *s, smart_str *header)
120{
121 const char *p;
122
123 do {
124 while (*s == ' ' || *s == '\t') s++;
125 p = s;
126 while (*p != 0 && *p != ':' && *p != '\r' && *p !='\n') p++;
127 if (*p == ':') {
128 p++;
129 if (p - s == sizeof("Proxy-Authorization:") - 1 &&
130 zend_binary_strcasecmp(s, sizeof("Proxy-Authorization:") - 1,
131 "Proxy-Authorization:", sizeof("Proxy-Authorization:") - 1) == 0) {
132 while (*p != 0 && *p != '\r' && *p !='\n') p++;
133 smart_str_appendl(header, s, p - s);
134 smart_str_appendl(header, "\r\n", sizeof("\r\n")-1);
135 return SUCCESS;
136 } else {
137 while (*p != 0 && *p != '\r' && *p !='\n') p++;
138 }
139 }
140 s = p;
141 while (*s == '\r' || *s == '\n') s++;
142 } while (*s != 0);
143
144 return FAILURE;
145}
146
155
156static void php_stream_http_response_header_info_init(
158{
159 memset(header_info, 0, sizeof(php_stream_http_response_header_info));
160 header_info->follow_location = 1;
161}
162
163/* Trim white spaces from response header line and update its length */
164static bool php_stream_http_response_header_trim(char *http_header_line,
165 size_t *http_header_line_length)
166{
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--;
171 }
172
173 /* The primary definition of an HTTP header in RFC 7230 states:
174 * > Each header field consists of a case-insensitive field name followed
175 * > by a colon (":"), optional leading whitespace, the field value, and
176 * > optional trailing whitespace. */
177
178 /* Strip trailing whitespace */
179 bool space_trim = (*http_header_line_end == ' ' || *http_header_line_end == '\t');
180 if (space_trim) {
181 do {
182 http_header_line_end--;
183 } while (http_header_line_end >= http_header_line &&
184 (*http_header_line_end == ' ' || *http_header_line_end == '\t'));
185 }
186 http_header_line_end++;
187 *http_header_line_end = '\0';
188 *http_header_line_length = http_header_line_end - http_header_line;
189
190 return space_trim;
191}
192
193/* Process folding headers of the current line and if there are none, parse last full response
194 * header line. It returns NULL if the last header is finished, otherwise it returns updated
195 * last header line. */
196static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *wrapper,
198 zend_string *last_header_line_str, char *header_line, size_t *header_line_length,
199 int response_code, zval *response_header,
201{
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;
205
206 /* Process non empty header line. */
207 if (header_line && (*header_line != '\n' && *header_line != '\r')) {
208 /* Removing trailing white spaces. */
209 if (php_stream_http_response_header_trim(header_line, header_line_length) &&
210 *header_line_length == 0) {
211 /* Only spaces so treat as an empty folding header. */
212 return last_header_line_str;
213 }
214
215 /* Process folding headers if starting with a space or a tab. */
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;
219 /* Remove the leading white spaces. */
220 while (*http_folded_header_line == ' ' || *http_folded_header_line == '\t') {
221 http_folded_header_line++;
222 http_folded_header_line_length--;
223 }
224 /* It has to have some characters because it would get returned after the call
225 * php_stream_http_response_header_trim above. */
226 ZEND_ASSERT(http_folded_header_line_length > 0);
227 /* Concatenate last header line, space and current header line. */
228 zend_string *extended_header_str = zend_string_concat3(
229 last_header_line, last_header_line_length,
230 " ", 1,
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;
234 /* Return new header line. */
235 return last_header_line_str;
236 }
237 }
238
239 /* Find header separator position. */
240 char *last_header_value = memchr(last_header_line, ':', last_header_line_length);
241 if (last_header_value) {
242 /* Verify there is no space in header name */
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);
250 return NULL;
251 }
252 ++last_header_name;
253 }
254
255 last_header_value++; /* Skip ':'. */
256
257 /* Strip leading whitespace. */
258 while (last_header_value < last_header_line_end
259 && (*last_header_value == ' ' || *last_header_value == '\t')) {
260 last_header_value++;
261 }
262 } else {
263 /* There is no colon which means invalid response so error. */
264 header_info->error = true;
266 "HTTP invalid response format (no colon in header line)!");
267 zend_string_efree(last_header_line_str);
268 return NULL;
269 }
270
271 bool store_header = true;
272 zval *tmpzval = NULL;
273
274 if (!strncasecmp(last_header_line, "Location:", sizeof("Location:")-1)) {
275 /* Check if the location should be followed. */
276 if (context && (tmpzval = php_stream_context_get_option(context, "http", "follow_location")) != NULL) {
277 header_info->follow_location = zval_is_true(tmpzval);
278 } else if (!((response_code >= 300 && response_code < 304)
279 || 307 == response_code || 308 == response_code)) {
280 /* The redirection should not be automatic if follow_location is not set and
281 * response_code not in (300, 301, 302, 303 and 307)
282 * see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.1
283 * RFC 7238 defines 308: http://tools.ietf.org/html/rfc7238 */
284 header_info->follow_location = 0;
285 }
286 size_t last_header_value_len = strlen(last_header_value);
287 if (last_header_value_len > HTTP_HEADER_MAX_LOCATION_SIZE) {
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);
293 return NULL;
294 }
295 if (header_info->location_len == 0) {
296 header_info->location = emalloc(last_header_value_len + 1);
297 } else if (header_info->location_len <= last_header_value_len) {
298 header_info->location = erealloc(header_info->location, last_header_value_len + 1);
299 }
300 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)) {
305 /* https://www.rfc-editor.org/rfc/rfc9110.html#name-content-length */
306 const char *ptr = last_header_value;
307 /* must contain only digits, no + or - symbols */
308 if (*ptr >= '0' && *ptr <= '9') {
309 char *endptr = NULL;
310 size_t parsed = ZEND_STRTOUL(ptr, &endptr, 10);
311 /* check whether there was no garbage in the header value and the conversion was successful */
312 if (endptr && !*endptr) {
313 /* truncate for 32-bit such that no negative file sizes occur */
314 header_info->file_size = MIN(parsed, ZEND_LONG_MAX);
315 php_stream_notify_file_size(context, header_info->file_size, last_header_line, 0);
316 }
317 }
318 } else if (
319 !strncasecmp(last_header_line, "Transfer-Encoding:", sizeof("Transfer-Encoding:")-1)
320 && !strncasecmp(last_header_value, "Chunked", sizeof("Chunked")-1)
321 ) {
322 /* Create filter to decode response body. */
324 bool decode = true;
325
326 if (context && (tmpzval = php_stream_context_get_option(context, "http", "auto_decode")) != NULL) {
327 decode = zend_is_true(tmpzval);
328 }
329 if (decode) {
330 if (header_info->transfer_encoding != NULL) {
331 /* Prevent a memory leak in case there are more transfer-encoding headers. */
333 }
335 "dechunk", NULL, php_stream_is_persistent(stream));
336 if (header_info->transfer_encoding != NULL) {
337 /* Do not store transfer-encoding header. */
338 store_header = false;
339 }
340 }
341 }
342 }
343
344 if (store_header) {
345 zval http_header;
346 ZVAL_NEW_STR(&http_header, last_header_line_str);
347 zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_header);
348 } else {
349 zend_string_efree(last_header_line_str);
350 }
351
352 return NULL;
353}
354
355static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
356 const char *path, const char *mode, int options, zend_string **opened_path,
357 php_stream_context *context, int redirect_max, int flags,
358 zval *response_header STREAMS_DC) /* {{{ */
359{
360 php_stream *stream = NULL;
361 php_url *resource = NULL;
362 int use_ssl;
363 int use_proxy = 0;
364 zend_string *tmp = NULL;
365 char *ua_str = NULL;
366 zval *ua_zval = NULL, *tmpzval = NULL, ssl_proxy_peer_name;
367 int reqok = 0;
368 char *http_header_line = NULL;
369 zend_string *last_header_line_str = NULL;
371 char tmp_line[128];
372 size_t chunk_size = 0;
373 int eol_detect = 0;
374 zend_string *transport_string;
375 zend_string *errstr = NULL;
376 int have_header = 0;
377 bool request_fulluri = false, ignore_errors = false;
378 struct timeval timeout;
379 char *user_headers = NULL;
380 int header_init = ((flags & HTTP_WRAPPER_HEADER_INIT) != 0);
381 int redirected = ((flags & HTTP_WRAPPER_REDIRECTED) != 0);
382 int redirect_keep_method = ((flags & HTTP_WRAPPER_KEEP_METHOD) != 0);
383 int response_code;
384 smart_str req_buf = {0};
385 bool custom_request_method;
386
387 tmp_line[0] = '\0';
388
389 if (redirect_max < 1) {
390 php_stream_wrapper_log_error(wrapper, options, "Redirection limit reached, aborting");
391 return NULL;
392 }
393
394 resource = php_url_parse(path);
395 if (resource == NULL) {
396 return NULL;
397 }
398
399 ZEND_ASSERT(resource->scheme);
400 if (!zend_string_equals_literal_ci(resource->scheme, "http") &&
401 !zend_string_equals_literal_ci(resource->scheme, "https")) {
402 if (!context ||
403 (tmpzval = php_stream_context_get_option(context, wrapper->wops->label, "proxy")) == NULL ||
404 Z_TYPE_P(tmpzval) != IS_STRING ||
405 Z_STRLEN_P(tmpzval) == 0) {
406 php_url_free(resource);
408 }
409 /* Called from a non-http wrapper with http proxying requested (i.e. ftp) */
410 request_fulluri = true;
411 use_ssl = 0;
412 use_proxy = 1;
413 transport_string = zend_string_copy(Z_STR_P(tmpzval));
414 } else {
415 /* Normal http request (possibly with proxy) */
416
417 if (strpbrk(mode, "awx+")) {
418 php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper does not support writeable connections");
419 php_url_free(resource);
420 return NULL;
421 }
422
423 /* Should we send the entire path in the request line, default to no. */
424 if (context && (tmpzval = php_stream_context_get_option(context, "http", "request_fulluri")) != NULL) {
425 request_fulluri = zend_is_true(tmpzval);
426 }
427
428 use_ssl = (ZSTR_LEN(resource->scheme) > 4) && ZSTR_VAL(resource->scheme)[4] == 's';
429 /* choose default ports */
430 if (use_ssl && resource->port == 0)
431 resource->port = 443;
432 else if (resource->port == 0)
433 resource->port = 80;
434
435 if (context &&
436 (tmpzval = php_stream_context_get_option(context, wrapper->wops->label, "proxy")) != NULL &&
437 Z_TYPE_P(tmpzval) == IS_STRING &&
438 Z_STRLEN_P(tmpzval) > 0) {
439 use_proxy = 1;
440 transport_string = zend_string_copy(Z_STR_P(tmpzval));
441 } else {
442 transport_string = zend_strpprintf(0, "%s://%s:%d", use_ssl ? "ssl" : "tcp", ZSTR_VAL(resource->host), resource->port);
443 }
444 }
445
446 if (request_fulluri && (strchr(path, '\n') != NULL || strchr(path, '\r') != NULL)) {
447 php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper full URI path does not allow CR or LF characters");
448 php_url_free(resource);
449 zend_string_release(transport_string);
450 return NULL;
451 }
452
453 if (context && (tmpzval = php_stream_context_get_option(context, wrapper->wops->label, "timeout")) != NULL) {
454 double d = zval_get_double(tmpzval);
455#ifndef PHP_WIN32
456 const double timeoutmax = (double) PHP_TIMEOUT_ULL_MAX / 1000000.0;
457#else
458 const double timeoutmax = (double) LONG_MAX / 1000000.0;
459#endif
460
461 if (d > timeoutmax) {
462 php_stream_wrapper_log_error(wrapper, options, "timeout must be lower than " ZEND_ULONG_FMT, (zend_ulong)timeoutmax);
463 zend_string_release(transport_string);
464 php_url_free(resource);
465 return NULL;
466 }
467#ifndef PHP_WIN32
468 timeout.tv_sec = (time_t) d;
469 timeout.tv_usec = (size_t) ((d - timeout.tv_sec) * 1000000);
470#else
471 timeout.tv_sec = (long) d;
472 timeout.tv_usec = (long) ((d - timeout.tv_sec) * 1000000);
473#endif
474 } else {
475#ifndef PHP_WIN32
476 timeout.tv_sec = FG(default_socket_timeout);
477#else
478 timeout.tv_sec = (long)FG(default_socket_timeout);
479#endif
480 timeout.tv_usec = 0;
481 }
482
483 stream = php_stream_xport_create(ZSTR_VAL(transport_string), ZSTR_LEN(transport_string), options,
485 NULL, &timeout, context, &errstr, NULL);
486
487 if (stream) {
489 }
490
491 if (errstr) {
492 php_stream_wrapper_log_error(wrapper, options, "%s", ZSTR_VAL(errstr));
493 zend_string_release_ex(errstr, 0);
494 errstr = NULL;
495 }
496
497 zend_string_release(transport_string);
498
499 if (stream && use_proxy && use_ssl) {
500 smart_str header = {0};
501
502 /* Set peer_name or name verification will try to use the proxy server name */
503 if (!context || (tmpzval = php_stream_context_get_option(context, "ssl", "peer_name")) == NULL) {
504 ZVAL_STR_COPY(&ssl_proxy_peer_name, resource->host);
505 php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name", &ssl_proxy_peer_name);
506 zval_ptr_dtor(&ssl_proxy_peer_name);
507 }
508
509 smart_str_appendl(&header, "CONNECT ", sizeof("CONNECT ")-1);
510 smart_str_appends(&header, ZSTR_VAL(resource->host));
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);
514
515 /* check if we have Proxy-Authorization header */
516 if (context && (tmpzval = php_stream_context_get_option(context, "http", "header")) != NULL) {
517 const char *s;
518
519 if (Z_TYPE_P(tmpzval) == IS_ARRAY) {
520 zval *tmpheader = NULL;
521
522 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(tmpzval), tmpheader) {
523 if (Z_TYPE_P(tmpheader) == IS_STRING) {
524 s = Z_STRVAL_P(tmpheader);
525 if (php_stream_handle_proxy_authorization_header(s, &header) == SUCCESS) {
526 goto finish;
527 }
528 }
530 } else if (Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval)) {
531 s = Z_STRVAL_P(tmpzval);
532 if (php_stream_handle_proxy_authorization_header(s, &header) == SUCCESS) {
533 goto finish;
534 }
535 }
536 }
537finish:
538 smart_str_appendl(&header, "\r\n", sizeof("\r\n")-1);
539
540 if (php_stream_write(stream, ZSTR_VAL(header.s), ZSTR_LEN(header.s)) != ZSTR_LEN(header.s)) {
541 php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy");
542 php_stream_close(stream);
543 stream = NULL;
544 }
545 smart_str_free(&header);
546
547 if (stream) {
548 char header_line[HTTP_HEADER_BLOCK_SIZE];
549
550 /* get response header */
551 while (php_stream_gets(stream, header_line, HTTP_HEADER_BLOCK_SIZE-1) != NULL) {
552 if (header_line[0] == '\n' ||
553 header_line[0] == '\r' ||
554 header_line[0] == '\0') {
555 break;
556 }
557 }
558 }
559
560 /* enable SSL transport layer */
561 if (stream) {
563 php_stream_xport_crypto_enable(stream, 1) < 0) {
564 php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy");
565 php_stream_close(stream);
566 stream = NULL;
567 }
568 }
569 }
570
571 php_stream_http_response_header_info_init(&header_info);
572
573 if (stream == NULL)
574 goto out;
575
576 /* avoid buffering issues while reading header */
578 chunk_size = php_stream_set_chunk_size(stream, 1);
579
580 /* avoid problems with auto-detecting when reading the headers -> the headers
581 * are always in canonical \r\n format */
584
586
588
589 if (header_init && context && (tmpzval = php_stream_context_get_option(context, "http", "max_redirects")) != NULL) {
590 redirect_max = (int)zval_get_long(tmpzval);
591 }
592
593 custom_request_method = 0;
594 if (context && (tmpzval = php_stream_context_get_option(context, "http", "method")) != NULL) {
595 if (Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval) > 0) {
596 /* As per the RFC, automatically redirected requests MUST NOT use other methods than
597 * GET and HEAD unless it can be confirmed by the user. */
598 if (!redirected || redirect_keep_method
599 || zend_string_equals_literal(Z_STR_P(tmpzval), "GET")
600 || zend_string_equals_literal(Z_STR_P(tmpzval), "HEAD")
601 ) {
602 custom_request_method = 1;
603 smart_str_append(&req_buf, Z_STR_P(tmpzval));
604 smart_str_appendc(&req_buf, ' ');
605 }
606 }
607 }
608
609 if (!custom_request_method) {
610 smart_str_appends(&req_buf, "GET ");
611 }
612
613 if (request_fulluri) {
614 /* Ask for everything */
615 smart_str_appends(&req_buf, path);
616 } else {
617 /* Send the traditional /path/to/file?query_string */
618
619 /* file */
620 if (resource->path && ZSTR_LEN(resource->path)) {
621 smart_str_appends(&req_buf, ZSTR_VAL(resource->path));
622 } else {
623 smart_str_appendc(&req_buf, '/');
624 }
625
626 /* query string */
627 if (resource->query) {
628 smart_str_appendc(&req_buf, '?');
629 smart_str_appends(&req_buf, ZSTR_VAL(resource->query));
630 }
631 }
632
633 /* protocol version we are speaking */
634 if (context && (tmpzval = php_stream_context_get_option(context, "http", "protocol_version")) != NULL) {
635 char *protocol_version;
636 spprintf(&protocol_version, 0, "%.1F", zval_get_double(tmpzval));
637
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);
642 } else {
643 smart_str_appends(&req_buf, " HTTP/1.1\r\n");
644 }
645
646 if (context && (tmpzval = php_stream_context_get_option(context, "http", "header")) != NULL) {
647 tmp = NULL;
648
649 if (Z_TYPE_P(tmpzval) == IS_ARRAY) {
650 zval *tmpheader = NULL;
651 smart_str tmpstr = {0};
652
653 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(tmpzval), tmpheader) {
654 if (Z_TYPE_P(tmpheader) == IS_STRING) {
655 smart_str_append(&tmpstr, Z_STR_P(tmpheader));
656 smart_str_appendl(&tmpstr, "\r\n", sizeof("\r\n") - 1);
657 }
659 smart_str_0(&tmpstr);
660 /* Remove newlines and spaces from start and end. there's at least one extra \r\n at the end that needs to go. */
661 if (tmpstr.s) {
662 tmp = php_trim(tmpstr.s, NULL, 0, 3);
663 smart_str_free(&tmpstr);
664 }
665 } else if (Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval)) {
666 /* Remove newlines and spaces from start and end php_trim will estrndup() */
667 tmp = php_trim(Z_STR_P(tmpzval), NULL, 0, 3);
668 }
669 if (tmp && ZSTR_LEN(tmp)) {
670 char *s;
671 char *t;
672
673 user_headers = estrndup(ZSTR_VAL(tmp), ZSTR_LEN(tmp));
674
675 if (ZSTR_IS_INTERNED(tmp)) {
676 tmp = zend_string_init(ZSTR_VAL(tmp), ZSTR_LEN(tmp), 0);
677 } else if (GC_REFCOUNT(tmp) > 1) {
678 GC_DELREF(tmp);
679 tmp = zend_string_init(ZSTR_VAL(tmp), ZSTR_LEN(tmp), 0);
680 }
681
682 /* Make lowercase for easy comparison against 'standard' headers */
684 t = ZSTR_VAL(tmp);
685
686 if (!header_init && !redirect_keep_method) {
687 /* strip POST headers on redirect */
688 strip_header(user_headers, t, "content-length:");
689 strip_header(user_headers, t, "content-type:");
690 }
691
692 if (check_has_header(t, "user-agent:")) {
693 have_header |= HTTP_HEADER_USER_AGENT;
694 }
695 if (check_has_header(t, "host:")) {
696 have_header |= HTTP_HEADER_HOST;
697 }
698 if (check_has_header(t, "from:")) {
699 have_header |= HTTP_HEADER_FROM;
700 }
701 if (check_has_header(t, "authorization:")) {
702 have_header |= HTTP_HEADER_AUTH;
703 }
704 if (check_has_header(t, "content-length:")) {
705 have_header |= HTTP_HEADER_CONTENT_LENGTH;
706 }
707 if (check_has_header(t, "content-type:")) {
708 have_header |= HTTP_HEADER_TYPE;
709 }
710 if (check_has_header(t, "connection:")) {
711 have_header |= HTTP_HEADER_CONNECTION;
712 }
713
714 /* remove Proxy-Authorization header */
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;
718
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++;
722 if (*p == 0) {
723 if (s == t) {
724 efree(user_headers);
725 user_headers = NULL;
726 } else {
727 while (s > t && (*(s-1) == '\r' || *(s-1) == '\n')) s--;
728 user_headers[s - t] = 0;
729 }
730 } else {
731 memmove(user_headers + (s - t), user_headers + (p - t), strlen(p) + 1);
732 }
733 }
734
735 }
736 if (tmp) {
738 }
739 }
740
741 /* auth header if it was specified */
742 if (((have_header & HTTP_HEADER_AUTH) == 0) && resource->user) {
743 /* make scratch large enough to hold the whole URL (over-estimate) */
744 size_t scratch_len = strlen(path) + 1;
745 char *scratch = emalloc(scratch_len);
746 zend_string *stmp;
747
748 /* decode the strings first */
749 php_url_decode(ZSTR_VAL(resource->user), ZSTR_LEN(resource->user));
750
751 strcpy(scratch, ZSTR_VAL(resource->user));
752 strcat(scratch, ":");
753
754 /* Note: password is optional! */
755 if (resource->pass) {
756 php_url_decode(ZSTR_VAL(resource->pass), ZSTR_LEN(resource->pass));
757 strcat(scratch, ZSTR_VAL(resource->pass));
758 }
759
760 stmp = php_base64_encode((unsigned char*)scratch, strlen(scratch));
761
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");
765
767
768 zend_string_free(stmp);
769 efree(scratch);
770 }
771
772 /* if the user has configured who they are, send a From: line */
773 if (!(have_header & HTTP_HEADER_FROM) && FG(from_address)) {
774 smart_str_appends(&req_buf, "From: ");
775 smart_str_appends(&req_buf, FG(from_address));
776 smart_str_appends(&req_buf, "\r\n");
777 }
778
779 /* Send Host: header so name-based virtual hosts work */
780 if ((have_header & HTTP_HEADER_HOST) == 0) {
781 smart_str_appends(&req_buf, "Host: ");
782 smart_str_appends(&req_buf, ZSTR_VAL(resource->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);
787 }
788 smart_str_appends(&req_buf, "\r\n");
789 }
790
791 /* Send a Connection: close header to avoid hanging when the server
792 * interprets the RFC literally and establishes a keep-alive connection,
793 * unless the user specifically requests something else by specifying a
794 * Connection header in the context options. Send that header even for
795 * HTTP/1.0 to avoid issues when the server respond with an HTTP/1.1
796 * keep-alive response, which is the preferred response type. */
797 if ((have_header & HTTP_HEADER_CONNECTION) == 0) {
798 smart_str_appends(&req_buf, "Connection: close\r\n");
799 }
800
801 if (context &&
802 (ua_zval = php_stream_context_get_option(context, "http", "user_agent")) != NULL &&
803 Z_TYPE_P(ua_zval) == IS_STRING) {
804 ua_str = Z_STRVAL_P(ua_zval);
805 } else if (FG(user_agent)) {
806 ua_str = FG(user_agent);
807 }
808
809 if (((have_header & HTTP_HEADER_USER_AGENT) == 0) && ua_str) {
810#define _UA_HEADER "User-Agent: %s\r\n"
811 char *ua;
812 size_t ua_len;
813
814 ua_len = sizeof(_UA_HEADER) + strlen(ua_str);
815
816 /* ensure the header is only sent if user_agent is not blank */
817 if (ua_len > sizeof(_UA_HEADER)) {
818 ua = emalloc(ua_len + 1);
819 if ((ua_len = slprintf(ua, ua_len, _UA_HEADER, ua_str)) > 0) {
820 ua[ua_len] = 0;
821 smart_str_appendl(&req_buf, ua, ua_len);
822 } else {
823 php_error_docref(NULL, E_WARNING, "Cannot construct User-agent header");
824 }
825 efree(ua);
826 }
827 }
828
829 if (user_headers) {
830 /* A bit weird, but some servers require that Content-Length be sent prior to Content-Type for POST
831 * see bug #44603 for details. Since Content-Type maybe part of user's headers we need to do this check first.
832 */
833 if (
834 (header_init || redirect_keep_method) &&
835 context &&
836 !(have_header & HTTP_HEADER_CONTENT_LENGTH) &&
837 (tmpzval = php_stream_context_get_option(context, "http", "content")) != NULL &&
838 Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval) > 0
839 ) {
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");
843 have_header |= HTTP_HEADER_CONTENT_LENGTH;
844 }
845
846 smart_str_appends(&req_buf, user_headers);
847 smart_str_appends(&req_buf, "\r\n");
848 efree(user_headers);
849 }
850
851 /* Request content, such as for POST requests */
852 if ((header_init || redirect_keep_method) && context &&
853 (tmpzval = php_stream_context_get_option(context, "http", "content")) != NULL &&
854 Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval) > 0) {
855 if (!(have_header & HTTP_HEADER_CONTENT_LENGTH)) {
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");
859 }
860 if (!(have_header & HTTP_HEADER_TYPE)) {
861 smart_str_appends(&req_buf, "Content-Type: application/x-www-form-urlencoded\r\n");
862 php_error_docref(NULL, E_NOTICE, "Content-type not specified assuming application/x-www-form-urlencoded");
863 }
864 smart_str_appends(&req_buf, "\r\n");
865 smart_str_appendl(&req_buf, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval));
866 } else {
867 smart_str_appends(&req_buf, "\r\n");
868 }
869
870 /* send it */
871 php_stream_write(stream, ZSTR_VAL(req_buf.s), ZSTR_LEN(req_buf.s));
872
873 if (Z_ISUNDEF_P(response_header)) {
874 array_init(response_header);
875 }
876
877 {
878 /* get response header */
879 size_t tmp_line_len;
880 if (!php_stream_eof(stream) &&
881 php_stream_get_line(stream, tmp_line, sizeof(tmp_line) - 1, &tmp_line_len) != NULL) {
882 zval http_response;
883
884 if (tmp_line_len > 9) {
885 response_code = atoi(tmp_line + 9);
886 } else {
887 response_code = 0;
888 }
889 if (context && NULL != (tmpzval = php_stream_context_get_option(context, "http", "ignore_errors"))) {
890 ignore_errors = zend_is_true(tmpzval);
891 }
892 /* when we request only the header, don't fail even on error codes */
893 if ((options & STREAM_ONLY_GET_HEADERS) || ignore_errors) {
894 reqok = 1;
895 }
896
897 /* status codes of 1xx are "informational", and will be followed by a real response
898 * e.g "100 Continue". RFC 7231 states that unexpected 1xx status MUST be parsed,
899 * and MAY be ignored. As such, we need to skip ahead to the "real" status*/
900 if (response_code >= 100 && response_code < 200 && response_code != 101) {
901 /* consume lines until we find a line starting 'HTTP/1' */
902 while (
903 !php_stream_eof(stream)
904 && php_stream_get_line(stream, tmp_line, sizeof(tmp_line) - 1, &tmp_line_len) != NULL
905 && ( tmp_line_len < sizeof("HTTP/1") - 1 || strncasecmp(tmp_line, "HTTP/1", sizeof("HTTP/1") - 1) )
906 );
907
908 if (tmp_line_len > 9) {
909 response_code = atoi(tmp_line + 9);
910 } else {
911 response_code = 0;
912 }
913 }
914 /* all status codes in the 2xx range are defined by the specification as successful;
915 * all status codes in the 3xx range are for redirection, and so also should never
916 * fail */
917 if (response_code >= 200 && response_code < 400) {
918 reqok = 1;
919 } else {
920 switch(response_code) {
921 case 403:
923 tmp_line, response_code);
924 break;
925 default:
926 /* safety net in the event tmp_line == NULL */
927 if (!tmp_line_len) {
928 tmp_line[0] = '\0';
929 }
931 tmp_line, response_code);
932 }
933 }
934 if (tmp_line_len >= 1 && tmp_line[tmp_line_len - 1] == '\n') {
935 --tmp_line_len;
936 if (tmp_line_len >= 1 &&tmp_line[tmp_line_len - 1] == '\r') {
937 --tmp_line_len;
938 }
939 } else {
940 // read and discard rest of status line
941 char *line = php_stream_get_line(stream, NULL, 0, NULL);
942 efree(line);
943 }
944 ZVAL_STRINGL(&http_response, tmp_line, tmp_line_len);
945 zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_response);
946 } else {
947 php_stream_close(stream);
948 stream = NULL;
949 php_stream_wrapper_log_error(wrapper, options, "HTTP request failed!");
950 goto out;
951 }
952 }
953
954 /* read past HTTP headers */
955 while (!php_stream_eof(stream)) {
956 size_t http_header_line_length;
957
958 if (http_header_line != NULL) {
959 efree(http_header_line);
960 }
961 if ((http_header_line = php_stream_get_line(stream, NULL, 0, &http_header_line_length))) {
962 bool last_line;
963 if (*http_header_line == '\r') {
964 if (http_header_line[1] != '\n') {
965 php_stream_close(stream);
966 stream = NULL;
968 "HTTP invalid header name (cannot start with CR character)!");
969 goto out;
970 }
971 last_line = true;
972 } else if (*http_header_line == '\n') {
973 last_line = true;
974 } else {
975 last_line = false;
976 }
977
978 if (last_header_line_str != NULL) {
979 /* Parse last header line. */
980 last_header_line_str = php_stream_http_response_headers_parse(wrapper, stream,
981 context, options, last_header_line_str, http_header_line,
982 &http_header_line_length, response_code, response_header, &header_info);
983 if (EXPECTED(last_header_line_str == NULL)) {
984 if (UNEXPECTED(header_info.error)) {
985 php_stream_close(stream);
986 stream = NULL;
987 goto out;
988 }
989 } else {
990 /* Folding header present so continue. */
991 continue;
992 }
993 } else if (!last_line) {
994 /* The first line cannot start with spaces. */
995 if (*http_header_line == ' ' || *http_header_line == '\t') {
996 php_stream_close(stream);
997 stream = NULL;
999 "HTTP invalid response format (folding header at the start)!");
1000 goto out;
1001 }
1002 /* Trim the first line if it is not the last line. */
1003 php_stream_http_response_header_trim(http_header_line, &http_header_line_length);
1004 }
1005 if (last_line) {
1006 /* For the last line the last header line must be NULL. */
1007 ZEND_ASSERT(last_header_line_str == NULL);
1008 break;
1009 }
1010 /* Save current line as the last line so it gets parsed in the next round. */
1011 last_header_line_str = zend_string_init(http_header_line, http_header_line_length, 0);
1012 } else {
1013 break;
1014 }
1015 }
1016
1017 /* If the stream was closed early, we still want to process the last line to keep BC. */
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);
1021 }
1022
1023 if (!reqok || (header_info.location != NULL && header_info.follow_location)) {
1024 if (!header_info.follow_location || (((options & STREAM_ONLY_GET_HEADERS) || ignore_errors) && redirect_max <= 1)) {
1025 goto out;
1026 }
1027
1028 if (header_info.location != NULL)
1030
1031 php_stream_close(stream);
1032 stream = NULL;
1033
1034 if (header_info.transfer_encoding) {
1036 header_info.transfer_encoding = NULL;
1037 }
1038
1039 if (header_info.location != NULL) {
1040
1041 char *new_path = NULL;
1042
1043 if (strlen(header_info.location) < 8 ||
1044 (strncasecmp(header_info.location, "http://", sizeof("http://")-1) &&
1045 strncasecmp(header_info.location, "https://", sizeof("https://")-1) &&
1046 strncasecmp(header_info.location, "ftp://", sizeof("ftp://")-1) &&
1047 strncasecmp(header_info.location, "ftps://", sizeof("ftps://")-1)))
1048 {
1049 char *loc_path = NULL;
1050 if (*header_info.location != '/') {
1051 if (*(header_info.location+1) != '\0' && resource->path) {
1052 char *s = strrchr(ZSTR_VAL(resource->path), '/');
1053 if (!s) {
1054 s = ZSTR_VAL(resource->path);
1055 if (!ZSTR_LEN(resource->path)) {
1056 zend_string_release_ex(resource->path, 0);
1057 resource->path = ZSTR_INIT_LITERAL("/", 0);
1058 s = ZSTR_VAL(resource->path);
1059 } else {
1060 *s = '/';
1061 }
1062 }
1063 s[1] = '\0';
1064 if (resource->path &&
1065 ZSTR_VAL(resource->path)[0] == '/' &&
1066 ZSTR_VAL(resource->path)[1] == '\0') {
1067 spprintf(&loc_path, 0, "%s%s", ZSTR_VAL(resource->path), header_info.location);
1068 } else {
1069 spprintf(&loc_path, 0, "%s/%s", ZSTR_VAL(resource->path), header_info.location);
1070 }
1071 } else {
1072 spprintf(&loc_path, 0, "/%s", header_info.location);
1073 }
1074 } else {
1075 loc_path = header_info.location;
1076 header_info.location = NULL;
1077 }
1078 if ((use_ssl && resource->port != 443) || (!use_ssl && resource->port != 80)) {
1079 spprintf(&new_path, 0, "%s://%s:%d%s", ZSTR_VAL(resource->scheme),
1080 ZSTR_VAL(resource->host), resource->port, loc_path);
1081 } else {
1082 spprintf(&new_path, 0, "%s://%s%s", ZSTR_VAL(resource->scheme),
1083 ZSTR_VAL(resource->host), loc_path);
1084 }
1085 efree(loc_path);
1086 } else {
1087 new_path = header_info.location;
1088 header_info.location = NULL;
1089 }
1090
1091 php_url_free(resource);
1092 /* check for invalid redirection URLs */
1093 if ((resource = php_url_parse(new_path)) == NULL) {
1094 php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path);
1095 efree(new_path);
1096 goto out;
1097 }
1098
1099#define CHECK_FOR_CNTRL_CHARS(val) { \
1100 if (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); \
1104 while (s < e) { \
1105 if (iscntrl(*s)) { \
1106 php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); \
1107 efree(new_path); \
1108 goto out; \
1109 } \
1110 s++; \
1111 } \
1112 } \
1113}
1114 /* check for control characters in login, password & path */
1115 if (strncasecmp(new_path, "http://", sizeof("http://") - 1) || strncasecmp(new_path, "https://", sizeof("https://") - 1)) {
1116 CHECK_FOR_CNTRL_CHARS(resource->user);
1117 CHECK_FOR_CNTRL_CHARS(resource->pass);
1118 CHECK_FOR_CNTRL_CHARS(resource->path);
1119 }
1120 int new_flags = HTTP_WRAPPER_REDIRECTED;
1121 if (response_code == 307 || response_code == 308) {
1122 /* RFC 7538 specifies that status code 308 does not allow changing the request method from POST to GET.
1123 * RFC 7231 does the same for status code 307.
1124 * To keep consistency between POST and PATCH requests, we'll also not change the request method from PATCH to GET, even though it's allowed it's not mandated by the RFC. */
1125 new_flags |= HTTP_WRAPPER_KEEP_METHOD;
1126 }
1127 stream = php_stream_url_wrap_http_ex(
1128 wrapper, new_path, mode, options, opened_path, context,
1129 --redirect_max, new_flags, response_header STREAMS_CC);
1130 efree(new_path);
1131 } else {
1132 php_stream_wrapper_log_error(wrapper, options, "HTTP request failed! %s", tmp_line);
1133 }
1134 }
1135out:
1136
1137 smart_str_free(&req_buf);
1138
1139 if (http_header_line) {
1140 efree(http_header_line);
1141 }
1142
1143 if (header_info.location != NULL) {
1144 efree(header_info.location);
1145 }
1146
1147 if (resource) {
1148 php_url_free(resource);
1149 }
1150
1151 if (stream) {
1152 if (header_init) {
1153 ZVAL_COPY(&stream->wrapperdata, response_header);
1154 }
1156
1157 /* Restore original chunk size now that we're done with headers */
1159 php_stream_set_chunk_size(stream, (int)chunk_size);
1160
1161 /* restore the users auto-detect-line-endings setting */
1162 stream->flags |= eol_detect;
1163
1164 /* as far as streams are concerned, we are now at the start of
1165 * the stream */
1166 stream->position = 0;
1167
1168 /* restore mode */
1169 strlcpy(stream->mode, mode, sizeof(stream->mode));
1170
1171 if (header_info.transfer_encoding) {
1173 }
1174
1175 /* It's possible that the server already sent in more data than just the headers.
1176 * We account for this by adjusting the progress counter by the difference of
1177 * already read header data and the body. */
1178 if (stream->writepos > stream->readpos) {
1180 }
1181 }
1182
1183 return stream;
1184}
1185/* }}} */
1186
1187php_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) /* {{{ */
1188{
1189 php_stream *stream;
1190 zval headers;
1191
1192 ZVAL_UNDEF(&headers);
1193
1194 zval_ptr_dtor(&BG(last_http_headers));
1195 ZVAL_UNDEF(&BG(last_http_headers));
1196
1197 stream = php_stream_url_wrap_http_ex(
1198 wrapper, path, mode, options, opened_path, context,
1200
1201 if (!Z_ISUNDEF(headers)) {
1202 ZVAL_COPY(&BG(last_http_headers), &headers);
1203
1205 "http_response_header", sizeof("http_response_header")-1, &headers, 0)) {
1206 zval_ptr_dtor(&headers);
1207 }
1208 }
1209
1210 return stream;
1211}
1212/* }}} */
1213
1214static int php_stream_http_stream_stat(php_stream_wrapper *wrapper, php_stream *stream, php_stream_statbuf *ssb) /* {{{ */
1215{
1216 /* one day, we could fill in the details based on Date: and Content-Length:
1217 * headers. For now, we return with a failure code to prevent the underlying
1218 * file's details from being used instead. */
1219 return -1;
1220}
1221/* }}} */
1222
1223static const php_stream_wrapper_ops http_stream_wops = {
1225 NULL, /* stream_close */
1226 php_stream_http_stream_stat,
1227 NULL, /* stat_url */
1228 NULL, /* opendir */
1229 "http",
1230 NULL, /* unlink */
1231 NULL, /* rename */
1232 NULL, /* mkdir */
1233 NULL, /* rmdir */
1234 NULL
1235};
1236
1238 &http_stream_wops,
1239 NULL,
1240 1 /* is_url */
1241};
strcat($n)
Definition bench.php:337
#define BG(v)
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)
char s[4]
Definition cdf.c:77
void * ptr
Definition ffi.c:3814
memcpy(ptr1, ptr2, size)
memset(ptr, 0, type->size)
char * mode
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
#define HTTP_HEADER_FROM
PHPAPI const php_stream_wrapper php_stream_http_wrapper
#define HTTP_HEADER_TYPE
#define HTTP_HEADER_USER_AGENT
#define HTTP_HEADER_AUTH
#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_HEADER_HOST
#define HTTP_WRAPPER_HEADER_INIT
#define _UA_HEADER
PHPAPI php_stream_filter * php_stream_filter_create(const char *filtername, zval *filterparams, uint8_t persistent)
Definition filter.c:220
PHPAPI void php_stream_filter_free(php_stream_filter *filter)
Definition filter.c:278
PHPAPI size_t php_url_decode(char *str, size_t len)
Definition url.c:582
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
#define memmove(a, b, c)
#define strlcpy
Definition php.h:159
#define PHPAPI
Definition php.h:71
int line
Definition php_ffi.h:54
PHP_JSON_API size_t int options
Definition php_json.h:102
#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)
Definition streams.c:2346
#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)
Definition streams.c:2407
#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)
Definition streams.c:2418
#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)
Definition transports.c:378
@ 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)
Definition transports.c:357
#define STREAM_XPORT_CLIENT
struct _php_stream_wrapper_ops php_stream_wrapper_ops
struct _php_stream php_stream
Definition php_streams.h:96
struct _php_stream_context php_stream_context
Definition php_streams.h:98
#define REPORT_ERRORS
#define php_stream_gets(stream, buf, maxlen)
struct _php_stream_filter php_stream_filter
Definition php_streams.h:99
#define STREAMS_DC
Definition php_streams.h:53
#define STREAMS_CC
Definition php_streams.h:54
#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 STREAM_WILL_CAST
#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
Definition php_streams.h:97
#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)
Definition string.c:628
uint32_t last_line
Definition phpdbg.h:237
const char * endptr
Definition session.c:1021
p
Definition session.c:1105
#define slprintf
Definition snprintf.h:89
#define spprintf
Definition spprintf.h:29
#define PHP_TIMEOUT_ULL_MAX
Definition file.h:64
#define FG(v)
Definition file.h:117
const php_stream_wrapper_ops * wops
uint32_t flags
zend_off_t readpos
php_stream_filter_chain readfilters
zend_off_t position
char mode[16]
zend_off_t writepos
Definition dce.c:49
Definition url.h:20
zend_string * user
Definition url.h:22
zend_string * pass
Definition url.h:23
zend_string * host
Definition url.h:24
unsigned short port
Definition url.h:25
zend_string * scheme
Definition url.h:21
zend_string * query
Definition url.h:27
zend_string * path
Definition url.h:26
zend_string * s
PHPAPI php_url * php_url_parse(char const *str)
Definition url.c:67
PHPAPI void php_url_free(php_url *theurl)
Definition url.c:32
ZEND_API zend_string * zend_strpprintf(size_t max_len, const char *format,...)
Definition zend.c:353
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)
Definition zend_API.h:952
#define array_init(arg)
Definition zend_API.h:537
#define estrndup(s, length)
Definition zend_alloc.h:165
#define efree(ptr)
Definition zend_alloc.h:155
#define erealloc(ptr, size)
Definition zend_alloc.h:159
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
strlen(string $string)
zend_string_release_ex(func->internal_function.function_name, 0)
#define strncasecmp(s1, s2, n)
#define E_NOTICE
Definition zend_errors.h:26
#define E_WARNING
Definition zend_errors.h:24
ZEND_API zval *ZEND_FASTCALL zend_hash_next_index_insert(HashTable *ht, zval *pData)
Definition zend_hash.c:1224
#define ZEND_HASH_FOREACH_END()
Definition zend_hash.h:1086
#define ZEND_HASH_FOREACH_VAL(ht, _val)
Definition zend_hash.h:1102
#define ZEND_ULONG_FMT
Definition zend_long.h:88
uint32_t zend_ulong
Definition zend_long.h:43
#define ZEND_STRTOUL(s0, s1, base)
Definition zend_long.h:86
#define ZEND_LONG_MAX
Definition zend_long.h:45
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 zval_is_true(op)
#define MIN(a, b)
#define LONG_MAX
#define EXPECTED(condition)
#define ZEND_ASSERT(c)
#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)
Definition zend_string.h:84
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_INIT_LITERAL(s, persistent)
#define zend_string_equals_literal(str, literal)
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
#define zend_string_equals_literal_ci(str, c)
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define ZVAL_UNDEF(z)
#define Z_STRVAL_P(zval_p)
Definition zend_types.h:975
#define Z_ISUNDEF_P(zval_p)
Definition zend_types.h:957
#define Z_ARRVAL_P(zval_p)
Definition zend_types.h:987
#define IS_STRING
Definition zend_types.h:606
#define ZVAL_STR_COPY(z, s)
#define IS_ARRAY
Definition zend_types.h:607
#define Z_ISUNDEF(zval)
Definition zend_types.h:956
#define Z_STR_P(zval_p)
Definition zend_types.h:972
#define GC_DELREF(p)
Definition zend_types.h:710
#define Z_STRLEN_P(zval_p)
Definition zend_types.h:978
@ FAILURE
Definition zend_types.h:61
#define ZVAL_NEW_STR(z, s)
#define ZVAL_COPY(z, v)
ZEND_RESULT_CODE zend_result
Definition zend_types.h:64
#define GC_REFCOUNT(p)
Definition zend_types.h:707
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
out($f, $s)