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