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