- spawn off http_cookie_api
authorMichael Wallner <mike@php.net>
Sat, 11 Feb 2006 21:19:29 +0000 (21:19 +0000)
committerMichael Wallner <mike@php.net>
Sat, 11 Feb 2006 21:19:29 +0000 (21:19 +0000)
- 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().

13 files changed:
config.m4
config.w32
http.c
http_api.c
http_cookie_api.c [new file with mode: 0644]
http_functions.c
http_request_object.c
package.xml
package2.xml
php_http_api.h
php_http_cookie_api.h [new file with mode: 0644]
php_http_request_object.h
tests/parse_cookie_001.phpt

index 91b532f..38d8451 100644 (file)
--- 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)
        ], [
index 5411da1..1966723 100644 (file)
@@ -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 (file)
--- 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
index 7762f20..811acba 100644 (file)
@@ -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 (file)
index 0000000..bc02315
--- /dev/null
@@ -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 <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+/* $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
+ */
index 225019d..b16cef1 100644 (file)
@@ -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:
  * <pre>
  * <?php
- * print_r(http_parse_cookie("foo=bar; path=/"));
+ * print_r(http_parse_cookie("foo=bar; bar=baz; path=/; domain=example.com; comment=; secure", 0, array("comment")));
  * 
  * stdClass Object
  * (
- *     [name] => foo
- *     [value] => bar
+ *     [cookies] => Array
+ *         (
+ *             [foo] => bar
+ *             [bar] => baz
+ *         )
+ * 
+ *     [extras] => Array
+ *         (
+ *             [comment] =>
+ *         )
+ * 
+ *     [flags] => 16
+ *     [expires] => 0
  *     [path] => /
+ *     [domain] => example.com
  * )
  * ?>
  * </pre> 
  */
 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);
        }
 }
 
index bb3ebf4..93ce010 100644 (file)
@@ -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;
                }
        }
 }
index 984ba8b..0b386ce 100644 (file)
@@ -31,11 +31,19 @@ HttpResponse
   </maintainers>
  <release>
   <version>0.23.0</version>
-  <date>2006-02-06</date>
+  <date>2006-02-11</date>
   <license>BSD, revised</license>
   <state>beta</state>
   <notes>+ Added &apos;etag&apos; request option.
++ Added &apos;encodecookies&apos; 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 &quot;resolve&quot; 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.
   </notes>
@@ -70,6 +78,7 @@ HttpResponse
     <file role="test" name="build_url_001.phpt"/>
     <file role="test" name="build_url_002.phpt"/>
     <file role="test" name="build_url_003.phpt"/>
+    <file role="test" name="build_url_004.phpt"/>
     <file role="test" name="chunked_decode_001.phpt"/>
     <file role="test" name="chunked_decode_002.phpt"/>
     <file role="test" name="chunked_decode_003.phpt"/>
@@ -104,6 +113,7 @@ HttpResponse
     <file role="test" name="HttpMessage_004.phpt"/>
     <file role="test" name="HttpMessage_005.phpt"/>
     <file role="test" name="HttpMessage_006.phpt"/>
+    <file role="test" name="HttpMessage_007.phpt"/>
     <file role="test" name="HttpQueryString_001.phpt"/>
     <file role="test" name="HttpQueryString_002.phpt"/>
     <file role="test" name="HttpRequestPool_001.phpt"/>
@@ -130,6 +140,7 @@ HttpResponse
     <file role="test" name="negotiation_001.phpt"/>
     <file role="test" name="ob_deflatehandler_001.phpt"/>
     <file role="test" name="ob_inflatehandler_001.phpt"/>
+    <file role="test" name="parse_cookie_001.phpt"/>
     <file role="test" name="parse_headers_001.phpt"/>
     <file role="test" name="parse_message_001.phpt"/>
     <file role="test" name="parse_message_002.phpt"/>
@@ -148,6 +159,7 @@ HttpResponse
     <file role="test" name="redirect_012_logging.phpt"/>
     <file role="test" name="redirect_013.phpt"/>
     <file role="test" name="redirect_013_logging.phpt"/>
+    <file role="test" name="request_cookies.phpt"/>
     <file role="test" name="request_etag.phpt"/>
     <file role="test" name="request_gzip.phpt"/>
     <file role="test" name="request_methods.phpt"/>
@@ -191,6 +203,7 @@ HttpResponse
    <file role="src" name="http.dsp"/>
    <file role="src" name="http_api.c"/>
    <file role="src" name="http_cache_api.c"/>
