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