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(¶ms[0], buf);
392 ZVAL_LONG(¶ms[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(¶ms[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(¶ms[0]);
406 zval_ptr_dtor(&func);
407 request = Z_STR(retval);
408 } else {
409 zval_ptr_dtor(¶ms[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(¶ms[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(¶ms[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(¶ms[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(¶ms[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