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