2
+----------------------------------------------------------------------+
4
+----------------------------------------------------------------------+
5
| Copyright (c) 1997-2004 The PHP Group |
6
+----------------------------------------------------------------------+
7
| This source file is subject to version 3.0 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_0.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
+----------------------------------------------------------------------+
20
/* $Id: php_http.c,v 1.55.2.19 2005/07/08 09:36:42 dmitry Exp $ */
23
#include "ext/standard/base64.h"
24
#include "ext/standard/md5.h"
26
static char *get_http_header_value(char *headers, char *type);
27
static int get_http_body(php_stream *socketd, int close, char *headers, char **response, int *out_size TSRMLS_DC);
28
static int get_http_headers(php_stream *socketd,char **response, int *out_size TSRMLS_DC);
30
#define smart_str_append_const(str, const) \
31
smart_str_appendl(str,const,sizeof(const)-1)
33
static int stream_alive(php_stream *stream TSRMLS_DC)
38
/* maybe better to use:
39
* php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL)
42
if (stream == NULL || stream->eof || php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT, (void**)&socket, 0) != SUCCESS) {
48
if (php_pollfd_for_ms(socket, PHP_POLLREADABLE, 0) > 0) {
49
if (0 == recv(socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) {
57
/* Proxy HTTP Authentication */
58
void proxy_authentication(zval* this_ptr, smart_str* soap_headers TSRMLS_DC)
60
zval **login, **password;
62
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_proxy_login", sizeof("_proxy_login"), (void **)&login) == SUCCESS) {
67
smart_str_appendl(&auth, Z_STRVAL_PP(login), Z_STRLEN_PP(login));
68
smart_str_appendc(&auth, ':');
69
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_proxy_password", sizeof("_proxy_password"), (void **)&password) == SUCCESS) {
70
smart_str_appendl(&auth, Z_STRVAL_PP(password), Z_STRLEN_PP(password));
73
buf = php_base64_encode(auth.c, auth.len, &len);
74
smart_str_append_const(soap_headers, "Proxy-Authorization: Basic ");
75
smart_str_appendl(soap_headers, buf, len);
76
smart_str_append_const(soap_headers, "\r\n");
78
smart_str_free(&auth);
82
/* HTTP Authentication */
83
void basic_authentication(zval* this_ptr, smart_str* soap_headers TSRMLS_DC)
85
zval **login, **password;
87
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login"), (void **)&login) == SUCCESS &&
88
!zend_hash_exists(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest"))) {
93
smart_str_appendl(&auth, Z_STRVAL_PP(login), Z_STRLEN_PP(login));
94
smart_str_appendc(&auth, ':');
95
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password"), (void **)&password) == SUCCESS) {
96
smart_str_appendl(&auth, Z_STRVAL_PP(password), Z_STRLEN_PP(password));
99
buf = php_base64_encode(auth.c, auth.len, &len);
100
smart_str_append_const(soap_headers, "Authorization: Basic ");
101
smart_str_appendl(soap_headers, buf, len);
102
smart_str_append_const(soap_headers, "\r\n");
104
smart_str_free(&auth);
108
static php_stream* http_connect(zval* this_ptr, php_url *phpurl, int use_ssl, int *use_proxy TSRMLS_DC)
111
zval **proxy_host, **proxy_port, **tmp;
114
php_stream_context *context = NULL;
119
int old_error_reporting;
121
struct timeval *timeout = NULL;
123
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_proxy_host", sizeof("_proxy_host"), (void **) &proxy_host) == SUCCESS &&
124
Z_TYPE_PP(proxy_host) == IS_STRING &&
125
zend_hash_find(Z_OBJPROP_P(this_ptr), "_proxy_port", sizeof("_proxy_port"), (void **) &proxy_port) == SUCCESS &&
126
Z_TYPE_PP(proxy_port) == IS_LONG) {
127
host = Z_STRVAL_PP(proxy_host);
128
port = Z_LVAL_PP(proxy_port);
134
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_connection_timeout", sizeof("_connection_timeout"), (void **) &tmp) == SUCCESS &&
135
Z_TYPE_PP(tmp) == IS_LONG && Z_LVAL_PP(tmp) > 0) {
136
tv.tv_sec = Z_LVAL_PP(tmp);
141
old_error_reporting = EG(error_reporting);
142
EG(error_reporting) &= ~(E_WARNING|E_NOTICE|E_USER_WARNING|E_USER_NOTICE);
145
namelen = spprintf(&name, 0, "%s://%s:%d", (use_ssl && !*use_proxy)? "ssl" : "tcp", host, port);
149
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_local_cert", sizeof("_local_cert"), (void **) &tmp) == SUCCESS &&
150
Z_TYPE_PP(tmp) == IS_STRING) {
151
context = php_stream_context_alloc();
152
php_stream_context_set_option(context, "ssl", "local_cert", *tmp);
153
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_passphrase", sizeof("_passphrase"), (void **) &tmp) == SUCCESS &&
154
Z_TYPE_PP(tmp) == IS_STRING) {
155
php_stream_context_set_option(context, "ssl", "passphrase", *tmp);
159
stream = php_stream_xport_create(name, namelen,
160
ENFORCE_SAFE_MODE | REPORT_ERRORS,
161
STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
162
NULL /*persistent_id*/,
168
stream = php_stream_sock_open_host(host, port, SOCK_STREAM, timeout, NULL);
172
if (stream && *use_proxy && use_ssl) {
173
smart_str soap_headers = {0};
175
int http_header_size;
177
smart_str_append_const(&soap_headers, "CONNECT ");
178
smart_str_appends(&soap_headers, phpurl->host);
179
smart_str_appendc(&soap_headers, ':');
180
smart_str_append_unsigned(&soap_headers, phpurl->port);
181
smart_str_append_const(&soap_headers, " HTTP/1.1\r\n");
182
proxy_authentication(this_ptr, &soap_headers TSRMLS_CC);
183
smart_str_append_const(&soap_headers, "\r\n");
184
if (php_stream_write(stream, soap_headers.c, soap_headers.len) != soap_headers.len) {
185
php_stream_close(stream);
188
smart_str_free(&soap_headers);
191
if (!get_http_headers(stream, &http_headers, &http_header_size TSRMLS_CC) || http_headers == NULL) {
192
php_stream_close(stream);
198
/* enable SSL transport layer */
200
if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0 ||
201
php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
202
php_stream_close(stream);
209
#if !defined(ZEND_ENGINE_2) && defined(HAVE_OPENSSL_EXT)
210
if (stream && use_ssl) {
211
/* enable SSL transport layer */
212
if (FAILURE == php_stream_sock_ssl_activate(stream, 1)) {
213
php_stream_close(stream);
218
EG(error_reporting) = old_error_reporting;
222
static int in_domain(const char *host, const char *domain)
224
if (domain[0] == '.') {
225
int l1 = strlen(host);
226
int l2 = strlen(domain);
228
return strcmp(host+l1-l2,domain) == 0;
233
return strcmp(host,domain) == 0;
237
int make_http_soap_request(zval *this_ptr,
244
int *buffer_len TSRMLS_DC)
247
smart_str soap_headers = {0};
248
int request_size, err;
249
php_url *phpurl = NULL;
254
char *http_headers, *http_body, *content_type, *http_version, *cookie_itt;
255
int http_header_size, http_body_size, http_close;
259
int content_type_xml = 0;
260
char *content_encoding;
262
if (this_ptr == NULL || Z_TYPE_P(this_ptr) != IS_OBJECT) {
267
request_size = buf_size;
268
/* Compress request */
269
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "compression", sizeof("compression"), (void **)&tmp) == SUCCESS && Z_TYPE_PP(tmp) == IS_LONG) {
270
int level = Z_LVAL_PP(tmp) & 0x0f;
271
int kind = Z_LVAL_PP(tmp) & SOAP_COMPRESSION_DEFLATE;
273
if ((Z_LVAL_PP(tmp) & SOAP_COMPRESSION_ACCEPT) != 0) {
274
smart_str_append_const(&soap_headers,"Accept-Encoding: gzip, deflate\r\n");
279
zval param1, param2, param3;
284
INIT_PZVAL(params[0]);
286
INIT_PZVAL(params[1]);
288
INIT_PZVAL(params[2]);
289
ZVAL_STRINGL(params[0], buf, buf_size, 0);
290
ZVAL_LONG(params[1], level);
291
if (kind == SOAP_COMPRESSION_DEFLATE) {
293
ZVAL_STRING(&func, "gzcompress", 0);
294
smart_str_append_const(&soap_headers,"Content-Encoding: deflate\r\n");
297
ZVAL_STRING(&func, "gzencode", 0);
298
smart_str_append_const(&soap_headers,"Content-Encoding: gzip\r\n");
299
ZVAL_LONG(params[2], 1);
301
if (call_user_function(CG(function_table), (zval**)NULL, &func, &retval, n, params TSRMLS_CC) == SUCCESS &&
302
Z_TYPE(retval) == IS_STRING) {
303
request = Z_STRVAL(retval);
304
request_size = Z_STRLEN(retval);
306
if (request != buf) {efree(request);}
312
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"), (void **)&tmp) == SUCCESS) {
313
php_stream_from_zval_no_verify(stream,tmp);
314
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"), (void **)&tmp) == SUCCESS && Z_TYPE_PP(tmp) == IS_LONG) {
315
use_proxy = Z_LVAL_PP(tmp);
321
if (location != NULL && location[0] != '\000') {
322
phpurl = php_url_parse(location);
326
if (phpurl == NULL || phpurl->host == NULL) {
327
if (phpurl != NULL) {php_url_free(phpurl);}
328
if (request != buf) {efree(request);}
329
add_soap_fault(this_ptr, "HTTP", "Unable to parse URL", NULL, NULL TSRMLS_CC);
334
if (phpurl->scheme != NULL && strcmp(phpurl->scheme, "https") == 0) {
336
} else if (phpurl->scheme == NULL || strcmp(phpurl->scheme, "http") != 0) {
337
php_url_free(phpurl);
338
if (request != buf) {efree(request);}
339
add_soap_fault(this_ptr, "HTTP", "Unknown protocol. Only http and https are allowed.", NULL, NULL TSRMLS_CC);
343
if (use_ssl && php_stream_locate_url_wrapper("https://", NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC) == NULL) {
344
php_url_free(phpurl);
345
if (request != buf) {efree(request);}
346
add_soap_fault(this_ptr, "HTTP", "SSL support not available in this build", NULL, NULL TSRMLS_CC);
350
#ifndef HAVE_OPENSSL_EXT
352
php_url_free(phpurl);
353
if (request != buf) {efree(request);}
354
add_soap_fault(this_ptr, "HTTP", "SSL support not available in this build", NULL, NULL TSRMLS_CC);
360
if (phpurl->port == 0) {
361
phpurl->port = use_ssl ? 443 : 80;
364
/* Check if request to the same host */
365
if (stream != NULL) {
367
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl"), (void **)&tmp) == SUCCESS &&
368
(orig = (php_url *) zend_fetch_resource(tmp TSRMLS_CC, -1, "httpurl", NULL, 1, le_url)) != NULL &&
369
((use_proxy && !use_ssl) ||
370
(((use_ssl && orig->scheme != NULL && strcmp(orig->scheme, "https") == 0) ||
371
(!use_ssl && orig->scheme == NULL) ||
372
(!use_ssl && strcmp(orig->scheme, "https") != 0)) &&
373
strcmp(orig->host, phpurl->host) == 0 &&
374
orig->port == phpurl->port))) {
376
php_stream_close(stream);
377
zend_hash_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl"));
378
zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
379
zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
385
/* Check if keep-alive connection is still opened */
386
if (stream != NULL && !stream_alive(stream TSRMLS_CC)) {
387
php_stream_close(stream);
388
zend_hash_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl"));
389
zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
390
zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
396
stream = http_connect(this_ptr, phpurl, use_ssl, &use_proxy TSRMLS_CC);
398
php_stream_auto_cleanup(stream);
399
add_property_resource(this_ptr, "httpsocket", php_stream_get_resource_id(stream));
400
add_property_long(this_ptr, "_use_proxy", use_proxy);
402
php_url_free(phpurl);
403
if (request != buf) {efree(request);}
404
add_soap_fault(this_ptr, "HTTP", "Could not connect to host", NULL, NULL TSRMLS_CC);
410
zval **cookies, **login, **password;
411
int ret = zend_list_insert(phpurl, le_url);
413
add_property_resource(this_ptr, "httpurl", ret);
414
/*zend_list_addref(ret);*/
416
smart_str_append_const(&soap_headers, "POST ");
417
if (use_proxy && !use_ssl) {
418
smart_str_appends(&soap_headers, phpurl->scheme);
419
smart_str_append_const(&soap_headers, "://");
420
smart_str_appends(&soap_headers, phpurl->host);
421
smart_str_appendc(&soap_headers, ':');
422
smart_str_append_unsigned(&soap_headers, phpurl->port);
425
smart_str_appends(&soap_headers, phpurl->path);
428
smart_str_appendc(&soap_headers, '?');
429
smart_str_appends(&soap_headers, phpurl->query);
431
smart_str_append_const(&soap_headers, " HTTP/1.1\r\n"
433
smart_str_appends(&soap_headers, phpurl->host);
434
if (phpurl->port != (use_ssl?443:80)) {
435
smart_str_appendc(&soap_headers, ':');
436
smart_str_append_unsigned(&soap_headers, phpurl->port);
438
smart_str_append_const(&soap_headers, "\r\n"
439
"Connection: Keep-Alive\r\n"
441
"Connection: close\r\n"
442
"Accept: text/html; text/xml; text/plain\r\n"
444
"User-Agent: PHP SOAP 0.1\r\n");
445
if (soap_version == SOAP_1_2) {
446
smart_str_append_const(&soap_headers,"Content-Type: application/soap+xml; charset=utf-8");
448
smart_str_append_const(&soap_headers,"; action=\"");
449
smart_str_appends(&soap_headers, soapaction);
450
smart_str_append_const(&soap_headers,"\"");
452
smart_str_append_const(&soap_headers,"\r\n");
454
smart_str_append_const(&soap_headers,"Content-Type: text/xml; charset=utf-8\r\n");
456
smart_str_append_const(&soap_headers, "SOAPAction: \"");
457
smart_str_appends(&soap_headers, soapaction);
458
smart_str_append_const(&soap_headers, "\"\r\n");
461
smart_str_append_const(&soap_headers,"Content-Length: ");
462
smart_str_append_long(&soap_headers, request_size);
463
smart_str_append_const(&soap_headers, "\r\n");
465
/* HTTP Authentication */
466
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login"), (void **)&login) == SUCCESS &&
467
Z_TYPE_PP(login) == IS_STRING) {
470
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest"), (void **)&digest) == SUCCESS) {
471
if (Z_TYPE_PP(digest) == IS_ARRAY) {
472
char HA1[33], HA2[33], response[33], cnonce[33], nc[9];
474
unsigned char hash[16];
476
PHP_MD5Init(&md5ctx);
477
sprintf(cnonce, "%d", rand());
478
PHP_MD5Update(&md5ctx, cnonce, strlen(cnonce));
479
PHP_MD5Final(hash, &md5ctx);
480
make_digest(cnonce, hash);
482
if (zend_hash_find(Z_ARRVAL_PP(digest), "nc", sizeof("nc"), (void **)&tmp) == SUCCESS &&
483
Z_TYPE_PP(tmp) == IS_LONG) {
485
sprintf(nc, "%08ld", Z_LVAL_PP(tmp));
487
add_assoc_long(*digest, "nc", 1);
488
strcpy(nc, "00000001");
491
PHP_MD5Init(&md5ctx);
492
PHP_MD5Update(&md5ctx, Z_STRVAL_PP(login), Z_STRLEN_PP(login));
493
PHP_MD5Update(&md5ctx, ":", 1);
494
if (zend_hash_find(Z_ARRVAL_PP(digest), "realm", sizeof("realm"), (void **)&tmp) == SUCCESS &&
495
Z_TYPE_PP(tmp) == IS_STRING) {
496
PHP_MD5Update(&md5ctx, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
498
PHP_MD5Update(&md5ctx, ":", 1);
499
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password"), (void **)&password) == SUCCESS &&
500
Z_TYPE_PP(password) == IS_STRING) {
501
PHP_MD5Update(&md5ctx, Z_STRVAL_PP(password), Z_STRLEN_PP(password));
503
PHP_MD5Final(hash, &md5ctx);
504
make_digest(HA1, hash);
505
if (zend_hash_find(Z_ARRVAL_PP(digest), "algorithm", sizeof("algorithm"), (void **)&tmp) == SUCCESS &&
506
Z_TYPE_PP(tmp) == IS_STRING &&
507
Z_STRLEN_PP(tmp) == sizeof("md5-sess")-1 &&
508
stricmp(Z_STRVAL_PP(tmp), "md5-sess") == 0) {
509
PHP_MD5Init(&md5ctx);
510
PHP_MD5Update(&md5ctx, HA1, 32);
511
PHP_MD5Update(&md5ctx, ":", 1);
512
if (zend_hash_find(Z_ARRVAL_PP(digest), "nonce", sizeof("nonce"), (void **)&tmp) == SUCCESS &&
513
Z_TYPE_PP(tmp) == IS_STRING) {
514
PHP_MD5Update(&md5ctx, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
516
PHP_MD5Update(&md5ctx, ":", 1);
517
PHP_MD5Update(&md5ctx, cnonce, 8);
518
PHP_MD5Final(hash, &md5ctx);
519
make_digest(HA1, hash);
522
PHP_MD5Init(&md5ctx);
523
PHP_MD5Update(&md5ctx, "POST:", sizeof("POST:")-1);
525
PHP_MD5Update(&md5ctx, phpurl->path, strlen(phpurl->path));
528
PHP_MD5Update(&md5ctx, "?", 1);
529
PHP_MD5Update(&md5ctx, phpurl->query, strlen(phpurl->query));
532
/* TODO: Support for qop="auth-int" */
534
if (zend_hash_find(Z_ARRVAL_PP(digest), "qop", sizeof("qop"), (void **)&tmp) == SUCCESS &&
535
Z_TYPE_PP(tmp) == IS_STRING &&
536
Z_STRLEN_PP(tmp) == sizeof("auth-int")-1 &&
537
stricmp(Z_STRVAL_PP(tmp), "auth-int") == 0) {
538
PHP_MD5Update(&md5ctx, ":", 1);
539
PHP_MD5Update(&md5ctx, HEntity, HASHHEXLEN);
542
PHP_MD5Final(hash, &md5ctx);
543
make_digest(HA2, hash);
545
PHP_MD5Init(&md5ctx);
546
PHP_MD5Update(&md5ctx, HA1, 32);
547
PHP_MD5Update(&md5ctx, ":", 1);
548
if (zend_hash_find(Z_ARRVAL_PP(digest), "nonce", sizeof("nonce"), (void **)&tmp) == SUCCESS &&
549
Z_TYPE_PP(tmp) == IS_STRING) {
550
PHP_MD5Update(&md5ctx, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
552
PHP_MD5Update(&md5ctx, ":", 1);
553
if (zend_hash_find(Z_ARRVAL_PP(digest), "qop", sizeof("qop"), (void **)&tmp) == SUCCESS &&
554
Z_TYPE_PP(tmp) == IS_STRING) {
555
PHP_MD5Update(&md5ctx, nc, 8);
556
PHP_MD5Update(&md5ctx, ":", 1);
557
PHP_MD5Update(&md5ctx, cnonce, 8);
558
PHP_MD5Update(&md5ctx, ":", 1);
559
/* TODO: Support for qop="auth-int" */
560
PHP_MD5Update(&md5ctx, "auth", sizeof("auth")-1);
561
PHP_MD5Update(&md5ctx, ":", 1);
563
PHP_MD5Update(&md5ctx, HA2, 32);
564
PHP_MD5Final(hash, &md5ctx);
565
make_digest(response, hash);
567
smart_str_append_const(&soap_headers, "Authorization: Digest username=\"");
568
smart_str_appendl(&soap_headers, Z_STRVAL_PP(login), Z_STRLEN_PP(login));
569
if (zend_hash_find(Z_ARRVAL_PP(digest), "realm", sizeof("realm"), (void **)&tmp) == SUCCESS &&
570
Z_TYPE_PP(tmp) == IS_STRING) {
571
smart_str_append_const(&soap_headers, "\", realm=\"");
572
smart_str_appendl(&soap_headers, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
574
if (zend_hash_find(Z_ARRVAL_PP(digest), "nonce", sizeof("nonce"), (void **)&tmp) == SUCCESS &&
575
Z_TYPE_PP(tmp) == IS_STRING) {
576
smart_str_append_const(&soap_headers, "\", nonce=\"");
577
smart_str_appendl(&soap_headers, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
579
smart_str_append_const(&soap_headers, "\", uri=\"");
581
smart_str_appends(&soap_headers, phpurl->path);
584
smart_str_appendc(&soap_headers, '?');
585
smart_str_appends(&soap_headers, phpurl->query);
587
if (zend_hash_find(Z_ARRVAL_PP(digest), "qop", sizeof("qop"), (void **)&tmp) == SUCCESS &&
588
Z_TYPE_PP(tmp) == IS_STRING) {
589
/* TODO: Support for qop="auth-int" */
590
smart_str_append_const(&soap_headers, "\", qop=\"auth");
591
smart_str_append_const(&soap_headers, "\", nc=\"");
592
smart_str_appendl(&soap_headers, nc, 8);
593
smart_str_append_const(&soap_headers, "\", cnonce=\"");
594
smart_str_appendl(&soap_headers, cnonce, 8);
596
smart_str_append_const(&soap_headers, "\", response=\"");
597
smart_str_appendl(&soap_headers, response, 32);
598
if (zend_hash_find(Z_ARRVAL_PP(digest), "opaque", sizeof("opaque"), (void **)&tmp) == SUCCESS &&
599
Z_TYPE_PP(tmp) == IS_STRING) {
600
smart_str_append_const(&soap_headers, "\", opaque=\"");
601
smart_str_appendl(&soap_headers, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
603
smart_str_append_const(&soap_headers, "\"\r\n");
609
smart_str auth = {0};
610
smart_str_appendl(&auth, Z_STRVAL_PP(login), Z_STRLEN_PP(login));
611
smart_str_appendc(&auth, ':');
612
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password"), (void **)&password) == SUCCESS &&
613
Z_TYPE_PP(password) == IS_STRING) {
614
smart_str_appendl(&auth, Z_STRVAL_PP(password), Z_STRLEN_PP(password));
617
buf = php_base64_encode(auth.c, auth.len, &len);
618
smart_str_append_const(&soap_headers, "Authorization: Basic ");
619
smart_str_appendl(&soap_headers, buf, len);
620
smart_str_append_const(&soap_headers, "\r\n");
622
smart_str_free(&auth);
626
/* Proxy HTTP Authentication */
627
if (use_proxy && !use_ssl) {
628
proxy_authentication(this_ptr, &soap_headers TSRMLS_CC);
631
/* Send cookies along with request */
632
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies"), (void **)&cookies) == SUCCESS) {
637
n = zend_hash_num_elements(Z_ARRVAL_PP(cookies));
639
zend_hash_internal_pointer_reset(Z_ARRVAL_PP(cookies));
640
smart_str_append_const(&soap_headers, "Cookie: ");
641
for (i = 0; i < n; i++) {
642
zend_hash_get_current_data(Z_ARRVAL_PP(cookies), (void **)&data);
643
zend_hash_get_current_key(Z_ARRVAL_PP(cookies), &key, NULL, FALSE);
645
if (Z_TYPE_PP(data) == IS_ARRAY) {
648
if (zend_hash_index_find(Z_ARRVAL_PP(data), 0, (void**)&value) == SUCCESS &&
649
Z_TYPE_PP(value) == IS_STRING) {
651
if ((zend_hash_index_find(Z_ARRVAL_PP(data), 1, (void**)&tmp) == FAILURE ||
652
strncmp(phpurl->path?phpurl->path:"/",Z_STRVAL_PP(tmp),Z_STRLEN_PP(tmp)) == 0) &&
653
(zend_hash_index_find(Z_ARRVAL_PP(data), 2, (void**)&tmp) == FAILURE ||
654
in_domain(phpurl->host,Z_STRVAL_PP(tmp))) &&
655
(use_ssl || zend_hash_index_find(Z_ARRVAL_PP(data), 3, (void**)&tmp) == FAILURE)) {
656
smart_str_appendl(&soap_headers, key, strlen(key));
657
smart_str_appendc(&soap_headers, '=');
658
smart_str_appendl(&soap_headers, Z_STRVAL_PP(value), Z_STRLEN_PP(value));
659
smart_str_appendc(&soap_headers, ';');
663
zend_hash_move_forward(Z_ARRVAL_PP(cookies));
665
smart_str_append_const(&soap_headers, "\r\n");
668
smart_str_append_const(&soap_headers, "\r\n");
669
smart_str_0(&soap_headers);
670
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "trace", sizeof("trace"), (void **) &trace) == SUCCESS &&
671
Z_LVAL_PP(trace) > 0) {
672
add_property_stringl(this_ptr, "__last_request_headers", soap_headers.c, soap_headers.len, 1);
674
smart_str_appendl(&soap_headers, request, request_size);
675
smart_str_0(&soap_headers);
677
err = php_stream_write(stream, soap_headers.c, soap_headers.len);
678
if (err != soap_headers.len) {
679
if (request != buf) {efree(request);}
680
smart_str_free(&soap_headers);
681
php_stream_close(stream);
682
zend_hash_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl"));
683
zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
684
zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
685
add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL TSRMLS_CC);
688
smart_str_free(&soap_headers);
693
if (!get_http_headers(stream, &http_headers, &http_header_size TSRMLS_CC)) {
694
if (http_headers) {efree(http_headers);}
695
if (request != buf) {efree(request);}
696
php_stream_close(stream);
697
zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
698
zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
699
add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL TSRMLS_CC);
703
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "trace", sizeof("trace"), (void **) &trace) == SUCCESS &&
704
Z_LVAL_PP(trace) > 0) {
705
add_property_stringl(this_ptr, "__last_response_headers", http_headers, http_header_size, 1);
708
/* Check to see what HTTP status was sent */
711
http_version = get_http_header_value(http_headers,"HTTP/");
715
if (!strncmp(http_version,"1.1", 3)) {
719
tmp = strstr(http_version," ");
722
http_status = atoi(tmp);
726
/* Try and get headers again */
727
if (http_status == 100) {
731
} while (http_status == 100);
733
/* Grab and send back every cookie */
735
/* Not going to worry about Path: because
736
we shouldn't be changing urls so path dont
739
cookie_itt = strstr(http_headers,"Set-Cookie: ");
741
char *end_pos, *cookie;
742
char *eqpos, *sempos;
745
if (zend_hash_find(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies"), (void **)&cookies) == FAILURE) {
747
MAKE_STD_ZVAL(tmp_cookies);
748
array_init(tmp_cookies);
749
zend_hash_update(Z_OBJPROP_P(this_ptr), "_cookies", sizeof("_cookies"), &tmp_cookies, sizeof(zval *), (void **)&cookies);
752
end_pos = strstr(cookie_itt,"\r\n");
753
cookie = get_http_header_value(cookie_itt,"Set-Cookie: ");
755
eqpos = strstr(cookie, "=");
756
sempos = strstr(cookie, ";");
757
if (eqpos != NULL && (sempos == NULL || sempos > eqpos)) {
758
smart_str name = {0};
762
if (sempos != NULL) {
763
cookie_len = sempos-(eqpos+1);
765
cookie_len = strlen(cookie)-(eqpos-cookie)-1;
768
smart_str_appendl(&name, cookie, eqpos - cookie);
771
ALLOC_INIT_ZVAL(zcookie);
773
add_index_stringl(zcookie, 0, eqpos + 1, cookie_len, 1);
775
if (sempos != NULL) {
776
char *options = cookie + cookie_len+1;
778
while (*options == ' ') {options++;}
779
sempos = strstr(options, ";");
780
if (strstr(options,"path=") == options) {
781
eqpos = options + sizeof("path=")-1;
782
add_index_stringl(zcookie, 1, eqpos, sempos?(sempos-eqpos):strlen(eqpos), 1);
783
} else if (strstr(options,"domain=") == options) {
784
eqpos = options + sizeof("domain=")-1;
785
add_index_stringl(zcookie, 2, eqpos, sempos?(sempos-eqpos):strlen(eqpos), 1);
786
} else if (strstr(options,"secure") == options) {
787
add_index_bool(zcookie, 3, 1);
789
if (sempos != NULL) {
796
if (!zend_hash_index_exists(Z_ARRVAL_P(zcookie), 1)) {
797
char *t = phpurl->path?phpurl->path:"/";
798
char *c = strrchr(t, '/');
800
add_index_stringl(zcookie, 1, t, c-t, 1);
803
if (!zend_hash_index_exists(Z_ARRVAL_P(zcookie), 2)) {
804
add_index_string(zcookie, 2, phpurl->host, 1);
807
add_assoc_zval_ex(*cookies, name.c, name.len+1, zcookie);
808
smart_str_free(&name);
811
cookie_itt = strstr(cookie_itt + sizeof("Set-Cookie: "), "Set-Cookie: ");
815
if (!get_http_body(stream, !http_1_1, http_headers, &http_body, &http_body_size TSRMLS_CC)) {
816
if (request != buf) {efree(request);}
817
php_stream_close(stream);
819
zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
820
zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
821
add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL TSRMLS_CC);
825
if (request != buf) {efree(request);}
827
/* See if the server requested a close */
829
connection = get_http_header_value(http_headers,"Proxy-Connection: ");
831
if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
836
} else if (http_1_1) {
840
connection = get_http_header_value(http_headers,"Connection: ");
842
if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) {
847
} else if (http_1_1) {
853
php_stream_close(stream);
854
zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
855
zend_hash_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy"));
858
/* Process HTTP status codes */
859
if (http_status >= 300 && http_status < 400) {
862
if ((loc = get_http_header_value(http_headers,"Location: ")) != NULL) {
863
php_url *new_url = php_url_parse(loc);
867
if (new_url != NULL) {
868
if (get_http_body(stream, !http_1_1, http_headers, &body, &body_size TSRMLS_CC)) {
871
php_stream_close(stream);
872
zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket"));
878
if (new_url->scheme == NULL && new_url->path != NULL) {
879
new_url->scheme = estrdup(phpurl->scheme);
880
new_url->host = estrdup(phpurl->host);
881
new_url->port = phpurl->port;
882
if (new_url->path && new_url->path[0] != '/') {
883
char *t = phpurl->path?phpurl->path:"/";
884
char *p = strrchr(t, '/');
885
char *s = emalloc((p - t) + strlen(new_url->path) + 2);
887
strncpy(s, t, (p - t) + 1);
889
strcat(s, new_url->path);
890
efree(new_url->path);
899
} else if (http_status == 401) {
900
/* Digest authentication */
901
zval **digest, **login, **password;
902
char *auth = get_http_header_value(http_headers, "WWW-Authenticate: ");
905
strstr(auth, "Digest") == auth &&
906
(zend_hash_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest"), (void **)&digest) == FAILURE ||
907
Z_TYPE_PP(digest) != IS_ARRAY) &&
908
zend_hash_find(Z_OBJPROP_P(this_ptr), "_login", sizeof("_login"), (void **)&login) == SUCCESS &&
909
Z_TYPE_PP(login) == IS_STRING &&
910
zend_hash_find(Z_OBJPROP_P(this_ptr), "_password", sizeof("_password"), (void **)&password) == SUCCESS &&
911
Z_TYPE_PP(password) == IS_STRING) {
915
s = auth + sizeof("Digest")-1;
918
while (*s == ' ') ++s;
920
while (*s != '\0' && *s != '=') ++s;
927
while (*s != '\0' && *s != '"') ++s;
930
while (*s != '\0' && *s != ' ' && *s != ',') ++s;
936
while (*s != '\0' && *s != ',') ++s;
943
if (digest == NULL) {
944
ALLOC_INIT_ZVAL(digest);
947
add_assoc_string(digest, name, val ,1);
951
if (digest != NULL) {
952
php_url *new_url = emalloc(sizeof(php_url));
957
add_property_zval_ex(this_ptr, "_digest", sizeof("_digest"), digest TSRMLS_CC);
960
if (phpurl->scheme) phpurl->scheme = estrdup(phpurl->scheme);
961
if (phpurl->user) phpurl->user = estrdup(phpurl->user);
962
if (phpurl->pass) phpurl->pass = estrdup(phpurl->pass);
963
if (phpurl->host) phpurl->host = estrdup(phpurl->host);
964
if (phpurl->path) phpurl->path = estrdup(phpurl->path);
965
if (phpurl->query) phpurl->query = estrdup(phpurl->query);
966
if (phpurl->fragment) phpurl->fragment = estrdup(phpurl->fragment);
976
if (auth) efree(auth);
979
/* Check and see if the server even sent a xml document */
980
content_type = get_http_header_value(http_headers,"Content-Type: ");
984
pos = strstr(content_type,";");
986
cmplen = pos - content_type;
988
cmplen = strlen(content_type);
990
if (strncmp(content_type, "text/xml", cmplen) == 0 ||
991
strncmp(content_type, "application/soap+xml", cmplen) == 0) {
992
content_type_xml = 1;
994
if (strncmp(http_body, "<?xml", 5)) {
997
ZVAL_STRINGL(err, http_body, http_body_size, 1);
998
add_soap_fault(this_ptr, "HTTP", "Didn't recieve an xml document", NULL, err TSRMLS_CC);
1000
efree(http_headers);
1006
efree(content_type);
1009
/* Decompress response */
1010
content_encoding = get_http_header_value(http_headers,"Content-Encoding: ");
1011
if (content_encoding) {
1017
if ((strcmp(content_encoding,"gzip") == 0 ||
1018
strcmp(content_encoding,"x-gzip") == 0) &&
1019
zend_hash_exists(EG(function_table), "gzinflate", sizeof("gzinflate"))) {
1020
ZVAL_STRING(&func, "gzinflate", 0);
1022
ZVAL_STRINGL(params[0], http_body+10, http_body_size-10, 0);
1023
INIT_PZVAL(params[0]);
1024
} else if (strcmp(content_encoding,"deflate") == 0 &&
1025
zend_hash_exists(EG(function_table), "gzuncompress", sizeof("gzuncompress"))) {
1026
ZVAL_STRING(&func, "gzuncompress", 0);
1028
ZVAL_STRINGL(params[0], http_body, http_body_size, 0);
1029
INIT_PZVAL(params[0]);
1031
efree(content_encoding);
1032
efree(http_headers);
1034
add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL TSRMLS_CC);
1037
if (call_user_function(CG(function_table), (zval**)NULL, &func, &retval, 1, params TSRMLS_CC) == SUCCESS &&
1038
Z_TYPE(retval) == IS_STRING) {
1040
*buffer = Z_STRVAL(retval);
1041
*buffer_len = Z_STRLEN(retval);
1043
efree(content_encoding);
1044
efree(http_headers);
1046
add_soap_fault(this_ptr, "HTTP", "Can't uncompress compressed response", NULL, NULL TSRMLS_CC);
1049
efree(content_encoding);
1051
*buffer = http_body;
1052
*buffer_len = http_body_size;
1055
efree(http_headers);
1057
if (http_status >= 400) {
1060
if (*buffer_len == 0) {
1062
} else if (*buffer_len > 0) {
1063
if (!content_type_xml) {
1066
while (*s != '\0' && *s < ' ') {
1069
if (strncmp(s, "<?xml", 5)) {
1077
if (http_status == 400) {
1078
add_soap_fault(this_ptr, "HTTP", "Bad Request", NULL, NULL TSRMLS_CC);
1079
} else if (http_status == 401) {
1080
add_soap_fault(this_ptr, "HTTP", "Unauthorized Request", NULL, NULL TSRMLS_CC);
1081
} else if (http_status == 405) {
1082
add_soap_fault(this_ptr, "HTTP", "Method not allowed", NULL, NULL TSRMLS_CC);
1083
} else if (http_status == 415) {
1084
add_soap_fault(this_ptr, "HTTP", "Unsupported Media Type", NULL, NULL TSRMLS_CC);
1085
} else if (http_status >= 400 && http_status < 500) {
1086
add_soap_fault(this_ptr, "HTTP", "Client Error", NULL, NULL TSRMLS_CC);
1087
} else if (http_status == 500) {
1088
add_soap_fault(this_ptr, "HTTP", "Internal Server Error", NULL, NULL TSRMLS_CC);
1089
} else if (http_status >= 500 && http_status < 600) {
1090
add_soap_fault(this_ptr, "HTTP", "Server Error", NULL, NULL TSRMLS_CC);
1092
add_soap_fault(this_ptr, "HTTP", "Unsupported HTTP status code", NULL, NULL TSRMLS_CC);
1101
static char *get_http_header_value(char *headers, char *type)
1103
char *pos, *tmp = NULL;
1104
int typelen, headerslen;
1106
typelen = strlen(type);
1107
headerslen = strlen(headers);
1109
/* header `titles' can be lower case, or any case combination, according
1110
* to the various RFC's. */
1113
/* start of buffer or start of line */
1114
if (strncasecmp(pos, type, typelen) == 0) {
1118
tmp = pos + typelen;
1119
eol = strstr(tmp, "\r\n");
1121
eol = headers + headerslen;
1123
return estrndup(tmp, eol - tmp);
1126
/* find next line */
1127
pos = strstr(pos, "\r\n");
1137
static int get_http_body(php_stream *stream, int close, char *headers, char **response, int *out_size TSRMLS_DC)
1139
char *header, *http_buf = NULL;
1140
int header_close = close, header_chunked = 0, header_length = 0, http_buf_size = 0;
1143
header = get_http_header_value(headers, "Connection: ");
1145
if(!strncasecmp(header, "close", sizeof("close")-1)) header_close = 1;
1149
header = get_http_header_value(headers, "Transfer-Encoding: ");
1151
if(!strncasecmp(header, "chunked", sizeof("chunked")-1)) header_chunked = 1;
1154
header = get_http_header_value(headers, "Content-Length: ");
1156
header_length = atoi(header);
1158
if (!header_length && !header_chunked) {
1159
/* Empty response */
1160
http_buf = emalloc(1);
1162
(*response) = http_buf;
1168
if (header_chunked) {
1169
char done, chunk_size[10];
1176
php_stream_gets(stream, chunk_size, sizeof(chunk_size));
1177
if (sscanf(chunk_size, "%x", &buf_size) > 0 ) {
1181
if (http_buf_size + buf_size + 1 < 0) {
1185
http_buf = erealloc(http_buf, http_buf_size + buf_size + 1);
1187
while (len_size < buf_size) {
1188
int len_read = php_stream_read(stream, http_buf + http_buf_size, buf_size - len_size);
1189
if (len_read <= 0) {
1194
len_size += len_read;
1195
http_buf_size += len_read;
1199
/* Eat up '\r' '\n' */
1200
php_stream_getc(stream);
1201
php_stream_getc(stream);
1203
/* Somthing wrong in chunked encoding */
1209
if (buf_size == 0) {
1214
if (http_buf == NULL) {
1215
http_buf = emalloc(1);
1218
} else if (header_length) {
1219
if (header_length < 0) {
1222
http_buf = emalloc(header_length + 1);
1223
while (http_buf_size < header_length) {
1224
int len_read = php_stream_read(stream, http_buf + http_buf_size, header_length - http_buf_size);
1225
if (len_read <= 0) {
1228
http_buf_size += len_read;
1230
} else if (header_close) {
1233
http_buf = erealloc(http_buf, http_buf_size + 4096 + 1);
1234
len_read = php_stream_read(stream, http_buf + http_buf_size, 4096);
1236
http_buf_size += len_read;
1238
} while(!php_stream_eof(stream));
1243
http_buf[http_buf_size] = '\0';
1244
(*response) = http_buf;
1245
(*out_size) = http_buf_size;
1249
static int get_http_headers(php_stream *stream, char **response, int *out_size TSRMLS_DC)
1252
smart_str tmp_response = {0};
1253
char headerbuf[8192];
1256
if (!php_stream_gets(stream, headerbuf, sizeof(headerbuf))) {
1260
if (strcmp(headerbuf, "\r\n") == 0) {
1261
/* empty line marks end of headers */
1266
/* add header to collection */
1267
smart_str_appends(&tmp_response, headerbuf);
1269
smart_str_0(&tmp_response);
1270
(*response) = tmp_response.c;
1271
(*out_size) = tmp_response.len;