xref: /PHP-8.2/ext/soap/php_http.c (revision a9dada29)
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: Brad Lafountain <rodif_bl@yahoo.com>                        |
14   |          Shane Caraveo <shane@caraveo.com>                           |
15   |          Dmitry Stogov <dmitry@php.net>                              |
16   +----------------------------------------------------------------------+
17 */
18 
19 #include "php_soap.h"
20 #include "ext/standard/base64.h"
21 #include "ext/standard/md5.h"
22 #include "ext/random/php_random.h"
23 #include "ext/hash/php_hash.h"
24 
25 static char *get_http_header_value_nodup(char *headers, char *type, size_t *len);
26 static char *get_http_header_value(char *headers, char *type);
27 static zend_string *get_http_body(php_stream *socketd, int close, char *headers);
28 static zend_string *get_http_headers(php_stream *socketd);
29 
30 #define smart_str_append_const(str, const) \
31 	smart_str_appendl(str,const,sizeof(const)-1)
32 
33 /* Proxy HTTP Authentication */
proxy_authentication(zval * this_ptr,smart_str * soap_headers)34 int proxy_authentication(zval* this_ptr, smart_str* soap_headers)
35 {
36 	zval *login = Z_CLIENT_PROXY_LOGIN_P(this_ptr);
37 	if (Z_TYPE_P(login) == IS_STRING) {
38 		smart_str auth = {0};
39 		smart_str_append(&auth, Z_STR_P(login));
40 		smart_str_appendc(&auth, ':');
41 
42 		zval *password = Z_CLIENT_PROXY_PASSWORD_P(this_ptr);
43 		if (Z_TYPE_P(password) == IS_STRING) {
44 			smart_str_append(&auth, Z_STR_P(password));
45 		}
46 		smart_str_0(&auth);
47 		zend_string *buf = php_base64_encode((unsigned char*)ZSTR_VAL(auth.s), ZSTR_LEN(auth.s));
48 		smart_str_append_const(soap_headers, "Proxy-Authorization: Basic ");
49 		smart_str_append(soap_headers, buf);
50 		smart_str_append_const(soap_headers, "\r\n");
51 		zend_string_release_ex(buf, 0);
52 		smart_str_free(&auth);
53 		return 1;
54 	}
55 	return 0;
56 }
57 
58 /* HTTP Authentication */
basic_authentication(zval * this_ptr,smart_str * soap_headers)59 int basic_authentication(zval* this_ptr, smart_str* soap_headers)
60 {
61 	zval *login = Z_CLIENT_LOGIN_P(this_ptr);
62 	zval *use_digest = Z_CLIENT_USE_DIGEST_P(this_ptr);
63 	if (Z_TYPE_P(login) == IS_STRING && Z_TYPE_P(use_digest) != IS_TRUE) {
64 		smart_str auth = {0};
65 		smart_str_append(&auth, Z_STR_P(login));
66 		smart_str_appendc(&auth, ':');
67 
68 		zval *password = Z_CLIENT_PASSWORD_P(this_ptr);
69 		if (Z_TYPE_P(password) == IS_STRING) {
70 			smart_str_append(&auth, Z_STR_P(password));
71 		}
72 		smart_str_0(&auth);
73 		zend_string *buf = php_base64_encode((unsigned char*)ZSTR_VAL(auth.s), ZSTR_LEN(auth.s));
74 		smart_str_append_const(soap_headers, "Authorization: Basic ");
75 		smart_str_append(soap_headers, buf);
76 		smart_str_append_const(soap_headers, "\r\n");
77 		zend_string_release_ex(buf, 0);
78 		smart_str_free(&auth);
79 		return 1;
80 	}
81 	return 0;
82 }
83 
http_context_add_header(const char * s,bool has_authorization,bool has_proxy_authorization,bool has_cookies,smart_str * soap_headers)84 static void http_context_add_header(const char *s,
85 									bool has_authorization,
86 									bool has_proxy_authorization,
87 									bool has_cookies,
88 									smart_str *soap_headers)
89 {
90 	const char *p;
91 	int name_len;
92 
93 	while (*s) {
94 		/* skip leading newlines and spaces */
95 		while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') {
96 			s++;
97 		}
98 		/* extract header name */
99 		p = s;
100 		name_len = -1;
101 		while (*p) {
102 			if (*p == ':') {
103 				if (name_len < 0) name_len = p - s;
104 				break;
105 			} else if (*p == ' ' || *p == '\t') {
106 				if (name_len < 0) name_len = p - s;
107 			} else if (*p == '\r' || *p == '\n') {
108 				break;
109 			}
110 			p++;
111 		}
112 		if (*p == ':') {
113 			/* extract header value */
114 			while (*p && *p != '\r' && *p != '\n') {
115 				p++;
116 			}
117 			/* skip some predefined headers */
118 			if ((name_len != sizeof("host")-1 ||
119 				 strncasecmp(s, "host", sizeof("host")-1) != 0) &&
120 				(name_len != sizeof("connection")-1 ||
121 				 strncasecmp(s, "connection", sizeof("connection")-1) != 0) &&
122 				(name_len != sizeof("user-agent")-1 ||
123 				 strncasecmp(s, "user-agent", sizeof("user-agent")-1) != 0) &&
124 				(name_len != sizeof("content-length")-1 ||
125 				 strncasecmp(s, "content-length", sizeof("content-length")-1) != 0) &&
126 				(name_len != sizeof("content-type")-1 ||
127 				 strncasecmp(s, "content-type", sizeof("content-type")-1) != 0) &&
128 				(!has_cookies ||
129 				 name_len != sizeof("cookie")-1 ||
130 				 strncasecmp(s, "cookie", sizeof("cookie")-1) != 0) &&
131 				(!has_authorization ||
132 				 name_len != sizeof("authorization")-1 ||
133 				 strncasecmp(s, "authorization", sizeof("authorization")-1) != 0) &&
134 				(!has_proxy_authorization ||
135 				 name_len != sizeof("proxy-authorization")-1 ||
136 				 strncasecmp(s, "proxy-authorization", sizeof("proxy-authorization")-1) != 0)) {
137 				/* add header */
138 				smart_str_appendl(soap_headers, s, p-s);
139 				smart_str_append_const(soap_headers, "\r\n");
140 			}
141 		}
142 		s = (*p) ? (p + 1) : p;
143 	}
144 }
145 
146 /* Additional HTTP headers */
http_context_headers(php_stream_context * context,bool has_authorization,bool has_proxy_authorization,bool has_cookies,smart_str * soap_headers)147 void http_context_headers(php_stream_context* context,
148                           bool has_authorization,
149                           bool has_proxy_authorization,
150                           bool has_cookies,
151                           smart_str* soap_headers)
152 {
153 	zval *tmp;
154 	if (context && (tmp = php_stream_context_get_option(context, "http", "header")) != NULL) {
155 		if (Z_TYPE_P(tmp) == IS_STRING && Z_STRLEN_P(tmp)) {
156 			http_context_add_header(Z_STRVAL_P(tmp), has_authorization, has_proxy_authorization, has_cookies, soap_headers);
157 		} else if (Z_TYPE_P(tmp) == IS_ARRAY) {
158 			zval *value;
159 			ZEND_HASH_FOREACH_VAL(Z_ARR_P(tmp), value) {
160 				if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value)) {
161 					http_context_add_header(Z_STRVAL_P(value), has_authorization, has_proxy_authorization, has_cookies, soap_headers);
162 				}
163 			} ZEND_HASH_FOREACH_END();
164 		}
165 	}
166 }
167 
http_connect(zval * this_ptr,php_url * phpurl,int use_ssl,php_stream_context * context,int * use_proxy)168 static php_stream* http_connect(zval* this_ptr, php_url *phpurl, int use_ssl, php_stream_context *context, int *use_proxy)
169 {
170 	php_stream *stream;
171 	zval *tmp, ssl_proxy_peer_name;
172 	char *host;
173 	char *name;
174 	char *protocol;
175 	zend_long namelen;
176 	int port;
177 	int old_error_reporting;
178 	struct timeval tv;
179 	struct timeval *timeout = NULL;
180 
181 	zval *proxy_host = Z_CLIENT_PROXY_HOST_P(this_ptr);
182 	zval *proxy_port = Z_CLIENT_PROXY_PORT_P(this_ptr);
183 	if (Z_TYPE_P(proxy_host) == IS_STRING && Z_TYPE_P(proxy_port) == IS_LONG) {
184 		host = Z_STRVAL_P(proxy_host);
185 		port = Z_LVAL_P(proxy_port);
186 		*use_proxy = 1;
187 	} else {
188 		host = ZSTR_VAL(phpurl->host);
189 		port = phpurl->port;
190 	}
191 
192 	tmp = Z_CLIENT_CONNECTION_TIMEOUT_P(this_ptr);
193 	if (Z_TYPE_P(tmp) == IS_LONG && Z_LVAL_P(tmp) > 0) {
194 		tv.tv_sec = Z_LVAL_P(tmp);
195 		tv.tv_usec = 0;
196 		timeout = &tv;
197 	}
198 
199 	old_error_reporting = EG(error_reporting);
200 	EG(error_reporting) &= ~(E_WARNING|E_NOTICE|E_USER_WARNING|E_USER_NOTICE);
201 
202 	/* Changed ternary operator to an if/else so that additional comparisons can be done on the ssl_method property */
203 	if (use_ssl && !*use_proxy) {
204 		tmp = Z_CLIENT_SSL_METHOD_P(this_ptr);
205 		if (Z_TYPE_P(tmp) == IS_LONG) {
206 			/* uses constants declared in soap.c to determine ssl uri protocol */
207 			switch (Z_LVAL_P(tmp)) {
208 				case SOAP_SSL_METHOD_TLS:
209 					protocol = "tls";
210 					break;
211 
212 				case SOAP_SSL_METHOD_SSLv2:
213 					protocol = "sslv2";
214 					break;
215 
216 				case SOAP_SSL_METHOD_SSLv3:
217 					protocol = "sslv3";
218 					break;
219 
220 				case SOAP_SSL_METHOD_SSLv23:
221 					protocol = "ssl";
222 					break;
223 
224 				default:
225 					protocol = "ssl";
226 					break;
227 
228 			}
229 		} else {
230 			protocol = "ssl";
231 		}
232 	} else {
233 		protocol = "tcp";
234 	}
235 
236 	namelen = spprintf(&name, 0, "%s://%s:%d", protocol, host, port);
237 
238 	stream = php_stream_xport_create(name, namelen,
239 		REPORT_ERRORS,
240 		STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
241 		NULL /*persistent_id*/,
242 		timeout,
243 		context,
244 		NULL, NULL);
245 	efree(name);
246 
247 	/* SSL & proxy */
248 	if (stream && *use_proxy && use_ssl) {
249 		smart_str soap_headers = {0};
250 
251 		/* Set peer_name or name verification will try to use the proxy server name */
252 		if (!context || (tmp = php_stream_context_get_option(context, "ssl", "peer_name")) == NULL) {
253 			ZVAL_STR_COPY(&ssl_proxy_peer_name, phpurl->host);
254 			php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name", &ssl_proxy_peer_name);
255 			zval_ptr_dtor(&ssl_proxy_peer_name);
256 		}
257 
258 		smart_str_append_const(&soap_headers, "CONNECT ");
259 		smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->host));
260 		smart_str_appendc(&soap_headers, ':');
261 		smart_str_append_unsigned(&soap_headers, phpurl->port);
262 		smart_str_append_const(&soap_headers, " HTTP/1.1\r\n");
263 		smart_str_append_const(&soap_headers, "Host: ");
264 		smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->host));
265 		if (phpurl->port != 80) {
266 			smart_str_appendc(&soap_headers, ':');
267 			smart_str_append_unsigned(&soap_headers, phpurl->port);
268 		}
269 		smart_str_append_const(&soap_headers, "\r\n");
270 		proxy_authentication(this_ptr, &soap_headers);
271 		smart_str_append_const(&soap_headers, "\r\n");
272 		if (php_stream_write(stream, ZSTR_VAL(soap_headers.s), ZSTR_LEN(soap_headers.s)) != ZSTR_LEN(soap_headers.s)) {
273 			php_stream_close(stream);
274 			stream = NULL;
275 		}
276 		smart_str_free(&soap_headers);
277 
278 		if (stream) {
279 			zend_string *http_headers = get_http_headers(stream);
280 			if (http_headers) {
281 				zend_string_free(http_headers);
282 			} else {
283 				php_stream_close(stream);
284 				stream = NULL;
285 			}
286 		}
287 		/* enable SSL transport layer */
288 		if (stream) {
289 			/* if a stream is created without encryption, check to see if SSL method parameter is specified and use
290 			   proper encrypyion method based on constants defined in soap.c */
291 			int crypto_method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
292 			tmp = Z_CLIENT_SSL_METHOD_P(this_ptr);
293 			if (Z_TYPE_P(tmp) == IS_LONG) {
294 				switch (Z_LVAL_P(tmp)) {
295 					case SOAP_SSL_METHOD_TLS:
296 						crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
297 						break;
298 
299 					case SOAP_SSL_METHOD_SSLv2:
300 						crypto_method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
301 						break;
302 
303 					case SOAP_SSL_METHOD_SSLv3:
304 						crypto_method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
305 						break;
306 
307 					case SOAP_SSL_METHOD_SSLv23:
308 						crypto_method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
309 						break;
310 
311 					default:
312 						crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
313 						break;
314 				}
315 			}
316 			if (php_stream_xport_crypto_setup(stream, crypto_method, NULL) < 0 ||
317 			    php_stream_xport_crypto_enable(stream, 1) < 0) {
318 				php_stream_close(stream);
319 				stream = NULL;
320 			}
321 		}
322 	}
323 
324 	EG(error_reporting) = old_error_reporting;
325 	return stream;
326 }
327 
in_domain(const char * host,const char * domain)328 static int in_domain(const char *host, const char *domain)
329 {
330 	if (domain[0] == '.') {
331 		int l1 = strlen(host);
332 		int l2 = strlen(domain);
333 		if (l1 > l2) {
334 			return strcmp(host+l1-l2,domain) == 0;
335 		} else {
336 			return 0;
337 		}
338 	} else {
339 		return strcmp(host,domain) == 0;
340 	}
341 }
342 
make_http_soap_request(zval * this_ptr,zend_string * buf,char * location,char * soapaction,int soap_version,zval * return_value)343 int make_http_soap_request(zval        *this_ptr,
344                            zend_string *buf,
345                            char        *location,
346                            char        *soapaction,
347                            int          soap_version,
348                            zval        *return_value)
349 {
350 	zend_string *request;
351 	smart_str soap_headers = {0};
352 	smart_str soap_headers_z = {0};
353 	size_t err;
354 	php_url *phpurl = NULL;
355 	php_stream *stream;
356 	zval *tmp;
357 	int use_proxy = 0;
358 	int use_ssl;
359 	zend_string *http_body;
360 	char *content_type, *http_version, *cookie_itt;
361 	size_t cookie_len;
362 	int http_close;
363 	zend_string *http_headers;
364 	char *connection;
365 	int http_1_1;
366 	int http_status;
367 	int content_type_xml = 0;
368 	zend_long redirect_max = 20;
369 	char *content_encoding;
370 	char *http_msg = NULL;
371 	bool old_allow_url_fopen;
372 	php_stream_context *context = NULL;
373 	bool has_authorization = 0;
374 	bool has_proxy_authorization = 0;
375 	bool has_cookies = 0;
376 
377 	if (this_ptr == NULL || Z_TYPE_P(this_ptr) != IS_OBJECT) {
378 		return FALSE;
379 	}
380 
381 	request = buf;
382 	/* Compress request */
383 	tmp = Z_CLIENT_COMPRESSION_P(this_ptr);
384 	if (Z_TYPE_P(tmp) == IS_LONG) {
385 		int level = Z_LVAL_P(tmp) & 0x0f;
386 		int kind  = Z_LVAL_P(tmp) & SOAP_COMPRESSION_DEFLATE;
387 
388 		if (level > 9) {level = 9;}
389 
390 	  if ((Z_LVAL_P(tmp) & SOAP_COMPRESSION_ACCEPT) != 0) {
391 			smart_str_append_const(&soap_headers_z,"Accept-Encoding: gzip, deflate\r\n");
392 	  }
393 	  if (level > 0) {
394 			zval func;
395 			zval retval;
396 			zval params[3];
397 			int n;
398 
399 			ZVAL_STR_COPY(&params[0], buf);
400 			ZVAL_LONG(&params[1], level);
401 			if (kind == SOAP_COMPRESSION_DEFLATE) {
402 				n = 2;
403 				ZVAL_STRING(&func, "gzcompress");
404 				smart_str_append_const(&soap_headers_z,"Content-Encoding: deflate\r\n");
405 			} else {
406 				n = 3;
407 				ZVAL_STRING(&func, "gzencode");
408 				smart_str_append_const(&soap_headers_z,"Content-Encoding: gzip\r\n");
409 				ZVAL_LONG(&params[2], 0x1f);
410 			}
411 			if (call_user_function(CG(function_table), (zval*)NULL, &func, &retval, n, params) == SUCCESS &&
412 			    Z_TYPE(retval) == IS_STRING) {
413 				zval_ptr_dtor(&params[0]);
414 				zval_ptr_dtor(&func);
415 				request = Z_STR(retval);
416 			} else {
417 				zval_ptr_dtor(&params[0]);
418 				zval_ptr_dtor(&func);
419 				if (request != buf) {
420 					zend_string_release_ex(request, 0);
421 				}
422 				smart_str_free(&soap_headers_z);
423 				return FALSE;
424 			}
425 	  }
426 	}
427 
428 	tmp = Z_CLIENT_HTTPSOCKET_P(this_ptr);
429 	if (Z_TYPE_P(tmp) == IS_RESOURCE) {
430 		php_stream_from_zval_no_verify(stream,tmp);
431 		tmp = Z_CLIENT_USE_PROXY_P(this_ptr);
432 		if (Z_TYPE_P(tmp) == IS_LONG) {
433 			use_proxy = Z_LVAL_P(tmp);
434 		}
435 	} else {
436 		stream = NULL;
437 	}
438 
439 	if (location != NULL && location[0] != '\000') {
440 		phpurl = php_url_parse(location);
441 	}
442 
443 	tmp = Z_CLIENT_STREAM_CONTEXT_P(this_ptr);
444 	if (Z_TYPE_P(tmp) == IS_RESOURCE) {
445 		context = php_stream_context_from_zval(tmp, 0);
446 	}
447 
448 	if (context &&
449 		(tmp = php_stream_context_get_option(context, "http", "max_redirects")) != NULL) {
450 		if (Z_TYPE_P(tmp) != IS_STRING || !is_numeric_string(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp), &redirect_max, NULL, 1)) {
451 			if (Z_TYPE_P(tmp) == IS_LONG)
452 				redirect_max = Z_LVAL_P(tmp);
453 		}
454 	}
455 
456 try_again:
457 	if (phpurl == NULL || phpurl->host == NULL) {
458 	  if (phpurl != NULL) {php_url_free(phpurl);}
459 		if (request != buf) {
460 			zend_string_release_ex(request, 0);
461 		}
462 		add_soap_fault(this_ptr, "HTTP", "Unable to parse URL", NULL, NULL);
463 		smart_str_free(&soap_headers_z);
464 		efree(http_msg);
465 		return FALSE;
466 	}
467 
468 	use_ssl = 0;
469 	if (phpurl->scheme != NULL && zend_string_equals_literal(phpurl->scheme, "https")) {
470 		use_ssl = 1;
471 	} else if (phpurl->scheme == NULL || !zend_string_equals_literal(phpurl->scheme, "http")) {
472 		php_url_free(phpurl);
473 		if (request != buf) {
474 			zend_string_release_ex(request, 0);
475 		}
476 		add_soap_fault(this_ptr, "HTTP", "Unknown protocol. Only http and https are allowed.", NULL, NULL);
477 		smart_str_free(&soap_headers_z);
478 		efree(http_msg);
479 		return FALSE;
480 	}
481 
482 	old_allow_url_fopen = PG(allow_url_fopen);
483 	PG(allow_url_fopen) = 1;
484 	if (use_ssl && php_stream_locate_url_wrapper("https://", NULL, STREAM_LOCATE_WRAPPERS_ONLY) == NULL) {
485 		php_url_free(phpurl);
486 		if (request != buf) {
487 			zend_string_release_ex(request, 0);
488 		}
489 		add_soap_fault(this_ptr, "HTTP", "SSL support is not available in this build", NULL, NULL);
490 		PG(allow_url_fopen) = old_allow_url_fopen;
491 		smart_str_free(&soap_headers_z);
492 		efree(http_msg);
493 		return FALSE;
494 	}
495 
496 	if (phpurl->port == 0) {
497 		phpurl->port = use_ssl ? 443 : 80;
498 	}
499 
500 	/* Check if request to the same host */
501 	if (stream != NULL) {
502 		php_url *orig;
503 		tmp = Z_CLIENT_HTTPURL_P(this_ptr);
504 		if (Z_TYPE_P(tmp) == IS_RESOURCE &&
505 			(orig = (php_url *) zend_fetch_resource_ex(tmp, "httpurl", le_url)) != NULL &&
506 		    ((use_proxy && !use_ssl) ||
507 		     (((use_ssl && orig->scheme != NULL && zend_string_equals_literal(orig->scheme, "https")) ||
508 		      (!use_ssl && orig->scheme == NULL) ||
509 		      (!use_ssl && !zend_string_equals_literal(orig->scheme, "https"))) &&
510 		     zend_string_equals(orig->host, phpurl->host) &&
511 		     orig->port == phpurl->port))) {
512 		} else {
513 			php_stream_close(stream);
514 			convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr));
515 			convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
516 			convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
517 			stream = NULL;
518 			use_proxy = 0;
519 		}
520 	}
521 
522 	/* Check if keep-alive connection is still opened */
523 	if (stream != NULL && php_stream_eof(stream)) {
524 		php_stream_close(stream);
525 		convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr));
526 		convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
527 		convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
528 		stream = NULL;
529 		use_proxy = 0;
530 	}
531 
532 	if (!stream) {
533 		stream = http_connect(this_ptr, phpurl, use_ssl, context, &use_proxy);
534 		if (stream) {
535 			php_stream_auto_cleanup(stream);
536 			ZVAL_RES(Z_CLIENT_HTTPSOCKET_P(this_ptr), stream->res);
537 			GC_ADDREF(stream->res);
538 			ZVAL_LONG(Z_CLIENT_USE_PROXY_P(this_ptr), use_proxy);
539 		} else {
540 			php_url_free(phpurl);
541 			if (request != buf) {
542 				zend_string_release_ex(request, 0);
543 			}
544 			add_soap_fault(this_ptr, "HTTP", "Could not connect to host", NULL, NULL);
545 			PG(allow_url_fopen) = old_allow_url_fopen;
546 			smart_str_free(&soap_headers_z);
547 			efree(http_msg);
548 			return FALSE;
549 		}
550 	}
551 	PG(allow_url_fopen) = old_allow_url_fopen;
552 
553 	if (stream) {
554 		zval *cookies, *login, *password;
555 		zend_resource *ret = zend_register_resource(phpurl, le_url);
556 		ZVAL_RES(Z_CLIENT_HTTPURL_P(this_ptr), ret);
557 		GC_ADDREF(ret);
558 
559 		if (context &&
560 		    (tmp = php_stream_context_get_option(context, "http", "protocol_version")) != NULL &&
561 		    Z_TYPE_P(tmp) == IS_DOUBLE &&
562 		    Z_DVAL_P(tmp) == 1.0) {
563 			http_1_1 = 0;
564 		} else {
565 			http_1_1 = 1;
566 		}
567 
568 		smart_str_append_const(&soap_headers, "POST ");
569 		if (use_proxy && !use_ssl) {
570 			smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->scheme));
571 			smart_str_append_const(&soap_headers, "://");
572 			smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->host));
573 			smart_str_appendc(&soap_headers, ':');
574 			smart_str_append_unsigned(&soap_headers, phpurl->port);
575 		}
576 		if (phpurl->path) {
577 			smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->path));
578 		} else {
579 			smart_str_appendc(&soap_headers, '/');
580 		}
581 		if (phpurl->query) {
582 			smart_str_appendc(&soap_headers, '?');
583 			smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->query));
584 		}
585 		if (phpurl->fragment) {
586 			smart_str_appendc(&soap_headers, '#');
587 			smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->fragment));
588 		}
589 		if (http_1_1) {
590 			smart_str_append_const(&soap_headers, " HTTP/1.1\r\n");
591 		} else {
592 			smart_str_append_const(&soap_headers, " HTTP/1.0\r\n");
593 		}
594 		smart_str_append_const(&soap_headers, "Host: ");
595 		smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->host));
596 		if (phpurl->port != (use_ssl?443:80)) {
597 			smart_str_appendc(&soap_headers, ':');
598 			smart_str_append_unsigned(&soap_headers, phpurl->port);
599 		}
600 		if (!http_1_1 || Z_TYPE_P(Z_CLIENT_KEEP_ALIVE_P(this_ptr)) == IS_FALSE) {
601 			smart_str_append_const(&soap_headers, "\r\n"
602 				"Connection: close\r\n");
603 		} else {
604 			smart_str_append_const(&soap_headers, "\r\n"
605 				"Connection: Keep-Alive\r\n");
606 		}
607 		tmp = Z_CLIENT_USER_AGENT_P(this_ptr);
608 		if (Z_TYPE_P(tmp) == IS_STRING) {
609 			if (Z_STRLEN_P(tmp) > 0) {
610 				smart_str_append_const(&soap_headers, "User-Agent: ");
611 				smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
612 				smart_str_append_const(&soap_headers, "\r\n");
613 			}
614 		} else if (context &&
615 		           (tmp = php_stream_context_get_option(context, "http", "user_agent")) != NULL &&
616 		           Z_TYPE_P(tmp) == IS_STRING) {
617 			if (Z_STRLEN_P(tmp) > 0) {
618 				smart_str_append_const(&soap_headers, "User-Agent: ");
619 				smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
620 				smart_str_append_const(&soap_headers, "\r\n");
621 			}
622 		} else if (FG(user_agent)) {
623 			smart_str_append_const(&soap_headers, "User-Agent: ");
624 			smart_str_appends(&soap_headers, FG(user_agent));
625 			smart_str_append_const(&soap_headers, "\r\n");
626 		} else {
627 			smart_str_append_const(&soap_headers, "User-Agent: PHP-SOAP/"PHP_VERSION"\r\n");
628 		}
629 
630 		smart_str_append_smart_str(&soap_headers, &soap_headers_z);
631 
632 		if (soap_version == SOAP_1_2) {
633 			if (context &&
634 				(tmp = php_stream_context_get_option(context, "http", "content_type")) != NULL &&
635 				Z_TYPE_P(tmp) == IS_STRING &&
636 				Z_STRLEN_P(tmp) > 0
637 			) {
638 				smart_str_append_const(&soap_headers, "Content-Type: ");
639 				smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
640 			} else {
641 				smart_str_append_const(&soap_headers, "Content-Type: application/soap+xml; charset=utf-8");
642 			}
643 			if (soapaction) {
644 				smart_str_append_const(&soap_headers,"; action=\"");
645 				smart_str_appends(&soap_headers, soapaction);
646 				smart_str_append_const(&soap_headers,"\"");
647 			}
648 			smart_str_append_const(&soap_headers,"\r\n");
649 		} else {
650 			if (context &&
651 				(tmp = php_stream_context_get_option(context, "http", "content_type")) != NULL &&
652 				Z_TYPE_P(tmp) == IS_STRING &&
653 				Z_STRLEN_P(tmp) > 0
654 			) {
655 				smart_str_append_const(&soap_headers, "Content-Type: ");
656 				smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
657 				smart_str_append_const(&soap_headers, "\r\n");
658 			} else {
659 				smart_str_append_const(&soap_headers, "Content-Type: text/xml; charset=utf-8\r\n");
660 			}
661 			if (soapaction) {
662 				smart_str_append_const(&soap_headers, "SOAPAction: \"");
663 				smart_str_appends(&soap_headers, soapaction);
664 				smart_str_append_const(&soap_headers, "\"\r\n");
665 			}
666 		}
667 		smart_str_append_const(&soap_headers,"Content-Length: ");
668 		smart_str_append_long(&soap_headers, request->len);
669 		smart_str_append_const(&soap_headers, "\r\n");
670 
671 		/* HTTP Authentication */
672 		login = Z_CLIENT_LOGIN_P(this_ptr);
673 		if (Z_TYPE_P(login) == IS_STRING) {
674 			zval *digest = Z_CLIENT_DIGEST_P(this_ptr);
675 
676 			has_authorization = 1;
677 			if (Z_TYPE_P(digest) == IS_ARRAY) {
678 				char          HA1[33], HA2[33], response[33], cnonce[33], nc[9];
679 				unsigned char nonce[16];
680 				PHP_MD5_CTX   md5ctx;
681 				unsigned char hash[16];
682 
683 				if (UNEXPECTED(php_random_bytes_throw(&nonce, sizeof(nonce)) != SUCCESS)) {
684 					ZEND_ASSERT(EG(exception));
685 					php_stream_close(stream);
686 					convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr));
687 					convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
688 					convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
689 					smart_str_free(&soap_headers_z);
690 					smart_str_free(&soap_headers);
691 					efree(http_msg);
692 					return FALSE;
693 				}
694 
695 				php_hash_bin2hex(cnonce, nonce, sizeof(nonce));
696 				cnonce[32] = 0;
697 
698 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nc", sizeof("nc")-1)) != NULL &&
699 					Z_TYPE_P(tmp) == IS_LONG) {
700 					Z_LVAL_P(tmp)++;
701 					snprintf(nc, sizeof(nc), "%08" ZEND_LONG_FMT_SPEC, Z_LVAL_P(tmp));
702 				} else {
703 					add_assoc_long(digest, "nc", 1);
704 					strcpy(nc, "00000001");
705 				}
706 
707 				PHP_MD5Init(&md5ctx);
708 				PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(login), Z_STRLEN_P(login));
709 				PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
710 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "realm", sizeof("realm")-1)) != NULL &&
711 					Z_TYPE_P(tmp) == IS_STRING) {
712 					PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
713 				}
714 				PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
715 				password = Z_CLIENT_PASSWORD_P(this_ptr);
716 				if (Z_TYPE_P(password) == IS_STRING) {
717 					PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(password), Z_STRLEN_P(password));
718 				}
719 				PHP_MD5Final(hash, &md5ctx);
720 				make_digest(HA1, hash);
721 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "algorithm", sizeof("algorithm")-1)) != NULL &&
722 					Z_TYPE_P(tmp) == IS_STRING &&
723 					Z_STRLEN_P(tmp) == sizeof("md5-sess")-1 &&
724 					stricmp(Z_STRVAL_P(tmp), "md5-sess") == 0) {
725 					PHP_MD5Init(&md5ctx);
726 					PHP_MD5Update(&md5ctx, (unsigned char*)HA1, 32);
727 					PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
728 					if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nonce", sizeof("nonce")-1)) != NULL &&
729 						Z_TYPE_P(tmp) == IS_STRING) {
730 						PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
731 					}
732 					PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
733 					PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8);
734 					PHP_MD5Final(hash, &md5ctx);
735 					make_digest(HA1, hash);
736 				}
737 
738 				PHP_MD5Init(&md5ctx);
739 				PHP_MD5Update(&md5ctx, (unsigned char*)"POST:", sizeof("POST:")-1);
740 				if (phpurl->path) {
741 					PHP_MD5Update(&md5ctx, (unsigned char*)ZSTR_VAL(phpurl->path), ZSTR_LEN(phpurl->path));
742 				} else {
743 					PHP_MD5Update(&md5ctx, (unsigned char*)"/", 1);
744 				}
745 				if (phpurl->query) {
746 					PHP_MD5Update(&md5ctx, (unsigned char*)"?", 1);
747 					PHP_MD5Update(&md5ctx, (unsigned char*)ZSTR_VAL(phpurl->query), ZSTR_LEN(phpurl->query));
748 				}
749 
750 				PHP_MD5Final(hash, &md5ctx);
751 				make_digest(HA2, hash);
752 
753 				PHP_MD5Init(&md5ctx);
754 				PHP_MD5Update(&md5ctx, (unsigned char*)HA1, 32);
755 				PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
756 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nonce", sizeof("nonce")-1)) != NULL &&
757 					Z_TYPE_P(tmp) == IS_STRING) {
758 					PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
759 				}
760 				PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
761 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "qop", sizeof("qop")-1)) != NULL &&
762 					Z_TYPE_P(tmp) == IS_STRING) {
763 					PHP_MD5Update(&md5ctx, (unsigned char*)nc, 8);
764 					PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
765 					PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8);
766 					PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
767 					/* TODO: Support for qop=auth-int */
768 					PHP_MD5Update(&md5ctx, (unsigned char*)"auth", sizeof("auth")-1);
769 					PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
770 				}
771 				PHP_MD5Update(&md5ctx, (unsigned char*)HA2, 32);
772 				PHP_MD5Final(hash, &md5ctx);
773 				make_digest(response, hash);
774 
775 				smart_str_append_const(&soap_headers, "Authorization: Digest username=\"");
776 				smart_str_appendl(&soap_headers, Z_STRVAL_P(login), Z_STRLEN_P(login));
777 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "realm", sizeof("realm")-1)) != NULL &&
778 					Z_TYPE_P(tmp) == IS_STRING) {
779 					smart_str_append_const(&soap_headers, "\", realm=\"");
780 					smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
781 				}
782 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nonce", sizeof("nonce")-1)) != NULL &&
783 					Z_TYPE_P(tmp) == IS_STRING) {
784 					smart_str_append_const(&soap_headers, "\", nonce=\"");
785 					smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
786 				}
787 				smart_str_append_const(&soap_headers, "\", uri=\"");
788 				if (phpurl->path) {
789 					smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->path));
790 				} else {
791 					smart_str_appendc(&soap_headers, '/');
792 				}
793 				if (phpurl->query) {
794 					smart_str_appendc(&soap_headers, '?');
795 					smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->query));
796 				}
797 				if (phpurl->fragment) {
798 					smart_str_appendc(&soap_headers, '#');
799 					smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->fragment));
800 				}
801 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "qop", sizeof("qop")-1)) != NULL &&
802 					Z_TYPE_P(tmp) == IS_STRING) {
803 					/* TODO: Support for qop=auth-int */
804 					smart_str_append_const(&soap_headers, "\", qop=auth");
805 					smart_str_append_const(&soap_headers, ", nc=");
806 					smart_str_appendl(&soap_headers, nc, 8);
807 					smart_str_append_const(&soap_headers, ", cnonce=\"");
808 					smart_str_appendl(&soap_headers, cnonce, 8);
809 				}
810 				smart_str_append_const(&soap_headers, "\", response=\"");
811 				smart_str_appendl(&soap_headers, response, 32);
812 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "opaque", sizeof("opaque")-1)) != NULL &&
813 					Z_TYPE_P(tmp) == IS_STRING) {
814 					smart_str_append_const(&soap_headers, "\", opaque=\"");
815 					smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
816 				}
817 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "algorithm", sizeof("algorithm")-1)) != NULL &&
818 					Z_TYPE_P(tmp) == IS_STRING) {
819 					smart_str_append_const(&soap_headers, "\", algorithm=\"");
820 					smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
821 				}
822 				smart_str_append_const(&soap_headers, "\"\r\n");
823 			} else {
824 				zend_string *buf;
825 
826 				smart_str auth = {0};
827 				smart_str_append(&auth, Z_STR_P(login));
828 				smart_str_appendc(&auth, ':');
829 				password = Z_CLIENT_PASSWORD_P(this_ptr);
830 				if (Z_TYPE_P(password) == IS_STRING) {
831 					smart_str_append(&auth, Z_STR_P(password));
832 				}
833 				smart_str_0(&auth);
834 				buf = php_base64_encode((unsigned char*)ZSTR_VAL(auth.s), ZSTR_LEN(auth.s));
835 				smart_str_append_const(&soap_headers, "Authorization: Basic ");
836 				smart_str_append(&soap_headers, buf);
837 				smart_str_append_const(&soap_headers, "\r\n");
838 				zend_string_release_ex(buf, 0);
839 				smart_str_free(&auth);
840 			}
841 		}
842 
843 		/* Proxy HTTP Authentication */
844 		if (use_proxy && !use_ssl) {
845 			has_proxy_authorization = proxy_authentication(this_ptr, &soap_headers);
846 		}
847 
848 		/* Send cookies along with request */
849 		cookies = Z_CLIENT_COOKIES_P(this_ptr);
850 		ZEND_ASSERT(Z_TYPE_P(cookies) == IS_ARRAY);
851 		if (zend_hash_num_elements(Z_ARRVAL_P(cookies)) != 0 && !HT_IS_PACKED(Z_ARRVAL_P(cookies))) {
852 			zval *data;
853 			zend_string *key;
854 			has_cookies = 1;
855 			bool first_cookie = true;
856 			smart_str_append_const(&soap_headers, "Cookie: ");
857 			ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(cookies), key, data) {
858 				if (key && Z_TYPE_P(data) == IS_ARRAY) {
859 					zval *value;
860 
861 					if ((value = zend_hash_index_find(Z_ARRVAL_P(data), 0)) != NULL &&
862 						Z_TYPE_P(value) == IS_STRING) {
863 					  zval *tmp;
864 					  if (((tmp = zend_hash_index_find(Z_ARRVAL_P(data), 1)) == NULL ||
865 						   Z_TYPE_P(tmp) != IS_STRING ||
866 						   strncmp(phpurl->path?ZSTR_VAL(phpurl->path):"/",Z_STRVAL_P(tmp),Z_STRLEN_P(tmp)) == 0) &&
867 						  ((tmp = zend_hash_index_find(Z_ARRVAL_P(data), 2)) == NULL ||
868 						   Z_TYPE_P(tmp) != IS_STRING ||
869 						   in_domain(ZSTR_VAL(phpurl->host),Z_STRVAL_P(tmp))) &&
870 						  (use_ssl || (tmp = zend_hash_index_find(Z_ARRVAL_P(data), 3)) == NULL)) {
871 							if (!first_cookie) {
872 								smart_str_appends(&soap_headers, "; ");
873 							}
874 							first_cookie = false;
875 							smart_str_append(&soap_headers, key);
876 							smart_str_appendc(&soap_headers, '=');
877 							smart_str_append(&soap_headers, Z_STR_P(value));
878 						}
879 					}
880 				}
881 			} ZEND_HASH_FOREACH_END();
882 			smart_str_append_const(&soap_headers, "\r\n");
883 		}
884 
885 		http_context_headers(context, has_authorization, has_proxy_authorization, has_cookies, &soap_headers);
886 
887 		smart_str_append_const(&soap_headers, "\r\n");
888 		smart_str_0(&soap_headers);
889 		if (Z_TYPE_P(Z_CLIENT_TRACE_P(this_ptr)) == IS_TRUE) {
890 			zval_ptr_dtor(Z_CLIENT_LAST_REQUEST_HEADERS_P(this_ptr));
891 			/* Need to copy the string here, as we continue appending to soap_headers below. */
892 			ZVAL_STRINGL(Z_CLIENT_LAST_REQUEST_HEADERS_P(this_ptr),
893 				ZSTR_VAL(soap_headers.s), ZSTR_LEN(soap_headers.s));
894 		}
895 		smart_str_appendl(&soap_headers, request->val, request->len);
896 		smart_str_0(&soap_headers);
897 
898 		err = php_stream_write(stream, ZSTR_VAL(soap_headers.s), ZSTR_LEN(soap_headers.s));
899 		if (err != ZSTR_LEN(soap_headers.s)) {
900 			if (request != buf) {
901 				zend_string_release_ex(request, 0);
902 			}
903 			php_stream_close(stream);
904 			convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr));
905 			convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
906 			convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
907 			add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL);
908 			smart_str_free(&soap_headers_z);
909 			efree(http_msg);
910 			return FALSE;
911 		}
912 		smart_str_free(&soap_headers);
913 	} else {
914 		add_soap_fault(this_ptr, "HTTP", "Failed to create stream??", NULL, NULL);
915 		smart_str_free(&soap_headers_z);
916 		efree(http_msg);
917 		return FALSE;
918 	}
919 
920 	if (!return_value) {
921 		php_stream_close(stream);
922 		convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
923 		convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
924 		smart_str_free(&soap_headers_z);
925 		efree(http_msg);
926 		return TRUE;
927 	}
928 
929 	do {
930 		http_headers = get_http_headers(stream);
931 		if (!http_headers) {
932 			if (request != buf) {
933 				zend_string_release_ex(request, 0);
934 			}
935 			php_stream_close(stream);
936 			convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
937 			convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
938 			add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL);
939 			smart_str_free(&soap_headers_z);
940 			efree(http_msg);
941 			return FALSE;
942 		}
943 
944 		if (Z_TYPE_P(Z_CLIENT_TRACE_P(this_ptr)) == IS_TRUE) {
945 			zval_ptr_dtor(Z_CLIENT_LAST_RESPONSE_HEADERS_P(this_ptr));
946 			ZVAL_STR_COPY(Z_CLIENT_LAST_RESPONSE_HEADERS_P(this_ptr), http_headers);
947 		}
948 
949 		/* Check to see what HTTP status was sent */
950 		http_1_1 = 0;
951 		http_status = 0;
952 		http_version = get_http_header_value(ZSTR_VAL(http_headers), "HTTP/");
953 		if (http_version) {
954 			char *tmp;
955 
956 			if (!strncmp(http_version,"1.1", 3)) {
957 				http_1_1 = 1;
958 			}
959 
960 			tmp = strstr(http_version," ");
961 			if (tmp != NULL) {
962 				tmp++;
963 				http_status = atoi(tmp);
964 			}
965 			tmp = strstr(tmp," ");
966 			if (tmp != NULL) {
967 				tmp++;
968 				if (http_msg) {
969 					efree(http_msg);
970 				}
971 				http_msg = estrdup(tmp);
972 			}
973 			efree(http_version);
974 
975 			/* Try and get headers again */
976 			if (http_status == 100) {
977 				zend_string_release_ex(http_headers, 0);
978 			}
979 		}
980 	} while (http_status == 100);
981 
982 	/* Grab and send back every cookie */
983 
984 	/* Not going to worry about Path: because
985 	   we shouldn't be changing urls so path doesn't
986 	   matter too much
987 	*/
988 	cookie_itt = ZSTR_VAL(http_headers);
989 
990 	while ((cookie_itt = get_http_header_value_nodup(cookie_itt, "Set-Cookie: ", &cookie_len))) {
991 		zval *cookies = Z_CLIENT_COOKIES_P(this_ptr);
992 		SEPARATE_ARRAY(cookies);
993 
994 		char *cookie = estrndup(cookie_itt, cookie_len);
995 		char *eqpos = strstr(cookie, "=");
996 		char *sempos = strstr(cookie, ";");
997 		if (eqpos != NULL && (sempos == NULL || sempos > eqpos)) {
998 			smart_str name = {0};
999 			int cookie_len;
1000 			zval zcookie;
1001 
1002 			if (sempos != NULL) {
1003 				cookie_len = sempos-(eqpos+1);
1004 			} else {
1005 				cookie_len = strlen(cookie)-(eqpos-cookie)-1;
1006 			}
1007 
1008 			smart_str_appendl(&name, cookie, eqpos - cookie);
1009 			smart_str_0(&name);
1010 
1011 			array_init(&zcookie);
1012 			add_index_stringl(&zcookie, 0, eqpos + 1, cookie_len);
1013 
1014 			if (sempos != NULL) {
1015 				char *options = cookie + cookie_len+1;
1016 				while (*options) {
1017 					while (*options == ' ') {options++;}
1018 					sempos = strstr(options, ";");
1019 					if (strstr(options,"path=") == options) {
1020 						eqpos = options + sizeof("path=")-1;
1021 						add_index_stringl(&zcookie, 1, eqpos, sempos?(size_t)(sempos-eqpos):strlen(eqpos));
1022 					} else if (strstr(options,"domain=") == options) {
1023 						eqpos = options + sizeof("domain=")-1;
1024 						add_index_stringl(&zcookie, 2, eqpos, sempos?(size_t)(sempos-eqpos):strlen(eqpos));
1025 					} else if (strstr(options,"secure") == options) {
1026 						add_index_bool(&zcookie, 3, 1);
1027 					}
1028 					if (sempos != NULL) {
1029 						options = sempos+1;
1030 					} else {
1031 					  break;
1032 					}
1033 				}
1034 			}
1035 			if (!zend_hash_index_exists(Z_ARRVAL(zcookie), 1)) {
1036 				char *t = phpurl->path?ZSTR_VAL(phpurl->path):"/";
1037 				char *c = strrchr(t, '/');
1038 				if (c) {
1039 					add_index_stringl(&zcookie, 1, t, c-t);
1040 				}
1041 			}
1042 			if (!zend_hash_index_exists(Z_ARRVAL(zcookie), 2)) {
1043 				add_index_str(&zcookie, 2, phpurl->host);
1044 				GC_ADDREF(phpurl->host);
1045 			}
1046 
1047 			zend_symtable_update(Z_ARRVAL_P(cookies), name.s, &zcookie);
1048 			smart_str_free(&name);
1049 		}
1050 
1051 		cookie_itt = cookie_itt + cookie_len;
1052 		efree(cookie);
1053 	}
1054 
1055 	/* See if the server requested a close */
1056 	if (http_1_1) {
1057 		http_close = FALSE;
1058 		if (use_proxy && !use_ssl) {
1059 			connection = get_http_header_value(ZSTR_VAL(http_headers), "Proxy-Connection: ");
1060 			if (connection) {
1061 				if (strncasecmp(connection, "close", sizeof("close")-1) == 0) {
1062 					http_close = TRUE;
1063 				}
1064 				efree(connection);
1065 			}
1066 		}
1067 		if (http_close == FALSE) {
1068 			connection = get_http_header_value(ZSTR_VAL(http_headers), "Connection: ");
1069 			if (connection) {
1070 				if (strncasecmp(connection, "close", sizeof("close")-1) == 0) {
1071 					http_close = TRUE;
1072 				}
1073 				efree(connection);
1074 			}
1075 		}
1076 	} else {
1077 		http_close = TRUE;
1078 		if (use_proxy && !use_ssl) {
1079 			connection = get_http_header_value(ZSTR_VAL(http_headers), "Proxy-Connection: ");
1080 			if (connection) {
1081 				if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
1082 					http_close = FALSE;
1083 				}
1084 				efree(connection);
1085 			}
1086 		}
1087 		if (http_close == TRUE) {
1088 			connection = get_http_header_value(ZSTR_VAL(http_headers), "Connection: ");
1089 			if (connection) {
1090 				if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
1091 					http_close = FALSE;
1092 				}
1093 				efree(connection);
1094 			}
1095 		}
1096 	}
1097 
1098 
1099 	http_body = get_http_body(stream, http_close, ZSTR_VAL(http_headers));
1100 	if (!http_body) {
1101 		if (request != buf) {
1102 			zend_string_release_ex(request, 0);
1103 		}
1104 		php_stream_close(stream);
1105 		zend_string_release_ex(http_headers, 0);
1106 		convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
1107 		convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
1108 		add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL);
1109 		if (http_msg) {
1110 			efree(http_msg);
1111 		}
1112 		smart_str_free(&soap_headers_z);
1113 		return FALSE;
1114 	}
1115 
1116 	if (request != buf) {
1117 		zend_string_release_ex(request, 0);
1118 	}
1119 
1120 	if (http_close) {
1121 		php_stream_close(stream);
1122 		convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
1123 		convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
1124 		stream = NULL;
1125 	}
1126 
1127 	/* Process HTTP status codes */
1128 	if (http_status >= 300 && http_status < 400) {
1129 		char *loc;
1130 
1131 		if ((loc = get_http_header_value(ZSTR_VAL(http_headers), "Location: ")) != NULL) {
1132 			php_url *new_url  = php_url_parse(loc);
1133 
1134 			if (new_url != NULL) {
1135 				zend_string_release_ex(http_headers, 0);
1136 				zend_string_release_ex(http_body, 0);
1137 				efree(loc);
1138 				if (new_url->scheme == NULL && new_url->path != NULL) {
1139 					new_url->scheme = phpurl->scheme ? zend_string_copy(phpurl->scheme) : NULL;
1140 					new_url->host = phpurl->host ? zend_string_copy(phpurl->host) : NULL;
1141 					new_url->port = phpurl->port;
1142 					if (new_url->path && ZSTR_VAL(new_url->path)[0] != '/') {
1143 						if (phpurl->path) {
1144 							char *t = ZSTR_VAL(phpurl->path);
1145 							char *p = strrchr(t, '/');
1146 							if (p) {
1147 								zend_string *s = zend_string_alloc((p - t) + ZSTR_LEN(new_url->path) + 2, 0);
1148 								strncpy(ZSTR_VAL(s), t, (p - t) + 1);
1149 								ZSTR_VAL(s)[(p - t) + 1] = 0;
1150 								strcat(ZSTR_VAL(s), ZSTR_VAL(new_url->path));
1151 								zend_string_release_ex(new_url->path, 0);
1152 								new_url->path = s;
1153 							}
1154 						} else {
1155 							zend_string *s = zend_string_alloc(ZSTR_LEN(new_url->path) + 2, 0);
1156 							ZSTR_VAL(s)[0] = '/';
1157 							ZSTR_VAL(s)[1] = 0;
1158 							strcat(ZSTR_VAL(s), ZSTR_VAL(new_url->path));
1159 							zend_string_release_ex(new_url->path, 0);
1160 							new_url->path = s;
1161 						}
1162 					}
1163 				}
1164 				phpurl = new_url;
1165 
1166 				if (--redirect_max < 1) {
1167 					add_soap_fault(this_ptr, "HTTP", "Redirection limit reached, aborting", NULL, NULL);
1168 					smart_str_free(&soap_headers_z);
1169 					efree(http_msg);
1170 					return FALSE;
1171 				}
1172 
1173 				goto try_again;
1174 			}
1175 		}
1176 	} else if (http_status == 401) {
1177 		/* Digest authentication */
1178 		zval *digest = Z_CLIENT_DIGEST_P(this_ptr);
1179 		zval *login = Z_CLIENT_LOGIN_P(this_ptr);
1180 		zval *password = Z_CLIENT_PASSWORD_P(this_ptr);
1181 		char *auth = get_http_header_value(ZSTR_VAL(http_headers), "WWW-Authenticate: ");
1182 		if (auth && strstr(auth, "Digest") == auth && Z_TYPE_P(digest) != IS_ARRAY
1183 				&& Z_TYPE_P(login) == IS_STRING && Z_TYPE_P(password) == IS_STRING) {
1184 			char *s;
1185 			zval digest;
1186 
1187 			ZVAL_UNDEF(&digest);
1188 			s = auth + sizeof("Digest")-1;
1189 			while (*s != '\0') {
1190 				char *name, *val;
1191 				while (*s == ' ') ++s;
1192 				name = s;
1193 				while (*s != '\0' && *s != '=') ++s;
1194 				if (*s == '=') {
1195 					*s = '\0';
1196 					++s;
1197 					if (*s == '"') {
1198 						++s;
1199 						val = s;
1200 						while (*s != '\0' && *s != '"') ++s;
1201 					} else {
1202 						val = s;
1203 						while (*s != '\0' && *s != ' ' && *s != ',') ++s;
1204 					}
1205 					if (*s != '\0') {
1206 						if (*s != ',') {
1207 							*s = '\0';
1208 							++s;
1209 							while (*s != '\0' && *s != ',') ++s;
1210 							if (*s != '\0') ++s;
1211 						} else {
1212 							*s = '\0';
1213 							++s;
1214 						}
1215 					}
1216 					if (Z_TYPE(digest) == IS_UNDEF) {
1217 						array_init(&digest);
1218 					}
1219 					add_assoc_string(&digest, name, val);
1220 				}
1221 			}
1222 
1223 			if (Z_TYPE(digest) != IS_UNDEF) {
1224 				php_url *new_url = emalloc(sizeof(php_url));
1225 
1226 				zval_ptr_dtor(Z_CLIENT_DIGEST_P(this_ptr));
1227 				ZVAL_COPY_VALUE(Z_CLIENT_DIGEST_P(this_ptr), &digest);
1228 
1229 				*new_url = *phpurl;
1230 				if (phpurl->scheme) phpurl->scheme = zend_string_copy(phpurl->scheme);
1231 				if (phpurl->user) phpurl->user = zend_string_copy(phpurl->user);
1232 				if (phpurl->pass) phpurl->pass = zend_string_copy(phpurl->pass);
1233 				if (phpurl->host) phpurl->host = zend_string_copy(phpurl->host);
1234 				if (phpurl->path) phpurl->path = zend_string_copy(phpurl->path);
1235 				if (phpurl->query) phpurl->query = zend_string_copy(phpurl->query);
1236 				if (phpurl->fragment) phpurl->fragment = zend_string_copy(phpurl->fragment);
1237 				phpurl = new_url;
1238 
1239 				efree(auth);
1240 				zend_string_release_ex(http_headers, 0);
1241 				zend_string_release_ex(http_body, 0);
1242 
1243 				goto try_again;
1244 			}
1245 		}
1246 		if (auth) efree(auth);
1247 	}
1248 	smart_str_free(&soap_headers_z);
1249 
1250 	/* Check and see if the server even sent a xml document */
1251 	content_type = get_http_header_value(ZSTR_VAL(http_headers), "Content-Type: ");
1252 	if (content_type) {
1253 		char *pos = NULL;
1254 		int cmplen;
1255 		pos = strstr(content_type,";");
1256 		if (pos != NULL) {
1257 			cmplen = pos - content_type;
1258 		} else {
1259 			cmplen = strlen(content_type);
1260 		}
1261 		if (strncmp(content_type, "text/xml", cmplen) == 0 ||
1262 		    strncmp(content_type, "application/soap+xml", cmplen) == 0) {
1263 			content_type_xml = 1;
1264 /*
1265 			if (strncmp(http_body, "<?xml", 5)) {
1266 				zval *err;
1267 				MAKE_STD_ZVAL(err);
1268 				ZVAL_STRINGL(err, http_body, http_body_size, 1);
1269 				add_soap_fault(this_ptr, "HTTP", "Didn't receive an xml document", NULL, err);
1270 				efree(content_type);
1271 				zend_string_release_ex(http_headers, 0);
1272 				efree(http_body);
1273 				return FALSE;
1274 			}
1275 */
1276 		}
1277 		efree(content_type);
1278 	}
1279 
1280 	/* Decompress response */
1281 	content_encoding = get_http_header_value(ZSTR_VAL(http_headers), "Content-Encoding: ");
1282 	if (content_encoding) {
1283 		zval func;
1284 		zval retval;
1285 		zval params[1];
1286 
1287 		/* Warning: the zlib function names are chosen in an unfortunate manner.
1288 		 * Check zlib.c to see how a function corresponds with a particular format. */
1289 		if ((strcmp(content_encoding,"gzip") == 0 ||
1290 		     strcmp(content_encoding,"x-gzip") == 0) &&
1291 		     zend_hash_str_exists(EG(function_table), "gzdecode", sizeof("gzdecode")-1)) {
1292 			ZVAL_STRING(&func, "gzdecode");
1293 			ZVAL_STR_COPY(&params[0], http_body);
1294 		} else if (strcmp(content_encoding,"deflate") == 0 &&
1295 		           zend_hash_str_exists(EG(function_table), "gzuncompress", sizeof("gzuncompress")-1)) {
1296 			ZVAL_STRING(&func, "gzuncompress");
1297 			ZVAL_STR_COPY(&params[0], http_body);
1298 		} else {
1299 			efree(content_encoding);
1300 			zend_string_release_ex(http_headers, 0);
1301 			zend_string_release_ex(http_body, 0);
1302 			if (http_msg) {
1303 				efree(http_msg);
1304 			}
1305 			add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL);
1306 			return FALSE;
1307 		}
1308 		if (call_user_function(CG(function_table), (zval*)NULL, &func, &retval, 1, params) == SUCCESS &&
1309 		    Z_TYPE(retval) == IS_STRING) {
1310 			zval_ptr_dtor(&params[0]);
1311 			zval_ptr_dtor(&func);
1312 			zend_string_release_ex(http_body, 0);
1313 			ZVAL_COPY_VALUE(return_value, &retval);
1314 		} else {
1315 			zval_ptr_dtor(&params[0]);
1316 			zval_ptr_dtor(&func);
1317 			efree(content_encoding);
1318 			zend_string_release_ex(http_headers, 0);
1319 			zend_string_release_ex(http_body, 0);
1320 			add_soap_fault(this_ptr, "HTTP", "Can't uncompress compressed response", NULL, NULL);
1321 			if (http_msg) {
1322 				efree(http_msg);
1323 			}
1324 			return FALSE;
1325 		}
1326 		efree(content_encoding);
1327 	} else {
1328 		ZVAL_STR(return_value, http_body);
1329 	}
1330 
1331 	zend_string_release_ex(http_headers, 0);
1332 
1333 	if (http_status >= 400) {
1334 		int error = 0;
1335 
1336 		if (Z_STRLEN_P(return_value) == 0) {
1337 			error = 1;
1338 		} else if (Z_STRLEN_P(return_value) > 0) {
1339 			if (!content_type_xml) {
1340 				char *s = Z_STRVAL_P(return_value);
1341 
1342 				while (*s != '\0' && *s < ' ') {
1343 					s++;
1344 				}
1345 				if (strncmp(s, "<?xml", 5)) {
1346 					error = 1;
1347 				}
1348 			}
1349 		}
1350 
1351 		if (error) {
1352 			zval_ptr_dtor(return_value);
1353 			ZVAL_UNDEF(return_value);
1354 			add_soap_fault(this_ptr, "HTTP", http_msg, NULL, NULL);
1355 			efree(http_msg);
1356 			return FALSE;
1357 		}
1358 	}
1359 
1360 	if (http_msg) {
1361 		efree(http_msg);
1362 	}
1363 
1364 	return TRUE;
1365 }
1366 
get_http_header_value_nodup(char * headers,char * type,size_t * len)1367 static char *get_http_header_value_nodup(char *headers, char *type, size_t *len)
1368 {
1369 	char *pos, *tmp = NULL;
1370 	int typelen, headerslen;
1371 
1372 	typelen = strlen(type);
1373 	headerslen = strlen(headers);
1374 
1375 	/* header `titles' can be lower case, or any case combination, according
1376 	 * to the various RFC's. */
1377 	pos = headers;
1378 	do {
1379 		/* start of buffer or start of line */
1380 		if (strncasecmp(pos, type, typelen) == 0) {
1381 			char *eol;
1382 
1383 			/* match */
1384 			tmp = pos + typelen;
1385 
1386 			/* strip leading whitespace */
1387 			while (*tmp == ' ' || *tmp == '\t') {
1388 				tmp++;
1389 			}
1390 
1391 			eol = strchr(tmp, '\n');
1392 			if (eol == NULL) {
1393 				eol = headers + headerslen;
1394 			} else if (eol > tmp) {
1395 				if (*(eol-1) == '\r') {
1396 					eol--;
1397 				}
1398 
1399 				/* strip trailing whitespace */
1400 				while (eol > tmp && (*(eol-1) == ' ' || *(eol-1) == '\t')) {
1401 					eol--;
1402 				}
1403 			}
1404 
1405 			*len = eol - tmp;
1406 			return tmp;
1407 		}
1408 
1409 		/* find next line */
1410 		pos = strchr(pos, '\n');
1411 		if (pos) {
1412 			pos++;
1413 		}
1414 
1415 	} while (pos);
1416 
1417 	return NULL;
1418 }
1419 
get_http_header_value(char * headers,char * type)1420 static char *get_http_header_value(char *headers, char *type)
1421 {
1422 	size_t len;
1423 	char *value;
1424 
1425 	value = get_http_header_value_nodup(headers, type, &len);
1426 
1427 	if (value) {
1428 		return estrndup(value, len);
1429 	}
1430 
1431 	return NULL;
1432 }
1433 
get_http_body(php_stream * stream,int close,char * headers)1434 static zend_string* get_http_body(php_stream *stream, int close, char *headers)
1435 {
1436 	zend_string *http_buf = NULL;
1437 	char *header;
1438 	int header_close = close, header_chunked = 0, header_length = 0, http_buf_size = 0;
1439 
1440 	if (!close) {
1441 		header = get_http_header_value(headers, "Connection: ");
1442 		if (header) {
1443 			if(!strncasecmp(header, "close", sizeof("close")-1)) header_close = 1;
1444 			efree(header);
1445 		}
1446 	}
1447 	header = get_http_header_value(headers, "Transfer-Encoding: ");
1448 	if (header) {
1449 		if(!strncasecmp(header, "chunked", sizeof("chunked")-1)) header_chunked = 1;
1450 		efree(header);
1451 	}
1452 	header = get_http_header_value(headers, "Content-Length: ");
1453 	if (header) {
1454 		header_length = atoi(header);
1455 		efree(header);
1456 		if (!header_length && !header_chunked) {
1457 			/* Empty response */
1458 			return ZSTR_EMPTY_ALLOC();
1459 		}
1460 	}
1461 
1462 	if (header_chunked) {
1463 		char ch, done, headerbuf[8192];
1464 
1465 		done = FALSE;
1466 
1467 		while (!done) {
1468 			int buf_size = 0;
1469 
1470 			php_stream_gets(stream, headerbuf, sizeof(headerbuf));
1471 			if (sscanf(headerbuf, "%x", &buf_size) > 0 ) {
1472 				if (buf_size > 0) {
1473 					size_t len_size = 0;
1474 
1475 					if (http_buf_size + buf_size + 1 < 0) {
1476 						if (http_buf) {
1477 							zend_string_release_ex(http_buf, 0);
1478 						}
1479 						return NULL;
1480 					}
1481 
1482 					if (http_buf) {
1483 						http_buf = zend_string_realloc(http_buf, http_buf_size + buf_size, 0);
1484 					} else {
1485 						http_buf = zend_string_alloc(buf_size, 0);
1486 					}
1487 
1488 					while (len_size < buf_size) {
1489 						ssize_t len_read = php_stream_read(stream, http_buf->val + http_buf_size, buf_size - len_size);
1490 						if (len_read <= 0) {
1491 							/* Error or EOF */
1492 							done = TRUE;
1493 						  break;
1494 						}
1495 						len_size += len_read;
1496 	 					http_buf_size += len_read;
1497 					}
1498 
1499 					/* Eat up '\r' '\n' */
1500 					ch = php_stream_getc(stream);
1501 					if (ch == '\r') {
1502 						ch = php_stream_getc(stream);
1503 					}
1504 					if (ch != '\n') {
1505 						/* Something wrong in chunked encoding */
1506 						if (http_buf) {
1507 							zend_string_release_ex(http_buf, 0);
1508 						}
1509 						return NULL;
1510 					}
1511 				}
1512 			} else {
1513 				/* Something wrong in chunked encoding */
1514 				if (http_buf) {
1515 					zend_string_release_ex(http_buf, 0);
1516 				}
1517 				return NULL;
1518 			}
1519 			if (buf_size == 0) {
1520 				done = TRUE;
1521 			}
1522 		}
1523 
1524 		/* Ignore trailer headers */
1525 		while (1) {
1526 			if (!php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
1527 				break;
1528 			}
1529 
1530 			if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
1531 			    (headerbuf[0] == '\n')) {
1532 				/* empty line marks end of headers */
1533 				break;
1534 			}
1535 		}
1536 
1537 		if (http_buf == NULL) {
1538 			return ZSTR_EMPTY_ALLOC();
1539 		}
1540 
1541 	} else if (header_length) {
1542 		if (header_length < 0 || header_length >= INT_MAX) {
1543 			return NULL;
1544 		}
1545 		http_buf = zend_string_alloc(header_length, 0);
1546 		while (http_buf_size < header_length) {
1547 			ssize_t len_read = php_stream_read(stream, http_buf->val + http_buf_size, header_length - http_buf_size);
1548 			if (len_read <= 0) {
1549 				break;
1550 			}
1551 			http_buf_size += len_read;
1552 		}
1553 	} else if (header_close) {
1554 		do {
1555 			ssize_t len_read;
1556 			if (http_buf) {
1557 				http_buf = zend_string_realloc(http_buf, http_buf_size + 4096, 0);
1558 			} else {
1559 				http_buf = zend_string_alloc(4096, 0);
1560 			}
1561 			len_read = php_stream_read(stream, http_buf->val + http_buf_size, 4096);
1562 			if (len_read > 0) {
1563 				http_buf_size += len_read;
1564 			}
1565 		} while(!php_stream_eof(stream));
1566 	} else {
1567 		return NULL;
1568 	}
1569 
1570 	http_buf->val[http_buf_size] = '\0';
1571 	http_buf->len = http_buf_size;
1572 	return http_buf;
1573 }
1574 
get_http_headers(php_stream * stream)1575 static zend_string *get_http_headers(php_stream *stream)
1576 {
1577 	smart_str tmp_response = {0};
1578 	char headerbuf[8192];
1579 
1580 	while (php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
1581 		if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
1582 		    (headerbuf[0] == '\n')) {
1583 			/* empty line marks end of headers */
1584 			smart_str_0(&tmp_response);
1585 			return tmp_response.s;
1586 		}
1587 
1588 		/* add header to collection */
1589 		smart_str_appends(&tmp_response, headerbuf);
1590 	}
1591 
1592 	smart_str_free(&tmp_response);
1593 	return NULL;
1594 }
1595