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