From e6b35fbe072a13b0e792952852fcd1499bd8e610 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Sat, 11 Feb 2006 21:19:29 +0000 Subject: [PATCH] - spawn off http_cookie_api - changed the workings, arguments and return value of http_parse_cookie() to something that seems to be more appropriate then the old implementation. - rename HttpRequest::getRequestCookie() to HttpRequest::getResponseCookies(), compliant to http_parse_cookie(). --- config.m4 | 5 +- config.w32 | 3 +- http.c | 2 + http_api.c | 194 -------------------- http_cookie_api.c | 357 ++++++++++++++++++++++++++++++++++++ http_functions.c | 60 ++++-- http_request_object.c | 136 +++++++------- package.xml | 16 +- package2.xml | 12 +- php_http_api.h | 3 - php_http_cookie_api.h | 80 ++++++++ php_http_request_object.h | 2 +- tests/parse_cookie_001.phpt | 34 +++- 13 files changed, 606 insertions(+), 298 deletions(-) create mode 100644 http_cookie_api.c create mode 100644 php_http_cookie_api.h diff --git a/config.m4 b/config.m4 index 91b532f..38d8451 100644 --- a/config.m4 +++ b/config.m4 @@ -232,7 +232,7 @@ dnl ---- http_headers_api.c http_message_api.c http_send_api.c http_url_api.c \ http_info_api.c http_request_method_api.c http_encoding_api.c \ http_filter_api.c http_request_body_api.c http_querystring_object.c \ - http_deflatestream_object.c http_inflatestream_object.c" + http_deflatestream_object.c http_inflatestream_object.c http_cookie_api.c" PHP_NEW_EXTENSION([http], $PHP_HTTP_SOURCES, $ext_shared) PHP_ADD_BUILD_DIR($ext_builddir/phpstr, 1) PHP_SUBST([HTTP_SHARED_LIBADD]) @@ -243,7 +243,8 @@ dnl ---- php_http_encoding_api.h phpstr/phpstr.h missing.h php_http_request_body_api.h \ php_http_exception_object.h php_http_message_object.h php_http_request_object.h \ php_http_requestpool_object.h php_http_response_object.h php_http_util_object.h \ - php_http_querystring_object.h php_http_deflatestream_object.h php_http_inflatestream_object.h" + php_http_querystring_object.h php_http_deflatestream_object.h php_http_inflatestream_object.h \ + php_http_cookie_api.h" ifdef([PHP_INSTALL_HEADERS], [ PHP_INSTALL_HEADERS(ext/http, $PHP_HTTP_HEADERS) ], [ diff --git a/config.w32 b/config.w32 index 5411da1..1966723 100644 --- a/config.w32 +++ b/config.w32 @@ -55,7 +55,8 @@ if (PHP_HTTP != "no") { "http_message_api.c http_send_api.c http_url_api.c "+ "http_info_api.c http_request_method_api.c http_encoding_api.c "+ "http_filter_api.c http_request_body_api.c http_querystring_object.c "+ - "http_deflatestream_object.c http_inflatestream_object.c", + "http_deflatestream_object.c http_inflatestream_object.c "+ + "http_cookie_api.c", null, "/I\"" + configure_module_dirname + "/phpstr\""); ADD_SOURCES(configure_module_dirname + "/phpstr", "phpstr.c", "http"); diff --git a/http.c b/http.c index 76d4dff..9b15c6c 100644 --- a/http.c +++ b/http.c @@ -24,6 +24,7 @@ #include "php_http_api.h" #include "php_http_send_api.h" +#include "php_http_cookie_api.h" #include "php_http_cache_api.h" #include "php_http_send_api.h" #include "php_http_message_api.h" @@ -239,6 +240,7 @@ PHP_MINIT_FUNCTION(http) REGISTER_INI_ENTRIES(); if ( (SUCCESS != PHP_MINIT_CALL(http_support)) || + (SUCCESS != PHP_MINIT_CALL(http_cookie)) || (SUCCESS != PHP_MINIT_CALL(http_send)) || (SUCCESS != PHP_MINIT_CALL(http_url)) || #ifdef HTTP_HAVE_CURL diff --git a/http_api.c b/http_api.c index 7762f20..811acba 100644 --- a/http_api.c +++ b/http_api.c @@ -84,200 +84,6 @@ char *_http_pretty_key(char *key, size_t key_len, zend_bool uctitle, zend_bool x } /* }}} */ -/* {{{ STATUS http_parse_cookie(char *, HashTable *) */ -PHP_HTTP_API STATUS _http_parse_cookie(const char *list, HashTable *items TSRMLS_DC) -{ -#define ST_QUOTE 1 -#define ST_VALUE 2 -#define ST_KEY 3 -#define ST_ASSIGN 4 -#define ST_ADD 5 - - zval array; - int first = 1, st = ST_KEY, keylen = 0, vallen = 0; - char *s, *c, *key = NULL, *val = NULL; - - INIT_ZARR(array, items); - - c = s = estrdup(list); - for(;;) { -#if 0 - char *tk = NULL, *tv = NULL; - - if (key) { - if (keylen) { - tk= estrndup(key, keylen); - } else { - tk = ecalloc(1, 7); - memcpy(tk, key, 3); - tk[3]='.'; tk[4]='.'; tk[5]='.'; - } - } - if (val) { - if (vallen) { - tv = estrndup(val, vallen); - } else { - tv = ecalloc(1, 7); - memcpy(tv, val, 3); - tv[3]='.'; tv[4]='.'; tv[5]='.'; - } - } - fprintf(stderr, "[%6s] %c \"%s=%s\"\n", - ( - st == ST_QUOTE ? "QUOTE" : - st == ST_VALUE ? "VALUE" : - st == ST_KEY ? "KEY" : - st == ST_ASSIGN ? "ASSIGN" : - st == ST_ADD ? "ADD": - "HUH?" - ), *c, tk, tv - ); - STR_FREE(tk); STR_FREE(tv); -#endif - switch (st) - { - case ST_QUOTE: - if (*c == '"') { - if (*(c-1) != '\\') { - st = ST_ADD; - } else { - memmove(c-1, c, strlen(c)+1); - } - } else { - if (!val) { - val = c; - } - if (!*c) { - --val; - st = ST_ADD; - } - } - break; - - case ST_VALUE: - switch (*c) - { - case '"': - if (!val) { - st = ST_QUOTE; - } - break; - - case ' ': - break; - - case ';': - goto add; - break; - - case '\0': - st = ST_ADD; - break; - - default: - if (!val) { - val = c; - } - break; - } - break; - - case ST_KEY: - switch (*c) - { - case ',': - case '\r': - case '\n': - case '\t': - case '\013': - case '\014': - goto failure; - break; - - case '=': - if (key) { - keylen = c - key; - st = ST_VALUE; - } else { - goto failure; - } - break; - - case ' ': - if (key) { - keylen = c - key; - st = ST_ASSIGN; - } - break; - - case '\0': - if (key) { - keylen = c - key; - st = ST_ADD; - } - break; - - default: - if (!key) { - key = c; - } - break; - } - break; - - case ST_ASSIGN: - if (*c == '=') { - st = ST_VALUE; - } else if (!*c || *c == ';') { - st = ST_ADD; - } else if (*c != ' ') { - goto failure; - } - break; - - case ST_ADD: - add: - if (val) { - vallen = c - val - (*c?1:0); - while (val[vallen-1] == ' ') --vallen; - } else { - val = ""; - vallen = 0; - } - if (first) { - first = 0; - add_assoc_stringl(&array, "name", key, keylen, 1); - add_assoc_stringl(&array, "value", val, vallen, 1); - } else { - key = estrndup(key, keylen); - add_assoc_stringl_ex(&array, key, keylen+1, val, vallen, 1); - efree(key); - } - st = ST_KEY; - key = val = NULL; - keylen = vallen = 0; - break; - } - - if (*c) { - ++c; - } else if (st == ST_ADD) { - goto add; - } else { - break; - } - } - - efree(s); - return SUCCESS; - -failure: - http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Unexpected character (%c) at pos %tu of %zu", *c, c-s, strlen(s)); - efree(s); - return FAILURE; -} -/* }}} */ - /* {{{ void http_error(long, long, char*) */ void _http_error_ex(long type TSRMLS_DC, long code, const char *format, ...) { diff --git a/http_cookie_api.c b/http_cookie_api.c new file mode 100644 index 0000000..bc02315 --- /dev/null +++ b/http_cookie_api.c @@ -0,0 +1,357 @@ +/* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2006, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php_http.h" +#include "php_http_api.h" +#include "php_http_date_api.h" +#include "php_http_cookie_api.h" + +PHP_MINIT_FUNCTION(http_cookie) +{ + HTTP_LONG_CONSTANT("HTTP_COOKIE_PARSE_RAW", HTTP_COOKIE_PARSE_RAW); + HTTP_LONG_CONSTANT("HTTP_COOKIE_SECURE", HTTP_COOKIE_SECURE); + HTTP_LONG_CONSTANT("HTTP_COOKIE_HTTPONLY", HTTP_COOKIE_HTTPONLY); + + return SUCCESS; +} + +PHP_HTTP_API http_cookie_list *_http_cookie_list_init(http_cookie_list *list TSRMLS_DC) +{ + if (!list) { + list = emalloc(sizeof(http_cookie_list)); + } + + zend_hash_init(&list->cookies, 0, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_init(&list->extras, 0, NULL, ZVAL_PTR_DTOR, 0); + + list->path = NULL; + list->domain = NULL; + list->expires = 0; + list->flags = 0; + + return list; +} + +PHP_HTTP_API void _http_cookie_list_dtor(http_cookie_list *list TSRMLS_DC) +{ + if (list) { + zend_hash_destroy(&list->cookies); + zend_hash_destroy(&list->extras); + + STR_SET(list->path, NULL); + STR_SET(list->domain, NULL); + } +} + +PHP_HTTP_API void _http_cookie_list_free(http_cookie_list **list TSRMLS_DC) +{ + if (list) { + http_cookie_list_dtor(*list); + efree(*list); + *list = NULL; + } +} + +PHP_HTTP_API const char *_http_cookie_list_get_cookie(http_cookie_list *list, const char *name, size_t name_len TSRMLS_DC) +{ + zval **cookie = NULL; + if ((SUCCESS != zend_hash_find(&list->cookies, name, name_len + 1, (void **) &cookie)) || (Z_TYPE_PP(cookie) != IS_STRING)) { + return NULL; + } + return Z_STRVAL_PP(cookie); +} + +PHP_HTTP_API const char *_http_cookie_list_get_extra(http_cookie_list *list, const char *name, size_t name_len TSRMLS_DC) +{ + zval **extra = NULL; + if ((SUCCESS != zend_hash_find(&list->extras,name, name_len + 1, (void **) &extra)) || (Z_TYPE_PP(extra) != IS_STRING)) { + return NULL; + } + return Z_STRVAL_PP(extra); +} + +PHP_HTTP_API void _http_cookie_list_add_cookie(http_cookie_list *list, const char *name, size_t name_len, const char *value, size_t value_len TSRMLS_DC) +{ + zval *cookie_value; + MAKE_STD_ZVAL(cookie_value); + ZVAL_STRINGL(cookie_value, estrndup(value, value_len), value_len, 0); + zend_hash_update(&list->cookies, name, name_len + 1, (void *) &cookie_value, sizeof(zval *), NULL); +} + +PHP_HTTP_API void _http_cookie_list_add_extra(http_cookie_list *list, const char *name, size_t name_len, const char *value, size_t value_len TSRMLS_DC) +{ + zval *cookie_value; + MAKE_STD_ZVAL(cookie_value); + ZVAL_STRINGL(cookie_value, estrndup(value, value_len), value_len, 0); + zend_hash_update(&list->extras, name, name_len + 1, (void *) &cookie_value, sizeof(zval *), NULL); +} + +#define http_cookie_list_set_item_ex(l, i, il, v, vl, f, a) _http_cookie_list_set_item_ex((l), (i), (il), (v), (vl), (f), (a) TSRMLS_CC) +static inline void _http_cookie_list_set_item_ex(http_cookie_list *list, const char *item, int item_len, const char *value, int value_len, long flags, char **allowed_extras TSRMLS_DC) +{ + char *key = estrndup(item, item_len); + + if (!strcasecmp(key, "path")) { + STR_SET(list->path, estrndup(value, value_len)); + } else if (!strcasecmp(key, "domain")) { + STR_SET(list->domain, estrndup(value, value_len)); + } else if (!strcasecmp(key, "expires")) { + const char *date = estrndup(value, value_len); + list->expires = http_parse_date(date); + efree(date); + } else if (!strcasecmp(key, "secure")) { + list->flags |= HTTP_COOKIE_SECURE; + } else if (!strcasecmp(key, "httpOnly")) { + list->flags |= HTTP_COOKIE_HTTPONLY; + } else { + /* check for extra */ + if (allowed_extras) { + for (; *allowed_extras; ++allowed_extras) { + if (!strcasecmp(key, *allowed_extras)) { + http_cookie_list_add_extra(list, key, item_len, value, value_len); + + efree(key); + return; + } + } + } + /* new cookie */ + http_cookie_list_add_cookie(list, key, item_len, value, value_len); + } + efree(key); +} + + +#define ST_QUOTE 1 +#define ST_VALUE 2 +#define ST_KEY 3 +#define ST_ASSIGN 4 +#define ST_ADD 5 + +/* {{{ http_cookie_list *http_parse_cookie(char *, long) */ +PHP_HTTP_API http_cookie_list *_http_parse_cookie_ex(http_cookie_list *list, const char *string, long flags, char **allowed_extras TSRMLS_DC) +{ + int free_list = !list, st = ST_KEY, keylen = 0, vallen = 0; + char *s, *c, *key = NULL, *val = NULL; + + list = http_cookie_list_init(list); + + c = s = estrdup(string); + for(;;) { +#if 0 + char *tk = NULL, *tv = NULL; + + if (key) { + if (keylen) { + tk= estrndup(key, keylen); + } else { + tk = ecalloc(1, 7); + memcpy(tk, key, 3); + tk[3]='.'; tk[4]='.'; tk[5]='.'; + } + } + if (val) { + if (vallen) { + tv = estrndup(val, vallen); + } else { + tv = ecalloc(1, 7); + memcpy(tv, val, 3); + tv[3]='.'; tv[4]='.'; tv[5]='.'; + } + } + fprintf(stderr, "[%6s] %c \"%s=%s\"\n", + ( + st == ST_QUOTE ? "QUOTE" : + st == ST_VALUE ? "VALUE" : + st == ST_KEY ? "KEY" : + st == ST_ASSIGN ? "ASSIGN" : + st == ST_ADD ? "ADD": + "HUH?" + ), *c, tk, tv + ); + STR_FREE(tk); STR_FREE(tv); +#endif + switch (st) + { + case ST_QUOTE: + if (*c == '"') { + if (*(c-1) != '\\') { + st = ST_ADD; + } else { + memmove(c-1, c, strlen(c)+1); + } + } else { + if (!val) { + val = c; + } + if (!*c) { + --val; + st = ST_ADD; + } + } + break; + + case ST_VALUE: + switch (*c) + { + case '"': + if (!val) { + st = ST_QUOTE; + } + break; + + case ' ': + break; + + case ';': + if (!*(c+1)) { + goto add; + } + case '\0': + st = ST_ADD; + break; + + default: + if (!val) { + val = c; + } + break; + } + break; + + case ST_KEY: + switch (*c) + { + case ',': + case '\r': + case '\n': + case '\t': + case '\013': + case '\014': + goto failure; + break; + + case '=': + if (key) { + keylen = c - key; + st = ST_VALUE; + } else { + goto failure; + } + break; + + case ' ': + if (key) { + keylen = c - key; + st = ST_ASSIGN; + } + break; + + case ';': + case '\0': + if (key) { + keylen = c - key; + st = ST_ADD; + } + break; + + default: + if (!key) { + key = c; + } + break; + } + break; + + case ST_ASSIGN: + if (*c == '=') { + st = ST_VALUE; + } else if (!*c || *c == ';') { + st = ST_ADD; + } else if (*c != ' ') { + goto failure; + } + break; + + case ST_ADD: + add: + if (val) { + vallen = c - val; + if (*c) --vallen; + while (val[vallen-1] == ' ') --vallen; + } else { + val = ""; + vallen = 0; + } + http_cookie_list_set_item_ex(list, key, keylen, val, vallen, flags, allowed_extras); + st = ST_KEY; + key = val = NULL; + keylen = vallen = 0; + break; + } + + if (*c) { + ++c; + } else if (st == ST_ADD) { + goto add; + } else { + break; + } + } + + efree(s); + return list; + +failure: + http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Unexpected character (%c) at pos %tu of %zu", *c, c-s, strlen(s)); + if (free_list) { + http_cookie_list_free(&list); + } else { + http_cookie_list_dtor(list); + } + efree(s); + return FAILURE; +} +/* }}} */ + +PHP_HTTP_API void _http_cookie_list_tostruct(http_cookie_list *list, zval *strct TSRMLS_DC) +{ + zval array, *cookies, *extras; + + INIT_ZARR(array, HASH_OF(strct)); + + MAKE_STD_ZVAL(cookies); + array_init(cookies); + zend_hash_copy(Z_ARRVAL_P(cookies), &list->cookies, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + add_assoc_zval(&array, "cookies", cookies); + + MAKE_STD_ZVAL(extras); + array_init(extras); + zend_hash_copy(Z_ARRVAL_P(extras), &list->extras, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + add_assoc_zval(&array, "extras", extras); + + add_assoc_long(&array, "flags", list->flags); + add_assoc_long(&array, "expires", (long) list->expires); + add_assoc_string(&array, "path", list->path?list->path:"", 1); + add_assoc_string(&array, "domain", list->domain?list->domain:"", 1); +} + +/* + * 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 index 225019d..b16cef1 100644 --- a/http_functions.c +++ b/http_functions.c @@ -27,6 +27,7 @@ #include "php_http_api.h" #include "php_http_cache_api.h" +#include "php_http_cookie_api.h" #include "php_http_date_api.h" #include "php_http_encoding_api.h" #include "php_http_headers_api.h" @@ -1038,41 +1039,76 @@ PHP_FUNCTION(http_parse_headers) } /* }}}*/ -/* {{{ proto object http_parse_cookie(string cookie) +/* {{{ proto object http_parse_cookie(string cookie[, int flags[, array allowed_extras]]) * * Parses HTTP cookies like sent in a response into a struct. * * Expects a string as parameter containing the value of a Set-Cookie response header. * - * Returns an stdClass object with the cookie params as properties on success or FALSE on failure. + * Returns an stdClass olike shown in the example on success or FALSE on failure. * * Example: *
  *  foo
- *     [value] => bar
+ *     [cookies] => Array
+ *         (
+ *             [foo] => bar
+ *             [bar] => baz
+ *         )
+ * 
+ *     [extras] => Array
+ *         (
+ *             [comment] =>
+ *         )
+ * 
+ *     [flags] => 16
+ *     [expires] => 0
  *     [path] => /
+ *     [domain] => example.com
  * )
  * ?>
  * 
*/ PHP_FUNCTION(http_parse_cookie) { - char *cookie; - int cookie_len; + char *cookie, **allowed_extras = NULL; + int i = 0, cookie_len; + long flags = 0; + zval *allowed_extras_array = NULL, **entry = NULL; + HashPosition pos; + http_cookie_list list; - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &cookie, &cookie_len)) { + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|la", &cookie, &cookie_len, &flags, &allowed_extras_array)) { RETURN_FALSE; } - object_init(return_value); - if (SUCCESS != http_parse_cookie(cookie, HASH_OF(return_value))) { - zval_dtor(return_value); - RETURN_FALSE; + if (allowed_extras_array) { + allowed_extras = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(allowed_extras_array)) + 1, sizeof(char *)); + FOREACH_VAL(pos, allowed_extras_array, entry) { + ZVAL_ADDREF(*entry); + convert_to_string_ex(entry); + allowed_extras[i] = estrndup(Z_STRVAL_PP(entry), Z_STRLEN_PP(entry)); + zval_ptr_dtor(entry); + } + } + + if (http_parse_cookie_ex(&list, cookie, flags, allowed_extras)) { + object_init(return_value); + http_cookie_list_tostruct(&list, return_value); + http_cookie_list_dtor(&list); + } else { + RETVAL_FALSE; + } + + if (allowed_extras) { + for (i = 0; allowed_extras[i]; ++i) { + efree(allowed_extras[i]); + } + efree(allowed_extras); } } diff --git a/http_request_object.c b/http_request_object.c index bb3ebf4..93ce010 100644 --- a/http_request_object.c +++ b/http_request_object.c @@ -20,6 +20,7 @@ #include "zend_interfaces.h" #include "php_http_api.h" +#include "php_http_cookie_api.h" #include "php_http_exception_object.h" #include "php_http_message_api.h" #include "php_http_message_object.h" @@ -134,8 +135,9 @@ HTTP_BEGIN_ARGS(getResponseHeader, 0) HTTP_ARG_VAL(name, 0) HTTP_END_ARGS; -HTTP_BEGIN_ARGS(getResponseCookie, 0) - HTTP_ARG_VAL(name, 0) +HTTP_BEGIN_ARGS(getResponseCookies, 0) + HTTP_ARG_VAL(flags, 0) + HTTP_ARG_VAL(allowed_extras, 0) HTTP_END_ARGS; HTTP_EMPTY_ARGS(getResponseBody); @@ -263,7 +265,7 @@ zend_function_entry http_request_object_fe[] = { HTTP_REQUEST_ME(getResponseData, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getResponseHeader, ZEND_ACC_PUBLIC) - HTTP_REQUEST_ME(getResponseCookie, ZEND_ACC_PUBLIC) + HTTP_REQUEST_ME(getResponseCookies, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getResponseCode, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getResponseStatus, ZEND_ACC_PUBLIC) HTTP_REQUEST_ME(getResponseBody, ZEND_ACC_PUBLIC) @@ -1493,107 +1495,97 @@ PHP_METHOD(HttpRequest, getResponseHeader) } /* }}} */ -/* {{{ proto array HttpRequest::getResponseCookie([string name]) +/* {{{ proto array HttpRequest::getResponseCookies([int flags[, array allowed_extras]]) * * Get response cookie(s) after the request has been sent. * - * Accepts a string as optional parameter specifying the name of the cookie to read. - * If the parameter is empty or omitted, an associative array with all received - * cookies will be returned. - * - * Returns either an associative array with the cookie's name, value and any - * additional params of the cookie matching name if requested, FALSE on failure, - * or an array containing all received cookies as arrays. + * Returns an array of stdClass objects like http_parse_cookie would return. * * If redirects were allowed and several responses were received, the data * references the last received response. */ -PHP_METHOD(HttpRequest, getResponseCookie) +PHP_METHOD(HttpRequest, getResponseCookies) { IF_RETVAL_USED { - zval *data, **headers; - char *cookie_name = NULL; - int cookie_len = 0; + long flags = 0; + zval *allowed_extras_array = NULL, *data, **headers; - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &cookie_name, &cookie_len)) { + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|la", &flags, &allowed_extras_array)) { RETURN_FALSE; } - array_init(return_value); - data = GET_PROP(responseData); if ( (Z_TYPE_P(data) == IS_ARRAY) && (SUCCESS == zend_hash_find(Z_ARRVAL_P(data), "headers", sizeof("headers"), (void **) &headers)) && (Z_TYPE_PP(headers) == IS_ARRAY)) { + int i = 0; ulong idx = 0; - char *key = NULL; - zval **header = NULL; - HashPosition pos1; - + char *key = NULL, **allowed_extras = NULL; + zval **header = NULL, **entry = NULL; + HashPosition pos, pos1, pos2; + + array_init(return_value); + + if (allowed_extras_array) { + allowed_extras = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(allowed_extras_array)) + 1, sizeof(char *)); + FOREACH_VAL(pos, allowed_extras_array, entry) { + ZVAL_ADDREF(*entry); + convert_to_string_ex(entry); + allowed_extras[i++] = estrndup(Z_STRVAL_PP(entry), Z_STRLEN_PP(entry)); + zval_ptr_dtor(entry); + } + } + convert_to_array(*headers); FOREACH_HASH_KEYVAL(pos1, Z_ARRVAL_PP(headers), key, idx, header) { if (key && !strcasecmp(key, "Set-Cookie")) { - /* several cookies? */ + http_cookie_list list; + if (Z_TYPE_PP(header) == IS_ARRAY) { - zval **cookie; - HashPosition pos2; - - FOREACH_HASH_VAL(pos2, Z_ARRVAL_PP(header), cookie) { - zval *cookie_hash; - MAKE_STD_ZVAL(cookie_hash); - array_init(cookie_hash); - - if (SUCCESS == http_parse_cookie(Z_STRVAL_PP(cookie), Z_ARRVAL_P(cookie_hash))) { - if (!cookie_len) { - add_next_index_zval(return_value, cookie_hash); - } else { - zval **name; - - if ( (SUCCESS == zend_hash_find(Z_ARRVAL_P(cookie_hash), "name", sizeof("name"), (void **) &name)) && - (!strcmp(Z_STRVAL_PP(name), cookie_name))) { - add_next_index_zval(return_value, cookie_hash); - return; /* <<< FOUND >>> */ - } else { - zval_dtor(cookie_hash); - efree(cookie_hash); - } - } - } else { - zval_dtor(cookie_hash); - efree(cookie_hash); + zval **single_header; + HashPosition pos; + + FOREACH_VAL(pos2, *header, single_header) { + ZVAL_ADDREF(*single_header); + convert_to_string_ex(single_header); + if (http_parse_cookie_ex(&list, Z_STRVAL_PP(single_header), flags, allowed_extras)) { + zval *cookie; + + MAKE_STD_ZVAL(cookie); + object_init(cookie); + http_cookie_list_tostruct(&list, cookie); + add_next_index_zval(return_value, cookie); + zval_ptr_dtor(&cookie); } + zval_ptr_dtor(single_header); } } else { - zval *cookie_hash; - - MAKE_STD_ZVAL(cookie_hash); - array_init(cookie_hash); + ZVAL_ADDREF(*header); convert_to_string_ex(header); - - if (SUCCESS == http_parse_cookie(Z_STRVAL_PP(header), Z_ARRVAL_P(cookie_hash))) { - if (!cookie_len) { - add_next_index_zval(return_value, cookie_hash); - } else { - zval **name; - - if ( (SUCCESS == zend_hash_find(Z_ARRVAL_P(cookie_hash), "name", sizeof("name"), (void **) &name)) && - (!strcmp(Z_STRVAL_PP(name), cookie_name))) { - add_next_index_zval(return_value, cookie_hash); - } else { - zval_dtor(cookie_hash); - efree(cookie_hash); - } - } - } else { - zval_dtor(cookie_hash); - efree(cookie_hash); + if (http_parse_cookie_ex(&list, Z_STRVAL_PP(header), flags, allowed_extras)) { + zval *cookie; + + MAKE_STD_ZVAL(cookie); + object_init(cookie); + http_cookie_list_tostruct(&list, cookie); + add_next_index_zval(return_value, cookie); + zval_ptr_dtor(&cookie); } + zval_ptr_dtor(header); } - break; } /* reset key */ key = NULL; } + + if (allowed_extras) { + for (i = 0; allowed_extras[i]; ++i) { + efree(allowed_extras[i]); + } + efree(allowed_extras); + } + } else { + RETURN_FALSE; } } } diff --git a/package.xml b/package.xml index 984ba8b..0b386ce 100644 --- a/package.xml +++ b/package.xml @@ -31,11 +31,19 @@ HttpResponse 0.23.0 - 2006-02-06 + 2006-02-11 BSD, revised beta + Added 'etag' request option. ++ Added 'encodecookies' request option (defaults to true). + Added missing accessors for response status text to HttpMessage and HttpRequest classes. ++ Added HttpMessage::reverse(). + +- Reimplemented http_parse_cookie(). +- Changed HttpRequest::getResponseCookie() to HttpRequest::getResponseCookies(), + compliant to http_parse_cookie(). +- http_build_url() now tries to "resolve" public hostname prior falling back + to localhost if neither HTTP_HOST nor SERVER_NAME is set. * Fixed PHP-4.4 and PHP-5.0 build. @@ -70,6 +78,7 @@ HttpResponse + @@ -104,6 +113,7 @@ HttpResponse + @@ -130,6 +140,7 @@ HttpResponse + @@ -148,6 +159,7 @@ HttpResponse + @@ -191,6 +203,7 @@ HttpResponse + @@ -221,6 +234,7 @@ HttpResponse + diff --git a/package2.xml b/package2.xml index ccaf772..785c7e9 100644 --- a/package2.xml +++ b/package2.xml @@ -52,8 +52,10 @@ HttpResponse + Added HttpMessage::reverse(). - Reimplemented http_parse_cookie(). -- http_build_url() now tries to "resolve" public hostname prior fallback to localhost - if neither HTTP_HOST nor SERVER_NAME are set. +- Changed HttpRequest::getResponseCookie() to HttpRequest::getResponseCookies(), + compliant to http_parse_cookie(). +- http_build_url() now tries to "resolve" public hostname prior falling back + to localhost if neither HTTP_HOST nor SERVER_NAME is set. * Fixed PHP-4.4 and PHP-5.0 build. ]]> @@ -85,6 +87,7 @@ HttpResponse + @@ -111,6 +114,7 @@ HttpResponse + @@ -146,6 +150,7 @@ HttpResponse + @@ -179,6 +184,7 @@ HttpResponse + @@ -204,6 +210,7 @@ HttpResponse + @@ -222,6 +229,7 @@ HttpResponse + diff --git a/php_http_api.h b/php_http_api.h index cbbaf6f..573b2a4 100644 --- a/php_http_api.h +++ b/php_http_api.h @@ -29,9 +29,6 @@ PHP_HTTP_API long _http_support(long feature); #define pretty_key(key, key_len, uctitle, xhyphen) _http_pretty_key(key, key_len, uctitle, xhyphen) extern char *_http_pretty_key(char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen); -#define http_parse_cookie(l, i) _http_parse_cookie((l), (i) TSRMLS_CC) -PHP_HTTP_API STATUS _http_parse_cookie(const char *list, HashTable *items TSRMLS_DC); - #define http_error(type, code, string) _http_error_ex(type, code, "%s", string) #define http_error_ex _http_error_ex extern void _http_error_ex(long type TSRMLS_DC, long code, const char *format, ...); diff --git a/php_http_cookie_api.h b/php_http_cookie_api.h new file mode 100644 index 0000000..c06e7ca --- /dev/null +++ b/php_http_cookie_api.h @@ -0,0 +1,80 @@ +/* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2006, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_HTTP_COOKIE_API_H +#define PHP_HTTP_COOKIE_API_H + +#define HTTP_COOKIE_SECURE 0x10L +#define HTTP_COOKIE_HTTPONLY 0x20L + +#define HTTP_COOKIE_PARSE_RAW 0x01L + +extern PHP_MINIT_FUNCTION(http_cookie); + +/* + generally a netscape cookie compliant struct, recognizing httpOnly attribute, too; + cookie params like those from rfc2109 and rfc2965 are just put into extras, if + one specifies them in allowed extras, else they're treated like cookies themself +*/ +typedef struct { + HashTable cookies; + HashTable extras; + long flags; + char *path; + char *domain; + time_t expires; +} http_cookie_list; + +#define http_cookie_list_new() _http_cookie_list_init(NULL TSRMLS_CC) +#define http_cookie_list_init(l) _http_cookie_list_init((l) TSRMLS_CC) +PHP_HTTP_API http_cookie_list *_http_cookie_list_init(http_cookie_list *list TSRMLS_DC); + +#define http_cookie_list_dtor(l) _http_cookie_list_dtor((l) TSRMLS_CC) +PHP_HTTP_API void _http_cookie_list_dtor(http_cookie_list *list TSRMLS_DC); + +#define http_cookie_list_free(l) _http_cookie_list_free((l) TSRMLS_CC) +PHP_HTTP_API void _http_cookie_list_free(http_cookie_list **list TSRMLS_DC); + +#define http_cookie_list_has_cookie(list, name, name_len) zend_hash_exists(&(list)->cookies, (name), (name_len)+1) +#define http_cookie_list_has_extra(list, name, name_len) zend_hash_exists(&(list)->extras, (name), (name_len)+1) + +#define http_cookie_list_add_cookie(l, n, nl, v, vl) _http_cookie_list_add_cookie((l), (n), (nl), (v), (vl) TSRMLS_CC) +PHP_HTTP_API void _http_cookie_list_add_cookie(http_cookie_list *list, const char *name, size_t name_len, const char *value, size_t value_len TSRMLS_DC); + +#define http_cookie_list_add_extra(l, n , nl, v, vl) _http_cookie_list_add_extra((l), (n), (nl), (v), (vl) TSRMLS_CC) +PHP_HTTP_API void _http_cookie_list_add_extra(http_cookie_list *list, const char *name, size_t name_len, const char *value, size_t value_len TSRMLS_DC); + +#define http_cookie_list_get_cookie(l, n, nl) _http_cookie_list_get_cookie((l), (n), (nl) TSRMLS_CC) +PHP_HTTP_API const char *_http_cookie_list_get_cookie(http_cookie_list *list, const char *name, size_t name_len TSRMLS_DC); + +#define http_cookie_list_get_extra(l, n, nl) _http_cookie_list_get_extra((l), (n), (nl) TSRMLS_CC) +PHP_HTTP_API const char *_http_cookie_list_get_extra(http_cookie_list *list, const char *name, size_t name_len TSRMLS_DC); + +#define http_parse_cookie(s) _http_parse_cookie_ex(NULL, (s), 0, NULL TSRMLS_CC) +#define http_parse_cookie_ex(l, s, f, a) _http_parse_cookie_ex((l), (s), (f), (a) TSRMLS_CC) +PHP_HTTP_API http_cookie_list *_http_parse_cookie_ex(http_cookie_list * list, const char *string, long flags, char **allowed_extras TSRMLS_DC); + +#define http_cookie_list_tostruct(l, s) _http_cookie_list_tostruct((l), (s) TSRMLS_CC) +PHP_HTTP_API void _http_cookie_list_tostruct(http_cookie_list *list, zval *strct 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 + */ diff --git a/php_http_request_object.h b/php_http_request_object.h index 7713e28..ebd5bf5 100644 --- a/php_http_request_object.h +++ b/php_http_request_object.h @@ -80,7 +80,7 @@ PHP_METHOD(HttpRequest, getPutFile); PHP_METHOD(HttpRequest, send); PHP_METHOD(HttpRequest, getResponseData); PHP_METHOD(HttpRequest, getResponseHeader); -PHP_METHOD(HttpRequest, getResponseCookie); +PHP_METHOD(HttpRequest, getResponseCookies); PHP_METHOD(HttpRequest, getResponseCode); PHP_METHOD(HttpRequest, getResponseStatus); PHP_METHOD(HttpRequest, getResponseBody); diff --git a/tests/parse_cookie_001.phpt b/tests/parse_cookie_001.phpt index 3e42f6b..d17c78d 100644 --- a/tests/parse_cookie_001.phpt +++ b/tests/parse_cookie_001.phpt @@ -8,20 +8,34 @@ include 'skip.inc'; --EXPECTF-- %sTEST -object(stdClass)#1 (4) { - ["name"]=> - string(4) "name" - ["value"]=> - string(5) "value" - ["foo"]=> - string(7) "bar"baz" - ["hey"]=> - string(7) "got\"it" +object(stdClass)#%d (%d) { + ["cookies"]=> + array(3) { + ["name"]=> + string(5) "value" + ["foo"]=> + string(7) "bar"baz" + ["hey"]=> + string(6) "got"it" + } + ["extras"]=> + array(1) { + ["comment"]=> + string(0) "" + } + ["flags"]=> + int(32) + ["expires"]=> + int(1) + ["path"]=> + string(1) "/" + ["domain"]=> + string(0) "" } Done -- 2.30.2