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