1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2017 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Brad Lafountain <rodif_bl@yahoo.com> |
16 | Shane Caraveo <shane@caraveo.com> |
17 | Dmitry Stogov <dmitry@zend.com> |
18 +----------------------------------------------------------------------+
19 */
20 /* $Id$ */
21
22 #include "php_soap.h"
23 #include "ext/standard/base64.h"
24 #include "ext/standard/md5.h"
25 #include "ext/standard/php_rand.h"
26
27 static char *get_http_header_value(char *headers, char *type);
28 static zend_string *get_http_body(php_stream *socketd, int close, char *headers);
29 static zend_string *get_http_headers(php_stream *socketd);
30
31 #define smart_str_append_const(str, const) \
32 smart_str_appendl(str,const,sizeof(const)-1)
33
34 /* Proxy HTTP Authentication */
proxy_authentication(zval * this_ptr,smart_str * soap_headers)35 int proxy_authentication(zval* this_ptr, smart_str* soap_headers)
36 {
37 zval *login, *password;
38
39 if ((login = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_proxy_login", sizeof("_proxy_login")-1)) != NULL &&
40 Z_TYPE_P(login) == IS_STRING) {
41 zend_string *buf;
42 smart_str auth = {0};
43
44 smart_str_appendl(&auth, Z_STRVAL_P(login), Z_STRLEN_P(login));
45 smart_str_appendc(&auth, ':');
46 if ((password = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_proxy_password", sizeof("_proxy_password")-1)) != NULL &&
47 Z_TYPE_P(password) == IS_STRING) {
48 smart_str_appendl(&auth, Z_STRVAL_P(password), Z_STRLEN_P(password));
49 }
50 smart_str_0(&auth);
51 buf = php_base64_encode((unsigned char*)ZSTR_VAL(auth.s), ZSTR_LEN(auth.s));
52 smart_str_append_const(soap_headers, "Proxy-Authorization: Basic ");
53 smart_str_appendl(soap_headers, (char*)ZSTR_VAL(buf), ZSTR_LEN(buf));
54 smart_str_append_const(soap_headers, "\r\n");
55 zend_string_release(buf);
56 smart_str_free(&auth);
57 return 1;
58 }
59 return 0;
60 }
61
62 /* HTTP Authentication */
basic_authentication(zval * this_ptr,smart_str * soap_headers)63 int basic_authentication(zval* this_ptr, smart_str* soap_headers)
64 {
65 zval *login, *password;
66
67 if ((login = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login")-1)) != NULL &&
68 Z_TYPE_P(login) == IS_STRING &&
69 !zend_hash_str_exists(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest")-1)) {
70 zend_string* buf;
71 smart_str auth = {0};
72
73 smart_str_appendl(&auth, Z_STRVAL_P(login), Z_STRLEN_P(login));
74 smart_str_appendc(&auth, ':');
75 if ((password = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password")-1)) != NULL &&
76 Z_TYPE_P(password) == IS_STRING) {
77 smart_str_appendl(&auth, Z_STRVAL_P(password), Z_STRLEN_P(password));
78 }
79 smart_str_0(&auth);
80 buf = php_base64_encode((unsigned char*)ZSTR_VAL(auth.s), ZSTR_LEN(auth.s));
81 smart_str_append_const(soap_headers, "Authorization: Basic ");
82 smart_str_appendl(soap_headers, (char*)ZSTR_VAL(buf), ZSTR_LEN(buf));
83 smart_str_append_const(soap_headers, "\r\n");
84 zend_string_release(buf);
85 smart_str_free(&auth);
86 return 1;
87 }
88 return 0;
89 }
90
91 /* Additional HTTP headers */
http_context_headers(php_stream_context * context,zend_bool has_authorization,zend_bool has_proxy_authorization,zend_bool has_cookies,smart_str * soap_headers)92 void http_context_headers(php_stream_context* context,
93 zend_bool has_authorization,
94 zend_bool has_proxy_authorization,
95 zend_bool has_cookies,
96 smart_str* soap_headers)
97 {
98 zval *tmp;
99
100 if (context &&
101 (tmp = php_stream_context_get_option(context, "http", "header")) != NULL &&
102 Z_TYPE_P(tmp) == IS_STRING && Z_STRLEN_P(tmp)) {
103 char *s = Z_STRVAL_P(tmp);
104 char *p;
105 int name_len;
106
107 while (*s) {
108 /* skip leading newlines and spaces */
109 while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') {
110 s++;
111 }
112 /* extract header name */
113 p = s;
114 name_len = -1;
115 while (*p) {
116 if (*p == ':') {
117 if (name_len < 0) name_len = p - s;
118 break;
119 } else if (*p == ' ' || *p == '\t') {
120 if (name_len < 0) name_len = p - s;
121 } else if (*p == '\r' || *p == '\n') {
122 break;
123 }
124 p++;
125 }
126 if (*p == ':') {
127 /* extract header value */
128 while (*p && *p != '\r' && *p != '\n') {
129 p++;
130 }
131 /* skip some predefined headers */
132 if ((name_len != sizeof("host")-1 ||
133 strncasecmp(s, "host", sizeof("host")-1) != 0) &&
134 (name_len != sizeof("connection")-1 ||
135 strncasecmp(s, "connection", sizeof("connection")-1) != 0) &&
136 (name_len != sizeof("user-agent")-1 ||
137 strncasecmp(s, "user-agent", sizeof("user-agent")-1) != 0) &&
138 (name_len != sizeof("content-length")-1 ||
139 strncasecmp(s, "content-length", sizeof("content-length")-1) != 0) &&
140 (name_len != sizeof("content-type")-1 ||
141 strncasecmp(s, "content-type", sizeof("content-type")-1) != 0) &&
142 (!has_cookies ||
143 name_len != sizeof("cookie")-1 ||
144 strncasecmp(s, "cookie", sizeof("cookie")-1) != 0) &&
145 (!has_authorization ||
146 name_len != sizeof("authorization")-1 ||
147 strncasecmp(s, "authorization", sizeof("authorization")-1) != 0) &&
148 (!has_proxy_authorization ||
149 name_len != sizeof("proxy-authorization")-1 ||
150 strncasecmp(s, "proxy-authorization", sizeof("proxy-authorization")-1) != 0)) {
151 /* add header */
152 smart_str_appendl(soap_headers, s, p-s);
153 smart_str_append_const(soap_headers, "\r\n");
154 }
155 }
156 s = (*p) ? (p + 1) : p;
157 }
158 }
159 }
160
http_connect(zval * this_ptr,php_url * phpurl,int use_ssl,php_stream_context * context,int * use_proxy)161 static php_stream* http_connect(zval* this_ptr, php_url *phpurl, int use_ssl, php_stream_context *context, int *use_proxy)
162 {
163 php_stream *stream;
164 zval *proxy_host, *proxy_port, *tmp, ssl_proxy_peer_name;
165 char *host;
166 char *name;
167 char *protocol;
168 zend_long namelen;
169 int port;
170 int old_error_reporting;
171 struct timeval tv;
172 struct timeval *timeout = NULL;
173
174 if ((proxy_host = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_proxy_host", sizeof("_proxy_host")-1)) != NULL &&
175 Z_TYPE_P(proxy_host) == IS_STRING &&
176 (proxy_port = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_proxy_port", sizeof("_proxy_port")-1)) != NULL &&
177 Z_TYPE_P(proxy_port) == IS_LONG) {
178 host = Z_STRVAL_P(proxy_host);
179 port = Z_LVAL_P(proxy_port);
180 *use_proxy = 1;
181 } else {
182 host = phpurl->host;
183 port = phpurl->port;
184 }
185 if ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_connection_timeout", sizeof("_connection_timeout")-1)) != NULL &&
186 Z_TYPE_P(tmp) == IS_LONG && Z_LVAL_P(tmp) > 0) {
187 tv.tv_sec = Z_LVAL_P(tmp);
188 tv.tv_usec = 0;
189 timeout = &tv;
190 }
191
192 old_error_reporting = EG(error_reporting);
193 EG(error_reporting) &= ~(E_WARNING|E_NOTICE|E_USER_WARNING|E_USER_NOTICE);
194
195 /* Changed ternary operator to an if/else so that additional comparisons can be done on the ssl_method property */
196 if (use_ssl && !*use_proxy) {
197 if ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_ssl_method", sizeof("_ssl_method")-1)) != NULL &&
198 Z_TYPE_P(tmp) == IS_LONG) {
199 /* uses constants declared in soap.c to determine ssl uri protocol */
200 switch (Z_LVAL_P(tmp)) {
201 case SOAP_SSL_METHOD_TLS:
202 protocol = "tls";
203 break;
204
205 case SOAP_SSL_METHOD_SSLv2:
206 protocol = "sslv2";
207 break;
208
209 case SOAP_SSL_METHOD_SSLv3:
210 protocol = "sslv3";
211 break;
212
213 case SOAP_SSL_METHOD_SSLv23:
214 protocol = "ssl";
215 break;
216
217 default:
218 protocol = "ssl";
219 break;
220
221 }
222 } else {
223 protocol = "ssl";
224 }
225 } else {
226 protocol = "tcp";
227 }
228
229 namelen = spprintf(&name, 0, "%s://%s:%d", protocol, host, port);
230
231 stream = php_stream_xport_create(name, namelen,
232 REPORT_ERRORS,
233 STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
234 NULL /*persistent_id*/,
235 timeout,
236 context,
237 NULL, NULL);
238 efree(name);
239
240 /* SSL & proxy */
241 if (stream && *use_proxy && use_ssl) {
242 smart_str soap_headers = {0};
243
244 /* Set peer_name or name verification will try to use the proxy server name */
245 if (!context || (tmp = php_stream_context_get_option(context, "ssl", "peer_name")) == NULL) {
246 ZVAL_STRING(&ssl_proxy_peer_name, phpurl->host);
247 php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name", &ssl_proxy_peer_name);
248 zval_ptr_dtor(&ssl_proxy_peer_name);
249 }
250
251 smart_str_append_const(&soap_headers, "CONNECT ");
252 smart_str_appends(&soap_headers, phpurl->host);
253 smart_str_appendc(&soap_headers, ':');
254 smart_str_append_unsigned(&soap_headers, phpurl->port);
255 smart_str_append_const(&soap_headers, " HTTP/1.1\r\n");
256 smart_str_append_const(&soap_headers, "Host: ");
257 smart_str_appends(&soap_headers, phpurl->host);
258 if (phpurl->port != 80) {
259 smart_str_appendc(&soap_headers, ':');
260 smart_str_append_unsigned(&soap_headers, phpurl->port);
261 }
262 smart_str_append_const(&soap_headers, "\r\n");
263 proxy_authentication(this_ptr, &soap_headers);
264 smart_str_append_const(&soap_headers, "\r\n");
265 if (php_stream_write(stream, ZSTR_VAL(soap_headers.s), ZSTR_LEN(soap_headers.s)) != ZSTR_LEN(soap_headers.s)) {
266 php_stream_close(stream);
267 stream = NULL;
268 }
269 smart_str_free(&soap_headers);
270
271 if (stream) {
272 zend_string *http_headers = get_http_headers(stream);
273 if (http_headers) {
274 zend_string_free(http_headers);
275 } else {
276 php_stream_close(stream);
277 stream = NULL;
278 }
279 }
280 /* enable SSL transport layer */
281 if (stream) {
282 /* if a stream is created without encryption, check to see if SSL method parameter is specified and use
283 proper encrypyion method based on constants defined in soap.c */
284 int crypto_method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
285 if ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_ssl_method", sizeof("_ssl_method")-1)) != NULL &&
286 Z_TYPE_P(tmp) == IS_LONG) {
287 switch (Z_LVAL_P(tmp)) {
288 case SOAP_SSL_METHOD_TLS:
289 crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
290 break;
291
292 case SOAP_SSL_METHOD_SSLv2:
293 crypto_method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
294 break;
295
296 case SOAP_SSL_METHOD_SSLv3:
297 crypto_method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
298 break;
299
300 case SOAP_SSL_METHOD_SSLv23:
301 crypto_method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
302 break;
303
304 default:
305 crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
306 break;
307 }
308 }
309 if (php_stream_xport_crypto_setup(stream, crypto_method, NULL) < 0 ||
310 php_stream_xport_crypto_enable(stream, 1) < 0) {
311 php_stream_close(stream);
312 stream = NULL;
313 }
314 }
315 }
316
317 EG(error_reporting) = old_error_reporting;
318 return stream;
319 }
320
in_domain(const char * host,const char * domain)321 static int in_domain(const char *host, const char *domain)
322 {
323 if (domain[0] == '.') {
324 int l1 = strlen(host);
325 int l2 = strlen(domain);
326 if (l1 > l2) {
327 return strcmp(host+l1-l2,domain) == 0;
328 } else {
329 return 0;
330 }
331 } else {
332 return strcmp(host,domain) == 0;
333 }
334 }
335
make_http_soap_request(zval * this_ptr,zend_string * buf,char * location,char * soapaction,int soap_version,zval * return_value)336 int make_http_soap_request(zval *this_ptr,
337 zend_string *buf,
338 char *location,
339 char *soapaction,
340 int soap_version,
341 zval *return_value)
342 {
343 zend_string *request;
344 smart_str soap_headers = {0};
345 smart_str soap_headers_z = {0};
346 int err;
347 php_url *phpurl = NULL;
348 php_stream *stream;
349 zval *trace, *tmp;
350 int use_proxy = 0;
351 int use_ssl;
352 zend_string *http_body;
353 char *content_type, *http_version, *cookie_itt;
354 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(request);
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(request);
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 && strcmp(phpurl->scheme, "https") == 0) {
458 use_ssl = 1;
459 } else if (phpurl->scheme == NULL || strcmp(phpurl->scheme, "http") != 0) {
460 php_url_free(phpurl);
461 if (request != buf) {
462 zend_string_release(request);
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(request);
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 && strcmp(orig->scheme, "https") == 0) ||
493 (!use_ssl && orig->scheme == NULL) ||
494 (!use_ssl && strcmp(orig->scheme, "https") != 0)) &&
495 strcmp(orig->host, 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_REFCOUNT(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(request);
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_REFCOUNT(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, phpurl->scheme);
557 smart_str_append_const(&soap_headers, "://");
558 smart_str_appends(&soap_headers, 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, 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, phpurl->query);
570 }
571 if (phpurl->fragment) {
572 smart_str_appendc(&soap_headers, '#');
573 smart_str_appends(&soap_headers, 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, 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 smart_str_append_const(&soap_headers,"Content-Type: application/soap+xml; charset=utf-8");
622 if (soapaction) {
623 smart_str_append_const(&soap_headers,"; action=\"");
624 smart_str_appends(&soap_headers, soapaction);
625 smart_str_append_const(&soap_headers,"\"");
626 }
627 smart_str_append_const(&soap_headers,"\r\n");
628 } else {
629 smart_str_append_const(&soap_headers,"Content-Type: text/xml; charset=utf-8\r\n");
630 if (soapaction) {
631 smart_str_append_const(&soap_headers, "SOAPAction: \"");
632 smart_str_appends(&soap_headers, soapaction);
633 smart_str_append_const(&soap_headers, "\"\r\n");
634 }
635 }
636 smart_str_append_const(&soap_headers,"Content-Length: ");
637 smart_str_append_long(&soap_headers, request->len);
638 smart_str_append_const(&soap_headers, "\r\n");
639
640 /* HTTP Authentication */
641 if ((login = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login")-1)) != NULL &&
642 Z_TYPE_P(login) == IS_STRING) {
643 zval *digest;
644
645 has_authorization = 1;
646 if ((digest = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest")-1)) != NULL) {
647 if (Z_TYPE_P(digest) == IS_ARRAY) {
648 char HA1[33], HA2[33], response[33], cnonce[33], nc[9];
649 PHP_MD5_CTX md5ctx;
650 unsigned char hash[16];
651
652 PHP_MD5Init(&md5ctx);
653 snprintf(cnonce, sizeof(cnonce), ZEND_LONG_FMT, php_rand());
654 PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, strlen(cnonce));
655 PHP_MD5Final(hash, &md5ctx);
656 make_digest(cnonce, hash);
657
658 if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nc", sizeof("nc")-1)) != NULL &&
659 Z_TYPE_P(tmp) == IS_LONG) {
660 Z_LVAL_P(tmp)++;
661 snprintf(nc, sizeof(nc), "%08ld", Z_LVAL_P(tmp));
662 } else {
663 add_assoc_long(digest, "nc", 1);
664 strcpy(nc, "00000001");
665 }
666
667 PHP_MD5Init(&md5ctx);
668 PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(login), Z_STRLEN_P(login));
669 PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
670 if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "realm", sizeof("realm")-1)) != NULL &&
671 Z_TYPE_P(tmp) == IS_STRING) {
672 PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
673 }
674 PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
675 if ((password = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password")-1)) != NULL &&
676 Z_TYPE_P(password) == IS_STRING) {
677 PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(password), Z_STRLEN_P(password));
678 }
679 PHP_MD5Final(hash, &md5ctx);
680 make_digest(HA1, hash);
681 if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "algorithm", sizeof("algorithm")-1)) != NULL &&
682 Z_TYPE_P(tmp) == IS_STRING &&
683 Z_STRLEN_P(tmp) == sizeof("md5-sess")-1 &&
684 stricmp(Z_STRVAL_P(tmp), "md5-sess") == 0) {
685 PHP_MD5Init(&md5ctx);
686 PHP_MD5Update(&md5ctx, (unsigned char*)HA1, 32);
687 PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
688 if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nonce", sizeof("nonce")-1)) != NULL &&
689 Z_TYPE_P(tmp) == IS_STRING) {
690 PHP_MD5Update(&md5ctx, (unsigned char*)Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
691 }
692 PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
693 PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8);
694 PHP_MD5Final(hash, &md5ctx);
695 make_digest(HA1, hash);
696 }
697
698 PHP_MD5Init(&md5ctx);
699 PHP_MD5Update(&md5ctx, (unsigned char*)"POST:", sizeof("POST:")-1);
700 if (phpurl->path) {
701 PHP_MD5Update(&md5ctx, (unsigned char*)phpurl->path, strlen(phpurl->path));
702 } else {
703 PHP_MD5Update(&md5ctx, (unsigned char*)"/", 1);
704 }
705 if (phpurl->query) {
706 PHP_MD5Update(&md5ctx, (unsigned char*)"?", 1);
707 PHP_MD5Update(&md5ctx, (unsigned char*)phpurl->query, strlen(phpurl->query));
708 }
709
710 PHP_MD5Final(hash, &md5ctx);
711 make_digest(HA2, hash);
712
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 if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "qop", sizeof("qop")-1)) != NULL &&
722 Z_TYPE_P(tmp) == IS_STRING) {
723 PHP_MD5Update(&md5ctx, (unsigned char*)nc, 8);
724 PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
725 PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8);
726 PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
727 /* TODO: Support for qop="auth-int" */
728 PHP_MD5Update(&md5ctx, (unsigned char*)"auth", sizeof("auth")-1);
729 PHP_MD5Update(&md5ctx, (unsigned char*)":", 1);
730 }
731 PHP_MD5Update(&md5ctx, (unsigned char*)HA2, 32);
732 PHP_MD5Final(hash, &md5ctx);
733 make_digest(response, hash);
734
735 smart_str_append_const(&soap_headers, "Authorization: Digest username=\"");
736 smart_str_appendl(&soap_headers, Z_STRVAL_P(login), Z_STRLEN_P(login));
737 if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "realm", sizeof("realm")-1)) != NULL &&
738 Z_TYPE_P(tmp) == IS_STRING) {
739 smart_str_append_const(&soap_headers, "\", realm=\"");
740 smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
741 }
742 if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nonce", sizeof("nonce")-1)) != NULL &&
743 Z_TYPE_P(tmp) == IS_STRING) {
744 smart_str_append_const(&soap_headers, "\", nonce=\"");
745 smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
746 }
747 smart_str_append_const(&soap_headers, "\", uri=\"");
748 if (phpurl->path) {
749 smart_str_appends(&soap_headers, phpurl->path);
750 } else {
751 smart_str_appendc(&soap_headers, '/');
752 }
753 if (phpurl->query) {
754 smart_str_appendc(&soap_headers, '?');
755 smart_str_appends(&soap_headers, phpurl->query);
756 }
757 if (phpurl->fragment) {
758 smart_str_appendc(&soap_headers, '#');
759 smart_str_appends(&soap_headers, phpurl->fragment);
760 }
761 if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "qop", sizeof("qop")-1)) != NULL &&
762 Z_TYPE_P(tmp) == IS_STRING) {
763 /* TODO: Support for qop="auth-int" */
764 smart_str_append_const(&soap_headers, "\", qop=\"auth");
765 smart_str_append_const(&soap_headers, "\", nc=\"");
766 smart_str_appendl(&soap_headers, nc, 8);
767 smart_str_append_const(&soap_headers, "\", cnonce=\"");
768 smart_str_appendl(&soap_headers, cnonce, 8);
769 }
770 smart_str_append_const(&soap_headers, "\", response=\"");
771 smart_str_appendl(&soap_headers, response, 32);
772 if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "opaque", sizeof("opaque")-1)) != NULL &&
773 Z_TYPE_P(tmp) == IS_STRING) {
774 smart_str_append_const(&soap_headers, "\", opaque=\"");
775 smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
776 }
777 if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "algorithm", sizeof("algorithm")-1)) != NULL &&
778 Z_TYPE_P(tmp) == IS_STRING) {
779 smart_str_append_const(&soap_headers, "\", algorithm=\"");
780 smart_str_appendl(&soap_headers, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
781 }
782 smart_str_append_const(&soap_headers, "\"\r\n");
783 }
784 } else {
785 zend_string *buf;
786
787 smart_str auth = {0};
788 smart_str_appendl(&auth, Z_STRVAL_P(login), Z_STRLEN_P(login));
789 smart_str_appendc(&auth, ':');
790 if ((password = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password")-1)) != NULL &&
791 Z_TYPE_P(password) == IS_STRING) {
792 smart_str_appendl(&auth, Z_STRVAL_P(password), Z_STRLEN_P(password));
793 }
794 smart_str_0(&auth);
795 buf = php_base64_encode((unsigned char*)ZSTR_VAL(auth.s), ZSTR_LEN(auth.s));
796 smart_str_append_const(&soap_headers, "Authorization: Basic ");
797 smart_str_appendl(&soap_headers, (char*)ZSTR_VAL(buf), ZSTR_LEN(buf));
798 smart_str_append_const(&soap_headers, "\r\n");
799 zend_string_release(buf);
800 smart_str_free(&auth);
801 }
802 }
803
804 /* Proxy HTTP Authentication */
805 if (use_proxy && !use_ssl) {
806 has_proxy_authorization = proxy_authentication(this_ptr, &soap_headers);
807 }
808
809 /* Send cookies along with request */
810 if ((cookies = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies")-1)) != NULL &&
811 Z_TYPE_P(cookies) == IS_ARRAY) {
812 zval *data;
813 zend_string *key;
814 int i, n;
815
816 has_cookies = 1;
817 n = zend_hash_num_elements(Z_ARRVAL_P(cookies));
818 if (n > 0) {
819 zend_hash_internal_pointer_reset(Z_ARRVAL_P(cookies));
820 smart_str_append_const(&soap_headers, "Cookie: ");
821 for (i = 0; i < n; i++) {
822 zend_ulong numindx;
823 int res = zend_hash_get_current_key(Z_ARRVAL_P(cookies), &key, &numindx);
824 data = zend_hash_get_current_data(Z_ARRVAL_P(cookies));
825
826 if (res == HASH_KEY_IS_STRING && Z_TYPE_P(data) == IS_ARRAY) {
827 zval *value;
828
829 if ((value = zend_hash_index_find(Z_ARRVAL_P(data), 0)) != NULL &&
830 Z_TYPE_P(value) == IS_STRING) {
831 zval *tmp;
832 if (((tmp = zend_hash_index_find(Z_ARRVAL_P(data), 1)) == NULL ||
833 Z_TYPE_P(tmp) != IS_STRING ||
834 strncmp(phpurl->path?phpurl->path:"/",Z_STRVAL_P(tmp),Z_STRLEN_P(tmp)) == 0) &&
835 ((tmp = zend_hash_index_find(Z_ARRVAL_P(data), 2)) == NULL ||
836 Z_TYPE_P(tmp) != IS_STRING ||
837 in_domain(phpurl->host,Z_STRVAL_P(tmp))) &&
838 (use_ssl || (tmp = zend_hash_index_find(Z_ARRVAL_P(data), 3)) == NULL)) {
839 smart_str_append(&soap_headers, key);
840 smart_str_appendc(&soap_headers, '=');
841 smart_str_append(&soap_headers, Z_STR_P(value));
842 smart_str_appendc(&soap_headers, ';');
843 }
844 }
845 }
846 zend_hash_move_forward(Z_ARRVAL_P(cookies));
847 }
848 smart_str_append_const(&soap_headers, "\r\n");
849 }
850 }
851
852 http_context_headers(context, has_authorization, has_proxy_authorization, has_cookies, &soap_headers);
853
854 smart_str_append_const(&soap_headers, "\r\n");
855 smart_str_0(&soap_headers);
856 if ((trace = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "trace", sizeof("trace")-1)) != NULL &&
857 (Z_TYPE_P(trace) == IS_TRUE || (Z_TYPE_P(trace) == IS_LONG && Z_LVAL_P(trace) != 0))) {
858 add_property_stringl(this_ptr, "__last_request_headers", ZSTR_VAL(soap_headers.s), ZSTR_LEN(soap_headers.s));
859 }
860 smart_str_appendl(&soap_headers, request->val, request->len);
861 smart_str_0(&soap_headers);
862
863 err = php_stream_write(stream, ZSTR_VAL(soap_headers.s), ZSTR_LEN(soap_headers.s));
864 if (err != ZSTR_LEN(soap_headers.s)) {
865 if (request != buf) {
866 zend_string_release(request);
867 }
868 php_stream_close(stream);
869 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl")-1);
870 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
871 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
872 add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL);
873 smart_str_free(&soap_headers_z);
874 return FALSE;
875 }
876 smart_str_free(&soap_headers);
877 } else {
878 add_soap_fault(this_ptr, "HTTP", "Failed to create stream??", NULL, NULL);
879 smart_str_free(&soap_headers_z);
880 return FALSE;
881 }
882
883 if (!return_value) {
884 php_stream_close(stream);
885 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
886 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
887 smart_str_free(&soap_headers_z);
888 return TRUE;
889 }
890
891 do {
892 http_headers = get_http_headers(stream);
893 if (!http_headers) {
894 if (request != buf) {
895 zend_string_release(request);
896 }
897 php_stream_close(stream);
898 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
899 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
900 add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL);
901 smart_str_free(&soap_headers_z);
902 return FALSE;
903 }
904
905 if ((trace = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "trace", sizeof("trace")-1)) != NULL &&
906 (Z_TYPE_P(trace) == IS_TRUE || (Z_TYPE_P(trace) == IS_LONG && Z_LVAL_P(trace) != 0))) {
907 add_property_str(this_ptr, "__last_response_headers", zend_string_copy(http_headers));
908 }
909
910 /* Check to see what HTTP status was sent */
911 http_1_1 = 0;
912 http_status = 0;
913 http_version = get_http_header_value(ZSTR_VAL(http_headers), "HTTP/");
914 if (http_version) {
915 char *tmp;
916
917 if (!strncmp(http_version,"1.1", 3)) {
918 http_1_1 = 1;
919 }
920
921 tmp = strstr(http_version," ");
922 if (tmp != NULL) {
923 tmp++;
924 http_status = atoi(tmp);
925 }
926 tmp = strstr(tmp," ");
927 if (tmp != NULL) {
928 tmp++;
929 if (http_msg) {
930 efree(http_msg);
931 }
932 http_msg = estrdup(tmp);
933 }
934 efree(http_version);
935
936 /* Try and get headers again */
937 if (http_status == 100) {
938 zend_string_release(http_headers);
939 }
940 }
941 } while (http_status == 100);
942
943 /* Grab and send back every cookie */
944
945 /* Not going to worry about Path: because
946 we shouldn't be changing urls so path dont
947 matter too much
948 */
949 cookie_itt = strstr(ZSTR_VAL(http_headers), "Set-Cookie: ");
950 while (cookie_itt) {
951 char *cookie;
952 char *eqpos, *sempos;
953 zval *cookies;
954
955 if ((cookies = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies")-1)) == NULL ||
956 Z_TYPE_P(cookies) != IS_ARRAY) {
957 zval tmp_cookies;
958 array_init(&tmp_cookies);
959 cookies = zend_hash_str_update(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies")-1, &tmp_cookies);
960 }
961
962 cookie = get_http_header_value(cookie_itt,"Set-Cookie: ");
963
964 eqpos = strstr(cookie, "=");
965 sempos = strstr(cookie, ";");
966 if (eqpos != NULL && (sempos == NULL || sempos > eqpos)) {
967 smart_str name = {0};
968 int cookie_len;
969 zval zcookie;
970
971 if (sempos != NULL) {
972 cookie_len = sempos-(eqpos+1);
973 } else {
974 cookie_len = strlen(cookie)-(eqpos-cookie)-1;
975 }
976
977 smart_str_appendl(&name, cookie, eqpos - cookie);
978 smart_str_0(&name);
979
980 array_init(&zcookie);
981 add_index_stringl(&zcookie, 0, eqpos + 1, cookie_len);
982
983 if (sempos != NULL) {
984 char *options = cookie + cookie_len+1;
985 while (*options) {
986 while (*options == ' ') {options++;}
987 sempos = strstr(options, ";");
988 if (strstr(options,"path=") == options) {
989 eqpos = options + sizeof("path=")-1;
990 add_index_stringl(&zcookie, 1, eqpos, sempos?(sempos-eqpos):strlen(eqpos));
991 } else if (strstr(options,"domain=") == options) {
992 eqpos = options + sizeof("domain=")-1;
993 add_index_stringl(&zcookie, 2, eqpos, sempos?(sempos-eqpos):strlen(eqpos));
994 } else if (strstr(options,"secure") == options) {
995 add_index_bool(&zcookie, 3, 1);
996 }
997 if (sempos != NULL) {
998 options = sempos+1;
999 } else {
1000 break;
1001 }
1002 }
1003 }
1004 if (!zend_hash_index_exists(Z_ARRVAL(zcookie), 1)) {
1005 char *t = phpurl->path?phpurl->path:"/";
1006 char *c = strrchr(t, '/');
1007 if (c) {
1008 add_index_stringl(&zcookie, 1, t, c-t);
1009 }
1010 }
1011 if (!zend_hash_index_exists(Z_ARRVAL(zcookie), 2)) {
1012 add_index_string(&zcookie, 2, phpurl->host);
1013 }
1014
1015 zend_symtable_update(Z_ARRVAL_P(cookies), name.s, &zcookie);
1016 smart_str_free(&name);
1017 }
1018
1019 cookie_itt = strstr(cookie_itt + sizeof("Set-Cookie: "), "Set-Cookie: ");
1020 efree(cookie);
1021 }
1022
1023 /* See if the server requested a close */
1024 if (http_1_1) {
1025 http_close = FALSE;
1026 if (use_proxy && !use_ssl) {
1027 connection = get_http_header_value(ZSTR_VAL(http_headers), "Proxy-Connection: ");
1028 if (connection) {
1029 if (strncasecmp(connection, "close", sizeof("close")-1) == 0) {
1030 http_close = TRUE;
1031 }
1032 efree(connection);
1033 }
1034 }
1035 if (http_close == FALSE) {
1036 connection = get_http_header_value(ZSTR_VAL(http_headers), "Connection: ");
1037 if (connection) {
1038 if (strncasecmp(connection, "close", sizeof("close")-1) == 0) {
1039 http_close = TRUE;
1040 }
1041 efree(connection);
1042 }
1043 }
1044 } else {
1045 http_close = TRUE;
1046 if (use_proxy && !use_ssl) {
1047 connection = get_http_header_value(ZSTR_VAL(http_headers), "Proxy-Connection: ");
1048 if (connection) {
1049 if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
1050 http_close = FALSE;
1051 }
1052 efree(connection);
1053 }
1054 }
1055 if (http_close == TRUE) {
1056 connection = get_http_header_value(ZSTR_VAL(http_headers), "Connection: ");
1057 if (connection) {
1058 if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
1059 http_close = FALSE;
1060 }
1061 efree(connection);
1062 }
1063 }
1064 }
1065
1066
1067 http_body = get_http_body(stream, http_close, ZSTR_VAL(http_headers));
1068 if (!http_body) {
1069 if (request != buf) {
1070 zend_string_release(request);
1071 }
1072 php_stream_close(stream);
1073 zend_string_release(http_headers);
1074 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
1075 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
1076 add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL);
1077 if (http_msg) {
1078 efree(http_msg);
1079 }
1080 smart_str_free(&soap_headers_z);
1081 return FALSE;
1082 }
1083
1084 if (request != buf) {
1085 zend_string_release(request);
1086 }
1087
1088 if (http_close) {
1089 php_stream_close(stream);
1090 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
1091 zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
1092 stream = NULL;
1093 }
1094
1095 /* Process HTTP status codes */
1096 if (http_status >= 300 && http_status < 400) {
1097 char *loc;
1098
1099 if ((loc = get_http_header_value(ZSTR_VAL(http_headers), "Location: ")) != NULL) {
1100 php_url *new_url = php_url_parse(loc);
1101
1102 if (new_url != NULL) {
1103 zend_string_release(http_headers);
1104 zend_string_release(http_body);
1105 efree(loc);
1106 if (new_url->scheme == NULL && new_url->path != NULL) {
1107 new_url->scheme = phpurl->scheme ? estrdup(phpurl->scheme) : NULL;
1108 new_url->host = phpurl->host ? estrdup(phpurl->host) : NULL;
1109 new_url->port = phpurl->port;
1110 if (new_url->path && new_url->path[0] != '/') {
1111 if (phpurl->path) {
1112 char *t = phpurl->path;
1113 char *p = strrchr(t, '/');
1114 if (p) {
1115 char *s = emalloc((p - t) + strlen(new_url->path) + 2);
1116 strncpy(s, t, (p - t) + 1);
1117 s[(p - t) + 1] = 0;
1118 strcat(s, new_url->path);
1119 efree(new_url->path);
1120 new_url->path = s;
1121 }
1122 } else {
1123 char *s = emalloc(strlen(new_url->path) + 2);
1124 s[0] = '/'; s[1] = 0;
1125 strcat(s, new_url->path);
1126 efree(new_url->path);
1127 new_url->path = s;
1128 }
1129 }
1130 }
1131 phpurl = new_url;
1132
1133 if (--redirect_max < 1) {
1134 add_soap_fault(this_ptr, "HTTP", "Redirection limit reached, aborting", NULL, NULL);
1135 smart_str_free(&soap_headers_z);
1136 return FALSE;
1137 }
1138
1139 goto try_again;
1140 }
1141 }
1142 } else if (http_status == 401) {
1143 /* Digest authentication */
1144 zval *digest, *login, *password;
1145 char *auth = get_http_header_value(ZSTR_VAL(http_headers), "WWW-Authenticate: ");
1146
1147 if (auth &&
1148 strstr(auth, "Digest") == auth &&
1149 ((digest = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest")-1)) == NULL ||
1150 Z_TYPE_P(digest) != IS_ARRAY) &&
1151 (login = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login")-1)) != NULL &&
1152 Z_TYPE_P(login) == IS_STRING &&
1153 (password = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password")-1)) != NULL &&
1154 Z_TYPE_P(password) == IS_STRING) {
1155 char *s;
1156 zval digest;
1157
1158 ZVAL_UNDEF(&digest);
1159 s = auth + sizeof("Digest")-1;
1160 while (*s != '\0') {
1161 char *name, *val;
1162 while (*s == ' ') ++s;
1163 name = s;
1164 while (*s != '\0' && *s != '=') ++s;
1165 if (*s == '=') {
1166 *s = '\0';
1167 ++s;
1168 if (*s == '"') {
1169 ++s;
1170 val = s;
1171 while (*s != '\0' && *s != '"') ++s;
1172 } else {
1173 val = s;
1174 while (*s != '\0' && *s != ' ' && *s != ',') ++s;
1175 }
1176 if (*s != '\0') {
1177 if (*s != ',') {
1178 *s = '\0';
1179 ++s;
1180 while (*s != '\0' && *s != ',') ++s;
1181 if (*s != '\0') ++s;
1182 } else {
1183 *s = '\0';
1184 ++s;
1185 }
1186 }
1187 if (Z_TYPE(digest) == IS_UNDEF) {
1188 array_init(&digest);
1189 }
1190 add_assoc_string(&digest, name, val);
1191 }
1192 }
1193
1194 if (Z_TYPE(digest) != IS_UNDEF) {
1195 php_url *new_url = emalloc(sizeof(php_url));
1196
1197 Z_DELREF(digest);
1198 add_property_zval_ex(this_ptr, "_digest", sizeof("_digest")-1, &digest);
1199
1200 *new_url = *phpurl;
1201 if (phpurl->scheme) phpurl->scheme = estrdup(phpurl->scheme);
1202 if (phpurl->user) phpurl->user = estrdup(phpurl->user);
1203 if (phpurl->pass) phpurl->pass = estrdup(phpurl->pass);
1204 if (phpurl->host) phpurl->host = estrdup(phpurl->host);
1205 if (phpurl->path) phpurl->path = estrdup(phpurl->path);
1206 if (phpurl->query) phpurl->query = estrdup(phpurl->query);
1207 if (phpurl->fragment) phpurl->fragment = estrdup(phpurl->fragment);
1208 phpurl = new_url;
1209
1210 efree(auth);
1211 zend_string_release(http_headers);
1212 zend_string_release(http_body);
1213
1214 goto try_again;
1215 }
1216 }
1217 if (auth) efree(auth);
1218 }
1219 smart_str_free(&soap_headers_z);
1220
1221 /* Check and see if the server even sent a xml document */
1222 content_type = get_http_header_value(ZSTR_VAL(http_headers), "Content-Type: ");
1223 if (content_type) {
1224 char *pos = NULL;
1225 int cmplen;
1226 pos = strstr(content_type,";");
1227 if (pos != NULL) {
1228 cmplen = pos - content_type;
1229 } else {
1230 cmplen = strlen(content_type);
1231 }
1232 if (strncmp(content_type, "text/xml", cmplen) == 0 ||
1233 strncmp(content_type, "application/soap+xml", cmplen) == 0) {
1234 content_type_xml = 1;
1235 /*
1236 if (strncmp(http_body, "<?xml", 5)) {
1237 zval *err;
1238 MAKE_STD_ZVAL(err);
1239 ZVAL_STRINGL(err, http_body, http_body_size, 1);
1240 add_soap_fault(this_ptr, "HTTP", "Didn't receive an xml document", NULL, err);
1241 efree(content_type);
1242 zend_string_release(http_headers);
1243 efree(http_body);
1244 return FALSE;
1245 }
1246 */
1247 }
1248 efree(content_type);
1249 }
1250
1251 /* Decompress response */
1252 content_encoding = get_http_header_value(ZSTR_VAL(http_headers), "Content-Encoding: ");
1253 if (content_encoding) {
1254 zval func;
1255 zval retval;
1256 zval params[1];
1257
1258 if ((strcmp(content_encoding,"gzip") == 0 ||
1259 strcmp(content_encoding,"x-gzip") == 0) &&
1260 zend_hash_str_exists(EG(function_table), "gzinflate", sizeof("gzinflate")-1)) {
1261 ZVAL_STRING(&func, "gzinflate");
1262 ZVAL_STRINGL(¶ms[0], http_body->val+10, http_body->len-10);
1263 } else if (strcmp(content_encoding,"deflate") == 0 &&
1264 zend_hash_str_exists(EG(function_table), "gzuncompress", sizeof("gzuncompress")-1)) {
1265 ZVAL_STRING(&func, "gzuncompress");
1266 ZVAL_STR_COPY(¶ms[0], http_body);
1267 } else {
1268 efree(content_encoding);
1269 zend_string_release(http_headers);
1270 zend_string_release(http_body);
1271 if (http_msg) {
1272 efree(http_msg);
1273 }
1274 add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL);
1275 return FALSE;
1276 }
1277 if (call_user_function(CG(function_table), (zval*)NULL, &func, &retval, 1, params) == SUCCESS &&
1278 Z_TYPE(retval) == IS_STRING) {
1279 zval_ptr_dtor(¶ms[0]);
1280 zval_ptr_dtor(&func);
1281 zend_string_release(http_body);
1282 ZVAL_COPY_VALUE(return_value, &retval);
1283 } else {
1284 zval_ptr_dtor(¶ms[0]);
1285 zval_ptr_dtor(&func);
1286 efree(content_encoding);
1287 zend_string_release(http_headers);
1288 zend_string_release(http_body);
1289 add_soap_fault(this_ptr, "HTTP", "Can't uncompress compressed response", NULL, NULL);
1290 if (http_msg) {
1291 efree(http_msg);
1292 }
1293 return FALSE;
1294 }
1295 efree(content_encoding);
1296 } else {
1297 ZVAL_STR(return_value, http_body);
1298 }
1299
1300 zend_string_release(http_headers);
1301
1302 if (http_status >= 400) {
1303 int error = 0;
1304
1305 if (Z_STRLEN_P(return_value) == 0) {
1306 error = 1;
1307 } else if (Z_STRLEN_P(return_value) > 0) {
1308 if (!content_type_xml) {
1309 char *s = Z_STRVAL_P(return_value);
1310
1311 while (*s != '\0' && *s < ' ') {
1312 s++;
1313 }
1314 if (strncmp(s, "<?xml", 5)) {
1315 error = 1;
1316 }
1317 }
1318 }
1319
1320 if (error) {
1321 zval_ptr_dtor(return_value);
1322 ZVAL_UNDEF(return_value);
1323 add_soap_fault(this_ptr, "HTTP", http_msg, NULL, NULL);
1324 efree(http_msg);
1325 return FALSE;
1326 }
1327 }
1328
1329 if (http_msg) {
1330 efree(http_msg);
1331 }
1332
1333 return TRUE;
1334 }
1335
get_http_header_value(char * headers,char * type)1336 static char *get_http_header_value(char *headers, char *type)
1337 {
1338 char *pos, *tmp = NULL;
1339 int typelen, headerslen;
1340
1341 typelen = strlen(type);
1342 headerslen = strlen(headers);
1343
1344 /* header `titles' can be lower case, or any case combination, according
1345 * to the various RFC's. */
1346 pos = headers;
1347 do {
1348 /* start of buffer or start of line */
1349 if (strncasecmp(pos, type, typelen) == 0) {
1350 char *eol;
1351
1352 /* match */
1353 tmp = pos + typelen;
1354 eol = strchr(tmp, '\n');
1355 if (eol == NULL) {
1356 eol = headers + headerslen;
1357 } else if (eol > tmp && *(eol-1) == '\r') {
1358 eol--;
1359 }
1360 return estrndup(tmp, eol - tmp);
1361 }
1362
1363 /* find next line */
1364 pos = strchr(pos, '\n');
1365 if (pos) {
1366 pos++;
1367 }
1368
1369 } while (pos);
1370
1371 return NULL;
1372 }
1373
get_http_body(php_stream * stream,int close,char * headers)1374 static zend_string* get_http_body(php_stream *stream, int close, char *headers)
1375 {
1376 zend_string *http_buf = NULL;
1377 char *header;
1378 int header_close = close, header_chunked = 0, header_length = 0, http_buf_size = 0;
1379
1380 if (!close) {
1381 header = get_http_header_value(headers, "Connection: ");
1382 if (header) {
1383 if(!strncasecmp(header, "close", sizeof("close")-1)) header_close = 1;
1384 efree(header);
1385 }
1386 }
1387 header = get_http_header_value(headers, "Transfer-Encoding: ");
1388 if (header) {
1389 if(!strncasecmp(header, "chunked", sizeof("chunked")-1)) header_chunked = 1;
1390 efree(header);
1391 }
1392 header = get_http_header_value(headers, "Content-Length: ");
1393 if (header) {
1394 header_length = atoi(header);
1395 efree(header);
1396 if (!header_length && !header_chunked) {
1397 /* Empty response */
1398 return ZSTR_EMPTY_ALLOC();
1399 }
1400 }
1401
1402 if (header_chunked) {
1403 char ch, done, headerbuf[8192];
1404
1405 done = FALSE;
1406
1407 while (!done) {
1408 int buf_size = 0;
1409
1410 php_stream_gets(stream, headerbuf, sizeof(headerbuf));
1411 if (sscanf(headerbuf, "%x", &buf_size) > 0 ) {
1412 if (buf_size > 0) {
1413 int len_size = 0;
1414
1415 if (http_buf_size + buf_size + 1 < 0) {
1416 if (http_buf) {
1417 zend_string_release(http_buf);
1418 }
1419 return NULL;
1420 }
1421
1422 if (http_buf) {
1423 http_buf = zend_string_realloc(http_buf, http_buf_size + buf_size, 0);
1424 } else {
1425 http_buf = zend_string_alloc(buf_size, 0);
1426 }
1427
1428 while (len_size < buf_size) {
1429 int len_read = php_stream_read(stream, http_buf->val + http_buf_size, buf_size - len_size);
1430 if (len_read <= 0) {
1431 /* Error or EOF */
1432 done = TRUE;
1433 break;
1434 }
1435 len_size += len_read;
1436 http_buf_size += len_read;
1437 }
1438
1439 /* Eat up '\r' '\n' */
1440 ch = php_stream_getc(stream);
1441 if (ch == '\r') {
1442 ch = php_stream_getc(stream);
1443 }
1444 if (ch != '\n') {
1445 /* Somthing wrong in chunked encoding */
1446 if (http_buf) {
1447 zend_string_release(http_buf);
1448 }
1449 return NULL;
1450 }
1451 }
1452 } else {
1453 /* Somthing wrong in chunked encoding */
1454 if (http_buf) {
1455 zend_string_release(http_buf);
1456 }
1457 return NULL;
1458 }
1459 if (buf_size == 0) {
1460 done = TRUE;
1461 }
1462 }
1463
1464 /* Ignore trailer headers */
1465 while (1) {
1466 if (!php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
1467 break;
1468 }
1469
1470 if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
1471 (headerbuf[0] == '\n')) {
1472 /* empty line marks end of headers */
1473 break;
1474 }
1475 }
1476
1477 if (http_buf == NULL) {
1478 return ZSTR_EMPTY_ALLOC();
1479 }
1480
1481 } else if (header_length) {
1482 if (header_length < 0 || header_length >= INT_MAX) {
1483 return NULL;
1484 }
1485 http_buf = zend_string_alloc(header_length, 0);
1486 while (http_buf_size < header_length) {
1487 int len_read = php_stream_read(stream, http_buf->val + http_buf_size, header_length - http_buf_size);
1488 if (len_read <= 0) {
1489 break;
1490 }
1491 http_buf_size += len_read;
1492 }
1493 } else if (header_close) {
1494 do {
1495 int len_read;
1496 if (http_buf) {
1497 http_buf = zend_string_realloc(http_buf, http_buf_size + 4096, 0);
1498 } else {
1499 http_buf = zend_string_alloc(4096, 0);
1500 }
1501 len_read = php_stream_read(stream, http_buf->val + http_buf_size, 4096);
1502 if (len_read > 0) {
1503 http_buf_size += len_read;
1504 }
1505 } while(!php_stream_eof(stream));
1506 } else {
1507 return NULL;
1508 }
1509
1510 http_buf->val[http_buf_size] = '\0';
1511 http_buf->len = http_buf_size;
1512 return http_buf;
1513 }
1514
get_http_headers(php_stream * stream)1515 static zend_string *get_http_headers(php_stream *stream)
1516 {
1517 smart_str tmp_response = {0};
1518 char headerbuf[8192];
1519
1520 while (php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
1521 if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') ||
1522 (headerbuf[0] == '\n')) {
1523 /* empty line marks end of headers */
1524 smart_str_0(&tmp_response);
1525 return tmp_response.s;
1526 }
1527
1528 /* add header to collection */
1529 smart_str_appends(&tmp_response, headerbuf);
1530 }
1531
1532 smart_str_free(&tmp_response);
1533 return NULL;
1534 }
1535 /*
1536 * Local variables:
1537 * tab-width: 4
1538 * c-basic-offset: 4
1539 * End:
1540 * vim600: sw=4 ts=4 fdm=marker
1541 * vim<600: sw=4 ts=4
1542 */
1543