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