From b68739f7f9c9838bed94dae6ef614059c1302ecf Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Wed, 5 Mar 2003 14:23:56 +0000 Subject: [PATCH] - Remove legacy non-streams code (as discussed with Brad). - Make get_http_header_value() work using case-insensitive compares for the header names, as per relevant RFC's. - General performance boost for get_http_headers(). - Fix a crash bug when a malformed URL is passed to the soap client. - Implement https:// support in the the soap client. (does not verify the remote server certificate; it provides an encrypted link only). - Provide a hook for libxml to parse files using any registered PHP wrapper, including https://, ftps:// and any user-space streams. --- ext/soap/php_http.c | 265 +++++++++++++++++++------------------------- ext/soap/php_http.h | 18 +-- ext/soap/php_soap.h | 22 ++-- ext/soap/php_xml.c | 28 +++++ ext/soap/php_xml.h | 5 + ext/soap/soap.c | 36 +++--- 6 files changed, 167 insertions(+), 207 deletions(-) diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index fdb2403468..5f68e94e84 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -7,7 +7,7 @@ void send_http_soap_request(zval *this_ptr, xmlDoc *doc, char *function_name, ch int buf_size,err,ret; sdlPtr sdl; php_url *phpurl = NULL; - SOAP_STREAM stream; + php_stream *stream; zval **trace; FETCH_THIS_SOCKET(stream); @@ -26,6 +26,7 @@ void send_http_soap_request(zval *this_ptr, xmlDoc *doc, char *function_name, ch if(!stream) { char *url; + int use_ssl; if(!sdl) { @@ -42,35 +43,46 @@ void send_http_soap_request(zval *this_ptr, xmlDoc *doc, char *function_name, ch } phpurl = php_url_parse(url); + if (phpurl == NULL) { + php_error(E_ERROR, "Unable to parse URL \"%s\"", url); + } + use_ssl = strcmp(phpurl->scheme, "https") == 0; +#if !HAVE_OPENSSL_EXT + if (use_ssl) { + php_error(E_ERROR, "SSL support not available in this build"); + } +#endif + if (phpurl->port == 0) { - if (strcmp(phpurl->scheme, "http") == 0) - phpurl->port = 80; - else if (strcmp(phpurl->scheme, "https") == 0) - phpurl->port = 443; + phpurl->port = use_ssl ? 443 : 80; } -#ifdef PHP_HAVE_STREAMS stream = php_stream_sock_open_host(phpurl->host, (unsigned short)phpurl->port, SOCK_STREAM, NULL, NULL); -#else - stream = get_socket(phpurl->host, phpurl->port, 10); -#endif + if(stream) { - ret = zend_list_insert((void *)stream, le_http_socket); - add_property_resource(this_ptr, "httpsocket", ret); - zend_list_addref(ret); + +#if HAVE_OPENSSL_EXT + /* fire up SSL, if requested */ + if (use_ssl) { + if (FAILURE == php_stream_sock_ssl_activate(stream, 1)) { + php_error(E_ERROR, "SSL Connection attempt failed"); + } + } +#endif + + add_property_resource(this_ptr, "httpsocket", php_stream_get_resource_id(stream)); ret = zend_list_insert(phpurl, le_url); add_property_resource(this_ptr, "httpurl", ret); zend_list_addref(ret); } else { - php_error(E_ERROR,"Could not connect to host"); + php_error(E_ERROR, "Could not connect to host"); } } - if(stream) - { + if (stream) { zval **cookies; char *header = "POST %s HTTP/1.1\r\nConnection: close\r\nAccept: text/html; text/xml; text/plain\r\nUser-Agent: PHP SOAP 0.1\r\nHost: %s\r\nContent-Type: text/xml\r\nContent-Length: %d\r\nSOAPAction: \"%s\"\r\n"; int size = strlen(header) + strlen(phpurl->host) + strlen(phpurl->path) + 10; @@ -88,11 +100,8 @@ void send_http_soap_request(zval *this_ptr, xmlDoc *doc, char *function_name, ch sprintf(soap_headers, header, phpurl->path, phpurl->host, buf_size, soapaction); } -#ifdef PHP_HAVE_STREAMS err = php_stream_write(stream, soap_headers, strlen(soap_headers)); -#else - err = send(stream, soap_headers, strlen(soap_headers), 0); -#endif + if(err != (int)strlen(soap_headers)) php_error(E_ERROR,"Failed Sending HTTP Headers"); @@ -120,31 +129,22 @@ void send_http_soap_request(zval *this_ptr, xmlDoc *doc, char *function_name, ch smart_str_appendl(&cookie_str, "\r\n", 2); smart_str_0(&cookie_str); -#ifdef PHP_HAVE_STREAMS err = php_stream_write(stream, cookie_str.c, cookie_str.len); -#else - err = send(stream, cookie_str.c, cookie_str.len,0); -#endif + if(err != (int)cookie_str.len) php_error(E_ERROR,"Failed Sending HTTP Headers"); smart_str_free(&cookie_str); } -#ifdef PHP_HAVE_STREAMS err = php_stream_write(stream, "\r\n", 2); -#else - err = send(stream, "\r\n", 2, 0); -#endif + if(err != 2) php_error(E_ERROR,"Failed Sending HTTP Headers"); -#ifdef PHP_HAVE_STREAMS err = php_stream_write(stream, buf, buf_size); -#else - err = send(stream, buf, buf_size, 0); -#endif + if(err != (int)strlen(buf)) php_error(E_ERROR,"Failed Sending HTTP Content"); @@ -159,7 +159,7 @@ void get_http_soap_response(zval *this_ptr, char **buffer, int *buffer_len TSRML int http_header_size, http_body_size, http_close; sdlPtr sdl; zval **socket_ref; - SOAP_STREAM stream; + php_stream *stream; zval **trace; FETCH_THIS_SDL(sdl); @@ -244,15 +244,9 @@ void get_http_soap_response(zval *this_ptr, char **buffer, int *buffer_len TSRML } */ - if(http_close) - { -#ifdef PHP_HAVE_STREAMS + if (http_close) { php_stream_close(stream); -#else - SOCK_CLOSE(stream); -#endif - zend_list_delete(Z_RESVAL_PP(socket_ref)); - zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", strlen("httpsocket") + 1); + zend_hash_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")); } /* Check and see if the server even sent a xml document */ @@ -336,164 +330,127 @@ void get_http_soap_response(zval *this_ptr, char **buffer, int *buffer_len TSRML char *get_http_header_value(char *headers, char *type) { - char *tmp = NULL,*var = NULL; - int size; + char *pos, *tmp = NULL; + int typelen, headerslen; + + typelen = strlen(type); + headerslen = strlen(headers); + + /* header `titles' can be lower case, or any case combination, according + * to the various RFC's. */ + pos = headers; + do { + /* start of buffer or start of line */ + if (strncasecmp(pos, type, typelen) == 0) { + char *eol; + + /* match */ + tmp = pos + typelen; + eol = strstr(tmp, "\r\n"); + if (eol == NULL) { + eol = headers + headerslen; + } + return estrndup(tmp, eol - tmp); + } + + /* find next line */ + pos = strstr(pos, "\r\n"); + if (pos) + pos += 2; - tmp = strstr(headers, type); - if(tmp != NULL) - { - tmp += strlen(type); - size = strstr(tmp, "\r\n") - tmp; - var = emalloc(size + 1); - strncpy(var, tmp, size); - var[size] = '\0'; - } - return var; + } while (pos); + + return NULL; } -int get_http_body(SOAP_STREAM stream, char *headers, char **response, int *out_size TSRMLS_DC) +int get_http_body(php_stream *stream, char *headers, char **response, int *out_size TSRMLS_DC) { - char *trans_enc, *content_length, *http_buf; + char *trans_enc, *content_length, *http_buf = NULL; int http_buf_size = 0; -/* TSRMLS_FETCH();*/ trans_enc = get_http_header_value(headers, "Transfer-Encoding: "); content_length = get_http_header_value(headers, "Content-Length: "); - /* this is temp... - netscape enterprise server sends in lowercase??? - */ - if(content_length == NULL) - content_length = get_http_header_value(headers, "Content-length: "); - - if(trans_enc && !strcmp(trans_enc, "chunked")) - { + if (trans_enc && !strcmp(trans_enc, "chunked")) { int cur = 0, size = 0, buf_size = 0, len_size; char done, chunk_size[10]; done = FALSE; - http_buf = emalloc(1); - *http_buf = '\0'; - while(!done) - { - for (cur = 0; cur < 3 || !(chunk_size[cur - 2] == '\r' && chunk_size[cur - 1] == '\n'); cur++) -#ifdef PHP_HAVE_STREAMS - chunk_size[cur] = php_stream_getc(stream); -#else - chunk_size[cur] = php_sock_fgetc(stream); -#endif - chunk_size[cur] = '\0'; - if(sscanf(chunk_size,"%x",&buf_size) != -1) - { - http_buf = erealloc(http_buf,http_buf_size + buf_size); + http_buf = NULL; + + while (!done) { + php_stream_gets(stream, chunk_size, sizeof(chunk_size)); + + if (sscanf(chunk_size, "%x", &buf_size) != -1) { + http_buf = erealloc(http_buf, http_buf_size + buf_size + 1); len_size = 0; - while(http_buf_size < buf_size) - { -#ifdef PHP_HAVE_STREAMS - len_size += php_stream_read(stream, &http_buf[http_buf_size], buf_size - len_size); -#else - len_size += php_sock_fread(&http_buf[http_buf_size], buf_size - len_size, stream); -#endif + + while (http_buf_size < buf_size) { + len_size += php_stream_read(stream, http_buf + http_buf_size, buf_size - len_size); http_buf_size += len_size; } -#ifdef PHP_HAVE_STREAMS - php_stream_getc(stream);php_stream_getc(stream); -#else + /* Eat up '\r' '\n' */ - php_sock_fgetc(stream);php_sock_fgetc(stream); -#endif + php_stream_getc(stream);php_stream_getc(stream); } - if(buf_size == 0) + if (buf_size == 0) { done = TRUE; + } } efree(trans_enc); - } - else if(content_length) - { + + if (http_buf == NULL) { + http_buf = estrndup("", 1); + http_buf_size = 1; + } else { + http_buf[http_buf_size] = '\0'; + } + + } else if (content_length) { int size; size = atoi(content_length); http_buf = emalloc(size + 1); - while(http_buf_size < size) -#ifdef PHP_HAVE_STREAMS - http_buf_size += php_stream_read(stream, &http_buf[http_buf_size], size - http_buf_size); -#else - http_buf_size += php_sock_fread(&http_buf[http_buf_size], size - http_buf_size, stream); -#endif + while(http_buf_size < size) { + http_buf_size += php_stream_read(stream, http_buf + http_buf_size, size - http_buf_size); + } http_buf[size] = '\0'; efree(content_length); + } else { + php_error(E_ERROR, "Don't know how to read http body, No Content-Length or chunked data"); } - else - php_error(E_ERROR,"Don't know how to read http body, No Content-Length or chunked data"); (*response) = http_buf; (*out_size) = http_buf_size; return TRUE; } -int get_http_headers(SOAP_STREAM stream, char **response, int *out_size TSRMLS_DC) +int get_http_headers(php_stream *stream, char **response, int *out_size TSRMLS_DC) { - int done; + int done = FALSE; char chr; smart_str tmp_response = {0}; - - done = FALSE; + char headerbuf[8192]; while(!done) { -#ifdef PHP_HAVE_STREAMS - chr = php_stream_getc(stream); -#else - chr = php_sock_fgetc(stream); -#endif - if(chr != EOF) - { - smart_str_appendc(&tmp_response, chr); - if(tmp_response.c[tmp_response.len - 2] == '\r' && tmp_response.c[tmp_response.len - 1] == '\n' && - tmp_response.c[tmp_response.len - 4] == '\r' && tmp_response.c[tmp_response.len - 3] == '\n') - { - smart_str_0(&tmp_response); - done = TRUE; - } - } else { - return FALSE; + if (!php_stream_gets(stream, headerbuf, sizeof(headerbuf))) { + break; + } + + if (strcmp(headerbuf, "\r\n") == 0) { + /* 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 TRUE; + return done; } -#ifndef PHP_HAVE_STREAMS -SOCKET get_socket(char* host,int portno,int time) -{ - SOCKET socketd = -1; - struct sockaddr_in server; - struct timeval timeout; - - memset(&server, 0, sizeof(server)); - socketd = socket(AF_INET,SOCK_STREAM,0); - if (socketd == SOCK_ERR) { - if(socketd > 0) - SOCK_CLOSE(socketd); - return FALSE; - } - server.sin_family = AF_INET; - - if(php_lookup_hostname(host,&server.sin_addr)) { - if(socketd > 0) - SOCK_CLOSE(socketd); - return FALSE; - } - server.sin_port = htons((unsigned short)portno); - timeout.tv_sec = time; - timeout.tv_usec = 0; - if (php_connect_nonb(socketd, (struct sockaddr *)&server, sizeof(struct sockaddr_in), &timeout) == SOCK_CONN_ERR) { - if(socketd > 0) - SOCK_CLOSE(socketd); - return FALSE; - } - return socketd; -} -#endif diff --git a/ext/soap/php_http.h b/ext/soap/php_http.h index d7f5b44c7a..2aa3f75ae1 100644 --- a/ext/soap/php_http.h +++ b/ext/soap/php_http.h @@ -5,21 +5,7 @@ void send_http_soap_request(zval *this_ptr, xmlDoc *doc, char *function_name, ch void get_http_soap_response(zval *this_ptr, char **buffer, int *buffer_len TSRMLS_DC); char *get_http_header_value(char *headers, char *type); -int get_http_body(SOAP_STREAM socketd, char *headers, char **response, int *out_size TSRMLS_DC); -int get_http_headers(SOAP_STREAM socketd,char **response, int *out_size TSRMLS_DC); - -#ifndef PHP_HAVE_STREAMS -#ifndef ZEND_WIN32 -# ifndef closesocket -# define closesocket close -# endif -#endif - -#ifndef SOCK_CLOSE -# define SOCK_CLOSE(s) shutdown(s, 0); closesocket(s) -#endif - -SOCKET get_socket(char* host,int portno,int timeout); -#endif +int get_http_body(php_stream *socketd, char *headers, char **response, int *out_size TSRMLS_DC); +int get_http_headers(php_stream *socketd,char **response, int *out_size TSRMLS_DC); #endif diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index 23ba3a6141..fb09934bc2 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -16,19 +16,11 @@ # include "ext/domxml/php_domxml.h" #endif -#ifdef PHP_WIN32 -# ifdef PHP_HAVE_STREAMS -# define SOAP_STREAM php_stream * -# else -# define SOAP_STREAM SOCKET -# endif -#else -# ifdef PHP_HAVE_STREAMS -# define SOAP_STREAM php_stream * -# else -# define SOCKET unsigned int -# define SOAP_STREAM SOCKET -# endif +#ifndef PHP_HAVE_STREAMS +# error You lose - must be compiled against PHP 4.3.0 or later +#endif + +#ifndef PHP_WIN32 # define TRUE 1 # define FALSE 0 # define stricmp strcasecmp @@ -401,8 +393,8 @@ int my_call_user_function(HashTable *function_table, zval **object_pp, zval *fun ss = NULL; \ } -#define FIND_SOCKET_PROPERTY(ss,tmp) zend_hash_find(Z_OBJPROP_P(ss), "httpsocket", sizeof("httpsocket"), (void **)&tmp) -#define FETCH_SOCKET_RES(ss,tmp) ss = (SOAP_STREAM)zend_fetch_resource(tmp TSRMLS_CC, -1, "httpsocket", NULL, 1, le_http_socket) +#define FIND_SOCKET_PROPERTY(ss,tmp) zend_hash_find(Z_OBJPROP_P(ss), "httpsocket", sizeof("httpsocket"), (void **)&tmp) +#define FETCH_SOCKET_RES(ss,tmp) php_stream_from_zval_no_verify(ss,tmp) #define GET_THIS_OBJECT(o) \ o = getThis(); \ diff --git a/ext/soap/php_xml.c b/ext/soap/php_xml.c index 7995356198..786ebc97a5 100644 --- a/ext/soap/php_xml.c +++ b/ext/soap/php_xml.c @@ -1,5 +1,33 @@ #include "php_soap.h" +/* Channel libxml file io layer through the PHP streams subsystem. + * This allows use of ftps:// and https:// urls */ + +int php_stream_xmlIO_match_wrapper(const char *filename) +{ + TSRMLS_FETCH(); + return php_stream_locate_url_wrapper(filename, NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC) ? 1 : 0; + +} + +void *php_stream_xmlIO_open_wrapper(const char *filename) +{ + TSRMLS_FETCH(); + return php_stream_open_wrapper(filename, "rb", ENFORCE_SAFE_MODE|REPORT_ERRORS, NULL); +} + +int php_stream_xmlIO_read(void *context, char *buffer, int len) +{ + TSRMLS_FETCH(); + return php_stream_read((php_stream*)context, buffer, len); +} + +int php_stream_xmlIO_close(void *context) +{ + TSRMLS_FETCH(); + return php_stream_close((php_stream*)context); +} + xmlNsPtr attr_find_ns(xmlAttrPtr node) { if(node->ns) diff --git a/ext/soap/php_xml.h b/ext/soap/php_xml.h index d17caba007..461bf9dd97 100644 --- a/ext/soap/php_xml.h +++ b/ext/soap/php_xml.h @@ -20,4 +20,9 @@ xmlNodePtr get_node_with_attribute_recursive_ex(xmlNodePtr node, char *name, cha int parse_namespace(char *inval,char **value,char **namespace); xmlNodePtr check_and_resolve_href(xmlNodePtr data); +int php_stream_xmlIO_match_wrapper(const char *filename); +void *php_stream_xmlIO_open_wrapper(const char *filename); +int php_stream_xmlIO_read(void *context, char *buffer, int len); +int php_stream_xmlIO_close(void *context); + #endif diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 4a40fec6cf..6531269601 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -1,7 +1,6 @@ #include "php_soap.h" int le_sdl = 0; -int le_http_socket = 0; int le_url = 0; int le_service = 0; @@ -185,6 +184,11 @@ PHP_MINIT_FUNCTION(soap) /* TODO: add ini entry for always use soap errors */ ZEND_INIT_MODULE_GLOBALS(soap, php_soap_init_globals, php_soap_del_globals); + /* Enable php stream/wrapper support for libxml */ + xmlRegisterDefaultInputCallbacks(); + xmlRegisterInputCallbacks(php_stream_xmlIO_match_wrapper, php_stream_xmlIO_open_wrapper, + php_stream_xmlIO_read, php_stream_xmlIO_close); + /* Register SoapObject class */ /* BIG NOTE : THIS EMITS AN COMPILATION WARNING UNDER ZE2 - handle_function_call deprecated. soap_call_function_handler should be of type struct _zend_function, not (*handle_function_call). @@ -209,7 +213,6 @@ PHP_MINIT_FUNCTION(soap) zend_register_internal_class(&soap_param_class_entry TSRMLS_CC); le_sdl = register_list_destructors(NULL, NULL); - le_http_socket = register_list_destructors(delete_http_socket, NULL); le_url = register_list_destructors(delete_url, NULL); le_service = register_list_destructors(delete_service, NULL); @@ -781,6 +784,8 @@ PHP_FUNCTION(addfunction) if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &function_name) == FAILURE) php_error(E_ERROR, "Invalid parameters passed to addfunction"); + /* TODO: could use zend_is_callable here */ + if(function_name->type == IS_ARRAY) { if(service->type == SOAP_FUNCTIONS) @@ -1167,6 +1172,7 @@ PHP_FUNCTION(soapobject) ret = zend_list_insert(sdl, le_sdl); add_property_resource(thisObj, "sdl", ret); + /* FIXME: this is extremely bad practice */ add_property_resource(thisObj, "port", (long)get_binding_from_type(sdl, BINDING_SOAP)); zend_list_addref(ret); } @@ -1261,7 +1267,7 @@ PHP_FUNCTION(__generate) if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|ss", &function, &function_len, &args, &soap_action, &soap_action_len, &uri, &uri_len) == FAILURE) - php_error(E_ERROR, "Invalid arguments to SoapObject->__call"); + php_error(E_ERROR, "Invalid arguments to SoapObject->__generate"); arg_count = zend_hash_num_elements(Z_ARRVAL_P(args)); @@ -1331,6 +1337,7 @@ PHP_FUNCTION(__parse) } if (ret_params) { + FREE_ZVAL(ret_params[0]); efree(ret_params); } } @@ -1381,6 +1388,7 @@ PHP_FUNCTION(__call) } if (ret_params) { + FREE_ZVAL(ret_params[0]); efree(ret_params); } } @@ -1482,9 +1490,7 @@ PHP_FUNCTION(__getlastrequest) if(zend_hash_find(Z_OBJPROP_P(thisObj), "__last_request", sizeof("__last_request"), (void **)&tmp) == SUCCESS) { - *return_value = *(*tmp); - zval_copy_ctor(return_value); - return; + RETURN_STRINGL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp), 1); } RETURN_NULL(); } @@ -1498,9 +1504,7 @@ PHP_FUNCTION(__getlastresponse) if(zend_hash_find(Z_OBJPROP_P(thisObj), "__last_response", sizeof("__last_response"), (void **)&tmp) == SUCCESS) { - *return_value = *(*tmp); - zval_copy_ctor(return_value); - return; + RETURN_STRINGL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp), 1); } RETURN_NULL(); } @@ -1512,8 +1516,6 @@ void soap_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_refe zval *thisObj; char *function = Z_STRVAL(function_name->element); zend_function *builtin_function; - -// TSRMLS_FETCH(); GET_THIS_OBJECT(thisObj); @@ -1578,6 +1580,7 @@ void soap_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_refe } if (ret_params) { + FREE_ZVAL(ret_params[0]); efree(ret_params); } } @@ -2116,17 +2119,6 @@ void delete_sdl(void *handle) free(tmp); } -void delete_http_socket(void *handle) -{ - SOAP_STREAM stream = (SOAP_STREAM)handle; -#ifdef PHP_HAVE_STREAMS - TSRMLS_FETCH(); - php_stream_close(stream); -#else - SOCK_CLOSE(stream); -#endif -} - void delete_url(void *handle) { php_url_free((php_url*)handle); -- 2.50.1