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