xref: /PHP-5.5/ext/soap/php_http.c (revision c96d08b2)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2015 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 			int i, n;
817 
818 			has_cookies = 1;
819 			n = zend_hash_num_elements(Z_ARRVAL_PP(cookies));
820 			if (n > 0) {
821 				zend_hash_internal_pointer_reset(Z_ARRVAL_PP(cookies));
822 				smart_str_append_const(&soap_headers, "Cookie: ");
823 				for (i = 0; i < n; i++) {
824 					ulong numindx;
825 					int res = zend_hash_get_current_key(Z_ARRVAL_PP(cookies), &key, &numindx, FALSE);
826 					zend_hash_get_current_data(Z_ARRVAL_PP(cookies), (void **)&data);
827 
828 					if (res == HASH_KEY_IS_STRING && Z_TYPE_PP(data) == IS_ARRAY) {
829 					  zval** value;
830 
831 						if (zend_hash_index_find(Z_ARRVAL_PP(data), 0, (void**)&value) == SUCCESS &&
832 						    Z_TYPE_PP(value) == IS_STRING) {
833 						  zval **tmp;
834 						  if ((zend_hash_index_find(Z_ARRVAL_PP(data), 1, (void**)&tmp) == FAILURE ||
835 					           Z_TYPE_PP(tmp) != IS_STRING ||
836 						       strncmp(phpurl->path?phpurl->path:"/",Z_STRVAL_PP(tmp),Z_STRLEN_PP(tmp)) == 0) &&
837 						      (zend_hash_index_find(Z_ARRVAL_PP(data), 2, (void**)&tmp) == FAILURE ||
838 						       Z_TYPE_PP(tmp) != IS_STRING ||
839 						       in_domain(phpurl->host,Z_STRVAL_PP(tmp))) &&
840 						      (use_ssl || zend_hash_index_find(Z_ARRVAL_PP(data), 3, (void**)&tmp) == FAILURE)) {
841 								smart_str_appendl(&soap_headers, key, strlen(key));
842 								smart_str_appendc(&soap_headers, '=');
843 								smart_str_appendl(&soap_headers, Z_STRVAL_PP(value), Z_STRLEN_PP(value));
844 								smart_str_appendc(&soap_headers, ';');
845 							}
846 						}
847 					}
848 					zend_hash_move_forward(Z_ARRVAL_PP(cookies));
849 				}
850 				smart_str_append_const(&soap_headers, "\r\n");
851 			}
852 		}
853 
854 		http_context_headers(context, has_authorization, has_proxy_authorization, has_cookies, &soap_headers TSRMLS_CC);
855 
856 		smart_str_append_const(&soap_headers, "\r\n");
857 		smart_str_0(&soap_headers);
858 		if (zend_hash_find(Z_OBJPROP_P(this_ptr), "trace", sizeof("trace"), (void **) &trace) == SUCCESS &&
859 		    (Z_TYPE_PP(trace) == IS_BOOL || Z_TYPE_PP(trace) == IS_LONG) && Z_LVAL_PP(trace) != 0) {
860 			add_property_stringl(this_ptr, "__last_request_headers", soap_headers.c, soap_headers.len, 1);
861 		}
862 		smart_str_appendl(&soap_headers, request, request_size);
863 		smart_str_0(&soap_headers);
864 
865 		err = php_stream_write(stream, soap_headers.c, soap_headers.len);
866 		if (err != soap_headers.len) {
867 			if (request != buf) {efree(request);}
868 			php_stream_close(stream);
869 			zend_hash_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl"));
870 			zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
871 			zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
872 			add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL TSRMLS_CC);
873 			smart_str_free(&soap_headers_z);
874 			return FALSE;
875 		}
876 		smart_str_free(&soap_headers);
877 	} else {
878 		add_soap_fault(this_ptr, "HTTP", "Failed to create stream??", NULL, NULL TSRMLS_CC);
879 		smart_str_free(&soap_headers_z);
880 		return FALSE;
881 	}
882 
883 	if (!buffer) {
884 		php_stream_close(stream);
885 		zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
886 		zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
887 		smart_str_free(&soap_headers_z);
888 		return TRUE;
889 	}
890 
891 	do {
892 		if (!get_http_headers(stream, &http_headers, &http_header_size TSRMLS_CC)) {
893 			if (http_headers) {efree(http_headers);}
894 			if (request != buf) {efree(request);}
895 			php_stream_close(stream);
896 			zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
897 			zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
898 			add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL TSRMLS_CC);
899 			smart_str_free(&soap_headers_z);
900 			return FALSE;
901 		}
902 
903 		if (zend_hash_find(Z_OBJPROP_P(this_ptr), "trace", sizeof("trace"), (void **) &trace) == SUCCESS &&
904 		    (Z_TYPE_PP(trace) == IS_BOOL || Z_TYPE_PP(trace) == IS_LONG) && Z_LVAL_PP(trace) != 0) {
905 			add_property_stringl(this_ptr, "__last_response_headers", http_headers, http_header_size, 1);
906 		}
907 
908 		/* Check to see what HTTP status was sent */
909 		http_1_1 = 0;
910 		http_status = 0;
911 		http_version = get_http_header_value(http_headers,"HTTP/");
912 		if (http_version) {
913 			char *tmp;
914 
915 			if (!strncmp(http_version,"1.1", 3)) {
916 				http_1_1 = 1;
917 			}
918 
919 			tmp = strstr(http_version," ");
920 			if (tmp != NULL) {
921 				tmp++;
922 				http_status = atoi(tmp);
923 			}
924 			tmp = strstr(tmp," ");
925 			if (tmp != NULL) {
926 				tmp++;
927 				if (http_msg) {
928 					efree(http_msg);
929 				}
930 				http_msg = estrdup(tmp);
931 			}
932 			efree(http_version);
933 
934 			/* Try and get headers again */
935 			if (http_status == 100) {
936 				efree(http_headers);
937 			}
938 		}
939 	} while (http_status == 100);
940 
941 	/* Grab and send back every cookie */
942 
943 	/* Not going to worry about Path: because
944 	   we shouldn't be changing urls so path dont
945 	   matter too much
946 	*/
947 	cookie_itt = strstr(http_headers,"Set-Cookie: ");
948 	while (cookie_itt) {
949 		char *end_pos, *cookie;
950 		char *eqpos, *sempos;
951 		zval **cookies;
952 
953 		if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies"), (void **)&cookies) == FAILURE ||
954 		    Z_TYPE_PP(cookies) != IS_ARRAY) {
955 			zval *tmp_cookies;
956 			MAKE_STD_ZVAL(tmp_cookies);
957 			array_init(tmp_cookies);
958 			zend_hash_update(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies"), &tmp_cookies, sizeof(zval *), (void **)&cookies);
959 		}
960 
961 		end_pos = strstr(cookie_itt,"\r\n");
962 		cookie = get_http_header_value(cookie_itt,"Set-Cookie: ");
963 
964 		eqpos = strstr(cookie, "=");
965 		sempos = strstr(cookie, ";");
966 		if (eqpos != NULL && (sempos == NULL || sempos > eqpos)) {
967 			smart_str name = {0};
968 			int cookie_len;
969 			zval *zcookie;
970 
971 			if (sempos != NULL) {
972 				cookie_len = sempos-(eqpos+1);
973 			} else {
974 				cookie_len = strlen(cookie)-(eqpos-cookie)-1;
975 			}
976 
977 			smart_str_appendl(&name, cookie, eqpos - cookie);
978 			smart_str_0(&name);
979 
980 			ALLOC_INIT_ZVAL(zcookie);
981 			array_init(zcookie);
982 			add_index_stringl(zcookie, 0, eqpos + 1, cookie_len, 1);
983 
984 			if (sempos != NULL) {
985 				char *options = cookie + cookie_len+1;
986 				while (*options) {
987 					while (*options == ' ') {options++;}
988 					sempos = strstr(options, ";");
989 					if (strstr(options,"path=") == options) {
990 						eqpos = options + sizeof("path=")-1;
991 						add_index_stringl(zcookie, 1, eqpos, sempos?(sempos-eqpos):strlen(eqpos), 1);
992 					} else if (strstr(options,"domain=") == options) {
993 						eqpos = options + sizeof("domain=")-1;
994 						add_index_stringl(zcookie, 2, eqpos, sempos?(sempos-eqpos):strlen(eqpos), 1);
995 					} else if (strstr(options,"secure") == options) {
996 						add_index_bool(zcookie, 3, 1);
997 					}
998 					if (sempos != NULL) {
999 						options = sempos+1;
1000 					} else {
1001 					  break;
1002 					}
1003 				}
1004 			}
1005 			if (!zend_hash_index_exists(Z_ARRVAL_P(zcookie), 1)) {
1006 				char *t = phpurl->path?phpurl->path:"/";
1007 				char *c = strrchr(t, '/');
1008 				if (c) {
1009 					add_index_stringl(zcookie, 1, t, c-t, 1);
1010 				}
1011 			}
1012 			if (!zend_hash_index_exists(Z_ARRVAL_P(zcookie), 2)) {
1013 				add_index_string(zcookie, 2, phpurl->host, 1);
1014 			}
1015 
1016 			add_assoc_zval_ex(*cookies, name.c, name.len+1, zcookie);
1017 			smart_str_free(&name);
1018 		}
1019 
1020 		cookie_itt = strstr(cookie_itt + sizeof("Set-Cookie: "), "Set-Cookie: ");
1021 		efree(cookie);
1022 	}
1023 
1024 	/* See if the server requested a close */
1025 	if (http_1_1) {
1026 		http_close = FALSE;
1027 		if (use_proxy && !use_ssl) {
1028 			connection = get_http_header_value(http_headers,"Proxy-Connection: ");
1029 			if (connection) {
1030 				if (strncasecmp(connection, "close", sizeof("close")-1) == 0) {
1031 					http_close = TRUE;
1032 				}
1033 				efree(connection);
1034 			}
1035 		}
1036 		if (http_close == FALSE) {
1037 			connection = get_http_header_value(http_headers,"Connection: ");
1038 			if (connection) {
1039 				if (strncasecmp(connection, "close", sizeof("close")-1) == 0) {
1040 					http_close = TRUE;
1041 				}
1042 				efree(connection);
1043 			}
1044 		}
1045 	} else {
1046 		http_close = TRUE;
1047 		if (use_proxy && !use_ssl) {
1048 			connection = get_http_header_value(http_headers,"Proxy-Connection: ");
1049 			if (connection) {
1050 				if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
1051 					http_close = FALSE;
1052 				}
1053 				efree(connection);
1054 			}
1055 		}
1056 		if (http_close == TRUE) {
1057 			connection = get_http_header_value(http_headers,"Connection: ");
1058 			if (connection) {
1059 				if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
1060 					http_close = FALSE;
1061 				}
1062 				efree(connection);
1063 			}
1064 		}
1065 	}
1066 
1067 	if (!get_http_body(stream, http_close, http_headers, &http_body, &http_body_size TSRMLS_CC)) {
1068 		if (request != buf) {efree(request);}
1069 		php_stream_close(stream);
1070 		efree(http_headers);
1071 		zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
1072 		zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
1073 		add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL TSRMLS_CC);
1074 		if (http_msg) {
1075 			efree(http_msg);
1076 		}
1077 		smart_str_free(&soap_headers_z);
1078 		return FALSE;
1079 	}
1080 
1081 	if (request != buf) {efree(request);}
1082 
1083 	if (http_close) {
1084 		php_stream_close(stream);
1085 		zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
1086 		zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
1087 		stream = NULL;
1088 	}
1089 
1090 	/* Process HTTP status codes */
1091 	if (http_status >= 300 && http_status < 400) {
1092 		char *loc;
1093 
1094 		if ((loc = get_http_header_value(http_headers,"Location: ")) != NULL) {
1095 			php_url *new_url  = php_url_parse(loc);
1096 
1097 			if (new_url != NULL) {
1098 				efree(http_headers);
1099 				efree(http_body);
1100 				efree(loc);
1101 				if (new_url->scheme == NULL && new_url->path != NULL) {
1102 					new_url->scheme = phpurl->scheme ? estrdup(phpurl->scheme) : NULL;
1103 					new_url->host = phpurl->host ? estrdup(phpurl->host) : NULL;
1104 					new_url->port = phpurl->port;
1105 					if (new_url->path && new_url->path[0] != '/') {
1106 						if (phpurl->path) {
1107 							char *t = phpurl->path;
1108 							char *p = strrchr(t, '/');
1109 							if (p) {
1110 								char *s = emalloc((p - t) + strlen(new_url->path) + 2);
1111 								strncpy(s, t, (p - t) + 1);
1112 								s[(p - t) + 1] = 0;
1113 								strcat(s, new_url->path);
1114 								efree(new_url->path);
1115 								new_url->path = s;
1116 							}
1117 						} else {
1118 							char *s = emalloc(strlen(new_url->path) + 2);
1119 							s[0] = '/'; s[1] = 0;
1120 							strcat(s, new_url->path);
1121 							efree(new_url->path);
1122 							new_url->path = s;
1123 						}
1124 					}
1125 				}
1126 				phpurl = new_url;
1127 
1128 				if (--redirect_max < 1) {
1129 					add_soap_fault(this_ptr, "HTTP", "Redirection limit reached, aborting", NULL, NULL TSRMLS_CC);
1130 					smart_str_free(&soap_headers_z);
1131 					return FALSE;
1132 				}
1133 
1134 				goto try_again;
1135 			}
1136 		}
1137 	} else if (http_status == 401) {
1138 		/* Digest authentication */
1139 		zval **digest, **login, **password;
1140 		char *auth = get_http_header_value(http_headers, "WWW-Authenticate: ");
1141 
1142 		if (auth &&
1143 				strstr(auth, "Digest") == auth &&
1144 		    (zend_hash_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest"), (void **)&digest) == FAILURE ||
1145 		     Z_TYPE_PP(digest) != IS_ARRAY) &&
1146 		    zend_hash_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login"), (void **)&login) == SUCCESS &&
1147 		    Z_TYPE_PP(login) == IS_STRING &&
1148 		    zend_hash_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password"), (void **)&password) == SUCCESS &&
1149 		    Z_TYPE_PP(password) == IS_STRING) {
1150 			char *s;
1151 			zval *digest = NULL;
1152 
1153 			s = auth + sizeof("Digest")-1;
1154 			while (*s != '\0') {
1155 				char *name, *val;
1156 				while (*s == ' ') ++s;
1157 				name = s;
1158 				while (*s != '\0' && *s != '=') ++s;
1159 				if (*s == '=') {
1160 					*s = '\0';
1161 					++s;
1162 					if (*s == '"') {
1163 						++s;
1164 						val = s;
1165 						while (*s != '\0' && *s != '"') ++s;
1166 					} else {
1167 						val = s;
1168 						while (*s != '\0' && *s != ' ' && *s != ',') ++s;
1169 					}
1170 					if (*s != '\0') {
1171 						if (*s != ',') {
1172 							*s = '\0';
1173 							++s;
1174 							while (*s != '\0' && *s != ',') ++s;
1175 							if (*s != '\0') ++s;
1176 						} else {
1177 							*s = '\0';
1178 							++s;
1179 						}
1180 					}
1181 					if (digest == NULL) {
1182 						ALLOC_INIT_ZVAL(digest);
1183 						array_init(digest);
1184 					}
1185 					add_assoc_string(digest, name, val ,1);
1186 				}
1187 			}
1188 
1189 			if (digest != NULL) {
1190 				php_url *new_url  = emalloc(sizeof(php_url));
1191 
1192 				Z_DELREF_P(digest);
1193 				add_property_zval_ex(this_ptr, "_digest", sizeof("_digest"), digest TSRMLS_CC);
1194 
1195 				*new_url = *phpurl;
1196 				if (phpurl->scheme) phpurl->scheme = estrdup(phpurl->scheme);
1197 				if (phpurl->user) phpurl->user = estrdup(phpurl->user);
1198 				if (phpurl->pass) phpurl->pass = estrdup(phpurl->pass);
1199 				if (phpurl->host) phpurl->host = estrdup(phpurl->host);
1200 				if (phpurl->path) phpurl->path = estrdup(phpurl->path);
1201 				if (phpurl->query) phpurl->query = estrdup(phpurl->query);
1202 				if (phpurl->fragment) phpurl->fragment = estrdup(phpurl->fragment);
1203 				phpurl = new_url;
1204 
1205 				efree(auth);
1206 				efree(http_headers);
1207 				efree(http_body);
1208 
1209 				goto try_again;
1210 			}
1211 		}
1212 		if (auth) efree(auth);
1213 	}
1214 	smart_str_free(&soap_headers_z);
1215 
1216 	/* Check and see if the server even sent a xml document */
1217 	content_type = get_http_header_value(http_headers,"Content-Type: ");
1218 	if (content_type) {
1219 		char *pos = NULL;
1220 		int cmplen;
1221 		pos = strstr(content_type,";");
1222 		if (pos != NULL) {
1223 			cmplen = pos - content_type;
1224 		} else {
1225 			cmplen = strlen(content_type);
1226 		}
1227 		if (strncmp(content_type, "text/xml", cmplen) == 0 ||
1228 		    strncmp(content_type, "application/soap+xml", cmplen) == 0) {
1229 			content_type_xml = 1;
1230 /*
1231 			if (strncmp(http_body, "<?xml", 5)) {
1232 				zval *err;
1233 				MAKE_STD_ZVAL(err);
1234 				ZVAL_STRINGL(err, http_body, http_body_size, 1);
1235 				add_soap_fault(this_ptr, "HTTP", "Didn't receive an xml document", NULL, err TSRMLS_CC);
1236 				efree(content_type);
1237 				efree(http_headers);
1238 				efree(http_body);
1239 				return FALSE;
1240 			}
1241 */
1242 		}
1243 		efree(content_type);
1244 	}
1245 
1246 	/* Decompress response */
1247 	content_encoding = get_http_header_value(http_headers,"Content-Encoding: ");
1248 	if (content_encoding) {
1249 		zval func;
1250 		zval retval;
1251 	  zval param;
1252 		zval *params[1];
1253 
1254 		if ((strcmp(content_encoding,"gzip") == 0 ||
1255 		     strcmp(content_encoding,"x-gzip") == 0) &&
1256 		     zend_hash_exists(EG(function_table), "gzinflate", sizeof("gzinflate"))) {
1257 			ZVAL_STRING(&func, "gzinflate", 0);
1258 			params[0] = &param;
1259 			ZVAL_STRINGL(params[0], http_body+10, http_body_size-10, 0);
1260 			INIT_PZVAL(params[0]);
1261 		} else if (strcmp(content_encoding,"deflate") == 0 &&
1262 		           zend_hash_exists(EG(function_table), "gzuncompress", sizeof("gzuncompress"))) {
1263 			ZVAL_STRING(&func, "gzuncompress", 0);
1264 			params[0] = &param;
1265 			ZVAL_STRINGL(params[0], http_body, http_body_size, 0);
1266 			INIT_PZVAL(params[0]);
1267 		} else {
1268 			efree(content_encoding);
1269 			efree(http_headers);
1270 			efree(http_body);
1271 			if (http_msg) {
1272 				efree(http_msg);
1273 			}
1274 			add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL TSRMLS_CC);
1275 			return FALSE;
1276 		}
1277 		if (call_user_function(CG(function_table), (zval**)NULL, &func, &retval, 1, params TSRMLS_CC) == SUCCESS &&
1278 		    Z_TYPE(retval) == IS_STRING) {
1279 			efree(http_body);
1280 			*buffer = Z_STRVAL(retval);
1281 			*buffer_len = Z_STRLEN(retval);
1282 		} else {
1283 			efree(content_encoding);
1284 			efree(http_headers);
1285 			efree(http_body);
1286 			add_soap_fault(this_ptr, "HTTP", "Can't uncompress compressed response", NULL, NULL TSRMLS_CC);
1287 			if (http_msg) {
1288 				efree(http_msg);
1289 			}
1290 			return FALSE;
1291 		}
1292 		efree(content_encoding);
1293 	} else {
1294 		*buffer = http_body;
1295 		*buffer_len = http_body_size;
1296 	}
1297 
1298 	efree(http_headers);
1299 
1300 	if (http_status >= 400) {
1301 		int error = 0;
1302 
1303 		if (*buffer_len == 0) {
1304 			error = 1;
1305 		} else if (*buffer_len > 0) {
1306 			if (!content_type_xml) {
1307 				char *s = *buffer;
1308 
1309 				while (*s != '\0' && *s < ' ') {
1310 					s++;
1311 				}
1312 				if (strncmp(s, "<?xml", 5)) {
1313 					error = 1;
1314 				}
1315 			}
1316 		}
1317 
1318 		if (error) {
1319 			efree(*buffer);
1320 			add_soap_fault(this_ptr, "HTTP", http_msg, NULL, NULL TSRMLS_CC);
1321 			efree(http_msg);
1322 			return FALSE;
1323 		}
1324 	}
1325 
1326 	if (http_msg) {
1327 		efree(http_msg);
1328 	}
1329 
1330 	return TRUE;
1331 }
1332 
get_http_header_value(char * headers,char * type)1333 static char *get_http_header_value(char *headers, char *type)
1334 {
1335 	char *pos, *tmp = NULL;
1336 	int typelen, headerslen;
1337 
1338 	typelen = strlen(type);
1339 	headerslen = strlen(headers);
1340 
1341 	/* header `titles' can be lower case, or any case combination, according
1342 	 * to the various RFC's. */
1343 	pos = headers;
1344 	do {
1345 		/* start of buffer or start of line */
1346 		if (strncasecmp(pos, type, typelen) == 0) {
1347 			char *eol;
1348 
1349 			/* match */
1350 			tmp = pos + typelen;
1351 			eol = strchr(tmp, '\n');
1352 			if (eol == NULL) {
1353 				eol = headers + headerslen;
1354 			} else if (eol > tmp && *(eol-1) == '\r') {
1355 				eol--;
1356 			}
1357 			return estrndup(tmp, eol - tmp);
1358 		}
1359 
1360 		/* find next line */
1361 		pos = strchr(pos, '\n');
1362 		if (pos) {
1363 			pos++;
1364 		}
1365 
1366 	} while (pos);
1367 
1368 	return NULL;
1369 }
1370 
get_http_body(php_stream * stream,int close,char * headers,char ** response,int * out_size TSRMLS_DC)1371 static int get_http_body(php_stream *stream, int close, char *headers,  char **response, int *out_size TSRMLS_DC)
1372 {
1373 	char *header, *http_buf = NULL;
1374 	int header_close = close, header_chunked = 0, header_length = 0, http_buf_size = 0;
1375 
1376 	if (!close) {
1377 		header = get_http_header_value(headers, "Connection: ");
1378 		if (header) {
1379 			if(!strncasecmp(header, "close", sizeof("close")-1)) header_close = 1;
1380 			efree(header);
1381 		}
1382 	}
1383 	header = get_http_header_value(headers, "Transfer-Encoding: ");
1384 	if (header) {
1385 		if(!strncasecmp(header, "chunked", sizeof("chunked")-1)) header_chunked = 1;
1386 		efree(header);
1387 	}
1388 	header = get_http_header_value(headers, "Content-Length: ");
1389 	if (header) {
1390 		header_length = atoi(header);
1391 		efree(header);
1392 		if (!header_length && !header_chunked) {
1393 			/* Empty response */
1394 			http_buf = emalloc(1);
1395 			http_buf[0] = '\0';
1396 			(*response) = http_buf;
1397 			(*out_size) = 0;
1398 			return TRUE;
1399 		}
1400 	}
1401 
1402 	if (header_chunked) {
1403 		char ch, done, headerbuf[8192];
1404 
1405 		done = FALSE;
1406 
1407 		while (!done) {
1408 			int buf_size = 0;
1409 
1410 			php_stream_gets(stream, headerbuf, sizeof(headerbuf));
1411 			if (sscanf(headerbuf, "%x", &buf_size) > 0 ) {
1412 				if (buf_size > 0) {
1413 					int len_size = 0;
1414 
1415 					if (http_buf_size + buf_size + 1 < 0) {
1416 						efree(http_buf);
1417 						return FALSE;
1418 					}
1419 					http_buf = erealloc(http_buf, http_buf_size + buf_size + 1);
1420 
1421 					while (len_size < buf_size) {
1422 						int len_read = php_stream_read(stream, http_buf + http_buf_size, buf_size - len_size);
1423 						if (len_read <= 0) {
1424 							/* Error or EOF */
1425 							done = TRUE;
1426 						  break;
1427 						}
1428 						len_size += len_read;
1429 	 					http_buf_size += len_read;
1430  					}
1431 
1432 					/* Eat up '\r' '\n' */
1433 					ch = php_stream_getc(stream);
1434 					if (ch == '\r') {
1435 						ch = php_stream_getc(stream);
1436 					}
1437 					if (ch != '\n') {
1438 						/* Somthing wrong in chunked encoding */
1439 						if (http_buf) {
1440 							efree(http_buf);
1441 						}
1442 						return FALSE;
1443 					}
1444  				}
1445 			} else {
1446 				/* Somthing wrong in chunked encoding */
1447 				if (http_buf) {
1448 					efree(http_buf);
1449 				}
1450 				return FALSE;
1451 			}
1452 			if (buf_size == 0) {
1453 				done = TRUE;
1454 			}
1455 		}
1456 
1457 		/* Ignore trailer headers */
1458 		while (1) {
1459 			if (!php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
1460 				break;
1461 			}
1462 
1463 			if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
1464 			    (headerbuf[0] == '\n')) {
1465 				/* empty line marks end of headers */
1466 				break;
1467 			}
1468 		}
1469 
1470 		if (http_buf == NULL) {
1471 			http_buf = emalloc(1);
1472 		}
1473 
1474 	} else if (header_length) {
1475 		if (header_length < 0 || header_length >= INT_MAX) {
1476 			return FALSE;
1477 		}
1478 		http_buf = safe_emalloc(1, header_length, 1);
1479 		while (http_buf_size < header_length) {
1480 			int len_read = php_stream_read(stream, http_buf + http_buf_size, header_length - http_buf_size);
1481 			if (len_read <= 0) {
1482 				break;
1483 			}
1484 			http_buf_size += len_read;
1485 		}
1486 	} else if (header_close) {
1487 		do {
1488 			int len_read;
1489 			http_buf = erealloc(http_buf, http_buf_size + 4096 + 1);
1490 			len_read = php_stream_read(stream, http_buf + http_buf_size, 4096);
1491 			if (len_read > 0) {
1492 				http_buf_size += len_read;
1493 			}
1494 		} while(!php_stream_eof(stream));
1495 	} else {
1496 		return FALSE;
1497 	}
1498 
1499 	http_buf[http_buf_size] = '\0';
1500 	(*response) = http_buf;
1501 	(*out_size) = http_buf_size;
1502 	return TRUE;
1503 }
1504 
get_http_headers(php_stream * stream,char ** response,int * out_size TSRMLS_DC)1505 static int get_http_headers(php_stream *stream, char **response, int *out_size TSRMLS_DC)
1506 {
1507 	int done = FALSE;
1508 	smart_str tmp_response = {0};
1509 	char headerbuf[8192];
1510 
1511 	while (!done) {
1512 		if (!php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
1513 			break;
1514 		}
1515 
1516 		if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
1517 		    (headerbuf[0] == '\n')) {
1518 			/* empty line marks end of headers */
1519 			done = TRUE;
1520 			break;
1521 		}
1522 
1523 		/* add header to collection */
1524 		smart_str_appends(&tmp_response, headerbuf);
1525 	}
1526 	smart_str_0(&tmp_response);
1527 	(*response) = tmp_response.c;
1528 	(*out_size) = tmp_response.len;
1529 	return done;
1530 }
1531 /*
1532  * Local variables:
1533  * tab-width: 4
1534  * c-basic-offset: 4
1535  * End:
1536  * vim600: sw=4 ts=4 fdm=marker
1537  * vim<600: sw=4 ts=4
1538  */
1539