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