+   <file role="src" name="http_cookie_api.c"/>
    <file role="src" name="http_date_api.c"/>
    <file role="src" name="http_deflatestream_object.c"/>
    <file role="src" name="http_encoding_api.c"/>
@@ -221,6 +234,7 @@ HttpResponse
    <file role="src" name="php_http.h"/>
    <file role="src" name="php_http_api.h"/>
    <file role="src" name="php_http_cache_api.h"/>
+   <file role="src" name="php_http_cookie_api.h"/>
    <file role="src" name="php_http_date_api.h"/>
    <file role="src" name="php_http_deflatestream_object.h"/>
    <file role="src" name="php_http_encoding_api.h"/>
index ccaf772..785c7e9 100644 (file)
@@ -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.
 ]]></notes>
@@ -85,6 +87,7 @@ HttpResponse
    <file role="src" name="php_http_std_defs.h"/>
    <file role="src" name="php_http_api.h"/>
    <file role="src" name="php_http_cache_api.h"/>
+   <file role="src" name="php_http_cookie_api.h"/>
    <file role="src" name="php_http_date_api.h"/>
    <file role="src" name="php_http_encoding_api.h"/>
    <file role="src" name="php_http_filter_api.h"/>
@@ -111,6 +114,7 @@ HttpResponse
    <file role="src" name="http_functions.c"/>
    <file role="src" name="http_api.c"/>
    <file role="src" name="http_cache_api.c"/>
+   <file role="src" name="http_cookie_api.c"/>
    <file role="src" name="http_date_api.c"/>
    <file role="src" name="http_encoding_api.c"/>
    <file role="src" name="http_filter_api.c"/>
@@ -146,6 +150,7 @@ HttpResponse
     <file role="test" name="build_url_001.phpt"/>
     <file role="test" name="build_url_002.phpt"/>
     <file role="test" name="build_url_003.phpt"/>
+    <file role="test" name="build_url_004.phpt"/>
     <file role="test" name="chunked_decode_001.phpt"/>
     <file role="test" name="chunked_decode_002.phpt"/>
     <file role="test" name="chunked_decode_003.phpt"/>
@@ -179,6 +184,7 @@ HttpResponse
     <file role="test" name="HttpMessage_004.phpt"/>
     <file role="test" name="HttpMessage_005.phpt"/>
     <file role="test" name="HttpMessage_006.phpt"/>
+    <file role="test" name="HttpMessage_007.phpt"/>
     <file role="test" name="HttpQueryString_001.phpt"/>
     <file role="test" name="HttpQueryString_002.phpt"/>
     <file role="test" name="HttpRequest_001.phpt"/>
@@ -204,6 +210,7 @@ HttpResponse
     <file role="test" name="negotiation_001.phpt"/>
     <file role="test" name="ob_deflatehandler_001.phpt"/>
     <file role="test" name="ob_inflatehandler_001.phpt"/>
+    <file role="test" name="parse_cookie_001.phpt"/>
     <file role="test" name="parse_headers_001.phpt"/>
     <file role="test" name="parse_message_001.phpt"/>
     <file role="test" name="parse_message_002.phpt"/>
@@ -222,6 +229,7 @@ HttpResponse
     <file role="test" name="redirect_012_logging.phpt"/>
     <file role="test" name="redirect_013.phpt"/>
     <file role="test" name="redirect_013_logging.phpt"/>
+    <file role="test" name="request_cookies.phpt"/>
     <file role="test" name="request_etag.phpt"/>
     <file role="test" name="request_gzip.phpt"/>
     <file role="test" name="request_methods.phpt"/>
index cbbaf6f..573b2a4 100644 (file)
@@ -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 (file)
index 0000000..c06e7ca
--- /dev/null
@@ -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 <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+/* $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
+ */
index 7713e28..ebd5bf5 100644 (file)
@@ -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);
index 3e42f6b..d17c78d 100644 (file)
@@ -8,20 +8,34 @@ include 'skip.inc';
 <?php
 echo "-TEST\n";
 
-var_dump(http_parse_cookie('name="value"; foo="bar\"baz"; hey=got\"it'));
+var_dump(http_parse_cookie('name="value"; foo="bar\"baz"; hey=got"it ; path=/     ; comment=; expires='.http_date(1).' secure ; httpOnly', 0, array("comment")));
 
 echo "Done\n";
 ?>
 --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