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