/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2007 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Brad Lafountain | | Shane Caraveo | | Dmitry Stogov | +----------------------------------------------------------------------+ */ /* $Id$ */ #include "php_soap.h" #include "ext/standard/base64.h" #include "ext/standard/md5.h" #include "ext/standard/php_rand.h" static char *get_http_header_value(char *headers, char *type); static int get_http_body(php_stream *socketd, int close, char *headers, char **response, int *out_size TSRMLS_DC); static int get_http_headers(php_stream *socketd,char **response, int *out_size TSRMLS_DC); #define smart_str_append_const(str, const) \ smart_str_appendl(str,const,sizeof(const)-1) static int stream_alive(php_stream *stream TSRMLS_DC) { long socket; char buf; /* maybe better to use: * php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL) * here instead */ if (stream == NULL || stream->eof || php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT, (void**)&socket, 0) != SUCCESS) { return FALSE; } if (socket == -1) { return FALSE; } else { if (php_pollfd_for_ms(socket, PHP_POLLREADABLE, 0) > 0) { if (0 == recv(socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) { return FALSE; } } } return TRUE; } /* Proxy HTTP Authentication */ void proxy_authentication(zval* this_ptr, smart_str* soap_headers TSRMLS_DC) { soap_client_object *client; client = (soap_client_object*)zend_object_store_get_object(this_ptr TSRMLS_CC); if (client->proxy_login) { unsigned char* buf; int len; smart_str auth = {0}; smart_str_appends(&auth, client->proxy_login); smart_str_appendc(&auth, ':'); if (client->proxy_password) { smart_str_appends(&auth, client->proxy_login); } smart_str_0(&auth); buf = php_base64_encode((unsigned char*)auth.c, auth.len, &len); smart_str_append_const(soap_headers, "Proxy-Authorization: Basic "); smart_str_appendl(soap_headers, (char*)buf, len); smart_str_append_const(soap_headers, "\r\n"); efree(buf); smart_str_free(&auth); } } /* HTTP Authentication */ void basic_authentication(zval* this_ptr, smart_str* soap_headers TSRMLS_DC) { soap_client_object *client; client = (soap_client_object*)zend_object_store_get_object(this_ptr TSRMLS_CC); if (client->login && !client->digest) { unsigned char* buf; int len; smart_str auth = {0}; smart_str_appends(&auth, client->login); smart_str_appendc(&auth, ':'); if (client->password) { smart_str_appends(&auth, client->password); } smart_str_0(&auth); buf = php_base64_encode((unsigned char*)auth.c, auth.len, &len); smart_str_append_const(soap_headers, "Authorization: Basic "); smart_str_appendl(soap_headers, (char*)buf, len); smart_str_append_const(soap_headers, "\r\n"); efree(buf); smart_str_free(&auth); } } static php_stream* http_connect(zval* this_ptr, php_url *phpurl, int use_ssl, int *use_proxy TSRMLS_DC) { php_stream *stream; char *host; php_stream_context *context = NULL; char *name; long namelen; int port; int old_error_reporting; struct timeval tv; struct timeval *timeout = NULL; soap_client_object *client; client = (soap_client_object*)zend_object_store_get_object(this_ptr TSRMLS_CC); if (client->proxy_host) { host = client->proxy_host; port = client->proxy_port; *use_proxy = 1; } else { host = phpurl->host; port = phpurl->port; } if (client->connection_timeout) { tv.tv_sec = client->connection_timeout; tv.tv_usec = 0; timeout = &tv; } old_error_reporting = EG(error_reporting); EG(error_reporting) &= ~(E_WARNING|E_NOTICE|E_USER_WARNING|E_USER_NOTICE); context = client->stream_context; namelen = spprintf(&name, 0, "%s://%s:%d", (use_ssl && !*use_proxy)? "ssl" : "tcp", host, port); stream = php_stream_xport_create(name, namelen, REPORT_ERRORS, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, NULL /*persistent_id*/, timeout, context, NULL, NULL); efree(name); /* SSL & proxy */ if (stream && *use_proxy && use_ssl) { smart_str soap_headers = {0}; char *http_headers; int http_header_size; smart_str_append_const(&soap_headers, "CONNECT "); smart_str_appends(&soap_headers, phpurl->host); smart_str_appendc(&soap_headers, ':'); smart_str_append_unsigned(&soap_headers, phpurl->port); smart_str_append_const(&soap_headers, " HTTP/1.1\r\n"); proxy_authentication(this_ptr, &soap_headers TSRMLS_CC); smart_str_append_const(&soap_headers, "\r\n"); if (php_stream_write(stream, soap_headers.c, soap_headers.len) != soap_headers.len) { php_stream_close(stream); stream = NULL; } smart_str_free(&soap_headers); if (stream) { if (!get_http_headers(stream, &http_headers, &http_header_size TSRMLS_CC) || http_headers == NULL) { php_stream_close(stream); stream = NULL; } if (http_headers) { efree(http_headers); } } /* enable SSL transport layer */ if (stream) { if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0 || php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) { php_stream_close(stream); stream = NULL; } } } EG(error_reporting) = old_error_reporting; return stream; } static int in_domain(const char *host, const char *domain) { if (domain[0] == '.') { int l1 = strlen(host); int l2 = strlen(domain); if (l1 > l2) { return strcmp(host+l1-l2,domain) == 0; } else { return 0; } } else { return strcmp(host,domain) == 0; } } int 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) { char *request; smart_str soap_headers = {0}; smart_str soap_headers_z = {0}; int request_size, err; php_url *phpurl = NULL; php_stream *stream; int use_proxy = 0; int use_ssl; char *http_headers, *http_body, *content_type, *http_version, *cookie_itt; int http_header_size, http_body_size, http_close; char *connection; int http_1_1; int http_status; int content_type_xml = 0; char *content_encoding; char *http_msg = NULL; char *old_allow_url_fopen_list; soap_client_object *client; if (this_ptr == NULL || Z_TYPE_P(this_ptr) != IS_OBJECT) { return FALSE; } client = (soap_client_object*)zend_object_store_get_object(this_ptr TSRMLS_CC); request = buf; request_size = buf_size; /* Compress request */ if (client->compression) { int level = client->compression & 0x0f; int kind = client->compression & SOAP_COMPRESSION_DEFLATE; if (level > 9) {level = 9;} if ((client->compression & SOAP_COMPRESSION_ACCEPT) != 0) { smart_str_append_const(&soap_headers_z,"Accept-Encoding: gzip, deflate\r\n"); } if (level > 0) { zval func; zval retval; zval param1, param2, param3; zval *params[3]; int n; params[0] = ¶m1; INIT_PZVAL(params[0]); params[1] = ¶m2; INIT_PZVAL(params[1]); params[2] = ¶m3; INIT_PZVAL(params[2]); ZVAL_STRINGL(params[0], buf, buf_size, 0); ZVAL_LONG(params[1], level); if (kind == SOAP_COMPRESSION_DEFLATE) { n = 2; ZVAL_STRING(&func, "gzcompress", 0); smart_str_append_const(&soap_headers_z,"Content-Encoding: deflate\r\n"); } else { n = 3; ZVAL_STRING(&func, "gzencode", 0); smart_str_append_const(&soap_headers_z,"Content-Encoding: gzip\r\n"); ZVAL_LONG(params[2], 1); } if (call_user_function(CG(function_table), (zval**)NULL, &func, &retval, n, params TSRMLS_CC) == SUCCESS && Z_TYPE(retval) == IS_STRING) { request = Z_STRVAL(retval); request_size = Z_STRLEN(retval); } else { if (request != buf) {efree(request);} smart_str_free(&soap_headers_z); return FALSE; } } } if (client->stream) { stream = client->stream; use_proxy = client->use_proxy; } else { stream = NULL; } if (location != NULL && location[0] != '\000') { phpurl = php_url_parse(location); } try_again: if (phpurl == NULL || phpurl->host == NULL) { if (phpurl != NULL) {php_url_free(phpurl);} if (request != buf) {efree(request);} add_soap_fault(this_ptr, "HTTP", "Unable to parse URL", NULL, NULL TSRMLS_CC); smart_str_free(&soap_headers_z); return FALSE; } use_ssl = 0; if (phpurl->scheme != NULL && strcmp(phpurl->scheme, "https") == 0) { use_ssl = 1; } else if (phpurl->scheme == NULL || strcmp(phpurl->scheme, "http") != 0) { php_url_free(phpurl); if (request != buf) {efree(request);} add_soap_fault(this_ptr, "HTTP", "Unknown protocol. Only http and https are allowed.", NULL, NULL TSRMLS_CC); smart_str_free(&soap_headers_z); return FALSE; } old_allow_url_fopen_list = PG(allow_url_fopen_list); if (!old_allow_url_fopen_list) { old_allow_url_fopen_list = ""; } zend_alter_ini_entry("allow_url_fopen", sizeof("allow_url_fopen"), "*", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_RUNTIME); if (use_ssl && php_stream_locate_url_wrapper("https://", NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC) == NULL) { php_url_free(phpurl); if (request != buf) {efree(request);} add_soap_fault(this_ptr, "HTTP", "SSL support is not available in this build", NULL, NULL TSRMLS_CC); zend_alter_ini_entry("allow_url_fopen", sizeof("allow_url_fopen"), old_allow_url_fopen_list, strlen(old_allow_url_fopen_list), PHP_INI_SYSTEM, PHP_INI_STAGE_RUNTIME); smart_str_free(&soap_headers_z); return FALSE; } if (phpurl->port == 0) { phpurl->port = use_ssl ? 443 : 80; } /* Check if request to the same host */ if (stream != NULL) { if (client->url && ((use_proxy && !use_ssl) || (((use_ssl && client->url->scheme != NULL && strcmp(client->url->scheme, "https") == 0) || (!use_ssl && client->url->scheme == NULL) || (!use_ssl && strcmp(client->url->scheme, "https") != 0)) && strcmp(client->url->host, phpurl->host) == 0 && client->url->port == phpurl->port))) { } else { php_stream_close(stream); if (client->url) { php_url_free(client->url); client->url = NULL; } client->stream = NULL; client->use_proxy = 0; stream = NULL; use_proxy = 0; } } /* Check if keep-alive connection is still opened */ if (stream != NULL && !stream_alive(stream TSRMLS_CC)) { php_stream_close(stream); if (client->url) { php_url_free(client->url); client->url = NULL; } client->stream = NULL; client->use_proxy = 0; stream = NULL; use_proxy = 0; } if (!stream) { stream = http_connect(this_ptr, phpurl, use_ssl, &use_proxy TSRMLS_CC); if (stream) { php_stream_auto_cleanup(stream); client->stream = stream; client->use_proxy = use_proxy; } else { php_url_free(phpurl); if (request != buf) {efree(request);} add_soap_fault(this_ptr, "HTTP", "Could not connect to host", NULL, NULL TSRMLS_CC); zend_alter_ini_entry("allow_url_fopen", sizeof("allow_url_fopen"), old_allow_url_fopen_list, strlen(old_allow_url_fopen_list), PHP_INI_SYSTEM, PHP_INI_STAGE_RUNTIME); smart_str_free(&soap_headers_z); return FALSE; } } zend_alter_ini_entry("allow_url_fopen", sizeof("allow_url_fopen"), old_allow_url_fopen_list, strlen(old_allow_url_fopen_list), PHP_INI_SYSTEM, PHP_INI_STAGE_RUNTIME); if (stream) { if (client->url) { php_url_free(client->url); client->url = NULL; } client->url = phpurl; smart_str_append_const(&soap_headers, "POST "); if (use_proxy && !use_ssl) { smart_str_appends(&soap_headers, phpurl->scheme); smart_str_append_const(&soap_headers, "://"); smart_str_appends(&soap_headers, phpurl->host); smart_str_appendc(&soap_headers, ':'); smart_str_append_unsigned(&soap_headers, phpurl->port); } if (phpurl->path) { smart_str_appends(&soap_headers, phpurl->path); } else { smart_str_appendc(&soap_headers, '/'); } if (phpurl->query) { smart_str_appendc(&soap_headers, '?'); smart_str_appends(&soap_headers, phpurl->query); } if (phpurl->fragment) { smart_str_appendc(&soap_headers, '#'); smart_str_appends(&soap_headers, phpurl->fragment); } smart_str_append_const(&soap_headers, " HTTP/1.1\r\n" "Host: "); smart_str_appends(&soap_headers, phpurl->host); if (phpurl->port != (use_ssl?443:80)) { smart_str_appendc(&soap_headers, ':'); smart_str_append_unsigned(&soap_headers, phpurl->port); } smart_str_append_const(&soap_headers, "\r\n" "Connection: Keep-Alive\r\n"); /* "Connection: close\r\n" "Accept: text/html; text/xml; text/plain\r\n" */ if (client->user_agent) { if (client->user_agent[0] != 0) { smart_str_append_const(&soap_headers, "User-Agent: "); smart_str_appends(&soap_headers, client->user_agent); smart_str_append_const(&soap_headers, "\r\n"); } } else{ smart_str_append_const(&soap_headers, "User-Agent: PHP-SOAP/"PHP_VERSION"\r\n"); } smart_str_append(&soap_headers, &soap_headers_z); if (soap_version == SOAP_1_2) { smart_str_append_const(&soap_headers,"Content-Type: application/soap+xml; charset=utf-8"); if (soapaction) { smart_str_append_const(&soap_headers,"; action=\""); smart_str_appends(&soap_headers, soapaction); smart_str_append_const(&soap_headers,"\""); } smart_str_append_const(&soap_headers,"\r\n"); } else { smart_str_append_const(&soap_headers,"Content-Type: text/xml; charset=utf-8\r\n"); if (soapaction) { smart_str_append_const(&soap_headers, "SOAPAction: \""); smart_str_appends(&soap_headers, soapaction); smart_str_append_const(&soap_headers, "\"\r\n"); } } smart_str_append_const(&soap_headers,"Content-Length: "); smart_str_append_long(&soap_headers, request_size); smart_str_append_const(&soap_headers, "\r\n"); /* HTTP Authentication */ if (client->login) { if (client->digest) { if (client->digest == 2) { char HA1[33], HA2[33], response[33], cnonce[33], nc[9]; PHP_MD5_CTX md5ctx; unsigned char hash[16]; PHP_MD5Init(&md5ctx); snprintf(cnonce, sizeof(cnonce), "%ld", php_rand(TSRMLS_C)); PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, strlen(cnonce)); PHP_MD5Final(hash, &md5ctx); make_digest(cnonce, hash); client->digest_nc++; snprintf(nc, sizeof(nc), "%08ld", client->digest_nc); PHP_MD5Init(&md5ctx); PHP_MD5Update(&md5ctx, (unsigned char*)client->login, strlen(client->login)); PHP_MD5Update(&md5ctx, (unsigned char*)":", 1); if (client->digest_realm) { PHP_MD5Update(&md5ctx, (unsigned char*)client->digest_realm, strlen(client->digest_realm)); } PHP_MD5Update(&md5ctx, (unsigned char*)":", 1); if (client->password) { PHP_MD5Update(&md5ctx, (unsigned char*)client->password, strlen(client->password)); } PHP_MD5Final(hash, &md5ctx); make_digest(HA1, hash); if (client->digest_algorithm && stricmp(client->digest_algorithm, "md5-sess") == 0) { PHP_MD5Init(&md5ctx); PHP_MD5Update(&md5ctx, (unsigned char*)HA1, 32); PHP_MD5Update(&md5ctx, (unsigned char*)":", 1); if (client->digest_nonce) { PHP_MD5Update(&md5ctx, (unsigned char*)client->digest_nonce, strlen(client->digest_nonce)); } PHP_MD5Update(&md5ctx, (unsigned char*)":", 1); PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8); PHP_MD5Final(hash, &md5ctx); make_digest(HA1, hash); } PHP_MD5Init(&md5ctx); PHP_MD5Update(&md5ctx, (unsigned char*)"POST:", sizeof("POST:")-1); if (phpurl->path) { PHP_MD5Update(&md5ctx, (unsigned char*)phpurl->path, strlen(phpurl->path)); } else { PHP_MD5Update(&md5ctx, (unsigned char*)"/", 1); } if (phpurl->query) { PHP_MD5Update(&md5ctx, (unsigned char*)"?", 1); PHP_MD5Update(&md5ctx, (unsigned char*)phpurl->query, strlen(phpurl->query)); } /* TODO: Support for qop="auth-int" */ /* if (client->digest_qop && stricmp(client->digest_qop, "auth-int") == 0) { PHP_MD5Update(&md5ctx, ":", 1); PHP_MD5Update(&md5ctx, HEntity, HASHHEXLEN); } */ PHP_MD5Final(hash, &md5ctx); make_digest(HA2, hash); PHP_MD5Init(&md5ctx); PHP_MD5Update(&md5ctx, (unsigned char*)HA1, 32); PHP_MD5Update(&md5ctx, (unsigned char*)":", 1); if (client->digest_nonce) { PHP_MD5Update(&md5ctx, (unsigned char*)client->digest_nonce, strlen(client->digest_nonce)); } PHP_MD5Update(&md5ctx, (unsigned char*)":", 1); if (client->digest_qop) { PHP_MD5Update(&md5ctx, (unsigned char*)nc, 8); PHP_MD5Update(&md5ctx, (unsigned char*)":", 1); PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, 8); PHP_MD5Update(&md5ctx, (unsigned char*)":", 1); /* TODO: Support for qop="auth-int" */ PHP_MD5Update(&md5ctx, (unsigned char*)"auth", sizeof("auth")-1); PHP_MD5Update(&md5ctx, (unsigned char*)":", 1); } PHP_MD5Update(&md5ctx, (unsigned char*)HA2, 32); PHP_MD5Final(hash, &md5ctx); make_digest(response, hash); smart_str_append_const(&soap_headers, "Authorization: Digest username=\""); smart_str_appends(&soap_headers, client->login); if (client->digest_realm) { smart_str_append_const(&soap_headers, "\", realm=\""); smart_str_appends(&soap_headers, client->digest_realm); } if (client->digest_nonce) { smart_str_append_const(&soap_headers, "\", nonce=\""); smart_str_appends(&soap_headers, client->digest_nonce); } smart_str_append_const(&soap_headers, "\", uri=\""); if (phpurl->path) { smart_str_appends(&soap_headers, phpurl->path); } else { smart_str_appendc(&soap_headers, '/'); } if (phpurl->query) { smart_str_appendc(&soap_headers, '?'); smart_str_appends(&soap_headers, phpurl->query); } if (phpurl->fragment) { smart_str_appendc(&soap_headers, '#'); smart_str_appends(&soap_headers, phpurl->fragment); } if (client->digest_qop) { /* TODO: Support for qop="auth-int" */ smart_str_append_const(&soap_headers, "\", qop=\"auth"); smart_str_append_const(&soap_headers, "\", nc=\""); smart_str_appendl(&soap_headers, nc, 8); smart_str_append_const(&soap_headers, "\", cnonce=\""); smart_str_appendl(&soap_headers, cnonce, 8); } smart_str_append_const(&soap_headers, "\", response=\""); smart_str_appendl(&soap_headers, response, 32); if (client->digest_opaque) { smart_str_append_const(&soap_headers, "\", opaque=\""); smart_str_appends(&soap_headers, client->digest_opaque); } smart_str_append_const(&soap_headers, "\"\r\n"); } } else { unsigned char* buf; int len; smart_str auth = {0}; smart_str_appends(&auth, client->login); smart_str_appendc(&auth, ':'); if (client->password) { smart_str_appends(&auth, client->password); } smart_str_0(&auth); buf = php_base64_encode((unsigned char*)auth.c, auth.len, &len); smart_str_append_const(&soap_headers, "Authorization: Basic "); smart_str_appendl(&soap_headers, (char*)buf, len); smart_str_append_const(&soap_headers, "\r\n"); efree(buf); smart_str_free(&auth); } } /* Proxy HTTP Authentication */ if (use_proxy && !use_ssl) { proxy_authentication(this_ptr, &soap_headers TSRMLS_CC); } /* Send cookies along with request */ if (client->cookies) { zval **data; zstr key; uint key_len; int i, n; zend_uchar key_type; n = zend_hash_num_elements(Z_ARRVAL_P(client->cookies)); if (n > 0) { zend_hash_internal_pointer_reset(Z_ARRVAL_P(client->cookies)); smart_str_append_const(&soap_headers, "Cookie: "); for (i = 0; i < n; i++) { zend_hash_get_current_data(Z_ARRVAL_P(client->cookies), (void **)&data); key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(client->cookies), &key, &key_len, NULL, FALSE, NULL); if (Z_TYPE_PP(data) == IS_ARRAY) { zval** value; if (zend_hash_index_find(Z_ARRVAL_PP(data), 0, (void**)&value) == SUCCESS && (Z_TYPE_PP(value) == IS_STRING || Z_TYPE_PP(value) == IS_UNICODE)) { zval **tmp; if ((zend_hash_index_find(Z_ARRVAL_PP(data), 1, (void**)&tmp) == FAILURE || strncmp(phpurl->path?phpurl->path:"/",Z_STRVAL_PP(tmp),Z_STRLEN_PP(tmp)) == 0) && (zend_hash_index_find(Z_ARRVAL_PP(data), 2, (void**)&tmp) == FAILURE || in_domain(phpurl->host,Z_STRVAL_PP(tmp))) && (use_ssl || zend_hash_index_find(Z_ARRVAL_PP(data), 3, (void**)&tmp) == FAILURE)) { if (key_type == IS_STRING) { smart_str_appendl(&soap_headers, key.s, key_len-1); } else { UErrorCode status = U_ZERO_ERROR; char *res; int res_len; zend_unicode_to_string_ex(UG(utf8_conv), &res, &res_len, key.u, key_len-1, &status); smart_str_appendl(&soap_headers, res, res_len); efree(res); } smart_str_appendc(&soap_headers, '='); if (Z_TYPE_PP(value) == IS_STRING) { smart_str_appendl(&soap_headers, Z_STRVAL_PP(value), Z_STRLEN_PP(value)); } else { UErrorCode status = U_ZERO_ERROR; char *res; int res_len; zend_unicode_to_string_ex(UG(utf8_conv), &res, &res_len, Z_USTRVAL_PP(value), Z_USTRLEN_PP(value), &status); smart_str_appendl(&soap_headers, res, res_len); efree(res); } smart_str_appendc(&soap_headers, ';'); } } } zend_hash_move_forward(Z_ARRVAL_P(client->cookies)); } smart_str_append_const(&soap_headers, "\r\n"); } } smart_str_append_const(&soap_headers, "\r\n"); smart_str_0(&soap_headers); if (client->trace) { if (client->last_request_headers) { efree(client->last_request_headers); } client->last_request_headers = estrndup(soap_headers.c, soap_headers.len); } smart_str_appendl(&soap_headers, request, request_size); smart_str_0(&soap_headers); err = php_stream_write(stream, soap_headers.c, soap_headers.len); if (err != soap_headers.len) { if (request != buf) {efree(request);} smart_str_free(&soap_headers); php_stream_close(stream); if (client->url) { php_url_free(client->url); client->url = NULL; } client->stream = NULL; client->use_proxy = 0; add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL TSRMLS_CC); smart_str_free(&soap_headers_z); return FALSE; } smart_str_free(&soap_headers); } else { add_soap_fault(this_ptr, "HTTP", "Failed to create stream??", NULL, NULL TSRMLS_CC); smart_str_free(&soap_headers_z); return FALSE; } if (!buffer) { php_stream_close(stream); client->stream = NULL; client->use_proxy = 0; smart_str_free(&soap_headers_z); return TRUE; } do { if (!get_http_headers(stream, &http_headers, &http_header_size TSRMLS_CC)) { if (http_headers) {efree(http_headers);} if (request != buf) {efree(request);} php_stream_close(stream); client->stream = NULL; client->use_proxy = 0; add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL TSRMLS_CC); smart_str_free(&soap_headers_z); return FALSE; } if (client->trace) { if (client->last_response_headers) { efree(client->last_response_headers); } client->last_response_headers = estrndup(http_headers, http_header_size); } /* Check to see what HTTP status was sent */ http_1_1 = 0; http_status = 0; http_version = get_http_header_value(http_headers,"HTTP/"); if (http_version) { char *tmp; if (!strncmp(http_version,"1.1", 3)) { http_1_1 = 1; } tmp = strstr(http_version," "); if (tmp != NULL) { tmp++; http_status = atoi(tmp); } tmp = strstr(tmp," "); if (tmp != NULL) { tmp++; if (http_msg) { efree(http_msg); } http_msg = estrdup(tmp); } efree(http_version); /* Try and get headers again */ if (http_status == 100) { efree(http_headers); } } } while (http_status == 100); /* Grab and send back every cookie */ /* Not going to worry about Path: because we shouldn't be changing urls so path dont matter too much */ cookie_itt = strstr(http_headers,"Set-Cookie: "); while (cookie_itt) { char *end_pos, *cookie; char *eqpos, *sempos; if (!client->cookies) { MAKE_STD_ZVAL(client->cookies); array_init(client->cookies); } end_pos = strstr(cookie_itt,"\r\n"); cookie = get_http_header_value(cookie_itt,"Set-Cookie: "); eqpos = strstr(cookie, "="); sempos = strstr(cookie, ";"); if (eqpos != NULL && (sempos == NULL || sempos > eqpos)) { smart_str name = {0}; int cookie_len; zval *zcookie; zval *zvalue; if (sempos != NULL) { cookie_len = sempos-(eqpos+1); } else { cookie_len = strlen(cookie)-(eqpos-cookie)-1; } smart_str_appendl(&name, cookie, eqpos - cookie); smart_str_0(&name); ALLOC_INIT_ZVAL(zcookie); array_init(zcookie); MAKE_STD_ZVAL(zvalue); ZVAL_STRINGL(zvalue, eqpos + 1, cookie_len, 1); if (UG(unicode)) { zval_string_to_unicode_ex(zvalue, UG(utf8_conv) TSRMLS_CC); } add_index_zval(zcookie, 0, zvalue); if (sempos != NULL) { char *options = cookie + cookie_len+1; while (*options) { while (*options == ' ') {options++;} sempos = strstr(options, ";"); if (strstr(options,"path=") == options) { eqpos = options + sizeof("path=")-1; add_index_stringl(zcookie, 1, eqpos, sempos?(sempos-eqpos):strlen(eqpos), 1); } else if (strstr(options,"domain=") == options) { eqpos = options + sizeof("domain=")-1; add_index_stringl(zcookie, 2, eqpos, sempos?(sempos-eqpos):strlen(eqpos), 1); } else if (strstr(options,"secure") == options) { add_index_bool(zcookie, 3, 1); } if (sempos != NULL) { options = sempos+1; } else { break; } } } if (!zend_hash_index_exists(Z_ARRVAL_P(zcookie), 1)) { char *t = phpurl->path?phpurl->path:"/"; char *c = strrchr(t, '/'); if (c) { add_index_stringl(zcookie, 1, t, c-t, 1); } } if (!zend_hash_index_exists(Z_ARRVAL_P(zcookie), 2)) { add_index_string(zcookie, 2, phpurl->host, 1); } add_assoc_zval_ex(client->cookies, name.c, name.len+1, zcookie); smart_str_free(&name); } cookie_itt = strstr(cookie_itt + sizeof("Set-Cookie: "), "Set-Cookie: "); efree(cookie); } if (http_1_1) { http_close = FALSE; if (use_proxy && !use_ssl) { connection = get_http_header_value(http_headers,"Proxy-Connection: "); if (connection) { if (strncasecmp(connection, "close", sizeof("close")-1) == 0) { http_close = TRUE; } efree(connection); } } } else { http_close = TRUE; } if (!get_http_body(stream, http_close, http_headers, &http_body, &http_body_size TSRMLS_CC)) { if (request != buf) {efree(request);} php_stream_close(stream); efree(http_headers); client->stream = NULL; client->use_proxy = 0; add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL TSRMLS_CC); if (http_msg) { efree(http_msg); } smart_str_free(&soap_headers_z); return FALSE; } if (request != buf) {efree(request);} /* See if the server requested a close */ http_close = TRUE; connection = get_http_header_value(http_headers,"Proxy-Connection: "); if (connection) { if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) { http_close = FALSE; } efree(connection); /* } else if (http_1_1) { http_close = FALSE; */ } connection = get_http_header_value(http_headers,"Connection: "); if (connection) { if (strncasecmp(connection, "Keep-Alive", sizeof("Keep-Alive")-1) == 0) { http_close = FALSE; } efree(connection); /* } else if (http_1_1) { http_close = FALSE; */ } if (http_close) { php_stream_close(stream); client->stream = NULL; client->use_proxy = 0; stream = NULL; } /* Process HTTP status codes */ if (http_status >= 300 && http_status < 400) { char *loc; if ((loc = get_http_header_value(http_headers,"Location: ")) != NULL) { php_url *new_url = php_url_parse(loc); if (new_url != NULL) { efree(http_headers); efree(http_body); efree(loc); if (new_url->scheme == NULL && new_url->path != NULL) { new_url->scheme = phpurl->scheme ? estrdup(phpurl->scheme) : NULL; new_url->host = phpurl->host ? estrdup(phpurl->host) : NULL; new_url->port = phpurl->port; if (new_url->path && new_url->path[0] != '/') { char *t = phpurl->path; char *p = strrchr(t, '/'); if (p) { char *s = emalloc((p - t) + strlen(new_url->path) + 2); strncpy(s, t, (p - t) + 1); s[(p - t) + 1] = 0; strcat(s, new_url->path); efree(new_url->path); new_url->path = s; } } } phpurl = new_url; goto try_again; } } } else if (http_status == 401) { /* Digest authentication */ char *auth = get_http_header_value(http_headers, "WWW-Authenticate: "); if (auth && strstr(auth, "Digest") == auth && (client->digest < 2) && client->login && client->password) { char *s; php_url *new_url; client->digest = 2; s = auth + sizeof("Digest")-1; while (*s != '\0') { char *name, *val; while (*s == ' ') ++s; name = s; while (*s != '\0' && *s != '=') ++s; if (*s == '=') { *s = '\0'; ++s; if (*s == '"') { ++s; val = s; while (*s != '\0' && *s != '"') ++s; } else { val = s; while (*s != '\0' && *s != ' ' && *s != ',') ++s; } if (*s != '\0') { if (*s != ',') { *s = '\0'; ++s; while (*s != '\0' && *s != ',') ++s; if (*s != '\0') ++s; } else { *s = '\0'; ++s; } } if (strcmp(name, "realm") == 0) { client->digest_realm = estrdup(val); } else if (strcmp(name, "algorithm") == 0) { client->digest_algorithm = estrdup(val); } else if (strcmp(name, "nonce") == 0) { client->digest_nonce = estrdup(val); } else if (strcmp(name, "qop") == 0) { client->digest_qop = estrdup(val); } else if (strcmp(name, "opaque") == 0) { client->digest_opaque = estrdup(val); } } } new_url = emalloc(sizeof(php_url)); *new_url = *phpurl; if (phpurl->scheme) phpurl->scheme = estrdup(phpurl->scheme); if (phpurl->user) phpurl->user = estrdup(phpurl->user); if (phpurl->pass) phpurl->pass = estrdup(phpurl->pass); if (phpurl->host) phpurl->host = estrdup(phpurl->host); if (phpurl->path) phpurl->path = estrdup(phpurl->path); if (phpurl->query) phpurl->query = estrdup(phpurl->query); if (phpurl->fragment) phpurl->fragment = estrdup(phpurl->fragment); phpurl = new_url; efree(auth); efree(http_headers); efree(http_body); goto try_again; } if (auth) efree(auth); } smart_str_free(&soap_headers_z); /* Check and see if the server even sent a xml document */ content_type = get_http_header_value(http_headers,"Content-Type: "); if (content_type) { char *pos = NULL; int cmplen; pos = strstr(content_type,";"); if (pos != NULL) { cmplen = pos - content_type; } else { cmplen = strlen(content_type); } if (strncmp(content_type, "text/xml", cmplen) == 0 || strncmp(content_type, "application/soap+xml", cmplen) == 0) { content_type_xml = 1; /* if (strncmp(http_body, "= 400) { int error = 0; if (*buffer_len == 0) { error = 1; } else if (*buffer_len > 0) { if (!content_type_xml) { char *s = *buffer; while (*s != '\0' && *s < ' ') { s++; } if (strncmp(s, " tmp && *(eol-1) == '\r') { eol--; } return estrndup(tmp, eol - tmp); } /* find next line */ pos = strchr(pos, '\n'); if (pos) { pos++; } } while (pos); return NULL; } static int get_http_body(php_stream *stream, int close, char *headers, char **response, int *out_size TSRMLS_DC) { char *header, *http_buf = NULL; int header_close = close, header_chunked = 0, header_length = 0, http_buf_size = 0; if (!close) { header = get_http_header_value(headers, "Connection: "); if (header) { if(!strncasecmp(header, "close", sizeof("close")-1)) header_close = 1; efree(header); } } header = get_http_header_value(headers, "Transfer-Encoding: "); if (header) { if(!strncasecmp(header, "chunked", sizeof("chunked")-1)) header_chunked = 1; efree(header); } header = get_http_header_value(headers, "Content-Length: "); if (header) { header_length = atoi(header); efree(header); if (!header_length && !header_chunked) { /* Empty response */ http_buf = emalloc(1); http_buf[0] = '\0'; (*response) = http_buf; (*out_size) = 0; return TRUE; } } if (header_chunked) { char ch, done, chunk_size[10], headerbuf[8192]; done = FALSE; while (!done) { int buf_size = 0; php_stream_gets(stream, ZSTR(chunk_size), sizeof(chunk_size)); if (sscanf(chunk_size, "%x", &buf_size) > 0 ) { if (buf_size > 0) { int len_size = 0; if (http_buf_size + buf_size + 1 < 0) { efree(http_buf); return FALSE; } http_buf = erealloc(http_buf, http_buf_size + buf_size + 1); while (len_size < buf_size) { int len_read = php_stream_read(stream, http_buf + http_buf_size, buf_size - len_size); if (len_read <= 0) { /* Error or EOF */ done = TRUE; break; } len_size += len_read; http_buf_size += len_read; } /* Eat up '\r' '\n' */ ch = php_stream_getc(stream); if (ch == '\r') { ch = php_stream_getc(stream); } if (ch != '\n') { /* Somthing wrong in chunked encoding */ if (http_buf) { efree(http_buf); } return FALSE; } } } else { /* Somthing wrong in chunked encoding */ if (http_buf) { efree(http_buf); } return FALSE; } if (buf_size == 0) { done = TRUE; } } /* Ignore trailer headers */ while (1) { if (!php_stream_gets(stream, ZSTR(headerbuf), sizeof(headerbuf))) { break; } if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') || (headerbuf[0] == '\n')) { /* empty line marks end of headers */ break; } } if (http_buf == NULL) { http_buf = emalloc(1); } } else if (header_length) { if (header_length < 0) { return FALSE; } http_buf = emalloc(header_length + 1); while (http_buf_size < header_length) { int len_read = php_stream_read(stream, http_buf + http_buf_size, header_length - http_buf_size); if (len_read <= 0) { break; } http_buf_size += len_read; } } else if (header_close) { do { int len_read; http_buf = erealloc(http_buf, http_buf_size + 4096 + 1); len_read = php_stream_read(stream, http_buf + http_buf_size, 4096); if (len_read > 0) { http_buf_size += len_read; } } while(!php_stream_eof(stream)); } else { return FALSE; } http_buf[http_buf_size] = '\0'; (*response) = http_buf; (*out_size) = http_buf_size; return TRUE; } static int get_http_headers(php_stream *stream, char **response, int *out_size TSRMLS_DC) { int done = FALSE; smart_str tmp_response = {0}; char headerbuf[8192]; while (!done) { if (!php_stream_gets(stream, ZSTR(headerbuf), sizeof(headerbuf))) { break; } if ((headerbuf[0] == '\r' && headerbuf[1] == '\n') || (headerbuf[0] == '\n')) { /* empty line marks end of headers */ done = TRUE; break; } /* add header to collection */ smart_str_appends(&tmp_response, headerbuf); } smart_str_0(&tmp_response); (*response) = tmp_response.c; (*out_size) = tmp_response.len; return done; }