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