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