xref: /PHP-8.4/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/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 		efree(http_msg);
460 		return FALSE;
461 	}
462 
463 	use_ssl = 0;
464 	if (phpurl->scheme != NULL && zend_string_equals_literal(phpurl->scheme, "https")) {
465 		use_ssl = 1;
466 	} else if (phpurl->scheme == NULL || !zend_string_equals_literal(phpurl->scheme, "http")) {
467 		php_url_free(phpurl);
468 		if (request != buf) {
469 			zend_string_release_ex(request, 0);
470 		}
471 		add_soap_fault(this_ptr, "HTTP", "Unknown protocol. Only http and https are allowed.", NULL, NULL);
472 		smart_str_free(&soap_headers_z);
473 		efree(http_msg);
474 		return FALSE;
475 	}
476 
477 	old_allow_url_fopen = PG(allow_url_fopen);
478 	PG(allow_url_fopen) = 1;
479 	if (use_ssl && php_stream_locate_url_wrapper("https://", NULL, STREAM_LOCATE_WRAPPERS_ONLY) == NULL) {
480 		php_url_free(phpurl);
481 		if (request != buf) {
482 			zend_string_release_ex(request, 0);
483 		}
484 		add_soap_fault(this_ptr, "HTTP", "SSL support is not available in this build", NULL, NULL);
485 		PG(allow_url_fopen) = old_allow_url_fopen;
486 		smart_str_free(&soap_headers_z);
487 		efree(http_msg);
488 		return FALSE;
489 	}
490 
491 	if (phpurl->port == 0) {
492 		phpurl->port = use_ssl ? 443 : 80;
493 	}
494 
495 	/* Check if request to the same host */
496 	if (stream != NULL) {
497 		php_url *orig;
498 		tmp = Z_CLIENT_HTTPURL_P(this_ptr);
499 		if (Z_TYPE_P(tmp) == IS_OBJECT && instanceof_function(Z_OBJCE_P(tmp), soap_url_class_entry) &&
500 			(orig = Z_SOAP_URL_P(tmp)->url) != NULL &&
501 		    ((use_proxy && !use_ssl) ||
502 		     (((use_ssl && orig->scheme != NULL && zend_string_equals_literal(orig->scheme, "https")) ||
503 		      (!use_ssl && orig->scheme == NULL) ||
504 		      (!use_ssl && !zend_string_equals_literal(orig->scheme, "https"))) &&
505 		     zend_string_equals(orig->host, phpurl->host) &&
506 		     orig->port == phpurl->port))) {
507 		} else {
508 			php_stream_close(stream);
509 			convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr));
510 			convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
511 			convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
512 			stream = NULL;
513 			use_proxy = 0;
514 		}
515 	}
516 
517 	/* Check if keep-alive connection is still opened */
518 	if (stream != NULL && php_stream_eof(stream)) {
519 		php_stream_close(stream);
520 		convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr));
521 		convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
522 		convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
523 		stream = NULL;
524 		use_proxy = 0;
525 	}
526 
527 	if (!stream) {
528 		stream = http_connect(this_ptr, phpurl, use_ssl, context, &use_proxy);
529 		if (stream) {
530 			php_stream_auto_cleanup(stream);
531 			ZVAL_RES(Z_CLIENT_HTTPSOCKET_P(this_ptr), stream->res);
532 			GC_ADDREF(stream->res);
533 			ZVAL_LONG(Z_CLIENT_USE_PROXY_P(this_ptr), use_proxy);
534 		} else {
535 			php_url_free(phpurl);
536 			if (request != buf) {
537 				zend_string_release_ex(request, 0);
538 			}
539 			add_soap_fault(this_ptr, "HTTP", "Could not connect to host", NULL, NULL);
540 			PG(allow_url_fopen) = old_allow_url_fopen;
541 			smart_str_free(&soap_headers_z);
542 			efree(http_msg);
543 			return FALSE;
544 		}
545 	}
546 	PG(allow_url_fopen) = old_allow_url_fopen;
547 
548 	bool client_trace = Z_TYPE_P(Z_CLIENT_TRACE_P(this_ptr)) == IS_TRUE;
549 
550 	if (stream) {
551 		zval *cookies, *login, *password;
552 
553 		zval *url_zval = Z_CLIENT_HTTPURL_P(this_ptr);
554 		if (Z_TYPE_P(url_zval) == IS_OBJECT) {
555 			zval_ptr_dtor(url_zval);
556 		}
557 
558 		object_init_ex(url_zval, soap_url_class_entry);
559 		soap_url_object *url_obj = Z_SOAP_URL_P(url_zval);
560 		url_obj->url = phpurl;
561 
562 		if (context &&
563 		    (tmp = php_stream_context_get_option(context, "http", "protocol_version")) != NULL &&
564 		    Z_TYPE_P(tmp) == IS_DOUBLE &&
565 		    Z_DVAL_P(tmp) == 1.0) {
566 			http_1_1 = 0;
567 		} else {
568 			http_1_1 = 1;
569 		}
570 
571 		smart_str_append_const(&soap_headers, "POST ");
572 		if (use_proxy && !use_ssl) {
573 			smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->scheme));
574 			smart_str_append_const(&soap_headers, "://");
575 			smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->host));
576 			smart_str_appendc(&soap_headers, ':');
577 			smart_str_append_unsigned(&soap_headers, phpurl->port);
578 		}
579 		if (phpurl->path) {
580 			smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->path));
581 		} else {
582 			smart_str_appendc(&soap_headers, '/');
583 		}
584 		if (phpurl->query) {
585 			smart_str_appendc(&soap_headers, '?');
586 			smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->query));
587 		}
588 		if (phpurl->fragment) {
589 			smart_str_appendc(&soap_headers, '#');
590 			smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->fragment));
591 		}
592 		if (http_1_1) {
593 			smart_str_append_const(&soap_headers, " HTTP/1.1\r\n");
594 		} else {
595 			smart_str_append_const(&soap_headers, " HTTP/1.0\r\n");
596 		}
597 		smart_str_append_const(&soap_headers, "Host: ");
598 		smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->host));
599 		if (phpurl->port != (use_ssl?443:80)) {
600 			smart_str_appendc(&soap_headers, ':');
601 			smart_str_append_unsigned(&soap_headers, phpurl->port);
602 		}
603 		if (!http_1_1 || Z_TYPE_P(Z_CLIENT_KEEP_ALIVE_P(this_ptr)) == IS_FALSE) {
604 			smart_str_append_const(&soap_headers, "\r\n"
605 				"Connection: close\r\n");
606 		} else {
607 			smart_str_append_const(&soap_headers, "\r\n"
608 				"Connection: Keep-Alive\r\n");
609 		}
610 		tmp = Z_CLIENT_USER_AGENT_P(this_ptr);
611 		if (Z_TYPE_P(tmp) == IS_STRING) {
612 			if (Z_STRLEN_P(tmp) > 0) {
613 				smart_str_append_const(&soap_headers, "User-Agent: ");
614 				smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
615 				smart_str_append_const(&soap_headers, "\r\n");
616 			}
617 		} else if (context &&
618 		           (tmp = php_stream_context_get_option(context, "http", "user_agent")) != NULL &&
619 		           Z_TYPE_P(tmp) == IS_STRING) {
620 			if (Z_STRLEN_P(tmp) > 0) {
621 				smart_str_append_const(&soap_headers, "User-Agent: ");
622 				smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
623 				smart_str_append_const(&soap_headers, "\r\n");
624 			}
625 		} else if (FG(user_agent)) {
626 			smart_str_append_const(&soap_headers, "User-Agent: ");
627 			smart_str_appends(&soap_headers, FG(user_agent));
628 			smart_str_append_const(&soap_headers, "\r\n");
629 		} else {
630 			smart_str_append_const(&soap_headers, "User-Agent: PHP-SOAP/"PHP_VERSION"\r\n");
631 		}
632 
633 		smart_str_append_smart_str(&soap_headers, &soap_headers_z);
634 
635 		if (soap_version == SOAP_1_2) {
636 			if (context &&
637 				(tmp = php_stream_context_get_option(context, "http", "content_type")) != NULL &&
638 				Z_TYPE_P(tmp) == IS_STRING &&
639 				Z_STRLEN_P(tmp) > 0
640 			) {
641 				smart_str_append_const(&soap_headers, "Content-Type: ");
642 				smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
643 			} else {
644 				smart_str_append_const(&soap_headers, "Content-Type: application/soap+xml; charset=utf-8");
645 			}
646 			if (soapaction) {
647 				smart_str_append_const(&soap_headers,"; action=\"");
648 				smart_str_appends(&soap_headers, soapaction);
649 				smart_str_append_const(&soap_headers,"\"");
650 			}
651 			smart_str_append_const(&soap_headers,"\r\n");
652 		} else {
653 			if (context &&
654 				(tmp = php_stream_context_get_option(context, "http", "content_type")) != NULL &&
655 				Z_TYPE_P(tmp) == IS_STRING &&
656 				Z_STRLEN_P(tmp) > 0
657 			) {
658 				smart_str_append_const(&soap_headers, "Content-Type: ");
659 				smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
660 				smart_str_append_const(&soap_headers, "\r\n");
661 			} else {
662 				smart_str_append_const(&soap_headers, "Content-Type: text/xml; charset=utf-8\r\n");
663 			}
664 			if (soapaction) {
665 				smart_str_append_const(&soap_headers, "SOAPAction: \"");
666 				smart_str_appends(&soap_headers, soapaction);
667 				smart_str_append_const(&soap_headers, "\"\r\n");
668 			}
669 		}
670 		smart_str_append_const(&soap_headers,"Content-Length: ");
671 		smart_str_append_long(&soap_headers, request->len);
672 		smart_str_append_const(&soap_headers, "\r\n");
673 
674 		/* HTTP Authentication */
675 		login = Z_CLIENT_LOGIN_P(this_ptr);
676 		if (Z_TYPE_P(login) == IS_STRING) {
677 			zval *digest = Z_CLIENT_DIGEST_P(this_ptr);
678 
679 			has_authorization = 1;
680 			if (Z_TYPE_P(digest) == IS_ARRAY) {
681 				char          HA1[33], HA2[33], response[33], cnonce[33], nc[9];
682 				unsigned char nonce[16];
683 				PHP_MD5_CTX   md5ctx;
684 				unsigned char hash[16];
685 
686 				if (UNEXPECTED(php_random_bytes_throw(&nonce, sizeof(nonce)) != SUCCESS)) {
687 					ZEND_ASSERT(EG(exception));
688 					php_stream_close(stream);
689 					convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr));
690 					convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
691 					convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
692 					smart_str_free(&soap_headers_z);
693 					smart_str_free(&soap_headers);
694 					efree(http_msg);
695 					return FALSE;
696 				}
697 
698 				php_hash_bin2hex(cnonce, nonce, sizeof(nonce));
699 				cnonce[32] = 0;
700 
701 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nc", sizeof("nc")-1)) != NULL &&
702 					Z_TYPE_P(tmp) == IS_LONG) {
703 					Z_LVAL_P(tmp)++;
704 					snprintf(nc, sizeof(nc), "%08" ZEND_LONG_FMT_SPEC, Z_LVAL_P(tmp));
705 				} else {
706 					add_assoc_long(digest, "nc", 1);
707 					strcpy(nc, "00000001");
708 				}
709 
710 				PHP_MD5Init(&md5ctx);
711 				PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(login), Z_STRLEN_P(login));
712 				PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
713 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "realm", sizeof("realm")-1)) != NULL &&
714 					Z_TYPE_P(tmp) == IS_STRING) {
715 					PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
716 				}
717 				PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
718 				password = Z_CLIENT_PASSWORD_P(this_ptr);
719 				if (Z_TYPE_P(password) == IS_STRING) {
720 					PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(password), Z_STRLEN_P(password));
721 				}
722 				PHP_MD5Final(hash, &md5ctx);
723 				make_digest(HA1, hash);
724 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "algorithm", sizeof("algorithm")-1)) != NULL &&
725 					Z_TYPE_P(tmp) == IS_STRING &&
726 					Z_STRLEN_P(tmp) == sizeof("md5-sess")-1 &&
727 					stricmp(Z_STRVAL_P(tmp), "md5-sess") == 0) {
728 					PHP_MD5Init(&md5ctx);
729 					PHP_MD5Update(&md5ctx, (unsigned char*)HA1, 32);
730 					PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
731 					if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nonce", sizeof("nonce")-1)) != NULL &&
732 						Z_TYPE_P(tmp) == IS_STRING) {
733 						PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
734 					}
735 					PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
736 					PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8);
737 					PHP_MD5Final(hash, &md5ctx);
738 					make_digest(HA1, hash);
739 				}
740 
741 				PHP_MD5Init(&md5ctx);
742 				PHP_MD5Update(&md5ctx, (unsigned char*)"POST:", sizeof("POST:")-1);
743 				if (phpurl->path) {
744 					PHP_MD5Update(&md5ctx, (unsigned char*)ZSTR_VAL(phpurl->path), ZSTR_LEN(phpurl->path));
745 				} else {
746 					PHP_MD5Update(&md5ctx, (unsigned char*)"/", 1);
747 				}
748 				if (phpurl->query) {
749 					PHP_MD5Update(&md5ctx, (unsigned char*)"?", 1);
750 					PHP_MD5Update(&md5ctx, (unsigned char*)ZSTR_VAL(phpurl->query), ZSTR_LEN(phpurl->query));
751 				}
752 
753 				PHP_MD5Final(hash, &md5ctx);
754 				make_digest(HA2, hash);
755 
756 				PHP_MD5Init(&md5ctx);
757 				PHP_MD5Update(&md5ctx, (unsigned char*)HA1, 32);
758 				PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
759 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nonce", sizeof("nonce")-1)) != NULL &&
760 					Z_TYPE_P(tmp) == IS_STRING) {
761 					PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
762 				}
763 				PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
764 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "qop", sizeof("qop")-1)) != NULL &&
765 					Z_TYPE_P(tmp) == IS_STRING) {
766 					PHP_MD5Update(&md5ctx, (unsigned char*)nc, 8);
767 					PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
768 					PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8);
769 					PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
770 					/* TODO: Support for qop=auth-int */
771 					PHP_MD5Update(&md5ctx, (unsigned char*)"auth", sizeof("auth")-1);
772 					PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
773 				}
774 				PHP_MD5Update(&md5ctx, (unsigned char*)HA2, 32);
775 				PHP_MD5Final(hash, &md5ctx);
776 				make_digest(response, hash);
777 
778 				smart_str_append_const(&soap_headers, "Authorization: Digest username=\"");
779 				smart_str_appendl(&soap_headers, Z_STRVAL_P(login), Z_STRLEN_P(login));
780 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "realm", sizeof("realm")-1)) != NULL &&
781 					Z_TYPE_P(tmp) == IS_STRING) {
782 					smart_str_append_const(&soap_headers, "\", realm=\"");
783 					smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
784 				}
785 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nonce", sizeof("nonce")-1)) != NULL &&
786 					Z_TYPE_P(tmp) == IS_STRING) {
787 					smart_str_append_const(&soap_headers, "\", nonce=\"");
788 					smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
789 				}
790 				smart_str_append_const(&soap_headers, "\", uri=\"");
791 				if (phpurl->path) {
792 					smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->path));
793 				} else {
794 					smart_str_appendc(&soap_headers, '/');
795 				}
796 				if (phpurl->query) {
797 					smart_str_appendc(&soap_headers, '?');
798 					smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->query));
799 				}
800 				if (phpurl->fragment) {
801 					smart_str_appendc(&soap_headers, '#');
802 					smart_str_appends(&soap_headers, ZSTR_VAL(phpurl->fragment));
803 				}
804 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "qop", sizeof("qop")-1)) != NULL &&
805 					Z_TYPE_P(tmp) == IS_STRING) {
806 					/* TODO: Support for qop=auth-int */
807 					smart_str_append_const(&soap_headers, "\", qop=auth");
808 					smart_str_append_const(&soap_headers, ", nc=");
809 					smart_str_appendl(&soap_headers, nc, 8);
810 					smart_str_append_const(&soap_headers, ", cnonce=\"");
811 					smart_str_appendl(&soap_headers, cnonce, 8);
812 				}
813 				smart_str_append_const(&soap_headers, "\", response=\"");
814 				smart_str_appendl(&soap_headers, response, 32);
815 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "opaque", sizeof("opaque")-1)) != NULL &&
816 					Z_TYPE_P(tmp) == IS_STRING) {
817 					smart_str_append_const(&soap_headers, "\", opaque=\"");
818 					smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
819 				}
820 				if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "algorithm", sizeof("algorithm")-1)) != NULL &&
821 					Z_TYPE_P(tmp) == IS_STRING) {
822 					smart_str_append_const(&soap_headers, "\", algorithm=\"");
823 					smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
824 				}
825 				smart_str_append_const(&soap_headers, "\"\r\n");
826 			} else {
827 				zend_string *buf;
828 
829 				smart_str auth = {0};
830 				smart_str_append(&auth, Z_STR_P(login));
831 				smart_str_appendc(&auth, ':');
832 				password = Z_CLIENT_PASSWORD_P(this_ptr);
833 				if (Z_TYPE_P(password) == IS_STRING) {
834 					smart_str_append(&auth, Z_STR_P(password));
835 				}
836 				smart_str_0(&auth);
837 				buf = php_base64_encode((unsigned char*)ZSTR_VAL(auth.s), ZSTR_LEN(auth.s));
838 				smart_str_append_const(&soap_headers, "Authorization: Basic ");
839 				smart_str_append(&soap_headers, buf);
840 				smart_str_append_const(&soap_headers, "\r\n");
841 				zend_string_release_ex(buf, 0);
842 				smart_str_free(&auth);
843 			}
844 		}
845 
846 		/* Proxy HTTP Authentication */
847 		if (use_proxy && !use_ssl) {
848 			has_proxy_authorization = proxy_authentication(this_ptr, &soap_headers);
849 		}
850 
851 		/* Send cookies along with request */
852 		cookies = Z_CLIENT_COOKIES_P(this_ptr);
853 		ZEND_ASSERT(Z_TYPE_P(cookies) == IS_ARRAY);
854 		if (zend_hash_num_elements(Z_ARRVAL_P(cookies)) != 0 && !HT_IS_PACKED(Z_ARRVAL_P(cookies))) {
855 			zval *data;
856 			zend_string *key;
857 			has_cookies = 1;
858 			bool first_cookie = true;
859 			smart_str_append_const(&soap_headers, "Cookie: ");
860 			ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(cookies), key, data) {
861 				if (key && Z_TYPE_P(data) == IS_ARRAY) {
862 					zval *value;
863 
864 					if ((value = zend_hash_index_find(Z_ARRVAL_P(data), 0)) != NULL &&
865 						Z_TYPE_P(value) == IS_STRING) {
866 					  zval *tmp;
867 					  if (((tmp = zend_hash_index_find(Z_ARRVAL_P(data), 1)) == NULL ||
868 						   Z_TYPE_P(tmp) != IS_STRING ||
869 						   strncmp(phpurl->path?ZSTR_VAL(phpurl->path):"/",Z_STRVAL_P(tmp),Z_STRLEN_P(tmp)) == 0) &&
870 						  ((tmp = zend_hash_index_find(Z_ARRVAL_P(data), 2)) == NULL ||
871 						   Z_TYPE_P(tmp) != IS_STRING ||
872 						   in_domain(phpurl->host, Z_STR_P(tmp))) &&
873 						  (use_ssl || (tmp = zend_hash_index_find(Z_ARRVAL_P(data), 3)) == NULL)) {
874 							if (!first_cookie) {
875 								smart_str_appends(&soap_headers, "; ");
876 							}
877 							first_cookie = false;
878 							smart_str_append(&soap_headers, key);
879 							smart_str_appendc(&soap_headers, '=');
880 							smart_str_append(&soap_headers, Z_STR_P(value));
881 						}
882 					}
883 				}
884 			} ZEND_HASH_FOREACH_END();
885 			smart_str_append_const(&soap_headers, "\r\n");
886 		}
887 
888 		http_context_headers(context, has_authorization, has_proxy_authorization, has_cookies, &soap_headers);
889 
890 		smart_str_append_const(&soap_headers, "\r\n");
891 		smart_str_0(&soap_headers);
892 		if (client_trace) {
893 			zval_ptr_dtor(Z_CLIENT_LAST_REQUEST_HEADERS_P(this_ptr));
894 			/* Need to copy the string here, as we continue appending to soap_headers below. */
895 			ZVAL_STRINGL(Z_CLIENT_LAST_REQUEST_HEADERS_P(this_ptr),
896 				ZSTR_VAL(soap_headers.s), ZSTR_LEN(soap_headers.s));
897 		}
898 		smart_str_appendl(&soap_headers, request->val, request->len);
899 		smart_str_0(&soap_headers);
900 
901 		err = php_stream_write(stream, ZSTR_VAL(soap_headers.s), ZSTR_LEN(soap_headers.s));
902 		if (err != ZSTR_LEN(soap_headers.s)) {
903 			if (request != buf) {
904 				zend_string_release_ex(request, 0);
905 			}
906 			php_stream_close(stream);
907 			convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr));
908 			convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
909 			convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
910 			add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL);
911 			smart_str_free(&soap_headers_z);
912 			efree(http_msg);
913 			return FALSE;
914 		}
915 		smart_str_free(&soap_headers);
916 	} else {
917 		add_soap_fault(this_ptr, "HTTP", "Failed to create stream??", NULL, NULL);
918 		smart_str_free(&soap_headers_z);
919 		efree(http_msg);
920 		return FALSE;
921 	}
922 
923 	http_headers = NULL;
924 	if (return_value || client_trace) {
925 		do {
926 			http_headers = get_http_headers(stream);
927 			if (!http_headers) {
928 				if (request != buf) {
929 					zend_string_release_ex(request, 0);
930 				}
931 				php_stream_close(stream);
932 				convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
933 				convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
934 				add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL);
935 				smart_str_free(&soap_headers_z);
936 				efree(http_msg);
937 				return FALSE;
938 			}
939 
940 			if (client_trace) {
941 				zval_ptr_dtor(Z_CLIENT_LAST_RESPONSE_HEADERS_P(this_ptr));
942 				ZVAL_STR_COPY(Z_CLIENT_LAST_RESPONSE_HEADERS_P(this_ptr), http_headers);
943 			}
944 
945 			/* Check to see what HTTP status was sent */
946 			http_1_1 = 0;
947 			http_status = 0;
948 			http_version = get_http_header_value(ZSTR_VAL(http_headers), "HTTP/");
949 			if (http_version) {
950 				char *tmp;
951 
952 				if (!strncmp(http_version,"1.1", 3)) {
953 					http_1_1 = 1;
954 				}
955 
956 				tmp = strstr(http_version," ");
957 				if (tmp != NULL) {
958 					tmp++;
959 					http_status = atoi(tmp);
960 				}
961 				tmp = strstr(tmp," ");
962 				if (tmp != NULL) {
963 					tmp++;
964 					if (http_msg) {
965 						efree(http_msg);
966 					}
967 					http_msg = estrdup(tmp);
968 				}
969 				efree(http_version);
970 
971 				/* Try and get headers again */
972 				if (http_status == 100) {
973 					zend_string_release_ex(http_headers, 0);
974 				}
975 			}
976 		} while (http_status == 100);
977 	}
978 
979 	if (!return_value) {
980 		/* In this case, the headers were only fetched because client_trace was true. */
981 		if (request != buf) {
982 			zend_string_release_ex(request, 0);
983 		}
984 		php_stream_close(stream);
985 		if (http_headers) {
986 			zend_string_release_ex(http_headers, 0);
987 		}
988 		convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
989 		convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
990 		if (http_msg) {
991 			efree(http_msg);
992 		}
993 		smart_str_free(&soap_headers_z);
994 		return true;
995 	}
996 
997 	/* Grab and send back every cookie */
998 
999 	/* Not going to worry about Path: because
1000 	   we shouldn't be changing urls so path doesn't
1001 	   matter too much
1002 	*/
1003 	cookie_itt = ZSTR_VAL(http_headers);
1004 
1005 	while ((cookie_itt = get_http_header_value_nodup(cookie_itt, "Set-Cookie:", &cookie_len))) {
1006 		zval *cookies = Z_CLIENT_COOKIES_P(this_ptr);
1007 		SEPARATE_ARRAY(cookies);
1008 
1009 		char *cookie = estrndup(cookie_itt, cookie_len);
1010 		char *eqpos = strstr(cookie, "=");
1011 		char *sempos = strstr(cookie, ";");
1012 		if (eqpos != NULL && (sempos == NULL || sempos > eqpos)) {
1013 			smart_str name = {0};
1014 			int cookie_len;
1015 			zval zcookie;
1016 
1017 			if (sempos != NULL) {
1018 				cookie_len = sempos-(eqpos+1);
1019 			} else {
1020 				cookie_len = strlen(cookie)-(eqpos-cookie)-1;
1021 			}
1022 
1023 			smart_str_appendl(&name, cookie, eqpos - cookie);
1024 			smart_str_0(&name);
1025 
1026 			array_init(&zcookie);
1027 			add_index_stringl(&zcookie, 0, eqpos + 1, cookie_len);
1028 
1029 			if (sempos != NULL) {
1030 				char *options = cookie + cookie_len+1;
1031 				while (*options) {
1032 					while (*options == ' ') {options++;}
1033 					sempos = strstr(options, ";");
1034 					if (strstr(options,"path=") == options) {
1035 						eqpos = options + sizeof("path=")-1;
1036 						add_index_stringl(&zcookie, 1, eqpos, sempos?(size_t)(sempos-eqpos):strlen(eqpos));
1037 					} else if (strstr(options,"domain=") == options) {
1038 						eqpos = options + sizeof("domain=")-1;
1039 						add_index_stringl(&zcookie, 2, eqpos, sempos?(size_t)(sempos-eqpos):strlen(eqpos));
1040 					} else if (strstr(options,"secure") == options) {
1041 						add_index_bool(&zcookie, 3, 1);
1042 					}
1043 					if (sempos != NULL) {
1044 						options = sempos+1;
1045 					} else {
1046 					  break;
1047 					}
1048 				}
1049 			}
1050 			if (!zend_hash_index_exists(Z_ARRVAL(zcookie), 1)) {
1051 				char *t = phpurl->path?ZSTR_VAL(phpurl->path):"/";
1052 				char *c = strrchr(t, '/');
1053 				if (c) {
1054 					add_index_stringl(&zcookie, 1, t, c-t);
1055 				}
1056 			}
1057 			if (!zend_hash_index_exists(Z_ARRVAL(zcookie), 2)) {
1058 				add_index_str(&zcookie, 2, phpurl->host);
1059 				GC_ADDREF(phpurl->host);
1060 			}
1061 
1062 			zend_symtable_update(Z_ARRVAL_P(cookies), name.s, &zcookie);
1063 			smart_str_free(&name);
1064 		}
1065 
1066 		cookie_itt = cookie_itt + cookie_len;
1067 		efree(cookie);
1068 	}
1069 
1070 	/* See if the server requested a close */
1071 	if (http_1_1) {
1072 		http_close = FALSE;
1073 		if (use_proxy && !use_ssl) {
1074 			connection = get_http_header_value(ZSTR_VAL(http_headers), "Proxy-Connection:");
1075 			if (connection) {
1076 				if (strncasecmp(connection, "close", sizeof("close")-1) == 0) {
1077 					http_close = TRUE;
1078 				}
1079 				efree(connection);
1080 			}
1081 		}
1082 		if (http_close == FALSE) {
1083 			connection = get_http_header_value(ZSTR_VAL(http_headers), "Connection:");
1084 			if (connection) {
1085 				if (strncasecmp(connection, "close", sizeof("close")-1) == 0) {
1086 					http_close = TRUE;
1087 				}
1088 				efree(connection);
1089 			}
1090 		}
1091 	} else {
1092 		http_close = TRUE;
1093 		if (use_proxy && !use_ssl) {
1094 			connection = get_http_header_value(ZSTR_VAL(http_headers), "Proxy-Connection:");
1095 			if (connection) {
1096 				if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
1097 					http_close = FALSE;
1098 				}
1099 				efree(connection);
1100 			}
1101 		}
1102 		if (http_close == TRUE) {
1103 			connection = get_http_header_value(ZSTR_VAL(http_headers), "Connection:");
1104 			if (connection) {
1105 				if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
1106 					http_close = FALSE;
1107 				}
1108 				efree(connection);
1109 			}
1110 		}
1111 	}
1112 
1113 
1114 	http_body = get_http_body(stream, http_close, ZSTR_VAL(http_headers));
1115 	if (!http_body) {
1116 		if (request != buf) {
1117 			zend_string_release_ex(request, 0);
1118 		}
1119 		php_stream_close(stream);
1120 		zend_string_release_ex(http_headers, 0);
1121 		convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
1122 		convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
1123 		add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL);
1124 		if (http_msg) {
1125 			efree(http_msg);
1126 		}
1127 		smart_str_free(&soap_headers_z);
1128 		return FALSE;
1129 	}
1130 
1131 	if (request != buf) {
1132 		zend_string_release_ex(request, 0);
1133 	}
1134 
1135 	if (http_close) {
1136 		php_stream_close(stream);
1137 		convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr));
1138 		convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr));
1139 		stream = NULL;
1140 	}
1141 
1142 	/* Process HTTP status codes */
1143 	if (http_status >= 300 && http_status < 400) {
1144 		char *loc;
1145 
1146 		if ((loc = get_http_header_value(ZSTR_VAL(http_headers), "Location:")) != NULL) {
1147 			php_url *new_url  = php_url_parse(loc);
1148 
1149 			if (new_url != NULL) {
1150 				zend_string_release_ex(http_headers, 0);
1151 				zend_string_release_ex(http_body, 0);
1152 				efree(loc);
1153 				if (new_url->scheme == NULL && new_url->path != NULL) {
1154 					new_url->scheme = phpurl->scheme ? zend_string_copy(phpurl->scheme) : NULL;
1155 					new_url->host = phpurl->host ? zend_string_copy(phpurl->host) : NULL;
1156 					new_url->port = phpurl->port;
1157 					if (new_url->path && ZSTR_VAL(new_url->path)[0] != '/') {
1158 						if (phpurl->path) {
1159 							char *t = ZSTR_VAL(phpurl->path);
1160 							char *p = strrchr(t, '/');
1161 							if (p) {
1162 								zend_string *s = zend_string_alloc((p - t) + ZSTR_LEN(new_url->path) + 2, 0);
1163 								strncpy(ZSTR_VAL(s), t, (p - t) + 1);
1164 								ZSTR_VAL(s)[(p - t) + 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 						} else {
1170 							zend_string *s = zend_string_alloc(ZSTR_LEN(new_url->path) + 2, 0);
1171 							ZSTR_VAL(s)[0] = '/';
1172 							ZSTR_VAL(s)[1] = 0;
1173 							strcat(ZSTR_VAL(s), ZSTR_VAL(new_url->path));
1174 							zend_string_release_ex(new_url->path, 0);
1175 							new_url->path = s;
1176 						}
1177 					}
1178 				}
1179 				phpurl = new_url;
1180 
1181 				if (--redirect_max < 1) {
1182 					add_soap_fault(this_ptr, "HTTP", "Redirection limit reached, aborting", NULL, NULL);
1183 					smart_str_free(&soap_headers_z);
1184 					efree(http_msg);
1185 					return FALSE;
1186 				}
1187 
1188 				goto try_again;
1189 			}
1190 		}
1191 	} else if (http_status == 401) {
1192 		/* Digest authentication */
1193 		zval *digest = Z_CLIENT_DIGEST_P(this_ptr);
1194 		zval *login = Z_CLIENT_LOGIN_P(this_ptr);
1195 		zval *password = Z_CLIENT_PASSWORD_P(this_ptr);
1196 		char *auth = get_http_header_value(ZSTR_VAL(http_headers), "WWW-Authenticate:");
1197 		if (auth && strstr(auth, "Digest") == auth && Z_TYPE_P(digest) != IS_ARRAY
1198 				&& Z_TYPE_P(login) == IS_STRING && Z_TYPE_P(password) == IS_STRING) {
1199 			char *s;
1200 			zval digest;
1201 
1202 			ZVAL_UNDEF(&digest);
1203 			s = auth + sizeof("Digest")-1;
1204 			while (*s != '\0') {
1205 				char *name, *val;
1206 				while (*s == ' ') ++s;
1207 				name = s;
1208 				while (*s != '\0' && *s != '=') ++s;
1209 				if (*s == '=') {
1210 					*s = '\0';
1211 					++s;
1212 					if (*s == '"') {
1213 						++s;
1214 						val = s;
1215 						while (*s != '\0' && *s != '"') ++s;
1216 					} else {
1217 						val = s;
1218 						while (*s != '\0' && *s != ' ' && *s != ',') ++s;
1219 					}
1220 					if (*s != '\0') {
1221 						if (*s != ',') {
1222 							*s = '\0';
1223 							++s;
1224 							while (*s != '\0' && *s != ',') ++s;
1225 							if (*s != '\0') ++s;
1226 						} else {
1227 							*s = '\0';
1228 							++s;
1229 						}
1230 					}
1231 					if (Z_TYPE(digest) == IS_UNDEF) {
1232 						array_init(&digest);
1233 					}
1234 					add_assoc_string(&digest, name, val);
1235 				}
1236 			}
1237 
1238 			if (Z_TYPE(digest) != IS_UNDEF) {
1239 				php_url *new_url = emalloc(sizeof(php_url));
1240 
1241 				zval_ptr_dtor(Z_CLIENT_DIGEST_P(this_ptr));
1242 				ZVAL_COPY_VALUE(Z_CLIENT_DIGEST_P(this_ptr), &digest);
1243 
1244 				*new_url = *phpurl;
1245 				if (phpurl->scheme) phpurl->scheme = zend_string_copy(phpurl->scheme);
1246 				if (phpurl->user) phpurl->user = zend_string_copy(phpurl->user);
1247 				if (phpurl->pass) phpurl->pass = zend_string_copy(phpurl->pass);
1248 				if (phpurl->host) phpurl->host = zend_string_copy(phpurl->host);
1249 				if (phpurl->path) phpurl->path = zend_string_copy(phpurl->path);
1250 				if (phpurl->query) phpurl->query = zend_string_copy(phpurl->query);
1251 				if (phpurl->fragment) phpurl->fragment = zend_string_copy(phpurl->fragment);
1252 				phpurl = new_url;
1253 
1254 				efree(auth);
1255 				zend_string_release_ex(http_headers, 0);
1256 				zend_string_release_ex(http_body, 0);
1257 
1258 				goto try_again;
1259 			}
1260 		}
1261 		if (auth) efree(auth);
1262 	}
1263 	smart_str_free(&soap_headers_z);
1264 
1265 	/* Check and see if the server even sent a xml document */
1266 	content_type = get_http_header_value(ZSTR_VAL(http_headers), "Content-Type:");
1267 	if (content_type) {
1268 		char *pos = NULL;
1269 		int cmplen;
1270 		pos = strstr(content_type,";");
1271 		if (pos != NULL) {
1272 			cmplen = pos - content_type;
1273 		} else {
1274 			cmplen = strlen(content_type);
1275 		}
1276 		if (strncmp(content_type, "text/xml", cmplen) == 0 ||
1277 		    strncmp(content_type, "application/soap+xml", cmplen) == 0) {
1278 			content_type_xml = 1;
1279 /*
1280 			if (strncmp(http_body, "<?xml", 5)) {
1281 				zval *err;
1282 				MAKE_STD_ZVAL(err);
1283 				ZVAL_STRINGL(err, http_body, http_body_size, 1);
1284 				add_soap_fault(this_ptr, "HTTP", "Didn't receive an xml document", NULL, err);
1285 				efree(content_type);
1286 				zend_string_release_ex(http_headers, 0);
1287 				efree(http_body);
1288 				return FALSE;
1289 			}
1290 */
1291 		}
1292 		efree(content_type);
1293 	}
1294 
1295 	/* Decompress response */
1296 	content_encoding = get_http_header_value(ZSTR_VAL(http_headers), "Content-Encoding:");
1297 	if (content_encoding) {
1298 		zval func;
1299 		zval retval;
1300 		zval params[1];
1301 
1302 		/* Warning: the zlib function names are chosen in an unfortunate manner.
1303 		 * Check zlib.c to see how a function corresponds with a particular format. */
1304 		if ((strcmp(content_encoding,"gzip") == 0 ||
1305 		     strcmp(content_encoding,"x-gzip") == 0) &&
1306 		     zend_hash_str_exists(EG(function_table), "gzdecode", sizeof("gzdecode")-1)) {
1307 			ZVAL_STRING(&func, "gzdecode");
1308 			ZVAL_STR_COPY(&params[0], http_body);
1309 		} else if (strcmp(content_encoding,"deflate") == 0 &&
1310 		           zend_hash_str_exists(EG(function_table), "gzuncompress", sizeof("gzuncompress")-1)) {
1311 			ZVAL_STRING(&func, "gzuncompress");
1312 			ZVAL_STR_COPY(&params[0], http_body);
1313 		} else {
1314 			efree(content_encoding);
1315 			zend_string_release_ex(http_headers, 0);
1316 			zend_string_release_ex(http_body, 0);
1317 			if (http_msg) {
1318 				efree(http_msg);
1319 			}
1320 			add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL);
1321 			return FALSE;
1322 		}
1323 		if (call_user_function(CG(function_table), (zval*)NULL, &func, &retval, 1, params) == SUCCESS &&
1324 		    Z_TYPE(retval) == IS_STRING) {
1325 			zval_ptr_dtor(&params[0]);
1326 			zval_ptr_dtor(&func);
1327 			zend_string_release_ex(http_body, 0);
1328 			ZVAL_COPY_VALUE(return_value, &retval);
1329 		} else {
1330 			zval_ptr_dtor(&params[0]);
1331 			zval_ptr_dtor(&func);
1332 			efree(content_encoding);
1333 			zend_string_release_ex(http_headers, 0);
1334 			zend_string_release_ex(http_body, 0);
1335 			add_soap_fault(this_ptr, "HTTP", "Can't uncompress compressed response", NULL, NULL);
1336 			if (http_msg) {
1337 				efree(http_msg);
1338 			}
1339 			return FALSE;
1340 		}
1341 		efree(content_encoding);
1342 	} else {
1343 		ZVAL_STR(return_value, http_body);
1344 	}
1345 
1346 	zend_string_release_ex(http_headers, 0);
1347 
1348 	if (http_status >= 400) {
1349 		int error = 0;
1350 
1351 		if (Z_STRLEN_P(return_value) == 0) {
1352 			error = 1;
1353 		} else if (Z_STRLEN_P(return_value) > 0) {
1354 			if (!content_type_xml) {
1355 				char *s = Z_STRVAL_P(return_value);
1356 
1357 				while (*s != '\0' && *s < ' ') {
1358 					s++;
1359 				}
1360 				if (strncmp(s, "<?xml", 5)) {
1361 					error = 1;
1362 				}
1363 			}
1364 		}
1365 
1366 		if (error) {
1367 			zval_ptr_dtor(return_value);
1368 			ZVAL_UNDEF(return_value);
1369 			add_soap_fault(this_ptr, "HTTP", http_msg, NULL, NULL);
1370 			efree(http_msg);
1371 			return FALSE;
1372 		}
1373 	}
1374 
1375 	if (http_msg) {
1376 		efree(http_msg);
1377 	}
1378 
1379 	return TRUE;
1380 }
1381 
get_http_header_value_nodup(char * headers,char * type,size_t * len)1382 static char *get_http_header_value_nodup(char *headers, char *type, size_t *len)
1383 {
1384 	char *pos, *tmp = NULL;
1385 	int typelen, headerslen;
1386 
1387 	typelen = strlen(type);
1388 	headerslen = strlen(headers);
1389 
1390 	/* header `titles' can be lower case, or any case combination, according
1391 	 * to the various RFC's. */
1392 	pos = headers;
1393 	do {
1394 		/* start of buffer or start of line */
1395 		if (strncasecmp(pos, type, typelen) == 0) {
1396 			char *eol;
1397 
1398 			/* match */
1399 			tmp = pos + typelen;
1400 
1401 			/* strip leading whitespace */
1402 			while (*tmp == ' ' || *tmp == '\t') {
1403 				tmp++;
1404 			}
1405 
1406 			eol = strchr(tmp, '\n');
1407 			if (eol == NULL) {
1408 				eol = headers + headerslen;
1409 			} else if (eol > tmp) {
1410 				if (*(eol-1) == '\r') {
1411 					eol--;
1412 				}
1413 
1414 				/* strip trailing whitespace */
1415 				while (eol > tmp && (*(eol-1) == ' ' || *(eol-1) == '\t')) {
1416 					eol--;
1417 				}
1418 			}
1419 
1420 			*len = eol - tmp;
1421 			return tmp;
1422 		}
1423 
1424 		/* find next line */
1425 		pos = strchr(pos, '\n');
1426 		if (pos) {
1427 			pos++;
1428 		}
1429 
1430 	} while (pos);
1431 
1432 	return NULL;
1433 }
1434 
get_http_header_value(char * headers,char * type)1435 static char *get_http_header_value(char *headers, char *type)
1436 {
1437 	size_t len;
1438 	char *value;
1439 
1440 	value = get_http_header_value_nodup(headers, type, &len);
1441 
1442 	if (value) {
1443 		return estrndup(value, len);
1444 	}
1445 
1446 	return NULL;
1447 }
1448 
get_http_body(php_stream * stream,int close,char * headers)1449 static zend_string* get_http_body(php_stream *stream, int close, char *headers)
1450 {
1451 	zend_string *http_buf = NULL;
1452 	char *header;
1453 	int header_close = close, header_chunked = 0, header_length = 0, http_buf_size = 0;
1454 
1455 	if (!close) {
1456 		header = get_http_header_value(headers, "Connection:");
1457 		if (header) {
1458 			if(!strncasecmp(header, "close", sizeof("close")-1)) header_close = 1;
1459 			efree(header);
1460 		}
1461 	}
1462 	header = get_http_header_value(headers, "Transfer-Encoding:");
1463 	if (header) {
1464 		if(!strncasecmp(header, "chunked", sizeof("chunked")-1)) header_chunked = 1;
1465 		efree(header);
1466 	}
1467 	header = get_http_header_value(headers, "Content-Length:");
1468 	if (header) {
1469 		header_length = atoi(header);
1470 		efree(header);
1471 		if (!header_length && !header_chunked) {
1472 			/* Empty response */
1473 			return ZSTR_EMPTY_ALLOC();
1474 		}
1475 	}
1476 
1477 	if (header_chunked) {
1478 		char ch, done, headerbuf[8192];
1479 
1480 		done = FALSE;
1481 
1482 		while (!done) {
1483 			int buf_size = 0;
1484 
1485 			php_stream_gets(stream, headerbuf, sizeof(headerbuf));
1486 			if (sscanf(headerbuf, "%x", &buf_size) > 0 ) {
1487 				if (buf_size > 0) {
1488 					size_t len_size = 0;
1489 
1490 					if (http_buf_size + buf_size + 1 < 0) {
1491 						if (http_buf) {
1492 							zend_string_release_ex(http_buf, 0);
1493 						}
1494 						return NULL;
1495 					}
1496 
1497 					if (http_buf) {
1498 						http_buf = zend_string_realloc(http_buf, http_buf_size + buf_size, 0);
1499 					} else {
1500 						http_buf = zend_string_alloc(buf_size, 0);
1501 					}
1502 
1503 					while (len_size < buf_size) {
1504 						ssize_t len_read = php_stream_read(stream, http_buf->val + http_buf_size, buf_size - len_size);
1505 						if (len_read <= 0) {
1506 							/* Error or EOF */
1507 							done = TRUE;
1508 						  break;
1509 						}
1510 						len_size += len_read;
1511 	 					http_buf_size += len_read;
1512 					}
1513 
1514 					/* Eat up '\r' '\n' */
1515 					ch = php_stream_getc(stream);
1516 					if (ch == '\r') {
1517 						ch = php_stream_getc(stream);
1518 					}
1519 					if (ch != '\n') {
1520 						/* Something wrong in chunked encoding */
1521 						if (http_buf) {
1522 							zend_string_release_ex(http_buf, 0);
1523 						}
1524 						return NULL;
1525 					}
1526 				}
1527 			} else {
1528 				/* Something wrong in chunked encoding */
1529 				if (http_buf) {
1530 					zend_string_release_ex(http_buf, 0);
1531 				}
1532 				return NULL;
1533 			}
1534 			if (buf_size == 0) {
1535 				done = TRUE;
1536 			}
1537 		}
1538 
1539 		/* Ignore trailer headers */
1540 		while (1) {
1541 			if (!php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
1542 				break;
1543 			}
1544 
1545 			if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
1546 			    (headerbuf[0] == '\n')) {
1547 				/* empty line marks end of headers */
1548 				break;
1549 			}
1550 		}
1551 
1552 		if (http_buf == NULL) {
1553 			return ZSTR_EMPTY_ALLOC();
1554 		}
1555 
1556 	} else if (header_length) {
1557 		if (header_length < 0 || header_length >= INT_MAX) {
1558 			return NULL;
1559 		}
1560 		http_buf = zend_string_alloc(header_length, 0);
1561 		while (http_buf_size < header_length) {
1562 			ssize_t len_read = php_stream_read(stream, http_buf->val + http_buf_size, header_length - http_buf_size);
1563 			if (len_read <= 0) {
1564 				break;
1565 			}
1566 			http_buf_size += len_read;
1567 		}
1568 	} else if (header_close) {
1569 		do {
1570 			ssize_t len_read;
1571 			if (http_buf) {
1572 				http_buf = zend_string_realloc(http_buf, http_buf_size + 4096, 0);
1573 			} else {
1574 				http_buf = zend_string_alloc(4096, 0);
1575 			}
1576 			len_read = php_stream_read(stream, http_buf->val + http_buf_size, 4096);
1577 			if (len_read > 0) {
1578 				http_buf_size += len_read;
1579 			}
1580 		} while(!php_stream_eof(stream));
1581 	} else {
1582 		return NULL;
1583 	}
1584 
1585 	http_buf->val[http_buf_size] = '\0';
1586 	http_buf->len = http_buf_size;
1587 	return http_buf;
1588 }
1589 
get_http_headers(php_stream * stream)1590 static zend_string *get_http_headers(php_stream *stream)
1591 {
1592 	smart_str tmp_response = {0};
1593 	char headerbuf[8192];
1594 
1595 	while (php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
1596 		if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
1597 		    (headerbuf[0] == '\n')) {
1598 			/* empty line marks end of headers */
1599 			smart_str_0(&tmp_response);
1600 			return tmp_response.s;
1601 		}
1602 
1603 		/* add header to collection */
1604 		smart_str_appends(&tmp_response, headerbuf);
1605 	}
1606 
1607 	smart_str_free(&tmp_response);
1608 	return NULL;
1609 }
1610