From a19a05825d04d634834f7898ec1a5247fdd6095c Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Wed, 23 Feb 2005 10:50:01 +0000 Subject: [PATCH 1/1] * split into more reasonable modules * no need to have separate buffers for headers and body --- config.m4 | 2 +- config.w32 | 2 +- http.c | 2193 ++----------------------------------------- http_api.c | 718 +------------- http_curl_api.c | 684 ++++++++++++++ http_functions.c | 943 +++++++++++++++++++ http_methods.c | 1143 ++++++++++++++++++++++ package.xml | 12 +- php_http.h | 27 +- php_http_api.h | 111 ++- php_http_curl_api.h | 62 ++ 11 files changed, 2991 insertions(+), 2906 deletions(-) create mode 100644 http_curl_api.c create mode 100644 http_functions.c create mode 100644 http_methods.c create mode 100644 php_http_curl_api.h diff --git a/config.m4 b/config.m4 index 037d0f9..daaf7b4 100644 --- a/config.m4 +++ b/config.m4 @@ -59,7 +59,7 @@ dnl ---- dnl ---- dnl DONE dnl ---- - PHP_NEW_EXTENSION([http], [http.c http_api.c], [$ext_shared]) + PHP_NEW_EXTENSION([http], [http.c http_api.c http_functions.c http_methods.c], [$ext_shared]) PHP_SUBST([HTTP_SHARED_LIBADD]) AC_DEFINE([HAVE_HTTP], [1], [Have extended HTTP support]) fi diff --git a/config.w32 b/config.w32 index e3401b5..6d1db0f 100644 --- a/config.w32 +++ b/config.w32 @@ -4,7 +4,7 @@ ARG_ENABLE("http", "whether to enable extended HTTP support", "no"); if (PHP_HTTP != "no") { - EXTENSION("http", "http.c http_api.c") + EXTENSION("http", "http.c http_functions.c http_methods.c http_api.c http_curl_api.c") AC_DEFINE("HAVE_HTTP", 1, "Have extended HTTP support") if (CHECK_LIB("libcurl.lib", "http", PHP_HTTP) && CHECK_HEADER_ADD_INCLUDE("curl/easy.h", "CFLAGS_HTTP") && diff --git a/http.c b/http.c index af00267..b606bc3 100644 --- a/http.c +++ b/http.c @@ -34,6 +34,7 @@ #include "php_http.h" #include "php_http_api.h" +#include "php_http_curl_api.h" #ifdef ZEND_ENGINE_2 # include "ext/standard/php_http.h" @@ -114,63 +115,9 @@ function_entry http_functions[] = { }; /* }}} */ -#define RETURN_SUCCESS(v) RETURN_BOOL(SUCCESS == (v)) -#define HASH_ORNULL(z) ((z) ? Z_ARRVAL_P(z) : NULL) -#define NO_ARGS if (ZEND_NUM_ARGS()) WRONG_PARAM_COUNT - -#define array_copy(src, dst) zend_hash_copy(Z_ARRVAL_P(dst), Z_ARRVAL_P(src), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)) -#define array_merge(src, dst) zend_hash_merge(Z_ARRVAL_P(dst), Z_ARRVAL_P(src), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *), 1) #ifdef ZEND_ENGINE_2 -# define HTTP_REGISTER_CLASS_EX(classname, name, parent, flags) \ - { \ - zend_class_entry ce; \ - INIT_CLASS_ENTRY(ce, #classname, name## _class_methods); \ - ce.create_object = name## _new_object; \ - name## _ce = zend_register_internal_class_ex(&ce, parent, NULL TSRMLS_CC); \ - name## _ce->ce_flags |= flags; \ - memcpy(& name## _object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \ - name## _object_handlers.clone_obj = NULL; \ - name## _declare_default_properties(name## _ce); \ - } - -# define HTTP_REGISTER_CLASS(classname, name, parent, flags) \ - { \ - zend_class_entry ce; \ - INIT_CLASS_ENTRY(ce, #classname, name## _class_methods); \ - ce.create_object = NULL; \ - name## _ce = zend_register_internal_class_ex(&ce, parent, NULL TSRMLS_CC); \ - name## _ce->ce_flags |= flags; \ - } - -# define getObject(t, o) t * o = ((t *) zend_object_store_get_object(getThis() TSRMLS_CC)) -# define OBJ_PROP(o) o->zo.properties -# define DCL_PROP(a, t, n, v) zend_declare_property_ ##t(ce, (#n), sizeof(#n), (v), (ZEND_ACC_ ##a) TSRMLS_CC) -# define DCL_PROP_Z(a, n, v) zend_declare_property(ce, (#n), sizeof(#n), (v), (ZEND_ACC_ ##a) TSRMLS_CC) -# define DCL_PROP_N(a, n) zend_declare_property_null(ce, (#n), sizeof(#n), (ZEND_ACC_ ##a) TSRMLS_CC) -# define UPD_PROP(o, t, n, v) zend_update_property_ ##t(o->zo.ce, getThis(), (#n), sizeof(#n), (v) TSRMLS_CC) -# define SET_PROP(o, n, z) zend_update_property(o->zo.ce, getThis(), (#n), sizeof(#n), (z) TSRMLS_CC) -# define GET_PROP(o, n) zend_read_property(o->zo.ce, getThis(), (#n), sizeof(#n), 0 TSRMLS_CC) - -# define INIT_PARR(o, n) \ - { \ - zval *__tmp; \ - MAKE_STD_ZVAL(__tmp); \ - array_init(__tmp); \ - SET_PROP(o, n, __tmp); \ - } - -# define FREE_PARR(o, p) \ - { \ - zval *__tmp = NULL; \ - if (__tmp = GET_PROP(o, p)) { \ - zval_dtor(__tmp); \ - FREE_ZVAL(__tmp); \ - __tmp = NULL; \ - } \ - } - /* {{{ HTTPi */ zend_class_entry *httpi_ce; @@ -212,10 +159,6 @@ zend_function_entry httpi_class_methods[] = { zend_class_entry *httpi_response_ce; static zend_object_handlers httpi_response_object_handlers; -typedef struct { - zend_object zo; -} httpi_response_object; - #define httpi_response_declare_default_properties(ce) _httpi_response_declare_default_properties(ce TSRMLS_CC) static inline void _httpi_response_declare_default_properties(zend_class_entry *ce TSRMLS_DC) { @@ -300,431 +243,6 @@ zend_function_entry httpi_response_class_methods[] = { {NULL, NULL, NULL} }; - -/* {{{ proto void HTTPi_Response::__construct(bool cache, bool gzip) - * - */ -PHP_METHOD(HTTPi_Response, __construct) -{ - zend_bool do_cache = 0, do_gzip = 0; - getObject(httpi_response_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|bb", &do_cache, &do_gzip)) { - // throw exception - return; - } - - UPD_PROP(obj, long, cache, do_cache); - UPD_PROP(obj, long, gzip, do_gzip); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Response::setCache(bool cache) - * - */ -PHP_METHOD(HTTPi_Response, setCache) -{ - zend_bool do_cache = 0; - getObject(httpi_response_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_cache)) { - RETURN_FALSE; - } - - UPD_PROP(obj, long, cache, do_cache); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto bool HTTPi_Response::getCache() - * - */ -PHP_METHOD(HTTPi_Response, getCache) -{ - zval *do_cache = NULL; - getObject(httpi_response_object, obj); - - NO_ARGS; - - do_cache = GET_PROP(obj, cache); - RETURN_BOOL(Z_LVAL_P(do_cache)); -} -/* }}}*/ - -/* {{{ proto bool HTTPi_Response::setGzip(bool gzip) - * - */ -PHP_METHOD(HTTPi_Response, setGzip) -{ - zend_bool do_gzip = 0; - getObject(httpi_response_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_gzip)) { - RETURN_FALSE; - } - - UPD_PROP(obj, long, gzip, do_gzip); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto bool HTTPi_Response::getGzip() - * - */ -PHP_METHOD(HTTPi_Response, getGzip) -{ - zval *do_gzip = NULL; - getObject(httpi_response_object, obj); - - NO_ARGS; - - do_gzip = GET_PROP(obj, gzip); - RETURN_BOOL(Z_LVAL_P(do_gzip)); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Response::setCacheControl(string control[, bool raw = false]) - * - */ -PHP_METHOD(HTTPi_Response, setCacheControl) -{ - char *ccontrol; - int cc_len; - zend_bool raw = 0; - getObject(httpi_response_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &ccontrol, &cc_len, &raw)) { - RETURN_FALSE; - } - - if ((!raw) && (strcmp(ccontrol, "public") && strcmp(ccontrol, "private") && strcmp(ccontrol, "no-cache"))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol); - RETURN_FALSE; - } - - UPD_PROP(obj, long, raw_cache_header, raw); - UPD_PROP(obj, string, cacheControl, ccontrol); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto string HTTPi_Response::getCacheControl() - * - */ -PHP_METHOD(HTTPi_Response, getCacheControl) -{ - zval *ccontrol; - getObject(httpi_response_object, obj); - - NO_ARGS; - - ccontrol = GET_PROP(obj, cacheControl); - RETURN_STRINGL(Z_STRVAL_P(ccontrol), Z_STRLEN_P(ccontrol), 1); -} -/* }}} */ - -/* {{{ proto bool HTTPi::setContentType(string content_type) - * - */ -PHP_METHOD(HTTPi_Response, setContentType) -{ - char *ctype; - int ctype_len; - getObject(httpi_response_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ctype_len)) { - RETURN_FALSE; - } - - if (!strchr(ctype, '/')) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Content type '%s' doesn't seem to contain a primary and a secondary part", ctype); - RETURN_FALSE; - } - - UPD_PROP(obj, string, contentType, ctype); - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto string HTTPi_Response::getContentType() - * - */ -PHP_METHOD(HTTPi_Response, getContentType) -{ - zval *ctype; - getObject(httpi_response_object, obj); - - NO_ARGS; - - ctype = GET_PROP(obj, contentType); - RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Response::setContentDisposition(string filename[, bool inline = false]) - * - */ -PHP_METHOD(HTTPi_Response, setContentDisposition) -{ - char *file; - int file_len; - zend_bool is_inline = 0; - getObject(httpi_response_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &file, &file_len, &is_inline)) { - RETURN_FALSE; - } - - UPD_PROP(obj, string, dispoFile, file); - UPD_PROP(obj, long, dispoInline, is_inline); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto array HTTPi_Response::getContentDisposition() - * - */ -PHP_METHOD(HTTPi_Response, getContentDisposition) -{ - zval *file; - zval *is_inline; - getObject(httpi_response_object, obj); - - if (ZEND_NUM_ARGS()) { - WRONG_PARAM_COUNT; - } - - file = GET_PROP(obj, dispoFile); - is_inline = GET_PROP(obj, dispoInline); - - array_init(return_value); - add_assoc_stringl(return_value, "filename", Z_STRVAL_P(file), Z_STRLEN_P(file), 1); - add_assoc_bool(return_value, "inline", Z_LVAL_P(is_inline)); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Response::setETag(string etag) - * - */ -PHP_METHOD(HTTPi_Response, setETag) -{ - char *etag; - int etag_len; - getObject(httpi_response_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len)) { - RETURN_FALSE; - } - - UPD_PROP(obj, string, eTag, etag); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto string HTTPi_Response::getETag() - * - */ -PHP_METHOD(HTTPi_Response, getETag) -{ - zval *etag; - getObject(httpi_response_object, obj); - - NO_ARGS; - - etag = GET_PROP(obj, eTag); - RETURN_STRINGL(Z_STRVAL_P(etag), Z_STRLEN_P(etag), 1); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Response::setData(string data) - * - */ -PHP_METHOD(HTTPi_Response, setData) -{ - zval *the_data; - char *etag; - getObject(httpi_response_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_data)) { - RETURN_FALSE; - } - - convert_to_string_ex(&the_data); - SET_PROP(obj, data, the_data); - UPD_PROP(obj, long, lastModified, http_lmod(the_data, SEND_DATA)); - UPD_PROP(obj, long, send_mode, SEND_DATA); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto string HTTPi_Response::getData() - * - */ -PHP_METHOD(HTTPi_Response, getData) -{ - zval *the_data; - getObject(httpi_response_object, obj); - - NO_ARGS; - - the_data = GET_PROP(obj, data); - RETURN_STRINGL(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), 1); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Response::setStream(resource stream) - * - */ -PHP_METHOD(HTTPi_Response, setStream) -{ - zval *the_stream; - php_stream *the_real_stream; - char *etag; - getObject(httpi_response_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &the_stream)) { - RETURN_FALSE; - } - - php_stream_from_zval(the_real_stream, &the_stream); - - SET_PROP(obj, stream, the_stream); - UPD_PROP(obj, long, lastModified, http_lmod(the_real_stream, SEND_RSRC)); - UPD_PROP(obj, long, send_mode, SEND_RSRC); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto resource HTTPi_Response::getStream() - * - */ -PHP_METHOD(HTTPi_Response, getStream) -{ - zval *the_stream; - getObject(httpi_response_object, obj); - - NO_ARGS; - - the_stream = GET_PROP(obj, stream); - RETURN_RESOURCE(Z_LVAL_P(the_stream)); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Response::setFile(string file) - * - */ -PHP_METHOD(HTTPi_Response, setFile) -{ - zval *the_file; - getObject(httpi_response_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_file)) { - RETURN_FALSE; - } - - convert_to_string_ex(&the_file); - - UPD_PROP(obj, string, file, Z_STRVAL_P(the_file)); - UPD_PROP(obj, long, lastModified, http_lmod(the_file, -1)); - UPD_PROP(obj, long, send_mode, -1); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto string HTTPi_Response::getFile() - * - */ -PHP_METHOD(HTTPi_Response, getFile) -{ - zval *the_file; - getObject(httpi_response_object, obj); - - NO_ARGS; - - the_file = GET_PROP(obj, file); - RETURN_STRINGL(Z_STRVAL_P(the_file), Z_STRLEN_P(the_file), 1); -} -/* }}} */ - -PHP_METHOD(HTTPi_Response, send) -{ - zval *do_cache, *do_gzip; - getObject(httpi_response_object, obj); - - do_cache = GET_PROP(obj, cache); - do_gzip = GET_PROP(obj, gzip); - - /* caching */ - if (Z_LVAL_P(do_cache)) { - zval *cctrl, *etag, *lmod, *ccraw; - - etag = GET_PROP(obj, eTag); - lmod = GET_PROP(obj, lastModified); - cctrl = GET_PROP(obj, cacheControl); - ccraw = GET_PROP(obj, raw_cache_header); - - if (Z_LVAL_P(ccraw)) { - http_cache_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag), Z_STRVAL_P(cctrl), Z_STRLEN_P(cctrl)); - http_cache_last_modified(Z_LVAL_P(lmod), Z_LVAL_P(lmod) ? Z_LVAL_P(lmod) : time(NULL), Z_STRVAL_P(cctrl), Z_STRLEN_P(cctrl)); - } else { - char cc_header[42] = {0}; - sprintf(cc_header, "%s, must-revalidate, max-age=0", Z_STRVAL_P(cctrl)); - http_cache_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag), cc_header, strlen(cc_header)); - http_cache_last_modified(Z_LVAL_P(lmod), Z_LVAL_P(lmod) ? Z_LVAL_P(lmod) : time(NULL), cc_header, strlen(cc_header)); - } - } - - /* gzip */ - if (Z_LVAL_P(do_gzip)) { - /* ... */ - } - - /* content type */ - { - zval *ctype = GET_PROP(obj, contentType); - if (Z_STRLEN_P(ctype)) { - http_send_content_type(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype)); - } else { - http_send_content_type("application/x-octetstream", sizeof("application/x-octetstream") - 1); - } - } - - /* content disposition */ - { - zval *dispo_file = GET_PROP(obj, dispoFile); - if (Z_STRLEN_P(dispo_file)) { - zval *dispo_inline = GET_PROP(obj, dispoInline); - http_send_content_disposition(Z_STRVAL_P(dispo_file), Z_STRLEN_P(dispo_file), Z_LVAL_P(dispo_inline)); - } - } - - /* send */ - { - zval *send_mode = GET_PROP(obj, send_mode); - switch (Z_LVAL_P(send_mode)) - { - case SEND_DATA: - { - RETURN_SUCCESS(http_send_data(GET_PROP(obj, data))); - } - - case SEND_RSRC: - { - php_stream *the_real_stream; - zval *the_stream = GET_PROP(obj, stream); - php_stream_from_zval(the_real_stream, &the_stream); - RETURN_SUCCESS(http_send_stream(the_real_stream)); - } - - default: - { - RETURN_SUCCESS(http_send_file(GET_PROP(obj, file))); - } - } - } -} /* }}} */ /* {{{ HTTPi_Request */ @@ -733,14 +251,6 @@ PHP_METHOD(HTTPi_Response, send) zend_class_entry *httpi_request_ce; static zend_object_handlers httpi_request_object_handlers; -typedef struct { - zend_object zo; - CURL *ch; - - struct curl_httppost *post_data[2]; - -} httpi_request_object; - #define httpi_request_declare_default_properties(ce) _httpi_request_declare_default_properties(ce TSRMLS_CC) static inline void _httpi_request_declare_default_properties(zend_class_entry *ce TSRMLS_DC) { @@ -783,8 +293,6 @@ zend_object_value _httpi_request_new_object(zend_class_entry *ce TSRMLS_DC) o = ecalloc(1, sizeof(httpi_request_object)); o->zo.ce = ce; o->ch = curl_easy_init(); - o->post_data[0] = NULL; - o->post_data[1] = NULL; ALLOC_HASHTABLE(OBJ_PROP(o)); zend_hash_init(OBJ_PROP(o), 0, NULL, ZVAL_PTR_DTOR, 0); @@ -836,1654 +344,85 @@ zend_function_entry httpi_request_class_methods[] = { {NULL, NULL, NULL} }; -/* {{{ proto void HTTPi_Request::__construct([string url[, long request_method = HTTP_GET]]) - * - */ -PHP_METHOD(HTTPi_Request, __construct) -{ - char *URL = NULL; - int URL_len; - long meth = -1; - zval *info, *opts, *resp; - getObject(httpi_request_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &URL, &URL_len, &meth)) { - return; - } +#endif /* HTTP_HAVE_CURL */ +/* }}} */ - INIT_PARR(obj, options); - INIT_PARR(obj, responseInfo); - INIT_PARR(obj, responseData); - INIT_PARR(obj, postData); - INIT_PARR(obj, postFiles); +#endif /* ZEND_ENGINE_2 */ - if (URL) { - UPD_PROP(obj, string, url, URL); - } - if (meth > -1) { - UPD_PROP(obj, long, method, meth); - } -} +/* {{{ http_module_entry */ +zend_module_entry http_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + "http", + http_functions, + PHP_MINIT(http), + PHP_MSHUTDOWN(http), + PHP_RINIT(http), + PHP_RSHUTDOWN(http), + PHP_MINFO(http), +#if ZEND_MODULE_API_NO >= 20010901 + PHP_EXT_HTTP_VERSION, +#endif + STANDARD_MODULE_PROPERTIES +}; /* }}} */ -/* {{{ proto void HTTPi_Request::__destruct() - * - */ -PHP_METHOD(HTTPi_Request, __destruct) +/* {{{ php_http_init_globals(zend_http_globals *) */ +static void php_http_init_globals(zend_http_globals *http_globals) { - getObject(httpi_request_object, obj); - - NO_ARGS; - - FREE_PARR(obj, options); - FREE_PARR(obj, responseInfo); - FREE_PARR(obj, responseData); - FREE_PARR(obj, postData); - FREE_PARR(obj, postFiles); + http_globals->etag_started = 0; + http_globals->ctype = NULL; + http_globals->etag = NULL; + http_globals->lmod = 0; +#ifdef HTTP_HAVE_CURL + http_globals->curlbuf.data = NULL; + http_globals->curlbuf.used = 0; + http_globals->curlbuf.free = 0; + http_globals->curlbuf.size = 0; +#endif + http_globals->allowed_methods = NULL; } /* }}} */ -/* {{{ proto bool HTTPi_Request::setOptions(array options) - * - */ -PHP_METHOD(HTTPi_Request, setOptions) +/* {{{ static inline STATUS http_check_allowed_methods(char *, int) */ +#define http_check_allowed_methods(m, l) _http_check_allowed_methods((m), (l) TSRMLS_CC) +static inline void _http_check_allowed_methods(char *methods, int length TSRMLS_DC) { - zval *opts, *old_opts, **opt; - getObject(httpi_request_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &opts)) { - RETURN_FALSE; - } - - old_opts = GET_PROP(obj, options); - - /* headers and cookies need extra attention -- thus cannot use zend_hash_merge() or php_array_merge() directly */ - for ( zend_hash_internal_pointer_reset(Z_ARRVAL_P(opts)); - zend_hash_get_current_data(Z_ARRVAL_P(opts), (void **) &opt) == SUCCESS; - zend_hash_move_forward(Z_ARRVAL_P(opts))) { - char *key; - long idx; - if (HASH_KEY_IS_STRING == zend_hash_get_current_key(Z_ARRVAL_P(opts), &key, &idx, 0)) { - if (!strcmp(key, "headers")) { - zval **headers; - if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "headers", sizeof("headers"), (void **) &headers)) { - array_merge(*opt, *headers); - continue; - } - } else if (!strcmp(key, "cookies")) { - zval **cookies; - if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "cookies", sizeof("cookies"), (void **) &cookies)) { - array_merge(*opt, *cookies); - continue; - } - } - zval_add_ref(opt); - add_assoc_zval(old_opts, key, *opt); - } + if (length && SG(request_info).request_method && (!strstr(methods, SG(request_info).request_method))) { + char *allow_header = emalloc(length + sizeof("Allow: ")); + sprintf(allow_header, "Allow: %s", methods); + http_send_header(allow_header); + efree(allow_header); + http_send_status(405); + zend_bailout(); } - RETURN_TRUE; } /* }}} */ -/* {{{ proto array HTTPi_Request::getOptions() - * - */ -PHP_METHOD(HTTPi_Request, getOptions) +/* {{{ PHP_INI */ +PHP_INI_MH(update_allowed_methods) { - zval *opts; - getObject(httpi_request_object, obj); - - NO_ARGS; - - opts = GET_PROP(obj, options); - array_init(return_value); - array_copy(opts, return_value); + http_check_allowed_methods(new_value, new_value_length); + return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); } -/* }}} */ - -/* {{{ proto bool HTTPi_Request::setURL(string url) - * - */ -PHP_METHOD(HTTPi_Request, setURL) -{ - char *URL = NULL; - int URL_len; - getObject(httpi_request_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URL, &URL_len)) { - RETURN_FALSE; - } - UPD_PROP(obj, string, url, URL); - RETURN_TRUE; -} +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("http.allowed_methods", "OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT", PHP_INI_ALL, update_allowed_methods, allowed_methods, zend_http_globals, http_globals) +PHP_INI_END() /* }}} */ -/* {{{ proto string HTTPi_Request::getUrl() - * - */ -PHP_METHOD(HTTPi_Request, getURL) +/* {{{ PHP_MINIT_FUNCTION */ +PHP_MINIT_FUNCTION(http) { - zval *URL; - getObject(httpi_request_object, obj); + ZEND_INIT_MODULE_GLOBALS(http, php_http_init_globals, NULL); + REGISTER_INI_ENTRIES(); - NO_ARGS; - - URL = GET_PROP(obj, url); - RETURN_STRINGL(Z_STRVAL_P(URL), Z_STRLEN_P(URL), 1); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Request::setMethod(long request_method) - * - */ -PHP_METHOD(HTTPi_Request, setMethod) -{ - long meth; - getObject(httpi_request_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &meth)) { - RETURN_FALSE; - } - - UPD_PROP(obj, long, method, meth); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto long HTTPi_Request::getMethod() - * - */ -PHP_METHOD(HTTPi_Request, getMethod) -{ - zval *meth; - getObject(httpi_request_object, obj); - - NO_ARGS; - - meth = GET_PROP(obj, method); - RETURN_LONG(Z_LVAL_P(meth)); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Request::setContentType(string content_type) - * - */ -PHP_METHOD(HTTPi_Request, setContentType) -{ - char *ctype; - int ct_len; - getObject(httpi_request_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ct_len)) { - RETURN_FALSE; - } - - if (!strchr(ctype, '/')) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "Content-Type '%s' doesn't seem to contain a primary and a secondary part", - ctype); - RETURN_FALSE; - } - - UPD_PROP(obj, string, contentType, ctype); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto string HTTPi_Request::getContentType() - * - */ -PHP_METHOD(HTTPi_Request, getContentType) -{ - zval *ctype; - getObject(httpi_request_object, obj); - - NO_ARGS; - - ctype = GET_PROP(obj, contentType); - RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Request::setQueryData(mixed query_data) - * - */ -PHP_METHOD(HTTPi_Request, setQueryData) -{ - zval *qdata; - getObject(httpi_request_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &qdata)) { - RETURN_FALSE; - } - - if ((Z_TYPE_P(qdata) == IS_ARRAY) || (Z_TYPE_P(qdata) == IS_OBJECT)) { - smart_str qstr = {0}; - HTTP_URL_ARGSEP_OVERRIDE; - if (SUCCESS != php_url_encode_hash_ex(HASH_OF(qdata), &qstr, NULL, 0, NULL, 0, NULL, 0, NULL TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't encode query data"); - if (qstr.c) { - efree(qstr.c); - } - HTTP_URL_ARGSEP_RESTORE; - RETURN_FALSE; - } - HTTP_URL_ARGSEP_RESTORE; - smart_str_0(&qstr); - UPD_PROP(obj, string, queryData, qstr.c); - efree(qstr.c); - RETURN_TRUE; - } - - convert_to_string(qdata); - UPD_PROP(obj, string, queryData, Z_STRVAL_P(qdata)); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto string HTTPi_Request::getQueryData() - * - */ -PHP_METHOD(HTTPi_Request, getQueryData) -{ - zval *qdata; - getObject(httpi_request_object, obj); - - NO_ARGS; - - qdata = GET_PROP(obj, queryData); - RETURN_STRINGL(Z_STRVAL_P(qdata), Z_STRLEN_P(qdata), 1); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Request::addQueryData(array query_params) - * - */ -PHP_METHOD(HTTPi_Request, addQueryData) -{ - zval *qdata, *old_qdata; - smart_str qstr = {0}; - char *separator; - getObject(httpi_request_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &qdata)) { - RETURN_FALSE; - } - - old_qdata = GET_PROP(obj, queryData); - if (Z_STRLEN_P(old_qdata)) { - smart_str_appendl(&qstr, Z_STRVAL_P(old_qdata), Z_STRLEN_P(old_qdata)); - } - - HTTP_URL_ARGSEP_OVERRIDE; - if (SUCCESS != php_url_encode_hash_ex(HASH_OF(qdata), &qstr, NULL, 0, NULL, 0, NULL, 0, NULL TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't encode query data"); - if (qstr.c) { - efree(qstr.c); - } - HTTP_URL_ARGSEP_RESTORE; - RETURN_FALSE; - } - HTTP_URL_ARGSEP_RESTORE; - - smart_str_0(&qstr); - - UPD_PROP(obj, string, queryData, qstr.c); - efree(qstr.c); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto void HTTPi_Request::unsetQueryData() - * - */ -PHP_METHOD(HTTPi_Request, unsetQueryData) -{ - getObject(httpi_request_object, obj); - - NO_ARGS; - - UPD_PROP(obj, string, queryData, ""); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Request::addPostData(array post_data) - * - */ -PHP_METHOD(HTTPi_Request, addPostData) -{ - zval *post, *post_data; - getObject(httpi_request_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &post_data)) { - RETURN_FALSE; - } - - post = GET_PROP(obj, postData); - array_merge(post_data, post); - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto bool HTTPi_Request::setPostData(array post_data) - * - */ -PHP_METHOD(HTTPi_Request, setPostData) -{ - zval *post, *post_data; - getObject(httpi_request_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &post_data)) { - RETURN_FALSE; - } - - post = GET_PROP(obj, postData); - zend_hash_clean(Z_ARRVAL_P(post)); - array_copy(post_data, post); - - RETURN_TRUE; -} -/* }}}*/ - -/* {{{ proto array HTTPi_Request::getPostData() - * - */ -PHP_METHOD(HTTPi_Request, getPostData) -{ - zval *post_data; - getObject(httpi_request_object, obj); - - NO_ARGS; - - post_data = GET_PROP(obj, postData); - array_init(return_value); - array_copy(post_data, return_value); -} -/* }}} */ - -/* {{{ proto void HTTPi_Request::unsetPostData() - * - */ -PHP_METHOD(HTTPi_Request, unsetPostData) -{ - zval *post_data; - getObject(httpi_request_object, obj); - - NO_ARGS; - - post_data = GET_PROP(obj, postData); - zend_hash_clean(Z_ARRVAL_P(post_data)); -} -/* }}} */ - -/* {{{ proto bool HTTPi_Request::addPostFile(string name, string file[, string content_type = "application/x-octetstream"]) - * - */ -PHP_METHOD(HTTPi_Request, addPostFile) -{ - zval *files, *entry; - char *name, *file, *type = NULL; - int name_len, file_len, type_len = 0; - getObject(httpi_request_object, obj); - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s", &name, &name_len, &file, &file_len, &type, &type_len)) { - RETURN_FALSE; - } - - if (type_len) { - if (!strchr(type, '/')) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Content-Type '%s' doesn't seem to contain a primary and a secondary part", type); - RETURN_FALSE; - } - } else { - type = "application/x-octetstream"; - type_len = sizeof("application/x-octetstream") - 1; - } - - MAKE_STD_ZVAL(entry); - array_init(entry); - - add_assoc_stringl(entry, "name", name, name_len, 1); - add_assoc_stringl(entry, "type", type, type_len, 1); - add_assoc_stringl(entry, "file", file, file_len, 1); - - files = GET_PROP(obj, postFiles); - add_next_index_zval(files, entry); - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto array HTTPi_Request::getPostFiles() - * - */ -PHP_METHOD(HTTPi_Request, getPostFiles) -{ - zval *files; - getObject(httpi_request_object, obj); - - NO_ARGS; - - files = GET_PROP(obj, postFiles); - - array_init(return_value); - array_copy(files, return_value); -} -/* }}} */ - -/* {{{ proto void HTTPi_Request::unsetPostFiles() - * - */ -PHP_METHOD(HTTPi_Request, unsetPostFiles) -{ - zval *files; - getObject(httpi_request_object, obj); - - NO_ARGS; - - files = GET_PROP(obj, postFiles); - zend_hash_clean(Z_ARRVAL_P(files)); -} -/* }}} */ - -/* {{{ proto array HTTPi_Request::getResponseData() - * - */ -PHP_METHOD(HTTPi_Request, getResponseData) -{ - zval *data; - getObject(httpi_request_object, obj); - - NO_ARGS; - - data = GET_PROP(obj, responseData); - array_init(return_value); - array_copy(data, return_value); -} -/* }}} */ - -/* {{{ proto array HTTPi_Request::getResponseHeaders() - * - */ -PHP_METHOD(HTTPi_Request, getResponseHeaders) -{ - zval *data, **headers; - getObject(httpi_request_object, obj); - - NO_ARGS; - - array_init(return_value); - data = GET_PROP(obj, responseData); - if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &headers)) { - array_copy(*headers, return_value); - } -} -/* }}} */ - -/* {{{ proto string HTTPi_Request::getResponseBody() - * - */ -PHP_METHOD(HTTPi_Request, getResponseBody) -{ - zval *data, **body; - getObject(httpi_request_object, obj); - - NO_ARGS; - - data = GET_PROP(obj, responseData); - if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "body", sizeof("body"), (void **) &body)) { - RETURN_STRINGL(Z_STRVAL_PP(body), Z_STRLEN_PP(body), 1); - } else { - Z_TYPE_P(return_value) = IS_NULL; - } -} -/* }}} */ - -/* {{{ proto array HTTPi_Request::getResponseInfo() - * - */ -PHP_METHOD(HTTPi_Request, getResponseInfo) -{ - zval *info; - getObject(httpi_request_object, obj); - - NO_ARGS; - - info = GET_PROP(obj, responseInfo); - array_init(return_value); - array_copy(info, return_value); -} -/* }}}*/ - -/* {{{ proto bool HTTPi_Request::send() - * - */ -PHP_METHOD(HTTPi_Request, send) -{ - STATUS status = FAILURE; - zval *meth, *URL, *qdata, *opts, *info, *resp; - char *response_data, *request_uri; - size_t response_len; - getObject(httpi_request_object, obj); - - NO_ARGS; - - if ((!obj->ch) && (!(obj->ch = curl_easy_init()))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initilaize cURL"); - RETURN_FALSE; - } - - meth = GET_PROP(obj, method); - URL = GET_PROP(obj, url); - qdata = GET_PROP(obj, queryData); - opts = GET_PROP(obj, options); - info = GET_PROP(obj, responseInfo); - resp = GET_PROP(obj, responseData); - - // HTTP_URI_MAXLEN+1 big char * - request_uri = http_absolute_uri(Z_STRVAL_P(URL), NULL); - - if (Z_STRLEN_P(qdata) && (strlen(request_uri) < HTTP_URI_MAXLEN)) { - if (!strchr(request_uri, '?')) { - strcat(request_uri, "?"); - } else { - strcat(request_uri, "&"); - } - strncat(request_uri, Z_STRVAL_P(qdata), HTTP_URI_MAXLEN - strlen(request_uri)); - } - - switch (Z_LVAL_P(meth)) - { - case HTTP_GET: - status = http_get_ex(obj->ch, request_uri, Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len); - break; - - case HTTP_HEAD: - status = http_head_ex(obj->ch, request_uri, Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len); - break; - - case HTTP_POST: - { - zval *post_files, *post_data, **data; - - post_files = GET_PROP(obj, postFiles); - post_data = GET_PROP(obj, postData); - - if (!zend_hash_num_elements(Z_ARRVAL_P(post_files))) { - - /* urlencoded post */ - status = http_post_array_ex(obj->ch, request_uri, Z_ARRVAL_P(post_data), Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len); - - } else { - - /* - * multipart post - */ - - /* normal data */ - for ( zend_hash_internal_pointer_reset(Z_ARRVAL_P(post_data)); - zend_hash_get_current_data(Z_ARRVAL_P(post_data), (void **) &data) == SUCCESS; - zend_hash_move_forward(Z_ARRVAL_P(post_data))) { - - char *key; - long idx; - - if (HASH_KEY_IS_STRING == zend_hash_get_current_key(Z_ARRVAL_P(post_data), &key, &idx, 0)) { - convert_to_string_ex(data); - curl_formadd(&obj->post_data[0], &obj->post_data[1], - CURLFORM_COPYNAME, key, - CURLFORM_COPYCONTENTS, Z_STRVAL_PP(data), - CURLFORM_CONTENTSLENGTH, Z_STRLEN_PP(data), - CURLFORM_END - ); - } - } - - /* file data */ - for ( zend_hash_internal_pointer_reset(Z_ARRVAL_P(post_files)); - zend_hash_get_current_data(Z_ARRVAL_P(post_files), (void **) &data) == SUCCESS; - zend_hash_move_forward(Z_ARRVAL_P(post_files))) { - - zval **file, **type, **name; - - if ( - SUCCESS == zend_hash_find(Z_ARRVAL_PP(data), "name", sizeof("name"), (void **) &name) && - SUCCESS == zend_hash_find(Z_ARRVAL_PP(data), "type", sizeof("type"), (void **) &type) && - SUCCESS == zend_hash_find(Z_ARRVAL_PP(data), "file", sizeof("file"), (void **) &file) - ) { - - curl_formadd(&obj->post_data[0], &obj->post_data[1], - CURLFORM_COPYNAME, Z_STRVAL_PP(name), - CURLFORM_FILENAME, Z_STRVAL_PP(name), - CURLFORM_FILE, Z_STRVAL_PP(file), - CURLFORM_CONTENTTYPE, Z_STRVAL_PP(type), - CURLFORM_END - ); - } - } - - status = http_post_curldata_ex(obj->ch, request_uri, obj->post_data[0], Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len); - curl_formfree(obj->post_data[0]); - } - } - break; - - default: - break; - } - - efree(request_uri); - - /* final data handling */ - if (status != SUCCESS) { - RETURN_FALSE; - } else { - zval *zheaders, *zbody; - - MAKE_STD_ZVAL(zbody); - MAKE_STD_ZVAL(zheaders) - array_init(zheaders); - - if (SUCCESS != http_split_response_ex(response_data, response_len, zheaders, zbody)) { - zval_dtor(zheaders); - efree(zheaders), - efree(zbody); - efree(response_data); - RETURN_FALSE; - } - - add_assoc_zval(resp, "headers", zheaders); - add_assoc_zval(resp, "body", zbody); - - efree(response_data); - - RETURN_TRUE; - } - /* */ -} -/* }}} */ - -#endif /* HTTP_HAVE_CURL */ -/* }}} */ - -#endif /* ZEND_ENGINE_2 */ - -/* {{{ http_module_entry */ -zend_module_entry http_module_entry = { -#if ZEND_MODULE_API_NO >= 20010901 - STANDARD_MODULE_HEADER, -#endif - "http", - http_functions, - PHP_MINIT(http), - PHP_MSHUTDOWN(http), - PHP_RINIT(http), - PHP_RSHUTDOWN(http), - PHP_MINFO(http), -#if ZEND_MODULE_API_NO >= 20010901 - PHP_EXT_HTTP_VERSION, -#endif - STANDARD_MODULE_PROPERTIES -}; -/* }}} */ - -/* {{{ proto string http_date([int timestamp]) - * - * This function returns a valid HTTP date regarding RFC 822/1123 - * looking like: "Wed, 22 Dec 2004 11:34:47 GMT" - * - */ -PHP_FUNCTION(http_date) -{ - long t = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) { - RETURN_FALSE; - } - - if (t == -1) { - t = (long) time(NULL); - } - - RETURN_STRING(http_date(t), 0); -} -/* }}} */ - -/* {{{ proto string http_absolute_uri(string url[, string proto]) - * - * This function returns an absolute URI constructed from url. - * If the url is already abolute but a different proto was supplied, - * only the proto part of the URI will be updated. If url has no - * path specified, the path of the current REQUEST_URI will be taken. - * The host will be taken either from the Host HTTP header of the client - * the SERVER_NAME or just localhost if prior are not available. - * - * Some examples: - *
- *  url = "page.php"                    => http://www.example.com/current/path/page.php
- *  url = "/page.php"                   => http://www.example.com/page.php
- *  url = "/page.php", proto = "https"  => https://www.example.com/page.php
- * 
- * - */ -PHP_FUNCTION(http_absolute_uri) -{ - char *url = NULL, *proto = NULL; - int url_len = 0, proto_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &url, &url_len, &proto, &proto_len) != SUCCESS) { - RETURN_FALSE; - } - - RETURN_STRING(http_absolute_uri(url, proto), 0); -} -/* }}} */ - -/* {{{ proto string http_negotiate_language(array supported[, string default = 'en-US']) - * - * This function negotiates the clients preferred language based on its - * Accept-Language HTTP header. It returns the negotiated language or - * the default language if none match. - * - * The qualifier is recognized and languages without qualifier are rated highest. - * - * The supported parameter is expected to be an array having - * the supported languages as array values. - * - * Example: - *
- * 
- * 
- * - */ -PHP_FUNCTION(http_negotiate_language) -{ - zval *supported; - char *def = NULL; - int def_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) { - RETURN_FALSE; - } - - if (!def) { - def = "en-US"; - } - - RETURN_STRING(http_negotiate_language(supported, def), 0); -} -/* }}} */ - -/* {{{ proto string http_negotiate_charset(array supported[, string default = 'iso-8859-1']) - * - * This function negotiates the clients preferred charset based on its - * Accept-Charset HTTP header. It returns the negotiated charset or - * the default charset if none match. - * - * The qualifier is recognized and charset without qualifier are rated highest. - * - * The supported parameter is expected to be an array having - * the supported charsets as array values. - * - * Example: - *
- * 
- * 
- */ -PHP_FUNCTION(http_negotiate_charset) -{ - zval *supported; - char *def = NULL; - int def_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) { - RETURN_FALSE; - } - - if (!def) { - def = "iso-8859-1"; - } - - RETURN_STRING(http_negotiate_charset(supported, def), 0); -} -/* }}} */ - -/* {{{ proto bool http_send_status(int status) - * - * Send HTTP status code. - * - */ -PHP_FUNCTION(http_send_status) -{ - int status = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &status) != SUCCESS) { - RETURN_FALSE; - } - if (status < 100 || status > 510) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid HTTP status code (100-510): %d", status); - RETURN_FALSE; - } - - RETURN_SUCCESS(http_send_status(status)); -} -/* }}} */ - -/* {{{ proto bool http_send_last_modified([int timestamp]) - * - * This converts the given timestamp to a valid HTTP date and - * sends it as "Last-Modified" HTTP header. If timestamp is - * omitted, current time is sent. - * - */ -PHP_FUNCTION(http_send_last_modified) -{ - long t = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) { - RETURN_FALSE; - } - - if (t == -1) { - t = (long) time(NULL); - } - - RETURN_SUCCESS(http_send_last_modified(t)); -} -/* }}} */ - -/* {{{ proto bool http_send_content_type([string content_type = 'application/x-octetstream']) - * - * Sets the content type. - * - */ -PHP_FUNCTION(http_send_content_type) -{ - char *ct; - int ct_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &ct, &ct_len) != SUCCESS) { - RETURN_FALSE; - } - - if (!ct_len) { - RETURN_SUCCESS(http_send_content_type("application/x-octetstream", sizeof("application/x-octetstream") - 1)); - } - RETURN_SUCCESS(http_send_content_type(ct, ct_len)); -} -/* }}} */ - -/* {{{ proto bool http_send_content_disposition(string filename[, bool inline = false]) - * - * Set the Content Disposition. The Content-Disposition header is very useful - * if the data actually sent came from a file or something similar, that should - * be "saved" by the client/user (i.e. by browsers "Save as..." popup window). - * - */ -PHP_FUNCTION(http_send_content_disposition) -{ - char *filename; - int f_len; - zend_bool send_inline = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &filename, &f_len, &send_inline) != SUCCESS) { - RETURN_FALSE; - } - RETURN_SUCCESS(http_send_content_disposition(filename, f_len, send_inline)); -} -/* }}} */ - -/* {{{ proto bool http_match_modified([int timestamp]) - * - * Matches the given timestamp against the clients "If-Modified-Since" resp. - * "If-Unmodified-Since" HTTP headers. - * - */ -PHP_FUNCTION(http_match_modified) -{ - long t = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) { - RETURN_FALSE; - } - - // current time if not supplied (senseless though) - if (t == -1) { - t = (long) time(NULL); - } - - RETURN_BOOL(http_modified_match("HTTP_IF_MODIFIED_SINCE", t) || http_modified_match("HTTP_IF_UNMODIFIED_SINCE", t)); -} -/* }}} */ - -/* {{{ proto bool http_match_etag(string etag) - * - * This matches the given ETag against the clients - * "If-Match" resp. "If-None-Match" HTTP headers. - * - */ -PHP_FUNCTION(http_match_etag) -{ - int etag_len; - char *etag; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len) != SUCCESS) { - RETURN_FALSE; - } - - RETURN_BOOL(http_etag_match("HTTP_IF_NONE_MATCH", etag) || http_etag_match("HTTP_IF_MATCH", etag)); -} -/* }}} */ - -/* {{{ proto bool http_cache_last_modified([int timestamp_or_expires]]) - * - * If timestamp_or_exires is greater than 0, it is handled as timestamp - * and will be sent as date of last modification. If it is 0 or omitted, - * the current time will be sent as Last-Modified date. If it's negative, - * it is handled as expiration time in seconds, which means that if the - * requested last modification date is not between the calculated timespan, - * the Last-Modified header is updated and the actual body will be sent. - * - */ -PHP_FUNCTION(http_cache_last_modified) -{ - long last_modified = 0, send_modified = 0, t; - zval *zlm; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &last_modified) != SUCCESS) { - RETURN_FALSE; - } - - t = (long) time(NULL); - - /* 0 or omitted */ - if (!last_modified) { - /* does the client have? (att: caching "forever") */ - if (zlm = http_get_server_var("HTTP_IF_MODIFIED_SINCE")) { - last_modified = send_modified = http_parse_date(Z_STRVAL_P(zlm)); - /* send current time */ - } else { - send_modified = t; - } - /* negative value is supposed to be expiration time */ - } else if (last_modified < 0) { - last_modified += t; - send_modified = t; - /* send supplied time explicitly */ - } else { - send_modified = last_modified; - } - - RETURN_SUCCESS(http_cache_last_modified(last_modified, send_modified, HTTP_DEFAULT_CACHECONTROL, sizeof(HTTP_DEFAULT_CACHECONTROL) - 1)); -} -/* }}} */ - -/* {{{ proto bool http_cache_etag([string etag]) - * - * This function attempts to cache the HTTP body based on an ETag, - * either supplied or generated through calculation of the MD5 - * checksum of the output (uses output buffering). - * - * If clients "If-None-Match" header matches the supplied/calculated - * ETag, the body is considered cached on the clients side and - * a "304 Not Modified" status code is issued. - * - */ -PHP_FUNCTION(http_cache_etag) -{ - char *etag; - int etag_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &etag, &etag_len) != SUCCESS) { - RETURN_FALSE; - } - - RETURN_SUCCESS(http_cache_etag(etag, etag_len, HTTP_DEFAULT_CACHECONTROL, sizeof(HTTP_DEFAULT_CACHECONTROL) - 1)); -} -/* }}} */ - -/* {{{ proto string ob_httpetaghandler(string data, int mode) - * - * For use with ob_start(). - * Note that this has to be started as first output buffer. - * WARNING: Don't use with http_send_*(). - */ -PHP_FUNCTION(ob_httpetaghandler) -{ - char *data; - int data_len; - long mode; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &data, &data_len, &mode)) { - RETURN_FALSE; - } - - if (mode & PHP_OUTPUT_HANDLER_START) { - if (HTTP_G(etag_started)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "ob_httpetaghandler can only be used once"); - RETURN_STRINGL(data, data_len, 1); - } - http_send_header("Cache-Control: " HTTP_DEFAULT_CACHECONTROL); - HTTP_G(etag_started) = 1; - } - - if (OG(ob_nesting_level) > 1) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "ob_httpetaghandler must be started prior to other output buffers"); - RETURN_STRINGL(data, data_len, 1); - } - - Z_TYPE_P(return_value) = IS_STRING; - http_ob_etaghandler(data, data_len, &Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value), mode); -} -/* }}} */ - -/* {{{ proto void http_redirect([string url[, array params[, bool session,[ bool permanent]]]]) - * - * Redirect to a given url. - * The supplied url will be expanded with http_absolute_uri(), the params array will - * be treated with http_build_query() and the session identification will be appended - * if session is true. - * - * Depending on permanent the redirection will be issued with a permanent - * ("301 Moved Permanently") or a temporary ("302 Found") redirection - * status code. - * - * To be RFC compliant, "Redirecting to URI." will be displayed, - * if the client doesn't redirect immediatly. - */ -PHP_FUNCTION(http_redirect) -{ - int url_len; - zend_bool session = 0, permanent = 0; - zval *params = NULL; - smart_str qstr = {0}; - char *url, *URI, LOC[HTTP_URI_MAXLEN + 9], RED[HTTP_URI_MAXLEN * 2 + 34]; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!/bb", &url, &url_len, ¶ms, &session, &permanent) != SUCCESS) { - RETURN_FALSE; - } - - /* append session info */ - if (session && (PS(session_status) == php_session_active)) { - if (!params) { - MAKE_STD_ZVAL(params); - array_init(params); - } - if (add_assoc_string(params, PS(session_name), PS(id), 1) != SUCCESS) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not append session information"); - } - } - - /* treat params array with http_build_query() */ - if (params) { - if (php_url_encode_hash_ex(Z_ARRVAL_P(params), &qstr, NULL,0,NULL,0,NULL,0,NULL TSRMLS_CC) != SUCCESS) { - if (qstr.c) { - efree(qstr.c); - } - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not encode query parameters"); - RETURN_FALSE; - } - smart_str_0(&qstr); - } - - URI = http_absolute_uri(url, NULL); - if (qstr.c) { - snprintf(LOC, HTTP_URI_MAXLEN + strlen("Location: "), "Location: %s?%s", URI, qstr.c); - sprintf(RED, "Redirecting to %s?%s.\n", URI, qstr.c, URI, qstr.c); - efree(qstr.c); - } else { - snprintf(LOC, HTTP_URI_MAXLEN + strlen("Location: "), "Location: %s", URI); - sprintf(RED, "Redirecting to %s.\n", URI, URI); - } - efree(URI); - - if ((SUCCESS == http_send_header(LOC)) && (SUCCESS == http_send_status((permanent ? 301 : 302)))) { - php_body_write(RED, strlen(RED) TSRMLS_CC); - RETURN_TRUE; - } - RETURN_FALSE; -} -/* }}} */ - -/* {{{ proto bool http_send_data(string data) - * - * Sends raw data with support for (multiple) range requests. - * - */ -PHP_FUNCTION(http_send_data) -{ - zval *zdata; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zdata) != SUCCESS) { - RETURN_FALSE; - } - - convert_to_string_ex(&zdata); - http_send_header("Accept-Ranges: bytes"); - RETURN_SUCCESS(http_send_data(zdata)); -} -/* }}} */ - -/* {{{ proto bool http_send_file(string file) - * - * Sends a file with support for (multiple) range requests. - * - */ -PHP_FUNCTION(http_send_file) -{ - zval *zfile; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zfile) != SUCCESS) { - RETURN_FALSE; - } - - convert_to_string_ex(&zfile); - http_send_header("Accept-Ranges: bytes"); - RETURN_SUCCESS(http_send_file(zfile)); -} -/* }}} */ - -/* {{{ proto bool http_send_stream(resource stream) - * - * Sends an already opened stream with support for (multiple) range requests. - * - */ -PHP_FUNCTION(http_send_stream) -{ - zval *zstream; - php_stream *file; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream) != SUCCESS) { - RETURN_FALSE; - } - - php_stream_from_zval(file, &zstream); - http_send_header("Accept-Ranges: bytes"); - RETURN_SUCCESS(http_send_stream(file)); -} -/* }}} */ - -/* {{{ proto string http_chunked_decode(string encoded) - * - * This function decodes a string that was HTTP-chunked encoded. - * Returns false on failure. - */ -PHP_FUNCTION(http_chunked_decode) -{ - char *encoded = NULL, *decoded = NULL; - int encoded_len = 0, decoded_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &encoded, &encoded_len) != SUCCESS) { - RETURN_FALSE; - } - - if (SUCCESS == http_chunked_decode(encoded, encoded_len, &decoded, &decoded_len)) { - RETURN_STRINGL(decoded, decoded_len, 0); - } else { - RETURN_FALSE; - } -} -/* }}} */ - -/* {{{ proto array http_split_response(string http_response) - * - * This function splits an HTTP response into an array with headers and the - * content body. The returned array may look simliar to the following example: - * - *
- *  array(
- *         'Status' => '200 Ok',
- *         'Content-Type' => 'text/plain',
- *         'Content-Language' => 'en-US'
- *     ),
- *     1 => "Hello World!"
- * );
- * ?>
- * 
- */ -PHP_FUNCTION(http_split_response) -{ - zval *zresponse, *zbody, *zheaders; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zresponse) != SUCCESS) { - RETURN_FALSE; - } - - convert_to_string_ex(&zresponse); - - MAKE_STD_ZVAL(zbody); - MAKE_STD_ZVAL(zheaders); - array_init(zheaders); - - if (SUCCESS != http_split_response(zresponse, zheaders, zbody)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse HTTP response"); - RETURN_FALSE; - } - - array_init(return_value); - add_index_zval(return_value, 0, zheaders); - add_index_zval(return_value, 1, zbody); -} -/* }}} */ - -/* {{{ proto array http_parse_headers(string header) - * - */ -PHP_FUNCTION(http_parse_headers) -{ - char *header, *rnrn; - int header_len; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &header, &header_len)) { - RETURN_FALSE; - } - - array_init(return_value); - - if (rnrn = strstr(header, HTTP_CRLF HTTP_CRLF)) { - header_len = rnrn - header + 2; - } - if (SUCCESS != http_parse_headers(header, header_len, return_value)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse HTTP header"); - zval_dtor(return_value); - RETURN_FALSE; - } -} -/* }}}*/ - -/* {{{ proto array http_get_request_headers(void) - * - */ -PHP_FUNCTION(http_get_request_headers) -{ - if (ZEND_NUM_ARGS()) { - WRONG_PARAM_COUNT; - } - - array_init(return_value); - http_get_request_headers(return_value); -} -/* }}} */ - -/* {{{ HAVE_CURL */ -#ifdef HTTP_HAVE_CURL - -/* {{{ proto string http_get(string url[, array options[, array &info]]) - * - * Performs an HTTP GET request on the supplied url. - * - * The second parameter is expected to be an associative - * array where the following keys will be recognized: - *
- *  - redirect:         int, whether and how many redirects to follow
- *  - unrestrictedauth: bool, whether to continue sending credentials on
- *                      redirects to a different host
- *  - proxyhost:        string, proxy host in "host[:port]" format
- *  - proxyport:        int, use another proxy port as specified in proxyhost
- *  - proxyauth:        string, proxy credentials in "user:pass" format
- *  - proxyauthtype:    int, HTTP_AUTH_BASIC and/or HTTP_AUTH_NTLM
- *  - httpauth:         string, http credentials in "user:pass" format
- *  - httpauthtype:     int, HTTP_AUTH_BASIC, DIGEST and/or NTLM
- *  - compress:         bool, whether to allow gzip/deflate content encoding
- *                      (defaults to true)
- *  - port:             int, use another port as specified in the url
- *  - referer:          string, the referer to sends
- *  - useragent:        string, the user agent to send
- *                      (defaults to PECL::HTTP/version (PHP/version)))
- *  - headers:          array, list of custom headers as associative array
- *                      like array("header" => "value")
- *  - cookies:          array, list of cookies as associative array
- *                      like array("cookie" => "value")
- *  - cookiestore:      string, path to a file where cookies are/will be stored
- * 
- * - * The optional third parameter will be filled with some additional information - * in form af an associative array, if supplied, like the following example: - *
- *  'http://localhost',
- *     'response_code' => 403,
- *     'total_time' => 0.017,
- *     'namelookup_time' => 0.013,
- *     'connect_time' => 0.014,
- *     'pretransfer_time' => 0.014,
- *     'size_upload' => 0,
- *     'size_download' => 202,
- *     'speed_download' => 11882,
- *     'speed_upload' => 0,
- *     'header_size' => 145,
- *     'request_size' => 62,
- *     'ssl_verifyresult' => 0,
- *     'filetime' => -1,
- *     'content_length_download' => 202,
- *     'content_length_upload' => 0,
- *     'starttransfer_time' => 0.017,
- *     'content_type' => 'text/html; charset=iso-8859-1',
- *     'redirect_time' => 0,
- *     'redirect_count' => 0,
- *     'private' => '',
- *     'http_connectcode' => 0,
- *     'httpauth_avail' => 0,
- *     'proxyauth_avail' => 0,
- * )
- * ?>
- * 
- */ -PHP_FUNCTION(http_get) -{ - char *URL, *data = NULL; - size_t data_len = 0; - int URL_len; - zval *options = NULL, *info = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) { - RETURN_FALSE; - } - - if (info) { - zval_dtor(info); - array_init(info); - } - - if (SUCCESS == http_get(URL, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) { - RETURN_STRINGL(data, data_len, 0); - } else { - RETURN_FALSE; - } -} -/* }}} */ - -/* {{{ proto string http_head(string url[, array options[, array &info]]) - * - * Performs an HTTP HEAD request on the suppied url. - * Returns the HTTP response as string. - * See http_get() for a full list of available options. - */ -PHP_FUNCTION(http_head) -{ - char *URL, *data = NULL; - size_t data_len = 0; - int URL_len; - zval *options = NULL, *info = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) { - RETURN_FALSE; - } - - if (info) { - zval_dtor(info); - array_init(info); - } - - if (SUCCESS == http_head(URL, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) { - RETURN_STRINGL(data, data_len, 0); - } else { - RETURN_FALSE; - } -} -/* }}} */ - -/* {{{ proto string http_post_data(string url, string data[, array options[, &info]]) - * - * Performs an HTTP POST request, posting data. - * Returns the HTTP response as string. - * See http_get() for a full list of available options. - */ -PHP_FUNCTION(http_post_data) -{ - char *URL, *postdata, *data = NULL; - size_t data_len = 0; - int postdata_len, URL_len; - zval *options = NULL, *info = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!z", &URL, &URL_len, &postdata, &postdata_len, &options, &info) != SUCCESS) { - RETURN_FALSE; - } - - if (info) { - zval_dtor(info); - array_init(info); - } - - if (SUCCESS == http_post_data(URL, postdata, (size_t) postdata_len, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) { - RETURN_STRINGL(data, data_len, 0); - } else { - RETURN_FALSE; - } -} -/* }}} */ - -/* {{{ proto string http_post_array(string url, array data[, array options[, array &info]]) - * - * Performs an HTTP POST request, posting www-form-urlencoded array data. - * Returns the HTTP response as string. - * See http_get() for a full list of available options. - */ -PHP_FUNCTION(http_post_array) -{ - char *URL, *data = NULL; - size_t data_len = 0; - int URL_len; - zval *options = NULL, *info = NULL, *postdata; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a/!z", &URL, &URL_len, &postdata, &options, &info) != SUCCESS) { - RETURN_FALSE; - } - - if (info) { - zval_dtor(info); - array_init(info); - } - - if (SUCCESS == http_post_array(URL, Z_ARRVAL_P(postdata), HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) { - RETURN_STRINGL(data, data_len, 0); - } else { - RETURN_FALSE; - } -} -/* }}} */ - -#endif -/* }}} HAVE_CURL */ - - -/* {{{ proto bool http_auth_basic(string user, string pass[, string realm = "Restricted"]) - * - * Example: - *
- * Authorization failed!');
- * }
- * ?>
- * 
- */ -PHP_FUNCTION(http_auth_basic) -{ - char *realm = NULL, *user, *pass, *suser, *spass; - int r_len, u_len, p_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s", &user, &u_len, &pass, &p_len, &realm, &r_len) != SUCCESS) { - RETURN_FALSE; - } - - if (!realm) { - realm = "Restricted"; - } - - if (SUCCESS != http_auth_credentials(&suser, &spass)) { - http_auth_header("Basic", realm); - RETURN_FALSE; - } - - if (strcasecmp(suser, user)) { - http_auth_header("Basic", realm); - RETURN_FALSE; - } - - if (strcmp(spass, pass)) { - http_auth_header("Basic", realm); - RETURN_FALSE; - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto bool http_auth_basic_cb(mixed callback[, string realm = "Restricted"]) - * - * Example: - *
- * quoteSmart($user);
- *     if (strlen($realpass = $db->getOne($query)) {
- *         return $pass === $realpass;
- *     }
- *     return false;
- * }
- *
- * if (!http_auth_basic_cb('auth_cb')) {
- *     die('

Authorization failed

'); - * } - * ?> - *
- */ -PHP_FUNCTION(http_auth_basic_cb) -{ - zval *cb; - char *realm = NULL, *user, *pass; - int r_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &cb, &realm, &r_len) != SUCCESS) { - RETURN_FALSE; - } - - if (!realm) { - realm = "Restricted"; - } - - if (SUCCESS != http_auth_credentials(&user, &pass)) { - http_auth_header("Basic", realm); - RETURN_FALSE; - } - { - zval *zparams[2] = {NULL, NULL}, retval; - int result = 0; - - MAKE_STD_ZVAL(zparams[0]); - MAKE_STD_ZVAL(zparams[1]); - ZVAL_STRING(zparams[0], user, 0); - ZVAL_STRING(zparams[1], pass, 0); - - if (SUCCESS == call_user_function(EG(function_table), NULL, cb, - &retval, 2, zparams TSRMLS_CC)) { - result = Z_LVAL(retval); - } - - efree(user); - efree(pass); - efree(zparams[0]); - efree(zparams[1]); - - if (!result) { - http_auth_header("Basic", realm); - } - - RETURN_BOOL(result); - } -} -/* }}}*/ - - -/* {{{ php_http_init_globals(zend_http_globals *) */ -static void php_http_init_globals(zend_http_globals *http_globals) -{ - http_globals->etag_started = 0; - http_globals->ctype = NULL; - http_globals->etag = NULL; - http_globals->lmod = 0; -#ifdef HTTP_HAVE_CURL - http_globals->curlbuf.body.data = NULL; - http_globals->curlbuf.body.used = 0; - http_globals->curlbuf.body.free = 0; - http_globals->curlbuf.hdrs.data = NULL; - http_globals->curlbuf.hdrs.used = 0; - http_globals->curlbuf.hdrs.free = 0; -#endif - http_globals->allowed_methods = NULL; -} -/* }}} */ - -/* {{{ static inline STATUS http_check_allowed_methods(char *, int) */ -#define http_check_allowed_methods(m, l) _http_check_allowed_methods((m), (l) TSRMLS_CC) -static inline void _http_check_allowed_methods(char *methods, int length TSRMLS_DC) -{ - if (length && SG(request_info).request_method && (!strstr(methods, SG(request_info).request_method))) { - char *allow_header = emalloc(length + sizeof("Allow: ")); - sprintf(allow_header, "Allow: %s", methods); - http_send_header(allow_header); - efree(allow_header); - http_send_status(405); - zend_bailout(); - } -} -/* }}} */ - -/* {{{ PHP_INI */ -PHP_INI_MH(update_allowed_methods) -{ - http_check_allowed_methods(new_value, new_value_length); - return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); -} - -PHP_INI_BEGIN() - STD_PHP_INI_ENTRY("http.allowed_methods", "OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT", PHP_INI_ALL, update_allowed_methods, allowed_methods, zend_http_globals, http_globals) -PHP_INI_END() -/* }}} */ - -/* {{{ PHP_MINIT_FUNCTION */ -PHP_MINIT_FUNCTION(http) -{ - ZEND_INIT_MODULE_GLOBALS(http, php_http_init_globals, NULL); - REGISTER_INI_ENTRIES(); - -#if defined(HTTP_HAVE_CURL) && (LIBCURL_VERSION_NUM >= 0x070a05) - REGISTER_LONG_CONSTANT("HTTP_AUTH_BASIC", CURLAUTH_BASIC, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("HTTP_AUTH_DIGEST", CURLAUTH_DIGEST, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("HTTP_AUTH_NTLM", CURLAUTH_NTLM, CONST_CS | CONST_PERSISTENT); -#endif +#if defined(HTTP_HAVE_CURL) && (LIBCURL_VERSION_NUM >= 0x070a05) + REGISTER_LONG_CONSTANT("HTTP_AUTH_BASIC", CURLAUTH_BASIC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("HTTP_AUTH_DIGEST", CURLAUTH_DIGEST, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("HTTP_AUTH_NTLM", CURLAUTH_NTLM, CONST_CS | CONST_PERSISTENT); +#endif #ifdef ZEND_ENGINE_2 HTTP_REGISTER_CLASS(HTTPi, httpi, NULL, ZEND_ACC_FINAL_CLASS); @@ -2532,13 +471,11 @@ PHP_RSHUTDOWN_FUNCTION(http) HTTP_G(ctype) = NULL; } #ifdef HTTP_HAVE_CURL - if (HTTP_G(curlbuf).body.data) { - efree(HTTP_G(curlbuf).body.data); - HTTP_G(curlbuf).body.data = NULL; - } - if (HTTP_G(curlbuf).hdrs.data) { - efree(HTTP_G(curlbuf).hdrs.data); - HTTP_G(curlbuf).hdrs.data = NULL; + if (HTTP_G(curlbuf).data) { + efree(HTTP_G(curlbuf).data); + HTTP_G(curlbuf).data = NULL; + HTTP_G(curlbuf).used = 0; + HTTP_G(curlbuf).free = 0; } #endif return SUCCESS; diff --git a/http_api.c b/http_api.c index bb83e28..bee02cc 100644 --- a/http_api.c +++ b/http_api.c @@ -46,22 +46,6 @@ #include "php_http.h" #include "php_http_api.h" -#ifdef HTTP_HAVE_CURL - -# ifdef PHP_WIN32 -# include -# include -# endif - -# include -# include - -#endif - -#if !defined(CURLINFO_RESONSE_CODE) && defined(CURLINFO_HTTP_CODE) -#define CURLINFO_RESONSE_CODE CURLINFO_HTTP_CODE -#endif - ZEND_DECLARE_MODULE_GLOBALS(http) /* {{{ day/month names */ @@ -144,43 +128,9 @@ static int check_day(char *day, size_t len); static int check_month(char *month); static int check_tzone(char *tzone); -static char *pretty_key(char *key, int key_len, int uctitle, int xhyphen); static int http_ob_stack_get(php_ob_buffer *, php_ob_buffer **); -/* {{{ HAVE_CURL */ -#ifdef HTTP_HAVE_CURL -#define http_curl_initbuf(m) _http_curl_initbuf((m) TSRMLS_CC) -static inline void _http_curl_initbuf(http_curlbuf_member member TSRMLS_DC); -#define http_curl_freebuf(m) _http_curl_freebuf((m) TSRMLS_CC) -static inline void _http_curl_freebuf(http_curlbuf_member member TSRMLS_DC); -#define http_curl_sizebuf(m, l) _http_curl_sizebuf((m), (l) TSRMLS_CC) -static inline void _http_curl_sizebuf(http_curlbuf_member member, size_t len TSRMLS_DC); -#define http_curl_movebuf(m, d, l) _http_curl_movebuf((m), (d), (l) TSRMLS_CC) -static inline void _http_curl_movebuf(http_curlbuf_member member, char **data, size_t *data_len TSRMLS_DC); -#define http_curl_copybuf(m, d, l) _http_curl_copybuf((m), (d), (l) TSRMLS_CC) -static inline void _http_curl_copybuf(http_curlbuf_member member, char **data, size_t *data_len TSRMLS_DC); -#define http_curl_setopts(c, u, o) _http_curl_setopts((c), (u), (o) TSRMLS_CC) -static inline void _http_curl_setopts(CURL *ch, const char *url, HashTable *options TSRMLS_DC); - -#define http_curl_getopt(o, k) _http_curl_getopt((o), (k) TSRMLS_CC, 0) -#define http_curl_getopt1(o, k, t1) _http_curl_getopt((o), (k) TSRMLS_CC, 1, (t1)) -#define http_curl_getopt2(o, k, t1, t2) _http_curl_getopt((o), (k) TSRMLS_CC, 2, (t1), (t2)) -static inline zval *_http_curl_getopt(HashTable *options, char *key TSRMLS_DC, int checks, ...); - -static size_t http_curl_body_callback(char *, size_t, size_t, void *); -static size_t http_curl_hdrs_callback(char *, size_t, size_t, void *); - -#define http_curl_getinfo(c, h) _http_curl_getinfo((c), (h) TSRMLS_CC) -static inline void _http_curl_getinfo(CURL *ch, HashTable *info TSRMLS_DC); -#define http_curl_getinfo_ex(c, i, a) _http_curl_getinfo_ex((c), (i), (a) TSRMLS_CC) -static inline void _http_curl_getinfo_ex(CURL *ch, CURLINFO i, zval *array TSRMLS_DC); -#define http_curl_getinfoname(i) _http_curl_getinfoname((i) TSRMLS_CC) -static inline char *_http_curl_getinfoname(CURLINFO i TSRMLS_DC); - -#endif -/* }}} HAVE_CURL */ - /* {{{ static int http_sort_q(const void *, const void *) */ static int http_sort_q(const void *a, const void *b TSRMLS_DC) { @@ -258,487 +208,6 @@ static STATUS _http_send_chunk(const void *data, const size_t begin, } /* }}} */ -/* {{{ HAVE_CURL */ -#ifdef HTTP_HAVE_CURL - -/* {{{ static inline void http_curl_initbuf(http_curlbuf_member) */ -static inline void _http_curl_initbuf(http_curlbuf_member member TSRMLS_DC) -{ - http_curl_freebuf(member); - - if (member & CURLBUF_HDRS) { - HTTP_G(curlbuf).hdrs.data = emalloc(HTTP_CURLBUF_HDRSSIZE); - HTTP_G(curlbuf).hdrs.free = HTTP_CURLBUF_HDRSSIZE; - } - if (member & CURLBUF_BODY) { - HTTP_G(curlbuf).body.data = emalloc(HTTP_CURLBUF_BODYSIZE); - HTTP_G(curlbuf).body.free = HTTP_CURLBUF_BODYSIZE; - } -} -/* }}} */ - -/* {{{ static inline void http_curl_freebuf(http_curlbuf_member) */ -static inline void _http_curl_freebuf(http_curlbuf_member member TSRMLS_DC) -{ - if (member & CURLBUF_HDRS) { - if (HTTP_G(curlbuf).hdrs.data) { - efree(HTTP_G(curlbuf).hdrs.data); - HTTP_G(curlbuf).hdrs.data = NULL; - } - HTTP_G(curlbuf).hdrs.used = 0; - HTTP_G(curlbuf).hdrs.free = 0; - } - if (member & CURLBUF_BODY) { - if (HTTP_G(curlbuf).body.data) { - efree(HTTP_G(curlbuf).body.data); - HTTP_G(curlbuf).body.data = NULL; - } - HTTP_G(curlbuf).body.used = 0; - HTTP_G(curlbuf).body.free = 0; - } -} -/* }}} */ - -/* {{{ static inline void http_curl_copybuf(http_curlbuf_member, char **, - size_t *) */ -static inline void _http_curl_copybuf(http_curlbuf_member member, char **data, - size_t *data_len TSRMLS_DC) -{ - *data = NULL; - *data_len = 0; - - if ((member & CURLBUF_HDRS) && HTTP_G(curlbuf).hdrs.used) { - if ((member & CURLBUF_BODY) && HTTP_G(curlbuf).body.used) { - *data = emalloc(HTTP_G(curlbuf).hdrs.used + HTTP_G(curlbuf).body.used + 1); - } else { - *data = emalloc(HTTP_G(curlbuf).hdrs.used + 1); - } - memcpy(*data, HTTP_G(curlbuf).hdrs.data, HTTP_G(curlbuf).hdrs.used); - *data_len = HTTP_G(curlbuf).hdrs.used; - } - - if ((member & CURLBUF_BODY) && HTTP_G(curlbuf).body.used) { - if (*data) { - memcpy((*data) + HTTP_G(curlbuf).hdrs.used, - HTTP_G(curlbuf).body.data, HTTP_G(curlbuf).body.used); - *data_len = HTTP_G(curlbuf).hdrs.used + HTTP_G(curlbuf).body.used; - } else { - emalloc(HTTP_G(curlbuf).body.used + 1); - memcpy(*data, HTTP_G(curlbuf).body.data, HTTP_G(curlbuf).body.used); - *data_len = HTTP_G(curlbuf).body.used; - } - } - if (*data) { - (*data)[*data_len] = 0; - } else { - *data = ""; - } -} -/* }}} */ - -/* {{{ static inline void http_curl_movebuf(http_curlbuf_member, char **, - size_t *) */ -static inline void _http_curl_movebuf(http_curlbuf_member member, char **data, - size_t *data_len TSRMLS_DC) -{ - http_curl_copybuf(member, data, data_len); - http_curl_freebuf(member); -} -/* }}} */ - -/* {{{ static size_t http_curl_body_callback(char *, size_t, size_t, void *) */ -static size_t http_curl_body_callback(char *buf, size_t len, size_t n, void *s) -{ - TSRMLS_FETCH(); - - if ((len *= n) > HTTP_G(curlbuf).body.free) { - size_t bsize = HTTP_CURLBUF_BODYSIZE; - while (bsize < len) { - bsize *= 2; - } - HTTP_G(curlbuf).body.data = erealloc(HTTP_G(curlbuf).body.data, - HTTP_G(curlbuf).body.used + bsize); - HTTP_G(curlbuf).body.free += bsize; - } - - memcpy(HTTP_G(curlbuf).body.data + HTTP_G(curlbuf).body.used, buf, len); - HTTP_G(curlbuf).body.free -= len; - HTTP_G(curlbuf).body.used += len; - - return len; -} -/* }}} */ - -/* {{{ static size_t http_curl_hdrs_callback(char*, size_t, size_t, void *) */ -static size_t http_curl_hdrs_callback(char *buf, size_t len, size_t n, void *s) -{ - TSRMLS_FETCH(); - - /* discard previous headers */ - if ((HTTP_G(curlbuf).hdrs.used) && (!strncmp(buf, "HTTP/1.", strlen("HTTP/1.")))) { - http_curl_initbuf(CURLBUF_HDRS); - } - - if ((len *= n) > HTTP_G(curlbuf).hdrs.free) { - size_t bsize = HTTP_CURLBUF_HDRSSIZE; - while (bsize < len) { - bsize *= 2; - } - HTTP_G(curlbuf).hdrs.data = erealloc(HTTP_G(curlbuf).hdrs.data, - HTTP_G(curlbuf).hdrs.used + bsize); - HTTP_G(curlbuf).hdrs.free += bsize; - } - - memcpy(HTTP_G(curlbuf).hdrs.data + HTTP_G(curlbuf).hdrs.used, buf, len); - HTTP_G(curlbuf).hdrs.free -= len; - HTTP_G(curlbuf).hdrs.used += len; - - return len; -} -/* }}} */ - -/* {{{ static inline zval *http_curl_getopt(HashTable *, char *, int, ...) */ -static inline zval *_http_curl_getopt(HashTable *options, char *key TSRMLS_DC, int checks, ...) -{ - zval **zoption; - va_list types; - int i; - - if (SUCCESS != zend_hash_find(options, key, strlen(key) + 1, (void **) &zoption)) { - return NULL; - } - if (checks < 1) { - return *zoption; - } - - va_start(types, checks); - for (i = 0; i < checks; ++i) { - if ((va_arg(types, int)) == (Z_TYPE_PP(zoption))) { - va_end(types); - return *zoption; - } - } - va_end(types); - return NULL; -} -/* }}} */ - -/* {{{ static inline void http_curl_setopts(CURL *, char *, HashTable *) */ -static inline void _http_curl_setopts(CURL *ch, const char *url, HashTable *options TSRMLS_DC) -{ - zval *zoption; - - /* standard options */ - curl_easy_setopt(ch, CURLOPT_URL, url); - curl_easy_setopt(ch, CURLOPT_HEADER, 0); - curl_easy_setopt(ch, CURLOPT_NOPROGRESS, 1); - curl_easy_setopt(ch, CURLOPT_AUTOREFERER, 1); - curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, http_curl_body_callback); - curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, http_curl_hdrs_callback); -#if defined(ZTS) && (LIBCURL_VERSION_NUM >= 0x070a00) - curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1); -#endif - - if ((!options) || (1 > zend_hash_num_elements(options))) { - return; - } - - /* redirects, defaults to 0 */ - if (zoption = http_curl_getopt1(options, "redirect", IS_LONG)) { - curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, Z_LVAL_P(zoption) ? 1 : 0); - curl_easy_setopt(ch, CURLOPT_MAXREDIRS, Z_LVAL_P(zoption)); - if (zoption = http_curl_getopt2(options, "unrestrictedauth", IS_LONG, IS_BOOL)) { - curl_easy_setopt(ch, CURLOPT_UNRESTRICTED_AUTH, Z_LVAL_P(zoption)); - } - } else { - curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, 0); - } - - /* proxy */ - if (zoption = http_curl_getopt1(options, "proxyhost", IS_STRING)) { - curl_easy_setopt(ch, CURLOPT_PROXY, Z_STRVAL_P(zoption)); - /* port */ - if (zoption = http_curl_getopt1(options, "proxyport", IS_LONG)) { - curl_easy_setopt(ch, CURLOPT_PROXYPORT, Z_LVAL_P(zoption)); - } - /* user:pass */ - if (zoption = http_curl_getopt1(options, "proxyauth", IS_STRING)) { - curl_easy_setopt(ch, CURLOPT_PROXYUSERPWD, Z_STRVAL_P(zoption)); - } -#if LIBCURL_VERSION_NUM > 0x070a06 - /* auth method */ - if (zoption = http_curl_getopt1(options, "proxyauthtype", IS_LONG)) { - curl_easy_setopt(ch, CURLOPT_PROXYAUTH, Z_LVAL_P(zoption)); - } -#endif - } - - /* auth */ - if (zoption = http_curl_getopt1(options, "httpauth", IS_STRING)) { - curl_easy_setopt(ch, CURLOPT_USERPWD, Z_STRVAL_P(zoption)); - } -#if LIBCURL_VERSION_NUM > 0x070a05 - if (zoption = http_curl_getopt1(options, "httpauthtype", IS_LONG)) { - curl_easy_setopt(ch, CURLOPT_HTTPAUTH, Z_LVAL_P(zoption)); - } -#endif - - /* compress, enabled by default (empty string enables deflate and gzip) */ - if (zoption = http_curl_getopt2(options, "compress", IS_LONG, IS_BOOL)) { - if (Z_LVAL_P(zoption)) { - curl_easy_setopt(ch, CURLOPT_ENCODING, ""); - } - } else { - curl_easy_setopt(ch, CURLOPT_ENCODING, ""); - } - - /* another port */ - if (zoption = http_curl_getopt1(options, "port", IS_LONG)) { - curl_easy_setopt(ch, CURLOPT_PORT, Z_LVAL_P(zoption)); - } - - /* referer */ - if (zoption = http_curl_getopt1(options, "referer", IS_STRING)) { - curl_easy_setopt(ch, CURLOPT_REFERER, Z_STRVAL_P(zoption)); - } - - /* useragent, default "PECL::HTTP/version (PHP/version)" */ - if (zoption = http_curl_getopt1(options, "useragent", IS_STRING)) { - curl_easy_setopt(ch, CURLOPT_USERAGENT, Z_STRVAL_P(zoption)); - } else { - curl_easy_setopt(ch, CURLOPT_USERAGENT, - "PECL::HTTP/" PHP_EXT_HTTP_VERSION " (PHP/" PHP_VERSION ")"); - } - - /* cookies, array('name' => 'value') */ - if (zoption = http_curl_getopt1(options, "cookies", IS_ARRAY)) { - char *cookie_key; - zval **cookie_val; - int key_type; - smart_str qstr = {0}; - - zend_hash_internal_pointer_reset(Z_ARRVAL_P(zoption)); - while (HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_type(Z_ARRVAL_P(zoption)))) { - if (key_type == HASH_KEY_IS_STRING) { - zend_hash_get_current_key(Z_ARRVAL_P(zoption), &cookie_key, NULL, 0); - zend_hash_get_current_data(Z_ARRVAL_P(zoption), (void **) &cookie_val); - smart_str_appends(&qstr, cookie_key); - smart_str_appendl(&qstr, "=", 1); - smart_str_appendl(&qstr, Z_STRVAL_PP(cookie_val), Z_STRLEN_PP(cookie_val)); - smart_str_appendl(&qstr, "; ", 2); - zend_hash_move_forward(Z_ARRVAL_P(zoption)); - } - } - smart_str_0(&qstr); - - if (qstr.c) { - curl_easy_setopt(ch, CURLOPT_COOKIE, qstr.c); - /* FIXXXME: mem-leak */ - } - } - - /* cookiestore */ - if (zoption = http_curl_getopt1(options, "cookiestore", IS_STRING)) { - curl_easy_setopt(ch, CURLOPT_COOKIEFILE, Z_STRVAL_P(zoption)); - curl_easy_setopt(ch, CURLOPT_COOKIEJAR, Z_STRVAL_P(zoption)); - } - - /* additional headers, array('name' => 'value') */ - if (zoption = http_curl_getopt1(options, "headers", IS_ARRAY)) { - int key_type; - char *header_key, header[1024] = {0}; - zval **header_val; - struct curl_slist *headers = NULL; - - zend_hash_internal_pointer_reset(Z_ARRVAL_P(zoption)); - while (HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_type(Z_ARRVAL_P(zoption)))) { - if (key_type == HASH_KEY_IS_STRING) { - zend_hash_get_current_key(Z_ARRVAL_P(zoption), &header_key, NULL, 0); - zend_hash_get_current_data(Z_ARRVAL_P(zoption), (void **) &header_val); - snprintf(header, 1023, "%s: %s", header_key, Z_STRVAL_PP(header_val)); - headers = curl_slist_append(headers, header); - zend_hash_move_forward(Z_ARRVAL_P(zoption)); - } - } - if (headers) { - curl_easy_setopt(ch, CURLOPT_HTTPHEADER, headers); - } - } -} -/* }}} */ - -/* {{{ static inline char *http_curl_getinfoname(CURLINFO) */ -static inline char *_http_curl_getinfoname(CURLINFO i TSRMLS_DC) -{ -#define CASE(I) case CURLINFO_ ##I : { static char I[] = #I; return pretty_key(I, sizeof(#I)-1, 0, 0); } - switch (i) - { - /* CURLINFO_EFFECTIVE_URL = CURLINFO_STRING +1, */ - CASE(EFFECTIVE_URL); - /* CURLINFO_RESPONSE_CODE = CURLINFO_LONG +2, */ -#if LIBCURL_VERSION_NUM > 0x070a06 - CASE(RESPONSE_CODE); -#else - CASE(HTTP_CODE); -#endif - /* CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE +3, */ - CASE(TOTAL_TIME); - /* CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE +4, */ - CASE(NAMELOOKUP_TIME); - /* CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE +5, */ - CASE(CONNECT_TIME); - /* CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE +6, */ - CASE(PRETRANSFER_TIME); - /* CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE +7, */ - CASE(SIZE_UPLOAD); - /* CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE +8, */ - CASE(SIZE_DOWNLOAD); - /* CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE +9, */ - CASE(SPEED_DOWNLOAD); - /* CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE +10, */ - CASE(SPEED_UPLOAD); - /* CURLINFO_HEADER_SIZE = CURLINFO_LONG +11, */ - CASE(HEADER_SIZE); - /* CURLINFO_REQUEST_SIZE = CURLINFO_LONG +12, */ - CASE(REQUEST_SIZE); - /* CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG +13, */ - CASE(SSL_VERIFYRESULT); - /* CURLINFO_FILETIME = CURLINFO_LONG +14, */ - CASE(FILETIME); - /* CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE +15, */ - CASE(CONTENT_LENGTH_DOWNLOAD); - /* CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE +16, */ - CASE(CONTENT_LENGTH_UPLOAD); - /* CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE +17, */ - CASE(STARTTRANSFER_TIME); - /* CURLINFO_CONTENT_TYPE = CURLINFO_STRING +18, */ - CASE(CONTENT_TYPE); - /* CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE +19, */ - CASE(REDIRECT_TIME); - /* CURLINFO_REDIRECT_COUNT = CURLINFO_LONG +20, */ - CASE(REDIRECT_COUNT); - /* CURLINFO_PRIVATE = CURLINFO_STRING +21, * (mike) / - CASE(PRIVATE); - /* CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG +22, */ - CASE(HTTP_CONNECTCODE); -#if LIBCURL_VERSION_NUM > 0x070a07 - /* CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG +23, */ - CASE(HTTPAUTH_AVAIL); - /* CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG +24, */ - CASE(PROXYAUTH_AVAIL); -#endif - } -#undef CASE - return NULL; -} -/* }}} */ - -/* {{{ static inline void http_curl_getinfo_ex(CURL, CURLINFO, zval *) */ -static inline void _http_curl_getinfo_ex(CURL *ch, CURLINFO i, zval *array TSRMLS_DC) -{ - char *key; - if (key = http_curl_getinfoname(i)) { - switch (i & ~CURLINFO_MASK) - { - case CURLINFO_STRING: - { - char *c; - if (CURLE_OK == curl_easy_getinfo(ch, i, &c)) { - add_assoc_string(array, key, c ? c : "", 1); - } - } - break; - - case CURLINFO_DOUBLE: - { - double d; - if (CURLE_OK == curl_easy_getinfo(ch, i, &d)) { - add_assoc_double(array, key, d); - } - } - break; - - case CURLINFO_LONG: - { - long l; - if (CURLE_OK == curl_easy_getinfo(ch, i, &l)) { - add_assoc_long(array, key, l); - } - } - break; - } - } -} -/* }}} */ - -/* {{{ static inline http_curl_getinfo(CURL, HashTable *) */ -static inline void _http_curl_getinfo(CURL *ch, HashTable *info TSRMLS_DC) -{ - zval array; - Z_ARRVAL(array) = info; - -#define INFO(I) http_curl_getinfo_ex(ch, CURLINFO_ ##I , &array) - /* CURLINFO_EFFECTIVE_URL = CURLINFO_STRING +1, */ - INFO(EFFECTIVE_URL); -#if LIBCURL_VERSION_NUM > 0x070a06 - /* CURLINFO_RESPONSE_CODE = CURLINFO_LONG +2, */ - INFO(RESPONSE_CODE); -#else - INFO(HTTP_CODE); -#endif - /* CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE +3, */ - INFO(TOTAL_TIME); - /* CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE +4, */ - INFO(NAMELOOKUP_TIME); - /* CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE +5, */ - INFO(CONNECT_TIME); - /* CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE +6, */ - INFO(PRETRANSFER_TIME); - /* CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE +7, */ - INFO(SIZE_UPLOAD); - /* CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE +8, */ - INFO(SIZE_DOWNLOAD); - /* CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE +9, */ - INFO(SPEED_DOWNLOAD); - /* CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE +10, */ - INFO(SPEED_UPLOAD); - /* CURLINFO_HEADER_SIZE = CURLINFO_LONG +11, */ - INFO(HEADER_SIZE); - /* CURLINFO_REQUEST_SIZE = CURLINFO_LONG +12, */ - INFO(REQUEST_SIZE); - /* CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG +13, */ - INFO(SSL_VERIFYRESULT); - /* CURLINFO_FILETIME = CURLINFO_LONG +14, */ - INFO(FILETIME); - /* CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE +15, */ - INFO(CONTENT_LENGTH_DOWNLOAD); - /* CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE +16, */ - INFO(CONTENT_LENGTH_UPLOAD); - /* CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE +17, */ - INFO(STARTTRANSFER_TIME); - /* CURLINFO_CONTENT_TYPE = CURLINFO_STRING +18, */ - INFO(CONTENT_TYPE); - /* CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE +19, */ - INFO(REDIRECT_TIME); - /* CURLINFO_REDIRECT_COUNT = CURLINFO_LONG +20, */ - INFO(REDIRECT_COUNT); - /* CURLINFO_PRIVATE = CURLINFO_STRING +21, */ - INFO(PRIVATE); - /* CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG +22, */ - INFO(HTTP_CONNECTCODE); -#if LIBCURL_VERSION_NUM > 0x070a07 - /* CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG +23, */ - INFO(HTTPAUTH_AVAIL); - /* CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG +24, */ - INFO(PROXYAUTH_AVAIL); -#endif -#undef INFO -} -/* }}} */ - -#endif -/* }}} HAVE_CURL */ - /* {{{ Day/Month/TZ checks for http_parse_date() Originally by libcurl, Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. */ static int check_day(char *day, size_t len) @@ -784,8 +253,8 @@ static int check_tzone(char *tzone) } /* }}} */ -/* static char *pretty_key(char *, int, int, int) */ -static char *pretty_key(char *key, int key_len, int uctitle, int xhyphen) +/* char *pretty_key(char *, int, int, int) */ +char *pretty_key(char *key, int key_len, int uctitle, int xhyphen) { if (key && key_len) { int i, wasalpha; @@ -1481,7 +950,7 @@ PHP_HTTP_API char *_http_absolute_uri(const char *url, if ((PTR = strchr(URI, '\r')) || (PTR = strchr(URI, '\n'))) { PTR = 0; } - + return URI; } /* }}} */ @@ -2065,186 +1534,6 @@ PHP_HTTP_API void _http_get_request_headers(zval *array TSRMLS_DC) } /* }}} */ -/* {{{ HAVE_CURL */ -#ifdef HTTP_HAVE_CURL - -/* {{{ STATUS http_get(char *, HashTable *, HashTable *, char **, size_t *) */ -PHP_HTTP_API STATUS _http_get(const char *URL, HashTable *options, - HashTable *info, char **data, size_t *data_len TSRMLS_DC) -{ - STATUS rs; - CURL *ch = curl_easy_init(); - - if (!ch) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize curl"); - return FAILURE; - } - - rs = http_get_ex(ch, URL, options, info, data, data_len); - curl_easy_cleanup(ch); - return rs; -} -/* }}} */ - -/* {{{ STATUS http_get_ex(CURL *, char *, HashTable *, HashTable *, char **, size_t *) */ -PHP_HTTP_API STATUS _http_get_ex(CURL *ch, const char *URL, HashTable *options, - HashTable *info, char **data, size_t *data_len TSRMLS_DC) -{ - http_curl_initbuf(CURLBUF_EVRY); - http_curl_setopts(ch, URL, options); - curl_easy_setopt(ch, CURLOPT_NOBODY, 0); - curl_easy_setopt(ch, CURLOPT_POST, 0); - - if (CURLE_OK != curl_easy_perform(ch)) { - http_curl_freebuf(CURLBUF_EVRY); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request"); - return FAILURE; - } - if (info) { - http_curl_getinfo(ch, info); - } - http_curl_movebuf(CURLBUF_EVRY, data, data_len); - return SUCCESS; -} - -/* {{{ STATUS http_head(char *, HashTable *, HashTable *, char **data, size_t *) */ -PHP_HTTP_API STATUS _http_head(const char *URL, HashTable *options, - HashTable *info, char **data, size_t *data_len TSRMLS_DC) -{ - STATUS rs; - CURL *ch = curl_easy_init(); - - if (!ch) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize curl"); - return FAILURE; - } - - rs = http_head_ex(ch, URL, options, info, data, data_len); - curl_easy_cleanup(ch); - return rs; -} -/* }}} */ - -/* {{{ STATUS http_head_ex(CURL *, char *, HashTable *, HashTable *, char **data, size_t *) */ -PHP_HTTP_API STATUS _http_head_ex(CURL *ch, const char *URL, HashTable *options, - HashTable *info, char **data, size_t *data_len TSRMLS_DC) -{ - http_curl_initbuf(CURLBUF_HDRS); - http_curl_setopts(ch, URL, options); - curl_easy_setopt(ch, CURLOPT_NOBODY, 1); - curl_easy_setopt(ch, CURLOPT_POST, 0); - - if (CURLE_OK != curl_easy_perform(ch)) { - http_curl_freebuf(CURLBUF_HDRS); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request"); - return FAILURE; - } - if (info) { - http_curl_getinfo(ch, info); - } - http_curl_movebuf(CURLBUF_HDRS, data, data_len); - return SUCCESS; -} - -/* {{{ STATUS http_post_data(char *, char *, size_t, HashTable *, HashTable *, char **, size_t *) */ -PHP_HTTP_API STATUS _http_post_data(const char *URL, char *postdata, - size_t postdata_len, HashTable *options, HashTable *info, char **data, - size_t *data_len TSRMLS_DC) -{ - STATUS rs; - CURL *ch = curl_easy_init(); - - if (!ch) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize curl"); - return FAILURE; - } - rs = http_post_data_ex(ch, URL, postdata, postdata_len, options, info, data, data_len); - curl_easy_cleanup(ch); - return rs; -} -/* }}} */ - -/* {{{ STATUS http_post_data_ex(CURL *, char *, char *, size_t, HashTable *, HashTable *, char **, size_t *) */ -PHP_HTTP_API STATUS _http_post_data_ex(CURL *ch, const char *URL, char *postdata, - size_t postdata_len, HashTable *options, HashTable *info, char **data, - size_t *data_len TSRMLS_DC) -{ - http_curl_initbuf(CURLBUF_EVRY); - http_curl_setopts(ch, URL, options); - curl_easy_setopt(ch, CURLOPT_POST, 1); - curl_easy_setopt(ch, CURLOPT_POSTFIELDS, postdata); - curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, postdata_len); - - if (CURLE_OK != curl_easy_perform(ch)) { - http_curl_freebuf(CURLBUF_EVRY); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request"); - return FAILURE; - } - if (info) { - http_curl_getinfo(ch, info); - } - http_curl_movebuf(CURLBUF_EVRY, data, data_len); - return SUCCESS; -} -/* }}} */ - -/* {{{ STATUS http_post_array_ex(CURL *, char *, HashTable *, HashTable *, HashTable *, char **, size_t *) */ -PHP_HTTP_API STATUS _http_post_array_ex(CURL *ch, const char *URL, HashTable *postarray, - HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC) -{ - smart_str qstr = {0}; - STATUS status; - - HTTP_URL_ARGSEP_OVERRIDE; - if (php_url_encode_hash_ex(postarray, &qstr, NULL,0,NULL,0,NULL,0,NULL TSRMLS_CC) != SUCCESS) { - if (qstr.c) { - efree(qstr.c); - } - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not encode post data"); - HTTP_URL_ARGSEP_RESTORE; - return FAILURE; - } - smart_str_0(&qstr); - HTTP_URL_ARGSEP_RESTORE; - - if (ch) { - status = http_post_data_ex(ch, URL, qstr.c, qstr.len, options, info, data, data_len); - } else { - status = http_post_data(URL, qstr.c, qstr.len, options, info, data, data_len); - } - - if (qstr.c) { - efree(qstr.c); - } - return status; -} -/* }}} */ - -/* {{{ STATUS http_post_curldata_ex(CURL *, char *, curl_httppost *, HashTable *, HashTable *, char **, size_t *) */ -PHP_HTTP_API STATUS _http_post_curldata_ex(CURL *ch, const char *URL, - struct curl_httppost *curldata, HashTable *options, HashTable *info, - char **data, size_t *data_len TSRMLS_DC) -{ - http_curl_initbuf(CURLBUF_EVRY); - http_curl_setopts(ch, URL, options); - curl_easy_setopt(ch, CURLOPT_POST, 1); - curl_easy_setopt(ch, CURLOPT_HTTPPOST, curldata); - - if (CURLE_OK != curl_easy_perform(ch)) { - http_curl_freebuf(CURLBUF_EVRY); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request"); - return FAILURE; - } - if (info) { - http_curl_getinfo(ch, info); - } - http_curl_movebuf(CURLBUF_EVRY, data, data_len); - return SUCCESS;} -/* }}} */ - -#endif -/* }}} HAVE_CURL */ - /* {{{ STATUS http_auth_header(char *, char*) */ PHP_HTTP_API STATUS _http_auth_header(const char *type, const char *realm TSRMLS_DC) { @@ -2299,3 +1588,4 @@ PHP_HTTP_API STATUS _http_auth_credentials(char **user, char **pass TSRMLS_DC) * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ + diff --git a/http_curl_api.c b/http_curl_api.c new file mode 100644 index 0000000..ec3934d --- /dev/null +++ b/http_curl_api.c @@ -0,0 +1,684 @@ +/* + +----------------------------------------------------------------------+ + | PECL :: http | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, that | + | is bundled with this package in the file LICENSE, and is available | + | through the world-wide-web at http://www.php.net/license/3_0.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. | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Michael Wallner | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef PHP_WIN32 +# define _WINSOCKAPI_ +# define ZEND_INCLUDE_FULL_WINDOWS_HEADERS +# include +# include +#endif + +#include +#include + +#include "php.h" +#include "php_http.h" +#include "php_http_api.h" +#include "php_http_curl_api.h" + +#include "ext/standard/php_smart_str.h" + +ZEND_DECLARE_MODULE_GLOBALS(http) + +#define http_curl_initbuf() _http_curl_initbuf_ex(0 TSRMLS_CC) +#define http_curl_initbuf_ex(s) _http_curl_initbuf_ex((s) TSRMLS_CC) +static inline void _http_curl_initbuf_ex(size_t chunk_size TSRMLS_DC); + +#define http_curl_freebuf() _http_curl_freebuf(TSRMLS_C) +static inline void _http_curl_freebuf(TSRMLS_D); +#define http_curl_sizebuf(l) _http_curl_sizebuf((l) TSRMLS_CC) +static inline void _http_curl_sizebuf(size_t len TSRMLS_DC); +#define http_curl_movebuf(d, l) _http_curl_movebuf((d), (l) TSRMLS_CC) +static inline void _http_curl_movebuf(char **data, size_t *data_len TSRMLS_DC); +#define http_curl_copybuf(d, l) _http_curl_copybuf((d), (l) TSRMLS_CC) +static inline void _http_curl_copybuf(char **data, size_t *data_len TSRMLS_DC); +#define http_curl_setopts(c, u, o) _http_curl_setopts((c), (u), (o) TSRMLS_CC) +static inline void _http_curl_setopts(CURL *ch, const char *url, HashTable *options TSRMLS_DC); + +#define http_curl_getopt(o, k) _http_curl_getopt((o), (k) TSRMLS_CC, 0) +#define http_curl_getopt1(o, k, t1) _http_curl_getopt((o), (k) TSRMLS_CC, 1, (t1)) +#define http_curl_getopt2(o, k, t1, t2) _http_curl_getopt((o), (k) TSRMLS_CC, 2, (t1), (t2)) +static inline zval *_http_curl_getopt(HashTable *options, char *key TSRMLS_DC, int checks, ...); + +static size_t http_curl_body_callback(char *, size_t, size_t, void *); +static size_t http_curl_hdrs_callback(char *, size_t, size_t, void *); + +#define http_curl_getinfo(c, h) _http_curl_getinfo((c), (h) TSRMLS_CC) +static inline void _http_curl_getinfo(CURL *ch, HashTable *info TSRMLS_DC); +#define http_curl_getinfo_ex(c, i, a) _http_curl_getinfo_ex((c), (i), (a) TSRMLS_CC) +static inline void _http_curl_getinfo_ex(CURL *ch, CURLINFO i, zval *array TSRMLS_DC); +#define http_curl_getinfoname(i) _http_curl_getinfoname((i) TSRMLS_CC) +static inline char *_http_curl_getinfoname(CURLINFO i TSRMLS_DC); + +/* {{{ static inline void http_curl_initbuf(size_t chunk_size) */ +static inline void _http_curl_initbuf_ex(size_t chunk_size TSRMLS_DC) +{ + size_t size = (chunk_size > 0) ? chunk_size : HTTP_CURLBUF_SIZE; + + http_curl_freebuf(); + + HTTP_G(curlbuf).data = emalloc(size); + HTTP_G(curlbuf).free = size; + HTTP_G(curlbuf).size = size; +} +/* }}} */ + +/* {{{ static inline void http_curl_freebuf(void) */ +static inline void _http_curl_freebuf(TSRMLS_D) +{ + if (HTTP_G(curlbuf).data) { + efree(HTTP_G(curlbuf).data); + HTTP_G(curlbuf).data = NULL; + } + HTTP_G(curlbuf).used = 0; + HTTP_G(curlbuf).free = 0; + HTTP_G(curlbuf).size = 0; +} +/* }}} */ + +/* {{{ static inline void http_curl_copybuf(char **, size_t *) */ +static inline void _http_curl_copybuf(char **data, size_t *data_len TSRMLS_DC) +{ + *data_len = HTTP_G(curlbuf).used; + + *data = ecalloc(1, HTTP_G(curlbuf).used + 1); + memcpy(*data, HTTP_G(curlbuf).data, *data_len); +} +/* }}} */ + +/* {{{ static inline void http_curl_movebuf(char **, size_t *) */ +static inline void _http_curl_movebuf(char **data, size_t *data_len TSRMLS_DC) +{ + http_curl_copybuf(data, data_len); + http_curl_freebuf(); +} +/* }}} */ + +/* {{{ static inline void http_curl_sizebuf(size_t len) */ +static inline void _http_curl_sizebuf(size_t len TSRMLS_DC) +{ + if (len > HTTP_G(curlbuf).free) { + size_t bsize = HTTP_G(curlbuf).size; + while (bsize < len) { + bsize *= 2; + } + HTTP_G(curlbuf).data = erealloc(HTTP_G(curlbuf).data, HTTP_G(curlbuf).used + bsize); + HTTP_G(curlbuf).free += bsize; + } +} +/* }}} */ + +/* {{{ static size_t http_curl_body_callback(char *, size_t, size_t, void *) */ +static size_t http_curl_body_callback(char *buf, size_t len, size_t n, void *s) +{ + TSRMLS_FETCH(); + + http_curl_sizebuf(len *= n); + + memcpy(HTTP_G(curlbuf).data + HTTP_G(curlbuf).used, buf, len); + HTTP_G(curlbuf).free -= len; + HTTP_G(curlbuf).used += len; + return len; +} +/* }}} */ + +/* {{{ static size_t http_curl_hdrs_callback(char *, size_t, size_t, void *) */ +static size_t http_curl_hdrs_callback(char *buf, size_t len, size_t n, void *s) +{ + TSRMLS_FETCH(); + + /* discard previous headers */ + if ((HTTP_G(curlbuf).used) && (!strncmp(buf, "HTTP/1.", sizeof("HTTP/1.") - 1))) { + http_curl_initbuf(); + } + http_curl_sizebuf(len *= n); + + memcpy(HTTP_G(curlbuf).data + HTTP_G(curlbuf).used, buf, len); + HTTP_G(curlbuf).free -= len; + HTTP_G(curlbuf).used += len; + return len; +} +/* }}} */ + +/* {{{ static inline zval *http_curl_getopt(HashTable *, char *, int, ...) */ +static inline zval *_http_curl_getopt(HashTable *options, char *key TSRMLS_DC, int checks, ...) +{ + zval **zoption; + va_list types; + int i; + + if (SUCCESS != zend_hash_find(options, key, strlen(key) + 1, (void **) &zoption)) { + return NULL; + } + if (checks < 1) { + return *zoption; + } + + va_start(types, checks); + for (i = 0; i < checks; ++i) { + if ((va_arg(types, int)) == (Z_TYPE_PP(zoption))) { + va_end(types); + return *zoption; + } + } + va_end(types); + return NULL; +} +/* }}} */ + +/* {{{ static inline void http_curl_setopts(CURL *, char *, HashTable *) */ +static inline void _http_curl_setopts(CURL *ch, const char *url, HashTable *options TSRMLS_DC) +{ + zval *zoption; + + /* standard options */ + curl_easy_setopt(ch, CURLOPT_URL, url); + curl_easy_setopt(ch, CURLOPT_HEADER, 0); + curl_easy_setopt(ch, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(ch, CURLOPT_AUTOREFERER, 1); + curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, http_curl_body_callback); + curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, http_curl_hdrs_callback); +#if defined(ZTS) && (LIBCURL_VERSION_NUM >= 0x070a00) + curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1); +#endif + + if ((!options) || (1 > zend_hash_num_elements(options))) { + return; + } + + /* redirects, defaults to 0 */ + if (zoption = http_curl_getopt1(options, "redirect", IS_LONG)) { + curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, Z_LVAL_P(zoption) ? 1 : 0); + curl_easy_setopt(ch, CURLOPT_MAXREDIRS, Z_LVAL_P(zoption)); + if (zoption = http_curl_getopt2(options, "unrestrictedauth", IS_LONG, IS_BOOL)) { + curl_easy_setopt(ch, CURLOPT_UNRESTRICTED_AUTH, Z_LVAL_P(zoption)); + } + } else { + curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, 0); + } + + /* proxy */ + if (zoption = http_curl_getopt1(options, "proxyhost", IS_STRING)) { + curl_easy_setopt(ch, CURLOPT_PROXY, Z_STRVAL_P(zoption)); + /* port */ + if (zoption = http_curl_getopt1(options, "proxyport", IS_LONG)) { + curl_easy_setopt(ch, CURLOPT_PROXYPORT, Z_LVAL_P(zoption)); + } + /* user:pass */ + if (zoption = http_curl_getopt1(options, "proxyauth", IS_STRING)) { + curl_easy_setopt(ch, CURLOPT_PROXYUSERPWD, Z_STRVAL_P(zoption)); + } +#if LIBCURL_VERSION_NUM > 0x070a06 + /* auth method */ + if (zoption = http_curl_getopt1(options, "proxyauthtype", IS_LONG)) { + curl_easy_setopt(ch, CURLOPT_PROXYAUTH, Z_LVAL_P(zoption)); + } +#endif + } + + /* auth */ + if (zoption = http_curl_getopt1(options, "httpauth", IS_STRING)) { + curl_easy_setopt(ch, CURLOPT_USERPWD, Z_STRVAL_P(zoption)); + } +#if LIBCURL_VERSION_NUM > 0x070a05 + if (zoption = http_curl_getopt1(options, "httpauthtype", IS_LONG)) { + curl_easy_setopt(ch, CURLOPT_HTTPAUTH, Z_LVAL_P(zoption)); + } +#endif + + /* compress, enabled by default (empty string enables deflate and gzip) */ + if (zoption = http_curl_getopt2(options, "compress", IS_LONG, IS_BOOL)) { + if (Z_LVAL_P(zoption)) { + curl_easy_setopt(ch, CURLOPT_ENCODING, ""); + } + } else { + curl_easy_setopt(ch, CURLOPT_ENCODING, ""); + } + + /* another port */ + if (zoption = http_curl_getopt1(options, "port", IS_LONG)) { + curl_easy_setopt(ch, CURLOPT_PORT, Z_LVAL_P(zoption)); + } + + /* referer */ + if (zoption = http_curl_getopt1(options, "referer", IS_STRING)) { + curl_easy_setopt(ch, CURLOPT_REFERER, Z_STRVAL_P(zoption)); + } + + /* useragent, default "PECL::HTTP/version (PHP/version)" */ + if (zoption = http_curl_getopt1(options, "useragent", IS_STRING)) { + curl_easy_setopt(ch, CURLOPT_USERAGENT, Z_STRVAL_P(zoption)); + } else { + curl_easy_setopt(ch, CURLOPT_USERAGENT, + "PECL::HTTP/" PHP_EXT_HTTP_VERSION " (PHP/" PHP_VERSION ")"); + } + + /* cookies, array('name' => 'value') */ + if (zoption = http_curl_getopt1(options, "cookies", IS_ARRAY)) { + char *cookie_key; + zval **cookie_val; + int key_type; + smart_str qstr = {0}; + + zend_hash_internal_pointer_reset(Z_ARRVAL_P(zoption)); + while (HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_type(Z_ARRVAL_P(zoption)))) { + if (key_type == HASH_KEY_IS_STRING) { + zend_hash_get_current_key(Z_ARRVAL_P(zoption), &cookie_key, NULL, 0); + zend_hash_get_current_data(Z_ARRVAL_P(zoption), (void **) &cookie_val); + smart_str_appends(&qstr, cookie_key); + smart_str_appendl(&qstr, "=", 1); + smart_str_appendl(&qstr, Z_STRVAL_PP(cookie_val), Z_STRLEN_PP(cookie_val)); + smart_str_appendl(&qstr, "; ", 2); + zend_hash_move_forward(Z_ARRVAL_P(zoption)); + } + } + smart_str_0(&qstr); + + if (qstr.c) { + curl_easy_setopt(ch, CURLOPT_COOKIE, qstr.c); + /* FIXXXME: mem-leak */ + } + } + + /* cookiestore */ + if (zoption = http_curl_getopt1(options, "cookiestore", IS_STRING)) { + curl_easy_setopt(ch, CURLOPT_COOKIEFILE, Z_STRVAL_P(zoption)); + curl_easy_setopt(ch, CURLOPT_COOKIEJAR, Z_STRVAL_P(zoption)); + } + + /* additional headers, array('name' => 'value') */ + if (zoption = http_curl_getopt1(options, "headers", IS_ARRAY)) { + int key_type; + char *header_key, header[1024] = {0}; + zval **header_val; + struct curl_slist *headers = NULL; + + zend_hash_internal_pointer_reset(Z_ARRVAL_P(zoption)); + while (HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_type(Z_ARRVAL_P(zoption)))) { + if (key_type == HASH_KEY_IS_STRING) { + zend_hash_get_current_key(Z_ARRVAL_P(zoption), &header_key, NULL, 0); + zend_hash_get_current_data(Z_ARRVAL_P(zoption), (void **) &header_val); + snprintf(header, 1023, "%s: %s", header_key, Z_STRVAL_PP(header_val)); + headers = curl_slist_append(headers, header); + zend_hash_move_forward(Z_ARRVAL_P(zoption)); + } + } + if (headers) { + curl_easy_setopt(ch, CURLOPT_HTTPHEADER, headers); + } + } +} +/* }}} */ + +/* {{{ static inline char *http_curl_getinfoname(CURLINFO) */ +static inline char *_http_curl_getinfoname(CURLINFO i TSRMLS_DC) +{ +#define CASE(I) case CURLINFO_ ##I : { static char I[] = #I; return pretty_key(I, sizeof(#I)-1, 0, 0); } + switch (i) + { + /* CURLINFO_EFFECTIVE_URL = CURLINFO_STRING +1, */ + CASE(EFFECTIVE_URL); + /* CURLINFO_RESPONSE_CODE = CURLINFO_LONG +2, */ +#if LIBCURL_VERSION_NUM > 0x070a06 + CASE(RESPONSE_CODE); +#else + CASE(HTTP_CODE); +#endif + /* CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE +3, */ + CASE(TOTAL_TIME); + /* CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE +4, */ + CASE(NAMELOOKUP_TIME); + /* CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE +5, */ + CASE(CONNECT_TIME); + /* CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE +6, */ + CASE(PRETRANSFER_TIME); + /* CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE +7, */ + CASE(SIZE_UPLOAD); + /* CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE +8, */ + CASE(SIZE_DOWNLOAD); + /* CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE +9, */ + CASE(SPEED_DOWNLOAD); + /* CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE +10, */ + CASE(SPEED_UPLOAD); + /* CURLINFO_HEADER_SIZE = CURLINFO_LONG +11, */ + CASE(HEADER_SIZE); + /* CURLINFO_REQUEST_SIZE = CURLINFO_LONG +12, */ + CASE(REQUEST_SIZE); + /* CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG +13, */ + CASE(SSL_VERIFYRESULT); + /* CURLINFO_FILETIME = CURLINFO_LONG +14, */ + CASE(FILETIME); + /* CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE +15, */ + CASE(CONTENT_LENGTH_DOWNLOAD); + /* CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE +16, */ + CASE(CONTENT_LENGTH_UPLOAD); + /* CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE +17, */ + CASE(STARTTRANSFER_TIME); + /* CURLINFO_CONTENT_TYPE = CURLINFO_STRING +18, */ + CASE(CONTENT_TYPE); + /* CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE +19, */ + CASE(REDIRECT_TIME); + /* CURLINFO_REDIRECT_COUNT = CURLINFO_LONG +20, */ + CASE(REDIRECT_COUNT); + /* CURLINFO_PRIVATE = CURLINFO_STRING +21, * (mike) / + CASE(PRIVATE); + /* CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG +22, */ + CASE(HTTP_CONNECTCODE); +#if LIBCURL_VERSION_NUM > 0x070a07 + /* CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG +23, */ + CASE(HTTPAUTH_AVAIL); + /* CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG +24, */ + CASE(PROXYAUTH_AVAIL); +#endif + } +#undef CASE + return NULL; +} +/* }}} */ + +/* {{{ static inline void http_curl_getinfo_ex(CURL, CURLINFO, zval *) */ +static inline void _http_curl_getinfo_ex(CURL *ch, CURLINFO i, zval *array TSRMLS_DC) +{ + char *key; + if (key = http_curl_getinfoname(i)) { + switch (i & ~CURLINFO_MASK) + { + case CURLINFO_STRING: + { + char *c; + if (CURLE_OK == curl_easy_getinfo(ch, i, &c)) { + add_assoc_string(array, key, c ? c : "", 1); + } + } + break; + + case CURLINFO_DOUBLE: + { + double d; + if (CURLE_OK == curl_easy_getinfo(ch, i, &d)) { + add_assoc_double(array, key, d); + } + } + break; + + case CURLINFO_LONG: + { + long l; + if (CURLE_OK == curl_easy_getinfo(ch, i, &l)) { + add_assoc_long(array, key, l); + } + } + break; + } + } +} +/* }}} */ + +/* {{{ static inline http_curl_getinfo(CURL, HashTable *) */ +static inline void _http_curl_getinfo(CURL *ch, HashTable *info TSRMLS_DC) +{ + zval array; + Z_ARRVAL(array) = info; + +#define INFO(I) http_curl_getinfo_ex(ch, CURLINFO_ ##I , &array) + /* CURLINFO_EFFECTIVE_URL = CURLINFO_STRING +1, */ + INFO(EFFECTIVE_URL); +#if LIBCURL_VERSION_NUM > 0x070a06 + /* CURLINFO_RESPONSE_CODE = CURLINFO_LONG +2, */ + INFO(RESPONSE_CODE); +#else + INFO(HTTP_CODE); +#endif + /* CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE +3, */ + INFO(TOTAL_TIME); + /* CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE +4, */ + INFO(NAMELOOKUP_TIME); + /* CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE +5, */ + INFO(CONNECT_TIME); + /* CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE +6, */ + INFO(PRETRANSFER_TIME); + /* CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE +7, */ + INFO(SIZE_UPLOAD); + /* CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE +8, */ + INFO(SIZE_DOWNLOAD); + /* CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE +9, */ + INFO(SPEED_DOWNLOAD); + /* CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE +10, */ + INFO(SPEED_UPLOAD); + /* CURLINFO_HEADER_SIZE = CURLINFO_LONG +11, */ + INFO(HEADER_SIZE); + /* CURLINFO_REQUEST_SIZE = CURLINFO_LONG +12, */ + INFO(REQUEST_SIZE); + /* CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG +13, */ + INFO(SSL_VERIFYRESULT); + /* CURLINFO_FILETIME = CURLINFO_LONG +14, */ + INFO(FILETIME); + /* CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE +15, */ + INFO(CONTENT_LENGTH_DOWNLOAD); + /* CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE +16, */ + INFO(CONTENT_LENGTH_UPLOAD); + /* CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE +17, */ + INFO(STARTTRANSFER_TIME); + /* CURLINFO_CONTENT_TYPE = CURLINFO_STRING +18, */ + INFO(CONTENT_TYPE); + /* CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE +19, */ + INFO(REDIRECT_TIME); + /* CURLINFO_REDIRECT_COUNT = CURLINFO_LONG +20, */ + INFO(REDIRECT_COUNT); + /* CURLINFO_PRIVATE = CURLINFO_STRING +21, */ + INFO(PRIVATE); + /* CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG +22, */ + INFO(HTTP_CONNECTCODE); +#if LIBCURL_VERSION_NUM > 0x070a07 + /* CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG +23, */ + INFO(HTTPAUTH_AVAIL); + /* CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG +24, */ + INFO(PROXYAUTH_AVAIL); +#endif +#undef INFO +} +/* }}} */ + + + +/* {{{ STATUS http_get(char *, HashTable *, HashTable *, char **, size_t *) */ +PHP_HTTP_API STATUS _http_get(const char *URL, HashTable *options, + HashTable *info, char **data, size_t *data_len TSRMLS_DC) +{ + STATUS rs; + CURL *ch = curl_easy_init(); + + if (!ch) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize curl"); + return FAILURE; + } + + rs = http_get_ex(ch, URL, options, info, data, data_len); + curl_easy_cleanup(ch); + return rs; +} +/* }}} */ + +/* {{{ STATUS http_get_ex(CURL *, char *, HashTable *, HashTable *, char **, size_t *) */ +PHP_HTTP_API STATUS _http_get_ex(CURL *ch, const char *URL, HashTable *options, + HashTable *info, char **data, size_t *data_len TSRMLS_DC) +{ + http_curl_initbuf(); + http_curl_setopts(ch, URL, options); + curl_easy_setopt(ch, CURLOPT_NOBODY, 0); + curl_easy_setopt(ch, CURLOPT_POST, 0); + + if (CURLE_OK != curl_easy_perform(ch)) { + http_curl_freebuf(); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request"); + return FAILURE; + } + if (info) { + http_curl_getinfo(ch, info); + } + http_curl_movebuf(data, data_len); + return SUCCESS; +} + +/* {{{ STATUS http_head(char *, HashTable *, HashTable *, char **data, size_t *) */ +PHP_HTTP_API STATUS _http_head(const char *URL, HashTable *options, + HashTable *info, char **data, size_t *data_len TSRMLS_DC) +{ + STATUS rs; + CURL *ch = curl_easy_init(); + + if (!ch) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize curl"); + return FAILURE; + } + + rs = http_head_ex(ch, URL, options, info, data, data_len); + curl_easy_cleanup(ch); + return rs; +} +/* }}} */ + +/* {{{ STATUS http_head_ex(CURL *, char *, HashTable *, HashTable *, char **data, size_t *) */ +PHP_HTTP_API STATUS _http_head_ex(CURL *ch, const char *URL, HashTable *options, + HashTable *info, char **data, size_t *data_len TSRMLS_DC) +{ + http_curl_initbuf(); + http_curl_setopts(ch, URL, options); + curl_easy_setopt(ch, CURLOPT_NOBODY, 1); + curl_easy_setopt(ch, CURLOPT_POST, 0); + + if (CURLE_OK != curl_easy_perform(ch)) { + http_curl_freebuf(); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request"); + return FAILURE; + } + if (info) { + http_curl_getinfo(ch, info); + } + http_curl_movebuf(data, data_len); + return SUCCESS; +} + +/* {{{ STATUS http_post_data(char *, char *, size_t, HashTable *, HashTable *, char **, size_t *) */ +PHP_HTTP_API STATUS _http_post_data(const char *URL, char *postdata, + size_t postdata_len, HashTable *options, HashTable *info, char **data, + size_t *data_len TSRMLS_DC) +{ + STATUS rs; + CURL *ch = curl_easy_init(); + + if (!ch) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize curl"); + return FAILURE; + } + rs = http_post_data_ex(ch, URL, postdata, postdata_len, options, info, data, data_len); + curl_easy_cleanup(ch); + return rs; +} +/* }}} */ + +/* {{{ STATUS http_post_data_ex(CURL *, char *, char *, size_t, HashTable *, HashTable *, char **, size_t *) */ +PHP_HTTP_API STATUS _http_post_data_ex(CURL *ch, const char *URL, char *postdata, + size_t postdata_len, HashTable *options, HashTable *info, char **data, + size_t *data_len TSRMLS_DC) +{ + http_curl_initbuf(); + http_curl_setopts(ch, URL, options); + curl_easy_setopt(ch, CURLOPT_POST, 1); + curl_easy_setopt(ch, CURLOPT_POSTFIELDS, postdata); + curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, postdata_len); + + if (CURLE_OK != curl_easy_perform(ch)) { + http_curl_freebuf(); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request"); + return FAILURE; + } + if (info) { + http_curl_getinfo(ch, info); + } + http_curl_movebuf(data, data_len); + return SUCCESS; +} +/* }}} */ + +/* {{{ STATUS http_post_array_ex(CURL *, char *, HashTable *, HashTable *, HashTable *, char **, size_t *) */ +PHP_HTTP_API STATUS _http_post_array_ex(CURL *ch, const char *URL, HashTable *postarray, + HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC) +{ + smart_str qstr = {0}; + STATUS status; + + HTTP_URL_ARGSEP_OVERRIDE; + if (php_url_encode_hash_ex(postarray, &qstr, NULL,0,NULL,0,NULL,0,NULL TSRMLS_CC) != SUCCESS) { + if (qstr.c) { + efree(qstr.c); + } + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not encode post data"); + HTTP_URL_ARGSEP_RESTORE; + return FAILURE; + } + smart_str_0(&qstr); + HTTP_URL_ARGSEP_RESTORE; + + if (ch) { + status = http_post_data_ex(ch, URL, qstr.c, qstr.len, options, info, data, data_len); + } else { + status = http_post_data(URL, qstr.c, qstr.len, options, info, data, data_len); + } + + if (qstr.c) { + efree(qstr.c); + } + return status; +} +/* }}} */ + +/* {{{ STATUS http_post_curldata_ex(CURL *, char *, curl_httppost *, HashTable *, HashTable *, char **, size_t *) */ +PHP_HTTP_API STATUS _http_post_curldata_ex(CURL *ch, const char *URL, + struct curl_httppost *curldata, HashTable *options, HashTable *info, + char **data, size_t *data_len TSRMLS_DC) +{ + http_curl_initbuf(); + http_curl_setopts(ch, URL, options); + curl_easy_setopt(ch, CURLOPT_POST, 1); + curl_easy_setopt(ch, CURLOPT_HTTPPOST, curldata); + + if (CURLE_OK != curl_easy_perform(ch)) { + http_curl_freebuf(); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not perform request"); + return FAILURE; + } + if (info) { + http_curl_getinfo(ch, info); + } + http_curl_movebuf(data, data_len); + return SUCCESS;} +/* }}} */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/http_functions.c b/http_functions.c new file mode 100644 index 0000000..8b79f42 --- /dev/null +++ b/http_functions.c @@ -0,0 +1,943 @@ +/* + +----------------------------------------------------------------------+ + | PECL :: http | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, that | + | is bundled with this package in the file LICENSE, and is available | + | through the world-wide-web at http://www.php.net/license/3_0.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. | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Michael Wallner | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "snprintf.h" +#include "ext/standard/info.h" +#include "ext/session/php_session.h" +#include "ext/standard/php_string.h" +#include "ext/standard/php_smart_str.h" + +#include "SAPI.h" + +#include "php_http.h" +#include "php_http_api.h" +#include "php_http_curl_api.h" + +ZEND_DECLARE_MODULE_GLOBALS(http) + +/* {{{ proto string http_date([int timestamp]) + * + * This function returns a valid HTTP date regarding RFC 822/1123 + * looking like: "Wed, 22 Dec 2004 11:34:47 GMT" + * + */ +PHP_FUNCTION(http_date) +{ + long t = -1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) { + RETURN_FALSE; + } + + if (t == -1) { + t = (long) time(NULL); + } + + RETURN_STRING(http_date(t), 0); +} +/* }}} */ + +/* {{{ proto string http_absolute_uri(string url[, string proto]) + * + * This function returns an absolute URI constructed from url. + * If the url is already abolute but a different proto was supplied, + * only the proto part of the URI will be updated. If url has no + * path specified, the path of the current REQUEST_URI will be taken. + * The host will be taken either from the Host HTTP header of the client + * the SERVER_NAME or just localhost if prior are not available. + * + * Some examples: + *
+ *  url = "page.php"                    => http://www.example.com/current/path/page.php
+ *  url = "/page.php"                   => http://www.example.com/page.php
+ *  url = "/page.php", proto = "https"  => https://www.example.com/page.php
+ * 
+ * + */ +PHP_FUNCTION(http_absolute_uri) +{ + char *url = NULL, *proto = NULL; + int url_len = 0, proto_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &url, &url_len, &proto, &proto_len) != SUCCESS) { + RETURN_FALSE; + } + + RETURN_STRING(http_absolute_uri(url, proto), 0); +} +/* }}} */ + +/* {{{ proto string http_negotiate_language(array supported[, string default = 'en-US']) + * + * This function negotiates the clients preferred language based on its + * Accept-Language HTTP header. It returns the negotiated language or + * the default language if none match. + * + * The qualifier is recognized and languages without qualifier are rated highest. + * + * The supported parameter is expected to be an array having + * the supported languages as array values. + * + * Example: + *
+ * 
+ * 
+ * + */ +PHP_FUNCTION(http_negotiate_language) +{ + zval *supported; + char *def = NULL; + int def_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) { + RETURN_FALSE; + } + + if (!def) { + def = "en-US"; + } + + RETURN_STRING(http_negotiate_language(supported, def), 0); +} +/* }}} */ + +/* {{{ proto string http_negotiate_charset(array supported[, string default = 'iso-8859-1']) + * + * This function negotiates the clients preferred charset based on its + * Accept-Charset HTTP header. It returns the negotiated charset or + * the default charset if none match. + * + * The qualifier is recognized and charset without qualifier are rated highest. + * + * The supported parameter is expected to be an array having + * the supported charsets as array values. + * + * Example: + *
+ * 
+ * 
+ */ +PHP_FUNCTION(http_negotiate_charset) +{ + zval *supported; + char *def = NULL; + int def_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &supported, &def, &def_len) != SUCCESS) { + RETURN_FALSE; + } + + if (!def) { + def = "iso-8859-1"; + } + + RETURN_STRING(http_negotiate_charset(supported, def), 0); +} +/* }}} */ + +/* {{{ proto bool http_send_status(int status) + * + * Send HTTP status code. + * + */ +PHP_FUNCTION(http_send_status) +{ + int status = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &status) != SUCCESS) { + RETURN_FALSE; + } + if (status < 100 || status > 510) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid HTTP status code (100-510): %d", status); + RETURN_FALSE; + } + + RETURN_SUCCESS(http_send_status(status)); +} +/* }}} */ + +/* {{{ proto bool http_send_last_modified([int timestamp]) + * + * This converts the given timestamp to a valid HTTP date and + * sends it as "Last-Modified" HTTP header. If timestamp is + * omitted, current time is sent. + * + */ +PHP_FUNCTION(http_send_last_modified) +{ + long t = -1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) { + RETURN_FALSE; + } + + if (t == -1) { + t = (long) time(NULL); + } + + RETURN_SUCCESS(http_send_last_modified(t)); +} +/* }}} */ + +/* {{{ proto bool http_send_content_type([string content_type = 'application/x-octetstream']) + * + * Sets the content type. + * + */ +PHP_FUNCTION(http_send_content_type) +{ + char *ct; + int ct_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &ct, &ct_len) != SUCCESS) { + RETURN_FALSE; + } + + if (!ct_len) { + RETURN_SUCCESS(http_send_content_type("application/x-octetstream", sizeof("application/x-octetstream") - 1)); + } + RETURN_SUCCESS(http_send_content_type(ct, ct_len)); +} +/* }}} */ + +/* {{{ proto bool http_send_content_disposition(string filename[, bool inline = false]) + * + * Set the Content Disposition. The Content-Disposition header is very useful + * if the data actually sent came from a file or something similar, that should + * be "saved" by the client/user (i.e. by browsers "Save as..." popup window). + * + */ +PHP_FUNCTION(http_send_content_disposition) +{ + char *filename; + int f_len; + zend_bool send_inline = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &filename, &f_len, &send_inline) != SUCCESS) { + RETURN_FALSE; + } + RETURN_SUCCESS(http_send_content_disposition(filename, f_len, send_inline)); +} +/* }}} */ + +/* {{{ proto bool http_match_modified([int timestamp]) + * + * Matches the given timestamp against the clients "If-Modified-Since" resp. + * "If-Unmodified-Since" HTTP headers. + * + */ +PHP_FUNCTION(http_match_modified) +{ + long t = -1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &t) != SUCCESS) { + RETURN_FALSE; + } + + // current time if not supplied (senseless though) + if (t == -1) { + t = (long) time(NULL); + } + + RETURN_BOOL(http_modified_match("HTTP_IF_MODIFIED_SINCE", t) || http_modified_match("HTTP_IF_UNMODIFIED_SINCE", t)); +} +/* }}} */ + +/* {{{ proto bool http_match_etag(string etag) + * + * This matches the given ETag against the clients + * "If-Match" resp. "If-None-Match" HTTP headers. + * + */ +PHP_FUNCTION(http_match_etag) +{ + int etag_len; + char *etag; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len) != SUCCESS) { + RETURN_FALSE; + } + + RETURN_BOOL(http_etag_match("HTTP_IF_NONE_MATCH", etag) || http_etag_match("HTTP_IF_MATCH", etag)); +} +/* }}} */ + +/* {{{ proto bool http_cache_last_modified([int timestamp_or_expires]]) + * + * If timestamp_or_exires is greater than 0, it is handled as timestamp + * and will be sent as date of last modification. If it is 0 or omitted, + * the current time will be sent as Last-Modified date. If it's negative, + * it is handled as expiration time in seconds, which means that if the + * requested last modification date is not between the calculated timespan, + * the Last-Modified header is updated and the actual body will be sent. + * + */ +PHP_FUNCTION(http_cache_last_modified) +{ + long last_modified = 0, send_modified = 0, t; + zval *zlm; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &last_modified) != SUCCESS) { + RETURN_FALSE; + } + + t = (long) time(NULL); + + /* 0 or omitted */ + if (!last_modified) { + /* does the client have? (att: caching "forever") */ + if (zlm = http_get_server_var("HTTP_IF_MODIFIED_SINCE")) { + last_modified = send_modified = http_parse_date(Z_STRVAL_P(zlm)); + /* send current time */ + } else { + send_modified = t; + } + /* negative value is supposed to be expiration time */ + } else if (last_modified < 0) { + last_modified += t; + send_modified = t; + /* send supplied time explicitly */ + } else { + send_modified = last_modified; + } + + RETURN_SUCCESS(http_cache_last_modified(last_modified, send_modified, HTTP_DEFAULT_CACHECONTROL, sizeof(HTTP_DEFAULT_CACHECONTROL) - 1)); +} +/* }}} */ + +/* {{{ proto bool http_cache_etag([string etag]) + * + * This function attempts to cache the HTTP body based on an ETag, + * either supplied or generated through calculation of the MD5 + * checksum of the output (uses output buffering). + * + * If clients "If-None-Match" header matches the supplied/calculated + * ETag, the body is considered cached on the clients side and + * a "304 Not Modified" status code is issued. + * + */ +PHP_FUNCTION(http_cache_etag) +{ + char *etag; + int etag_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &etag, &etag_len) != SUCCESS) { + RETURN_FALSE; + } + + RETURN_SUCCESS(http_cache_etag(etag, etag_len, HTTP_DEFAULT_CACHECONTROL, sizeof(HTTP_DEFAULT_CACHECONTROL) - 1)); +} +/* }}} */ + +/* {{{ proto string ob_httpetaghandler(string data, int mode) + * + * For use with ob_start(). + * Note that this has to be started as first output buffer. + * WARNING: Don't use with http_send_*(). + */ +PHP_FUNCTION(ob_httpetaghandler) +{ + char *data; + int data_len; + long mode; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &data, &data_len, &mode)) { + RETURN_FALSE; + } + + if (mode & PHP_OUTPUT_HANDLER_START) { + if (HTTP_G(etag_started)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "ob_httpetaghandler can only be used once"); + RETURN_STRINGL(data, data_len, 1); + } + http_send_header("Cache-Control: " HTTP_DEFAULT_CACHECONTROL); + HTTP_G(etag_started) = 1; + } + + if (OG(ob_nesting_level) > 1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "ob_httpetaghandler must be started prior to other output buffers"); + RETURN_STRINGL(data, data_len, 1); + } + + Z_TYPE_P(return_value) = IS_STRING; + http_ob_etaghandler(data, data_len, &Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value), mode); +} +/* }}} */ + +/* {{{ proto void http_redirect([string url[, array params[, bool session,[ bool permanent]]]]) + * + * Redirect to a given url. + * The supplied url will be expanded with http_absolute_uri(), the params array will + * be treated with http_build_query() and the session identification will be appended + * if session is true. + * + * Depending on permanent the redirection will be issued with a permanent + * ("301 Moved Permanently") or a temporary ("302 Found") redirection + * status code. + * + * To be RFC compliant, "Redirecting to URI." will be displayed, + * if the client doesn't redirect immediatly. + */ +PHP_FUNCTION(http_redirect) +{ + int url_len; + zend_bool session = 0, permanent = 0; + zval *params = NULL; + smart_str qstr = {0}; + char *url, *URI, LOC[HTTP_URI_MAXLEN + 9], RED[HTTP_URI_MAXLEN * 2 + 34]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!/bb", &url, &url_len, ¶ms, &session, &permanent) != SUCCESS) { + RETURN_FALSE; + } + + /* append session info */ + if (session && (PS(session_status) == php_session_active)) { + if (!params) { + MAKE_STD_ZVAL(params); + array_init(params); + } + if (add_assoc_string(params, PS(session_name), PS(id), 1) != SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not append session information"); + } + } + + /* treat params array with http_build_query() */ + if (params) { + if (php_url_encode_hash_ex(Z_ARRVAL_P(params), &qstr, NULL,0,NULL,0,NULL,0,NULL TSRMLS_CC) != SUCCESS) { + if (qstr.c) { + efree(qstr.c); + } + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not encode query parameters"); + RETURN_FALSE; + } + smart_str_0(&qstr); + } + + URI = http_absolute_uri(url, NULL); + if (qstr.c) { + snprintf(LOC, HTTP_URI_MAXLEN + strlen("Location: "), "Location: %s?%s", URI, qstr.c); + sprintf(RED, "Redirecting to %s?%s.\n", URI, qstr.c, URI, qstr.c); + efree(qstr.c); + } else { + snprintf(LOC, HTTP_URI_MAXLEN + strlen("Location: "), "Location: %s", URI); + sprintf(RED, "Redirecting to %s.\n", URI, URI); + } + efree(URI); + + if ((SUCCESS == http_send_header(LOC)) && (SUCCESS == http_send_status((permanent ? 301 : 302)))) { + php_body_write(RED, strlen(RED) TSRMLS_CC); + RETURN_TRUE; + } + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto bool http_send_data(string data) + * + * Sends raw data with support for (multiple) range requests. + * + */ +PHP_FUNCTION(http_send_data) +{ + zval *zdata; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zdata) != SUCCESS) { + RETURN_FALSE; + } + + convert_to_string_ex(&zdata); + http_send_header("Accept-Ranges: bytes"); + RETURN_SUCCESS(http_send_data(zdata)); +} +/* }}} */ + +/* {{{ proto bool http_send_file(string file) + * + * Sends a file with support for (multiple) range requests. + * + */ +PHP_FUNCTION(http_send_file) +{ + zval *zfile; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zfile) != SUCCESS) { + RETURN_FALSE; + } + + convert_to_string_ex(&zfile); + http_send_header("Accept-Ranges: bytes"); + RETURN_SUCCESS(http_send_file(zfile)); +} +/* }}} */ + +/* {{{ proto bool http_send_stream(resource stream) + * + * Sends an already opened stream with support for (multiple) range requests. + * + */ +PHP_FUNCTION(http_send_stream) +{ + zval *zstream; + php_stream *file; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream) != SUCCESS) { + RETURN_FALSE; + } + + php_stream_from_zval(file, &zstream); + http_send_header("Accept-Ranges: bytes"); + RETURN_SUCCESS(http_send_stream(file)); +} +/* }}} */ + +/* {{{ proto string http_chunked_decode(string encoded) + * + * This function decodes a string that was HTTP-chunked encoded. + * Returns false on failure. + */ +PHP_FUNCTION(http_chunked_decode) +{ + char *encoded = NULL, *decoded = NULL; + int encoded_len = 0, decoded_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &encoded, &encoded_len) != SUCCESS) { + RETURN_FALSE; + } + + if (SUCCESS == http_chunked_decode(encoded, encoded_len, &decoded, &decoded_len)) { + RETURN_STRINGL(decoded, decoded_len, 0); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto array http_split_response(string http_response) + * + * This function splits an HTTP response into an array with headers and the + * content body. The returned array may look simliar to the following example: + * + *
+ *  array(
+ *         'Status' => '200 Ok',
+ *         'Content-Type' => 'text/plain',
+ *         'Content-Language' => 'en-US'
+ *     ),
+ *     1 => "Hello World!"
+ * );
+ * ?>
+ * 
+ */ +PHP_FUNCTION(http_split_response) +{ + zval *zresponse, *zbody, *zheaders; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zresponse) != SUCCESS) { + RETURN_FALSE; + } + + convert_to_string_ex(&zresponse); + + MAKE_STD_ZVAL(zbody); + MAKE_STD_ZVAL(zheaders); + array_init(zheaders); + + if (SUCCESS != http_split_response(zresponse, zheaders, zbody)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse HTTP response"); + RETURN_FALSE; + } + + array_init(return_value); + add_index_zval(return_value, 0, zheaders); + add_index_zval(return_value, 1, zbody); +} +/* }}} */ + +/* {{{ proto array http_parse_headers(string header) + * + */ +PHP_FUNCTION(http_parse_headers) +{ + char *header, *rnrn; + int header_len; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &header, &header_len)) { + RETURN_FALSE; + } + + array_init(return_value); + + if (rnrn = strstr(header, HTTP_CRLF HTTP_CRLF)) { + header_len = rnrn - header + 2; + } + if (SUCCESS != http_parse_headers(header, header_len, return_value)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse HTTP header"); + zval_dtor(return_value); + RETURN_FALSE; + } +} +/* }}}*/ + +/* {{{ proto array http_get_request_headers(void) + * + */ +PHP_FUNCTION(http_get_request_headers) +{ + if (ZEND_NUM_ARGS()) { + WRONG_PARAM_COUNT; + } + + array_init(return_value); + http_get_request_headers(return_value); +} +/* }}} */ + +/* {{{ HAVE_CURL */ +#ifdef HTTP_HAVE_CURL + +/* {{{ proto string http_get(string url[, array options[, array &info]]) + * + * Performs an HTTP GET request on the supplied url. + * + * The second parameter is expected to be an associative + * array where the following keys will be recognized: + *
+ *  - redirect:         int, whether and how many redirects to follow
+ *  - unrestrictedauth: bool, whether to continue sending credentials on
+ *                      redirects to a different host
+ *  - proxyhost:        string, proxy host in "host[:port]" format
+ *  - proxyport:        int, use another proxy port as specified in proxyhost
+ *  - proxyauth:        string, proxy credentials in "user:pass" format
+ *  - proxyauthtype:    int, HTTP_AUTH_BASIC and/or HTTP_AUTH_NTLM
+ *  - httpauth:         string, http credentials in "user:pass" format
+ *  - httpauthtype:     int, HTTP_AUTH_BASIC, DIGEST and/or NTLM
+ *  - compress:         bool, whether to allow gzip/deflate content encoding
+ *                      (defaults to true)
+ *  - port:             int, use another port as specified in the url
+ *  - referer:          string, the referer to sends
+ *  - useragent:        string, the user agent to send
+ *                      (defaults to PECL::HTTP/version (PHP/version)))
+ *  - headers:          array, list of custom headers as associative array
+ *                      like array("header" => "value")
+ *  - cookies:          array, list of cookies as associative array
+ *                      like array("cookie" => "value")
+ *  - cookiestore:      string, path to a file where cookies are/will be stored
+ * 
+ * + * The optional third parameter will be filled with some additional information + * in form af an associative array, if supplied, like the following example: + *
+ *  'http://localhost',
+ *     'response_code' => 403,
+ *     'total_time' => 0.017,
+ *     'namelookup_time' => 0.013,
+ *     'connect_time' => 0.014,
+ *     'pretransfer_time' => 0.014,
+ *     'size_upload' => 0,
+ *     'size_download' => 202,
+ *     'speed_download' => 11882,
+ *     'speed_upload' => 0,
+ *     'header_size' => 145,
+ *     'request_size' => 62,
+ *     'ssl_verifyresult' => 0,
+ *     'filetime' => -1,
+ *     'content_length_download' => 202,
+ *     'content_length_upload' => 0,
+ *     'starttransfer_time' => 0.017,
+ *     'content_type' => 'text/html; charset=iso-8859-1',
+ *     'redirect_time' => 0,
+ *     'redirect_count' => 0,
+ *     'private' => '',
+ *     'http_connectcode' => 0,
+ *     'httpauth_avail' => 0,
+ *     'proxyauth_avail' => 0,
+ * )
+ * ?>
+ * 
+ */ +PHP_FUNCTION(http_get) +{ + char *URL, *data = NULL; + size_t data_len = 0; + int URL_len; + zval *options = NULL, *info = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) { + RETURN_FALSE; + } + + if (info) { + zval_dtor(info); + array_init(info); + } + + if (SUCCESS == http_get(URL, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) { + RETURN_STRINGL(data, data_len, 0); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string http_head(string url[, array options[, array &info]]) + * + * Performs an HTTP HEAD request on the suppied url. + * Returns the HTTP response as string. + * See http_get() for a full list of available options. + */ +PHP_FUNCTION(http_head) +{ + char *URL, *data = NULL; + size_t data_len = 0; + int URL_len; + zval *options = NULL, *info = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a/!z", &URL, &URL_len, &options, &info) != SUCCESS) { + RETURN_FALSE; + } + + if (info) { + zval_dtor(info); + array_init(info); + } + + if (SUCCESS == http_head(URL, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) { + RETURN_STRINGL(data, data_len, 0); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string http_post_data(string url, string data[, array options[, &info]]) + * + * Performs an HTTP POST request, posting data. + * Returns the HTTP response as string. + * See http_get() for a full list of available options. + */ +PHP_FUNCTION(http_post_data) +{ + char *URL, *postdata, *data = NULL; + size_t data_len = 0; + int postdata_len, URL_len; + zval *options = NULL, *info = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!z", &URL, &URL_len, &postdata, &postdata_len, &options, &info) != SUCCESS) { + RETURN_FALSE; + } + + if (info) { + zval_dtor(info); + array_init(info); + } + + if (SUCCESS == http_post_data(URL, postdata, (size_t) postdata_len, HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) { + RETURN_STRINGL(data, data_len, 0); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string http_post_array(string url, array data[, array options[, array &info]]) + * + * Performs an HTTP POST request, posting www-form-urlencoded array data. + * Returns the HTTP response as string. + * See http_get() for a full list of available options. + */ +PHP_FUNCTION(http_post_array) +{ + char *URL, *data = NULL; + size_t data_len = 0; + int URL_len; + zval *options = NULL, *info = NULL, *postdata; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|a/!z", &URL, &URL_len, &postdata, &options, &info) != SUCCESS) { + RETURN_FALSE; + } + + if (info) { + zval_dtor(info); + array_init(info); + } + + if (SUCCESS == http_post_array(URL, Z_ARRVAL_P(postdata), HASH_ORNULL(options), HASH_ORNULL(info), &data, &data_len)) { + RETURN_STRINGL(data, data_len, 0); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +#endif +/* }}} HAVE_CURL */ + + +/* {{{ proto bool http_auth_basic(string user, string pass[, string realm = "Restricted"]) + * + * Example: + *
+ * Authorization failed!');
+ * }
+ * ?>
+ * 
+ */ +PHP_FUNCTION(http_auth_basic) +{ + char *realm = NULL, *user, *pass, *suser, *spass; + int r_len, u_len, p_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s", &user, &u_len, &pass, &p_len, &realm, &r_len) != SUCCESS) { + RETURN_FALSE; + } + + if (!realm) { + realm = "Restricted"; + } + + if (SUCCESS != http_auth_credentials(&suser, &spass)) { + http_auth_header("Basic", realm); + RETURN_FALSE; + } + + if (strcasecmp(suser, user)) { + http_auth_header("Basic", realm); + RETURN_FALSE; + } + + if (strcmp(spass, pass)) { + http_auth_header("Basic", realm); + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool http_auth_basic_cb(mixed callback[, string realm = "Restricted"]) + * + * Example: + *
+ * quoteSmart($user);
+ *     if (strlen($realpass = $db->getOne($query)) {
+ *         return $pass === $realpass;
+ *     }
+ *     return false;
+ * }
+ *
+ * if (!http_auth_basic_cb('auth_cb')) {
+ *     die('

Authorization failed

'); + * } + * ?> + *
+ */ +PHP_FUNCTION(http_auth_basic_cb) +{ + zval *cb; + char *realm = NULL, *user, *pass; + int r_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &cb, &realm, &r_len) != SUCCESS) { + RETURN_FALSE; + } + + if (!realm) { + realm = "Restricted"; + } + + if (SUCCESS != http_auth_credentials(&user, &pass)) { + http_auth_header("Basic", realm); + RETURN_FALSE; + } + { + zval *zparams[2] = {NULL, NULL}, retval; + int result = 0; + + MAKE_STD_ZVAL(zparams[0]); + MAKE_STD_ZVAL(zparams[1]); + ZVAL_STRING(zparams[0], user, 0); + ZVAL_STRING(zparams[1], pass, 0); + + if (SUCCESS == call_user_function(EG(function_table), NULL, cb, + &retval, 2, zparams TSRMLS_CC)) { + result = Z_LVAL(retval); + } + + efree(user); + efree(pass); + efree(zparams[0]); + efree(zparams[1]); + + if (!result) { + http_auth_header("Basic", realm); + } + + RETURN_BOOL(result); + } +} +/* }}}*/ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/http_methods.c b/http_methods.c new file mode 100644 index 0000000..2f7a429 --- /dev/null +++ b/http_methods.c @@ -0,0 +1,1143 @@ +/* + +----------------------------------------------------------------------+ + | PECL :: http | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, that | + | is bundled with this package in the file LICENSE, and is available | + | through the world-wide-web at http://www.php.net/license/3_0.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. | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Michael Wallner | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "php_http.h" +#include "php_http_api.h" +#include "php_http_curl_api.h" +#include "ext/standard/php_smart_str.h" + +#ifdef ZEND_ENGINE_2 + +/* {{{ HTTPi_Response */ + +/* {{{ proto void HTTPi_Response::__construct(bool cache, bool gzip) + * + */ +PHP_METHOD(HTTPi_Response, __construct) +{ + zend_bool do_cache = 0, do_gzip = 0; + getObject(httpi_response_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|bb", &do_cache, &do_gzip)) { + // throw exception + return; + } + + UPD_PROP(obj, long, cache, do_cache); + UPD_PROP(obj, long, gzip, do_gzip); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Response::setCache(bool cache) + * + */ +PHP_METHOD(HTTPi_Response, setCache) +{ + zend_bool do_cache = 0; + getObject(httpi_response_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_cache)) { + RETURN_FALSE; + } + + UPD_PROP(obj, long, cache, do_cache); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool HTTPi_Response::getCache() + * + */ +PHP_METHOD(HTTPi_Response, getCache) +{ + zval *do_cache = NULL; + getObject(httpi_response_object, obj); + + NO_ARGS; + + do_cache = GET_PROP(obj, cache); + RETURN_BOOL(Z_LVAL_P(do_cache)); +} +/* }}}*/ + +/* {{{ proto bool HTTPi_Response::setGzip(bool gzip) + * + */ +PHP_METHOD(HTTPi_Response, setGzip) +{ + zend_bool do_gzip = 0; + getObject(httpi_response_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_gzip)) { + RETURN_FALSE; + } + + UPD_PROP(obj, long, gzip, do_gzip); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool HTTPi_Response::getGzip() + * + */ +PHP_METHOD(HTTPi_Response, getGzip) +{ + zval *do_gzip = NULL; + getObject(httpi_response_object, obj); + + NO_ARGS; + + do_gzip = GET_PROP(obj, gzip); + RETURN_BOOL(Z_LVAL_P(do_gzip)); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Response::setCacheControl(string control[, bool raw = false]) + * + */ +PHP_METHOD(HTTPi_Response, setCacheControl) +{ + char *ccontrol; + int cc_len; + zend_bool raw = 0; + getObject(httpi_response_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &ccontrol, &cc_len, &raw)) { + RETURN_FALSE; + } + + if ((!raw) && (strcmp(ccontrol, "public") && strcmp(ccontrol, "private") && strcmp(ccontrol, "no-cache"))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol); + RETURN_FALSE; + } + + UPD_PROP(obj, long, raw_cache_header, raw); + UPD_PROP(obj, string, cacheControl, ccontrol); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HTTPi_Response::getCacheControl() + * + */ +PHP_METHOD(HTTPi_Response, getCacheControl) +{ + zval *ccontrol; + getObject(httpi_response_object, obj); + + NO_ARGS; + + ccontrol = GET_PROP(obj, cacheControl); + RETURN_STRINGL(Z_STRVAL_P(ccontrol), Z_STRLEN_P(ccontrol), 1); +} +/* }}} */ + +/* {{{ proto bool HTTPi::setContentType(string content_type) + * + */ +PHP_METHOD(HTTPi_Response, setContentType) +{ + char *ctype; + int ctype_len; + getObject(httpi_response_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ctype_len)) { + RETURN_FALSE; + } + + if (!strchr(ctype, '/')) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Content type '%s' doesn't seem to contain a primary and a secondary part", ctype); + RETURN_FALSE; + } + + UPD_PROP(obj, string, contentType, ctype); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HTTPi_Response::getContentType() + * + */ +PHP_METHOD(HTTPi_Response, getContentType) +{ + zval *ctype; + getObject(httpi_response_object, obj); + + NO_ARGS; + + ctype = GET_PROP(obj, contentType); + RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Response::setContentDisposition(string filename[, bool inline = false]) + * + */ +PHP_METHOD(HTTPi_Response, setContentDisposition) +{ + char *file; + int file_len; + zend_bool is_inline = 0; + getObject(httpi_response_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &file, &file_len, &is_inline)) { + RETURN_FALSE; + } + + UPD_PROP(obj, string, dispoFile, file); + UPD_PROP(obj, long, dispoInline, is_inline); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto array HTTPi_Response::getContentDisposition() + * + */ +PHP_METHOD(HTTPi_Response, getContentDisposition) +{ + zval *file; + zval *is_inline; + getObject(httpi_response_object, obj); + + if (ZEND_NUM_ARGS()) { + WRONG_PARAM_COUNT; + } + + file = GET_PROP(obj, dispoFile); + is_inline = GET_PROP(obj, dispoInline); + + array_init(return_value); + add_assoc_stringl(return_value, "filename", Z_STRVAL_P(file), Z_STRLEN_P(file), 1); + add_assoc_bool(return_value, "inline", Z_LVAL_P(is_inline)); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Response::setETag(string etag) + * + */ +PHP_METHOD(HTTPi_Response, setETag) +{ + char *etag; + int etag_len; + getObject(httpi_response_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len)) { + RETURN_FALSE; + } + + UPD_PROP(obj, string, eTag, etag); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HTTPi_Response::getETag() + * + */ +PHP_METHOD(HTTPi_Response, getETag) +{ + zval *etag; + getObject(httpi_response_object, obj); + + NO_ARGS; + + etag = GET_PROP(obj, eTag); + RETURN_STRINGL(Z_STRVAL_P(etag), Z_STRLEN_P(etag), 1); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Response::setData(string data) + * + */ +PHP_METHOD(HTTPi_Response, setData) +{ + zval *the_data; + char *etag; + getObject(httpi_response_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_data)) { + RETURN_FALSE; + } + + convert_to_string_ex(&the_data); + SET_PROP(obj, data, the_data); + UPD_PROP(obj, long, lastModified, http_lmod(the_data, SEND_DATA)); + UPD_PROP(obj, long, send_mode, SEND_DATA); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HTTPi_Response::getData() + * + */ +PHP_METHOD(HTTPi_Response, getData) +{ + zval *the_data; + getObject(httpi_response_object, obj); + + NO_ARGS; + + the_data = GET_PROP(obj, data); + RETURN_STRINGL(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), 1); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Response::setStream(resource stream) + * + */ +PHP_METHOD(HTTPi_Response, setStream) +{ + zval *the_stream; + php_stream *the_real_stream; + char *etag; + getObject(httpi_response_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &the_stream)) { + RETURN_FALSE; + } + + php_stream_from_zval(the_real_stream, &the_stream); + + SET_PROP(obj, stream, the_stream); + UPD_PROP(obj, long, lastModified, http_lmod(the_real_stream, SEND_RSRC)); + UPD_PROP(obj, long, send_mode, SEND_RSRC); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto resource HTTPi_Response::getStream() + * + */ +PHP_METHOD(HTTPi_Response, getStream) +{ + zval *the_stream; + getObject(httpi_response_object, obj); + + NO_ARGS; + + the_stream = GET_PROP(obj, stream); + RETURN_RESOURCE(Z_LVAL_P(the_stream)); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Response::setFile(string file) + * + */ +PHP_METHOD(HTTPi_Response, setFile) +{ + zval *the_file; + getObject(httpi_response_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_file)) { + RETURN_FALSE; + } + + convert_to_string_ex(&the_file); + + UPD_PROP(obj, string, file, Z_STRVAL_P(the_file)); + UPD_PROP(obj, long, lastModified, http_lmod(the_file, -1)); + UPD_PROP(obj, long, send_mode, -1); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HTTPi_Response::getFile() + * + */ +PHP_METHOD(HTTPi_Response, getFile) +{ + zval *the_file; + getObject(httpi_response_object, obj); + + NO_ARGS; + + the_file = GET_PROP(obj, file); + RETURN_STRINGL(Z_STRVAL_P(the_file), Z_STRLEN_P(the_file), 1); +} +/* }}} */ + +PHP_METHOD(HTTPi_Response, send) +{ + zval *do_cache, *do_gzip; + getObject(httpi_response_object, obj); + + do_cache = GET_PROP(obj, cache); + do_gzip = GET_PROP(obj, gzip); + + /* caching */ + if (Z_LVAL_P(do_cache)) { + zval *cctrl, *etag, *lmod, *ccraw; + + etag = GET_PROP(obj, eTag); + lmod = GET_PROP(obj, lastModified); + cctrl = GET_PROP(obj, cacheControl); + ccraw = GET_PROP(obj, raw_cache_header); + + if (Z_LVAL_P(ccraw)) { + http_cache_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag), Z_STRVAL_P(cctrl), Z_STRLEN_P(cctrl)); + http_cache_last_modified(Z_LVAL_P(lmod), Z_LVAL_P(lmod) ? Z_LVAL_P(lmod) : time(NULL), Z_STRVAL_P(cctrl), Z_STRLEN_P(cctrl)); + } else { + char cc_header[42] = {0}; + sprintf(cc_header, "%s, must-revalidate, max-age=0", Z_STRVAL_P(cctrl)); + http_cache_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag), cc_header, strlen(cc_header)); + http_cache_last_modified(Z_LVAL_P(lmod), Z_LVAL_P(lmod) ? Z_LVAL_P(lmod) : time(NULL), cc_header, strlen(cc_header)); + } + } + + /* gzip */ + if (Z_LVAL_P(do_gzip)) { + /* ... */ + } + + /* content type */ + { + zval *ctype = GET_PROP(obj, contentType); + if (Z_STRLEN_P(ctype)) { + http_send_content_type(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype)); + } else { + http_send_content_type("application/x-octetstream", sizeof("application/x-octetstream") - 1); + } + } + + /* content disposition */ + { + zval *dispo_file = GET_PROP(obj, dispoFile); + if (Z_STRLEN_P(dispo_file)) { + zval *dispo_inline = GET_PROP(obj, dispoInline); + http_send_content_disposition(Z_STRVAL_P(dispo_file), Z_STRLEN_P(dispo_file), Z_LVAL_P(dispo_inline)); + } + } + + /* send */ + { + zval *send_mode = GET_PROP(obj, send_mode); + switch (Z_LVAL_P(send_mode)) + { + case SEND_DATA: + { + RETURN_SUCCESS(http_send_data(GET_PROP(obj, data))); + } + + case SEND_RSRC: + { + php_stream *the_real_stream; + zval *the_stream = GET_PROP(obj, stream); + php_stream_from_zval(the_real_stream, &the_stream); + RETURN_SUCCESS(http_send_stream(the_real_stream)); + } + + default: + { + RETURN_SUCCESS(http_send_file(GET_PROP(obj, file))); + } + } + } +} +/* }}} */ +/* }}} */ + +#ifdef HTTP_HAVE_CURL +/* {{{ HTTPi_Request */ + +/* {{{ proto void HTTPi_Request::__construct([string url[, long request_method = HTTP_GET]]) + * + */ +PHP_METHOD(HTTPi_Request, __construct) +{ + char *URL = NULL; + int URL_len; + long meth = -1; + zval *info, *opts, *resp; + getObject(httpi_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &URL, &URL_len, &meth)) { + return; + } + + INIT_PARR(obj, options); + INIT_PARR(obj, responseInfo); + INIT_PARR(obj, responseData); + INIT_PARR(obj, postData); + INIT_PARR(obj, postFiles); + + if (URL) { + UPD_PROP(obj, string, url, URL); + } + if (meth > -1) { + UPD_PROP(obj, long, method, meth); + } +} +/* }}} */ + +/* {{{ proto void HTTPi_Request::__destruct() + * + */ +PHP_METHOD(HTTPi_Request, __destruct) +{ + getObject(httpi_request_object, obj); + + NO_ARGS; + + FREE_PARR(obj, options); + FREE_PARR(obj, responseInfo); + FREE_PARR(obj, responseData); + FREE_PARR(obj, postData); + FREE_PARR(obj, postFiles); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Request::setOptions(array options) + * + */ +PHP_METHOD(HTTPi_Request, setOptions) +{ + zval *opts, *old_opts, **opt; + getObject(httpi_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &opts)) { + RETURN_FALSE; + } + + old_opts = GET_PROP(obj, options); + + /* headers and cookies need extra attention -- thus cannot use zend_hash_merge() or php_array_merge() directly */ + for ( zend_hash_internal_pointer_reset(Z_ARRVAL_P(opts)); + zend_hash_get_current_data(Z_ARRVAL_P(opts), (void **) &opt) == SUCCESS; + zend_hash_move_forward(Z_ARRVAL_P(opts))) { + char *key; + long idx; + if (HASH_KEY_IS_STRING == zend_hash_get_current_key(Z_ARRVAL_P(opts), &key, &idx, 0)) { + if (!strcmp(key, "headers")) { + zval **headers; + if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "headers", sizeof("headers"), (void **) &headers)) { + array_merge(*opt, *headers); + continue; + } + } else if (!strcmp(key, "cookies")) { + zval **cookies; + if (SUCCESS == zend_hash_find(Z_ARRVAL_P(old_opts), "cookies", sizeof("cookies"), (void **) &cookies)) { + array_merge(*opt, *cookies); + continue; + } + } + zval_add_ref(opt); + add_assoc_zval(old_opts, key, *opt); + } + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto array HTTPi_Request::getOptions() + * + */ +PHP_METHOD(HTTPi_Request, getOptions) +{ + zval *opts; + getObject(httpi_request_object, obj); + + NO_ARGS; + + opts = GET_PROP(obj, options); + array_init(return_value); + array_copy(opts, return_value); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Request::setURL(string url) + * + */ +PHP_METHOD(HTTPi_Request, setURL) +{ + char *URL = NULL; + int URL_len; + getObject(httpi_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &URL, &URL_len)) { + RETURN_FALSE; + } + + UPD_PROP(obj, string, url, URL); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HTTPi_Request::getUrl() + * + */ +PHP_METHOD(HTTPi_Request, getURL) +{ + zval *URL; + getObject(httpi_request_object, obj); + + NO_ARGS; + + URL = GET_PROP(obj, url); + RETURN_STRINGL(Z_STRVAL_P(URL), Z_STRLEN_P(URL), 1); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Request::setMethod(long request_method) + * + */ +PHP_METHOD(HTTPi_Request, setMethod) +{ + long meth; + getObject(httpi_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &meth)) { + RETURN_FALSE; + } + + UPD_PROP(obj, long, method, meth); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto long HTTPi_Request::getMethod() + * + */ +PHP_METHOD(HTTPi_Request, getMethod) +{ + zval *meth; + getObject(httpi_request_object, obj); + + NO_ARGS; + + meth = GET_PROP(obj, method); + RETURN_LONG(Z_LVAL_P(meth)); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Request::setContentType(string content_type) + * + */ +PHP_METHOD(HTTPi_Request, setContentType) +{ + char *ctype; + int ct_len; + getObject(httpi_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ct_len)) { + RETURN_FALSE; + } + + if (!strchr(ctype, '/')) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Content-Type '%s' doesn't seem to contain a primary and a secondary part", + ctype); + RETURN_FALSE; + } + + UPD_PROP(obj, string, contentType, ctype); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HTTPi_Request::getContentType() + * + */ +PHP_METHOD(HTTPi_Request, getContentType) +{ + zval *ctype; + getObject(httpi_request_object, obj); + + NO_ARGS; + + ctype = GET_PROP(obj, contentType); + RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Request::setQueryData(mixed query_data) + * + */ +PHP_METHOD(HTTPi_Request, setQueryData) +{ + zval *qdata; + getObject(httpi_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &qdata)) { + RETURN_FALSE; + } + + if ((Z_TYPE_P(qdata) == IS_ARRAY) || (Z_TYPE_P(qdata) == IS_OBJECT)) { + smart_str qstr = {0}; + HTTP_URL_ARGSEP_OVERRIDE; + if (SUCCESS != php_url_encode_hash_ex(HASH_OF(qdata), &qstr, NULL, 0, NULL, 0, NULL, 0, NULL TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't encode query data"); + if (qstr.c) { + efree(qstr.c); + } + HTTP_URL_ARGSEP_RESTORE; + RETURN_FALSE; + } + HTTP_URL_ARGSEP_RESTORE; + smart_str_0(&qstr); + UPD_PROP(obj, string, queryData, qstr.c); + efree(qstr.c); + RETURN_TRUE; + } + + convert_to_string(qdata); + UPD_PROP(obj, string, queryData, Z_STRVAL_P(qdata)); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string HTTPi_Request::getQueryData() + * + */ +PHP_METHOD(HTTPi_Request, getQueryData) +{ + zval *qdata; + getObject(httpi_request_object, obj); + + NO_ARGS; + + qdata = GET_PROP(obj, queryData); + RETURN_STRINGL(Z_STRVAL_P(qdata), Z_STRLEN_P(qdata), 1); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Request::addQueryData(array query_params) + * + */ +PHP_METHOD(HTTPi_Request, addQueryData) +{ + zval *qdata, *old_qdata; + smart_str qstr = {0}; + char *separator; + getObject(httpi_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &qdata)) { + RETURN_FALSE; + } + + old_qdata = GET_PROP(obj, queryData); + if (Z_STRLEN_P(old_qdata)) { + smart_str_appendl(&qstr, Z_STRVAL_P(old_qdata), Z_STRLEN_P(old_qdata)); + } + + HTTP_URL_ARGSEP_OVERRIDE; + if (SUCCESS != php_url_encode_hash_ex(HASH_OF(qdata), &qstr, NULL, 0, NULL, 0, NULL, 0, NULL TSRMLS_CC)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't encode query data"); + if (qstr.c) { + efree(qstr.c); + } + HTTP_URL_ARGSEP_RESTORE; + RETURN_FALSE; + } + HTTP_URL_ARGSEP_RESTORE; + + smart_str_0(&qstr); + + UPD_PROP(obj, string, queryData, qstr.c); + efree(qstr.c); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto void HTTPi_Request::unsetQueryData() + * + */ +PHP_METHOD(HTTPi_Request, unsetQueryData) +{ + getObject(httpi_request_object, obj); + + NO_ARGS; + + UPD_PROP(obj, string, queryData, ""); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Request::addPostData(array post_data) + * + */ +PHP_METHOD(HTTPi_Request, addPostData) +{ + zval *post, *post_data; + getObject(httpi_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &post_data)) { + RETURN_FALSE; + } + + post = GET_PROP(obj, postData); + array_merge(post_data, post); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool HTTPi_Request::setPostData(array post_data) + * + */ +PHP_METHOD(HTTPi_Request, setPostData) +{ + zval *post, *post_data; + getObject(httpi_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &post_data)) { + RETURN_FALSE; + } + + post = GET_PROP(obj, postData); + zend_hash_clean(Z_ARRVAL_P(post)); + array_copy(post_data, post); + + RETURN_TRUE; +} +/* }}}*/ + +/* {{{ proto array HTTPi_Request::getPostData() + * + */ +PHP_METHOD(HTTPi_Request, getPostData) +{ + zval *post_data; + getObject(httpi_request_object, obj); + + NO_ARGS; + + post_data = GET_PROP(obj, postData); + array_init(return_value); + array_copy(post_data, return_value); +} +/* }}} */ + +/* {{{ proto void HTTPi_Request::unsetPostData() + * + */ +PHP_METHOD(HTTPi_Request, unsetPostData) +{ + zval *post_data; + getObject(httpi_request_object, obj); + + NO_ARGS; + + post_data = GET_PROP(obj, postData); + zend_hash_clean(Z_ARRVAL_P(post_data)); +} +/* }}} */ + +/* {{{ proto bool HTTPi_Request::addPostFile(string name, string file[, string content_type = "application/x-octetstream"]) + * + */ +PHP_METHOD(HTTPi_Request, addPostFile) +{ + zval *files, *entry; + char *name, *file, *type = NULL; + int name_len, file_len, type_len = 0; + getObject(httpi_request_object, obj); + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s", &name, &name_len, &file, &file_len, &type, &type_len)) { + RETURN_FALSE; + } + + if (type_len) { + if (!strchr(type, '/')) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Content-Type '%s' doesn't seem to contain a primary and a secondary part", type); + RETURN_FALSE; + } + } else { + type = "application/x-octetstream"; + type_len = sizeof("application/x-octetstream") - 1; + } + + MAKE_STD_ZVAL(entry); + array_init(entry); + + add_assoc_stringl(entry, "name", name, name_len, 1); + add_assoc_stringl(entry, "type", type, type_len, 1); + add_assoc_stringl(entry, "file", file, file_len, 1); + + files = GET_PROP(obj, postFiles); + add_next_index_zval(files, entry); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto array HTTPi_Request::getPostFiles() + * + */ +PHP_METHOD(HTTPi_Request, getPostFiles) +{ + zval *files; + getObject(httpi_request_object, obj); + + NO_ARGS; + + files = GET_PROP(obj, postFiles); + + array_init(return_value); + array_copy(files, return_value); +} +/* }}} */ + +/* {{{ proto void HTTPi_Request::unsetPostFiles() + * + */ +PHP_METHOD(HTTPi_Request, unsetPostFiles) +{ + zval *files; + getObject(httpi_request_object, obj); + + NO_ARGS; + + files = GET_PROP(obj, postFiles); + zend_hash_clean(Z_ARRVAL_P(files)); +} +/* }}} */ + +/* {{{ proto array HTTPi_Request::getResponseData() + * + */ +PHP_METHOD(HTTPi_Request, getResponseData) +{ + zval *data; + getObject(httpi_request_object, obj); + + NO_ARGS; + + data = GET_PROP(obj, responseData); + array_init(return_value); + array_copy(data, return_value); +} +/* }}} */ + +/* {{{ proto array HTTPi_Request::getResponseHeaders() + * + */ +PHP_METHOD(HTTPi_Request, getResponseHeaders) +{ + zval *data, **headers; + getObject(httpi_request_object, obj); + + NO_ARGS; + + array_init(return_value); + data = GET_PROP(obj, responseData); + if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &headers)) { + array_copy(*headers, return_value); + } +} +/* }}} */ + +/* {{{ proto string HTTPi_Request::getResponseBody() + * + */ +PHP_METHOD(HTTPi_Request, getResponseBody) +{ + zval *data, **body; + getObject(httpi_request_object, obj); + + NO_ARGS; + + data = GET_PROP(obj, responseData); + if (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "body", sizeof("body"), (void **) &body)) { + RETURN_STRINGL(Z_STRVAL_PP(body), Z_STRLEN_PP(body), 1); + } else { + Z_TYPE_P(return_value) = IS_NULL; + } +} +/* }}} */ + +/* {{{ proto array HTTPi_Request::getResponseInfo() + * + */ +PHP_METHOD(HTTPi_Request, getResponseInfo) +{ + zval *info; + getObject(httpi_request_object, obj); + + NO_ARGS; + + info = GET_PROP(obj, responseInfo); + array_init(return_value); + array_copy(info, return_value); +} +/* }}}*/ + +/* {{{ proto bool HTTPi_Request::send() + * + */ +PHP_METHOD(HTTPi_Request, send) +{ + STATUS status = FAILURE; + zval *meth, *URL, *qdata, *opts, *info, *resp; + char *response_data, *request_uri; + size_t response_len; + getObject(httpi_request_object, obj); + + NO_ARGS; + + if ((!obj->ch) && (!(obj->ch = curl_easy_init()))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initilaize cURL"); + RETURN_FALSE; + } + + meth = GET_PROP(obj, method); + URL = GET_PROP(obj, url); + qdata = GET_PROP(obj, queryData); + opts = GET_PROP(obj, options); + info = GET_PROP(obj, responseInfo); + resp = GET_PROP(obj, responseData); + + // HTTP_URI_MAXLEN+1 big char * + request_uri = http_absolute_uri(Z_STRVAL_P(URL), NULL); + + if (Z_STRLEN_P(qdata) && (strlen(request_uri) < HTTP_URI_MAXLEN)) { + if (!strchr(request_uri, '?')) { + strcat(request_uri, "?"); + } else { + strcat(request_uri, "&"); + } + strncat(request_uri, Z_STRVAL_P(qdata), HTTP_URI_MAXLEN - strlen(request_uri)); + } + + switch (Z_LVAL_P(meth)) + { + case HTTP_GET: + status = http_get_ex(obj->ch, request_uri, Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len); + break; + + case HTTP_HEAD: + status = http_head_ex(obj->ch, request_uri, Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len); + break; + + case HTTP_POST: + { + zval *post_files, *post_data, **data; + + post_files = GET_PROP(obj, postFiles); + post_data = GET_PROP(obj, postData); + + if (!zend_hash_num_elements(Z_ARRVAL_P(post_files))) { + + /* urlencoded post */ + status = http_post_array_ex(obj->ch, request_uri, Z_ARRVAL_P(post_data), Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len); + + } else { + + /* + * multipart post + */ + struct curl_httppost *http_post_data[2] = {NULL, NULL}; + + /* normal data */ + for ( zend_hash_internal_pointer_reset(Z_ARRVAL_P(post_data)); + zend_hash_get_current_data(Z_ARRVAL_P(post_data), (void **) &data) == SUCCESS; + zend_hash_move_forward(Z_ARRVAL_P(post_data))) { + + char *key; + long idx; + + if (HASH_KEY_IS_STRING == zend_hash_get_current_key(Z_ARRVAL_P(post_data), &key, &idx, 0)) { + convert_to_string_ex(data); + curl_formadd(&http_post_data[0], &http_post_data[1], + CURLFORM_COPYNAME, key, + CURLFORM_COPYCONTENTS, Z_STRVAL_PP(data), + CURLFORM_CONTENTSLENGTH, Z_STRLEN_PP(data), + CURLFORM_END + ); + } + } + + /* file data */ + for ( zend_hash_internal_pointer_reset(Z_ARRVAL_P(post_files)); + zend_hash_get_current_data(Z_ARRVAL_P(post_files), (void **) &data) == SUCCESS; + zend_hash_move_forward(Z_ARRVAL_P(post_files))) { + + zval **file, **type, **name; + + if ( + SUCCESS == zend_hash_find(Z_ARRVAL_PP(data), "name", sizeof("name"), (void **) &name) && + SUCCESS == zend_hash_find(Z_ARRVAL_PP(data), "type", sizeof("type"), (void **) &type) && + SUCCESS == zend_hash_find(Z_ARRVAL_PP(data), "file", sizeof("file"), (void **) &file) + ) { + + curl_formadd(&http_post_data[0], &http_post_data[1], + CURLFORM_COPYNAME, Z_STRVAL_PP(name), + CURLFORM_FILENAME, Z_STRVAL_PP(name), + CURLFORM_FILE, Z_STRVAL_PP(file), + CURLFORM_CONTENTTYPE, Z_STRVAL_PP(type), + CURLFORM_END + ); + } + } + + status = http_post_curldata_ex(obj->ch, request_uri, http_post_data[0], Z_ARRVAL_P(opts), Z_ARRVAL_P(info), &response_data, &response_len); + curl_formfree(http_post_data[0]); + } + } + break; + + default: + break; + } + + efree(request_uri); + + /* final data handling */ + if (status != SUCCESS) { + RETURN_FALSE; + } else { + zval *zheaders, *zbody; + + MAKE_STD_ZVAL(zbody); + MAKE_STD_ZVAL(zheaders) + array_init(zheaders); + + if (SUCCESS != http_split_response_ex(response_data, response_len, zheaders, zbody)) { + zval_dtor(zheaders); + efree(zheaders), + efree(zbody); + efree(response_data); + RETURN_FALSE; + } + + add_assoc_zval(resp, "headers", zheaders); + add_assoc_zval(resp, "body", zbody); + + efree(response_data); + + RETURN_TRUE; + } + /* */ +} +/* }}} */ +/* }}} */ +#endif /* HTTP_HAVE_CURL */ + +#endif /* ZEND_ENGINE_2 */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/package.xml b/package.xml index f8085c1..68f8379 100644 --- a/package.xml +++ b/package.xml @@ -43,12 +43,20 @@ EXPERIMENTAL docs/functions.html + config.w32 config.m4 - http.c + php_http.h - http_api.c php_http_api.h + php_http_curl_api.h + + http.c + http_functions.c + http_methods.c + http_api.c + http_curl_api.c + http_build_query.c php_http_build_query.h diff --git a/php_http.h b/php_http.h index 0de3905..3a46a72 100644 --- a/php_http.h +++ b/php_http.h @@ -38,12 +38,25 @@ extern zend_module_entry http_module_entry; # include "php_http_build_query.h" #else +typedef struct { + zend_object zo; +} httpi_response_object; + #ifdef HTTP_HAVE_CURL + +#include + +typedef struct { + zend_object zo; + CURL *ch; +} httpi_request_object; + typedef enum { HTTP_GET, HTTP_HEAD, HTTP_POST, } http_request_method; + #endif PHP_METHOD(HTTPi, date); @@ -176,16 +189,10 @@ ZEND_BEGIN_MODULE_GLOBALS(http) char *allowed_methods; #ifdef HTTP_HAVE_CURL struct { - struct { - char *data; - size_t used; - size_t free; - } body; - struct { - char *data; - size_t used; - size_t free; - } hdrs; + char *data; + size_t used; + size_t free; + size_t size; } curlbuf; #endif ZEND_END_MODULE_GLOBALS(http) diff --git a/php_http_api.h b/php_http_api.h index bdce2ad..fc4faac 100644 --- a/php_http_api.h +++ b/php_http_api.h @@ -28,6 +28,65 @@ # include "php_http_build_query.h" #endif +#define RETURN_SUCCESS(v) RETURN_BOOL(SUCCESS == (v)) +#define HASH_ORNULL(z) ((z) ? Z_ARRVAL_P(z) : NULL) +#define NO_ARGS if (ZEND_NUM_ARGS()) WRONG_PARAM_COUNT + +#define array_copy(src, dst) zend_hash_copy(Z_ARRVAL_P(dst), Z_ARRVAL_P(src), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)) +#define array_merge(src, dst) zend_hash_merge(Z_ARRVAL_P(dst), Z_ARRVAL_P(src), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *), 1) + +#ifdef ZEND_ENGINE_2 + +# define HTTP_REGISTER_CLASS_EX(classname, name, parent, flags) \ + { \ + zend_class_entry ce; \ + INIT_CLASS_ENTRY(ce, #classname, name## _class_methods); \ + ce.create_object = name## _new_object; \ + name## _ce = zend_register_internal_class_ex(&ce, parent, NULL TSRMLS_CC); \ + name## _ce->ce_flags |= flags; \ + memcpy(& name## _object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \ + name## _object_handlers.clone_obj = NULL; \ + name## _declare_default_properties(name## _ce); \ + } + +# define HTTP_REGISTER_CLASS(classname, name, parent, flags) \ + { \ + zend_class_entry ce; \ + INIT_CLASS_ENTRY(ce, #classname, name## _class_methods); \ + ce.create_object = NULL; \ + name## _ce = zend_register_internal_class_ex(&ce, parent, NULL TSRMLS_CC); \ + name## _ce->ce_flags |= flags; \ + } + +# define getObject(t, o) t * o = ((t *) zend_object_store_get_object(getThis() TSRMLS_CC)) +# define OBJ_PROP(o) o->zo.properties +# define DCL_PROP(a, t, n, v) zend_declare_property_ ##t(ce, (#n), sizeof(#n), (v), (ZEND_ACC_ ##a) TSRMLS_CC) +# define DCL_PROP_Z(a, n, v) zend_declare_property(ce, (#n), sizeof(#n), (v), (ZEND_ACC_ ##a) TSRMLS_CC) +# define DCL_PROP_N(a, n) zend_declare_property_null(ce, (#n), sizeof(#n), (ZEND_ACC_ ##a) TSRMLS_CC) +# define UPD_PROP(o, t, n, v) zend_update_property_ ##t(o->zo.ce, getThis(), (#n), sizeof(#n), (v) TSRMLS_CC) +# define SET_PROP(o, n, z) zend_update_property(o->zo.ce, getThis(), (#n), sizeof(#n), (z) TSRMLS_CC) +# define GET_PROP(o, n) zend_read_property(o->zo.ce, getThis(), (#n), sizeof(#n), 0 TSRMLS_CC) + +# define INIT_PARR(o, n) \ + { \ + zval *__tmp; \ + MAKE_STD_ZVAL(__tmp); \ + array_init(__tmp); \ + SET_PROP(o, n, __tmp); \ + } + +# define FREE_PARR(o, p) \ + { \ + zval *__tmp = NULL; \ + if (__tmp = GET_PROP(o, p)) { \ + zval_dtor(__tmp); \ + FREE_ZVAL(__tmp); \ + __tmp = NULL; \ + } \ + } + +#endif /* ZEND_ENGINE_2 */ + /* make functions that return SUCCESS|FAILURE more obvious */ typedef int STATUS; @@ -61,28 +120,6 @@ typedef enum { /* server vars shorthand */ #define HTTP_SERVER_VARS Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]) -/* override arg_separator.output to "&" for data used in outgoing requests */ -#include "zend_ini.h" -#define HTTP_URL_ARGSEP_OVERRIDE zend_alter_ini_entry("arg_separator.output", sizeof("arg_separator.output") - 1, "&", 1, ZEND_INI_ALL, ZEND_INI_STAGE_RUNTIME) -#define HTTP_URL_ARGSEP_RESTORE zend_restore_ini_entry("arg_separator.output", sizeof("arg_separator.output") - 1, ZEND_INI_STAGE_RUNTIME) - -/* {{{ HAVE_CURL */ -#ifdef HTTP_HAVE_CURL -#include - -/* CURL buffer size */ -#define HTTP_CURLBUF_BODYSIZE 16384 -#define HTTP_CURLBUF_HDRSSIZE 4096 - -/* {{{ http_curlbuf_member */ -typedef enum { - CURLBUF_BODY = 1, - CURLBUF_HDRS = 2, - CURLBUF_EVRY = 3 -} http_curlbuf_member; -/* }}} */ -#endif -/* }}} HAVE_CURL */ /* {{{ HTTP_GSC(var, name, ret) */ #define HTTP_GSC(var, name, ret) HTTP_GSP(var, name, return ret) @@ -98,6 +135,8 @@ typedef enum { } /* }}} */ +char *pretty_key(char *key, int key_len, int uctitle, int xhyphen); + /* {{{ public API */ #define http_date(t) _http_date((t) TSRMLS_CC) PHP_HTTP_API char *_http_date(time_t t TSRMLS_DC); @@ -198,34 +237,6 @@ PHP_HTTP_API STATUS _http_parse_headers(char *header, int header_len, zval *arra #define http_get_request_headers(h) _http_get_request_headers((h) TSRMLS_CC) PHP_HTTP_API void _http_get_request_headers(zval *array TSRMLS_DC); -/* {{{ HAVE_CURL */ -#ifdef HTTP_HAVE_CURL - -#define http_get(u, o, i, d, l) _http_get((u), (o), (i), (d), (l) TSRMLS_CC) -PHP_HTTP_API STATUS _http_get(const char *URL, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); -#define http_get_ex(c, u, o, i, d, l) _http_get_ex((c), (u), (o), (i), (d), (l) TSRMLS_CC) -PHP_HTTP_API STATUS _http_get_ex(CURL *ch, const char *URL, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); - -#define http_head(u, o, i, d, l) _http_head((u), (o), (i), (d), (l) TSRMLS_CC) -PHP_HTTP_API STATUS _http_head(const char *URL, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); -#define http_head_ex(c, u, o, i, d, l) _http_head_ex((c), (u), (o), (i), (d), (l) TSRMLS_CC) -PHP_HTTP_API STATUS _http_head_ex(CURL *ch, const char *URL, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); - -#define http_post_data(u, pd, pl, o, i, d, l) _http_post_data((u), (pd), (pl), (o), (i), (d), (l) TSRMLS_CC) -PHP_HTTP_API STATUS _http_post_data(const char *URL, char *postdata, size_t postdata_len, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); -#define http_post_data_ex(c, u, pd, pl, o, i, d, l) _http_post_data_ex((c), (u), (pd), (pl), (o), (i), (d), (l) TSRMLS_CC) -PHP_HTTP_API STATUS _http_post_data_ex(CURL *ch, const char *URL, char *postdata, size_t postdata_len, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); - -#define http_post_array(u, p, o, i, d, l) _http_post_array_ex(NULL, (u), (p), (o), (i), (d), (l) TSRMLS_CC) -#define http_post_array_ex(c, u, p, o, i, d, l) _http_post_array_ex((c), (u), (p), (o), (i), (d), (l) TSRMLS_CC) -PHP_HTTP_API STATUS _http_post_array_ex(CURL *ch, const char *URL, HashTable *postarray, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); - -#define http_post_curldata_ex(c, u, h, o, i, d, l) _http_post_curldata_ex((c), (u), (h), (o), (i), (d), (l) TSRMLS_CC) -PHP_HTTP_API STATUS _http_post_curldata_ex(CURL *ch, const char *URL, struct curl_httppost *curldata, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); - -#endif -/* }}} HAVE_CURL */ - #define http_auth_credentials(u, p) _http_auth_credentials((u), (p) TSRMLS_CC) PHP_HTTP_API STATUS _http_auth_credentials(char **user, char **pass TSRMLS_DC); diff --git a/php_http_curl_api.h b/php_http_curl_api.h new file mode 100644 index 0000000..b67de51 --- /dev/null +++ b/php_http_curl_api.h @@ -0,0 +1,62 @@ +/* + +----------------------------------------------------------------------+ + | PECL :: http | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, that | + | is bundled with this package in the file LICENSE, and is available | + | through the world-wide-web at http://www.php.net/license/3_0.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. | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Michael Wallner | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_HTTP_CURL_API_H +#define PHP_HTTP_CURL_API_H + +#include +#include "zend_ini.h" + +/* override arg_separator.output to "&" for data used in outgoing requests */ +#define HTTP_URL_ARGSEP_OVERRIDE zend_alter_ini_entry("arg_separator.output", sizeof("arg_separator.output") - 1, "&", 1, ZEND_INI_ALL, ZEND_INI_STAGE_RUNTIME) +#define HTTP_URL_ARGSEP_RESTORE zend_restore_ini_entry("arg_separator.output", sizeof("arg_separator.output") - 1, ZEND_INI_STAGE_RUNTIME) + +/* CURL buffer size */ +#define HTTP_CURLBUF_SIZE 16384 + +#define http_get(u, o, i, d, l) _http_get((u), (o), (i), (d), (l) TSRMLS_CC) +PHP_HTTP_API STATUS _http_get(const char *URL, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); +#define http_get_ex(c, u, o, i, d, l) _http_get_ex((c), (u), (o), (i), (d), (l) TSRMLS_CC) +PHP_HTTP_API STATUS _http_get_ex(CURL *ch, const char *URL, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); + +#define http_head(u, o, i, d, l) _http_head((u), (o), (i), (d), (l) TSRMLS_CC) +PHP_HTTP_API STATUS _http_head(const char *URL, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); +#define http_head_ex(c, u, o, i, d, l) _http_head_ex((c), (u), (o), (i), (d), (l) TSRMLS_CC) +PHP_HTTP_API STATUS _http_head_ex(CURL *ch, const char *URL, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); + +#define http_post_data(u, pd, pl, o, i, d, l) _http_post_data((u), (pd), (pl), (o), (i), (d), (l) TSRMLS_CC) +PHP_HTTP_API STATUS _http_post_data(const char *URL, char *postdata, size_t postdata_len, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); +#define http_post_data_ex(c, u, pd, pl, o, i, d, l) _http_post_data_ex((c), (u), (pd), (pl), (o), (i), (d), (l) TSRMLS_CC) +PHP_HTTP_API STATUS _http_post_data_ex(CURL *ch, const char *URL, char *postdata, size_t postdata_len, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); + +#define http_post_array(u, p, o, i, d, l) _http_post_array_ex(NULL, (u), (p), (o), (i), (d), (l) TSRMLS_CC) +#define http_post_array_ex(c, u, p, o, i, d, l) _http_post_array_ex((c), (u), (p), (o), (i), (d), (l) TSRMLS_CC) +PHP_HTTP_API STATUS _http_post_array_ex(CURL *ch, const char *URL, HashTable *postarray, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); + +#define http_post_curldata_ex(c, u, h, o, i, d, l) _http_post_curldata_ex((c), (u), (h), (o), (i), (d), (l) TSRMLS_CC) +PHP_HTTP_API STATUS _http_post_curldata_ex(CURL *ch, const char *URL, struct curl_httppost *curldata, HashTable *options, HashTable *info, char **data, size_t *data_len TSRMLS_DC); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ -- 2.30.2