Here is a patch I am submitting as a recommendation to implement a setcookie2() function to support the Set-Cookie2 response header defined in RFC 2965.

RFC 2965 obsoletes the original Netscape cookie specification and RFC 2109. Unfortunately, the only major browser I can find that implements Cookie2 and Set-Cookie2 from the client side is Opera. Nevertheless, the Set-Cookie2 header provides improvements over the original Set-Cookie header:

1) Clients can provide users with more control over what they accept and show them the provided Comment or a link to the CommentURL, allowing the application can tell the user how it is using the specific cookie.

2) Discard and Max-Age provide better control over cookie expiration and deletion. (Expires is not present.)

3) Port provides the ability to specify a list of ports for which the cookie is valid (at the given Domain).

There are a few statements in RFC 2965 that this patch does not cover:

"When it receives a Cookie header, the origin server SHOULD treat cookies with NAMEs whose prefix is $ specially, as an attribute for the cookie."

"User agents that receive in the same response both a Set-Cookie and Set-Cookie2 response header for the same cookie MUST discard the Set-Cookie information and use only the Set-Cookie2 information." (If the user agent obeys this rule, then this could potentially cause a problem setting PHPSESSID with Set-Cookie and setting cookies with setcookie2() in the same response.)

--
Ben Ramsey
http://benramsey.com/
Index: ext/standard/basic_functions.c
===================================================================
RCS file: /repository/php-src/ext/standard/basic_functions.c,v
retrieving revision 1.725.2.31.2.53
diff -u -w -r1.725.2.31.2.53 basic_functions.c
--- ext/standard/basic_functions.c      22 May 2007 15:38:27 -0000      
1.725.2.31.2.53
+++ ext/standard/basic_functions.c      25 Aug 2007 05:23:07 -0000
@@ -1640,6 +1640,22 @@
 ZEND_END_ARG_INFO()
 
 static
+ZEND_BEGIN_ARG_INFO_EX(arginfo_setcookie2, 0, 0, 1)
+       ZEND_ARG_INFO(0, name)
+       ZEND_ARG_INFO(0, value)
+       ZEND_ARG_INFO(0, maxage)
+       ZEND_ARG_INFO(0, discard)
+       ZEND_ARG_INFO(0, comment)
+       ZEND_ARG_INFO(0, commenturl)
+       ZEND_ARG_INFO(0, path)
+       ZEND_ARG_INFO(0, domain)
+       ZEND_ARG_INFO(0, portlist)
+       ZEND_ARG_INFO(0, secure)
+       ZEND_ARG_INFO(0, httponly)
+       ZEND_ARG_INFO(0, version)
+ZEND_END_ARG_INFO()
+
+static
 ZEND_BEGIN_ARG_INFO_EX(arginfo_setrawcookie, 0, 0, 1)
        ZEND_ARG_INFO(0, name)
        ZEND_ARG_INFO(0, value)
@@ -3412,6 +3428,7 @@
        PHP_FE(restore_include_path,                                            
                                        arginfo_restore_include_path)
 
        PHP_FE(setcookie,                                                       
                                                        arginfo_setcookie)
+       PHP_FE(setcookie2,                                                      
                                                        arginfo_setcookie2)
        PHP_FE(setrawcookie,                                                    
                                                arginfo_setrawcookie)
        PHP_FE(header,                                                          
                                                        arginfo_header)
        PHP_FE(headers_sent,                                                    
                                                arginfo_headers_sent)
Index: ext/standard/head.c
===================================================================
RCS file: /repository/php-src/ext/standard/head.c,v
retrieving revision 1.84.2.1.2.7
diff -u -w -r1.84.2.1.2.7 head.c
--- ext/standard/head.c 26 Feb 2007 02:12:36 -0000      1.84.2.1.2.7
+++ ext/standard/head.c 25 Aug 2007 05:23:07 -0000
@@ -144,6 +144,114 @@
        return result;
 }
 
+PHPAPI int php_setcookie2(char *name, int name_len, char *value, int 
value_len, char *maxage, int maxage_len, int discard, char *comment, int 
comment_len, char *commenturl, int commenturl_len, char *path, int path_len, 
char *domain, int domain_len, char *portlist, int portlist_len, int secure, int 
url_encode, int httponly, char *version, int version_len TSRMLS_DC)
+{
+    char *cookie, *encoded_value = NULL;
+    int len=sizeof("Set-Cookie2: ");
+    sapi_header_line ctr = {0};
+    int result;
+    
+    if (name && strpbrk(name, "=,; \t\r\n\013\014") != NULL) {   /* man 
isspace for \013 and \014 */
+        zend_error( E_WARNING, "Cookie names can not contain any of the 
folllowing '=,; \\t\\r\\n\\013\\014' (%s)", name );
+        return FAILURE;
+    }
+
+    if (!url_encode && value && strpbrk(value, ",; \t\r\n\013\014") != NULL) { 
/* man isspace for \013 and \014 */
+        zend_error( E_WARNING, "Cookie values can not contain any of the 
folllowing ',; \\t\\r\\n\\013\\014' (%s)", value );
+        return FAILURE;
+    }
+
+    len += name_len;
+    if (value && url_encode) {
+        int encoded_value_len;
+        encoded_value = php_url_encode(value, value_len, &encoded_value_len);
+        len += encoded_value_len;
+    } else if (value) {
+        encoded_value = estrdup(value);
+        len += value_len;
+    }
+    if (comment) {
+        len += comment_len;
+    }
+    if (commenturl) {
+        len += commenturl_len;
+    }
+    if (path) {
+        len += path_len;
+    }
+    if (domain) {
+        len += domain_len;
+    }
+    if (maxage) {
+        len += maxage_len;
+    }
+    if (portlist) {
+        len += portlist_len;
+    }
+    if (version) {
+        len += version_len;
+    }
+
+    cookie = emalloc(len + 100);
+
+    if (value && value_len == 0) {
+        snprintf(cookie, len + 100, "Set-Cookie2: %s=deleted; max-age=0; 
discard", name);
+    } else {
+        snprintf(cookie, len + 100, "Set-Cookie2: %s=%s", name, value ? 
encoded_value : "");
+    }
+
+    if (encoded_value) {
+        efree(encoded_value);
+    }
+
+    if (comment && comment_len > 0) {
+        strlcat(cookie, "; comment=", len + 100);
+        strlcat(cookie, comment, len + 100);
+    }
+    if (commenturl && commenturl_len > 0) {
+        strlcat(cookie, "; commenturl=\"", len + 100);
+        strlcat(cookie, commenturl, len + 100);
+        strlcat(cookie, "\"", len + 100);
+    }
+    if (discard) {
+        strlcat(cookie, "; discard", len + 100);
+    }
+    if (domain && domain_len > 0) {
+        strlcat(cookie, "; domain=", len + 100);
+        strlcat(cookie, domain, len + 100);
+    }
+    if (maxage && maxage_len > 0) {
+        strlcat(cookie, "; max-age=", len + 100);
+        strlcat(cookie, maxage, len + 100);
+    }
+    if (path && path_len > 0) {
+        strlcat(cookie, "; path=", len + 100);
+        strlcat(cookie, path, len + 100);
+    }
+    if (portlist && portlist_len > 0) {
+        strlcat(cookie, "; port=\"", len + 100);
+        strlcat(cookie, portlist, len + 100);
+        strlcat(cookie, "\"", len + 100);
+    }
+    if (secure) {
+        strlcat(cookie, "; secure", len + 100);
+    }
+    if (version && version_len > 0) {
+        strlcat(cookie, "; version=", len + 100);
+        strlcat(cookie, version, len + 100);
+    }
+    if (httponly) {
+        strlcat(cookie, "; httponly", len + 100);
+    }
+
+    ctr.line = cookie;
+    ctr.line_len = strlen(cookie);
+
+    result = sapi_header_op(SAPI_HEADER_ADD, &ctr TSRMLS_CC);
+    efree(cookie);
+    return result;
+}
+
 
 /* php_set_cookie(name, value, expires, path, domain, secure) */
 /* {{{ proto bool setcookie(string name [, string value [, int expires [, 
string path [, string domain [, bool secure[, bool httponly]]]]]])
@@ -169,6 +277,31 @@
 }
 /* }}} */
 
+/* {{{ proto bool setcookie2(string name [, string value [, int maxage [, bool 
discard [, string comment [, string commenturl [, string path [, string domain 
[, string portlist [, bool secure [, bool httponly [, int version=1]]]]]]]]]]])
+   Send an RFC2965 cookie2 */
+PHP_FUNCTION(setcookie2)
+{
+    char *name, *value = NULL, *maxage = NULL, *comment = NULL, *commenturl = 
NULL, *path = NULL, *domain = NULL, *portlist = NULL, *version = "1";
+    zend_bool discard = 0, secure = 0, httponly = 0;
+    int name_len, value_len = 0, maxage_len = 0, comment_len = 0, 
commenturl_len = 0, path_len = 0, domain_len = 0, portlist_len = 0, version_len;
+
+    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ssbsssssbbs", 
&name,
+                              &name_len, &value, &value_len, &maxage, 
&maxage_len, &discard, &comment, &comment_len,
+                              &commenturl, &commenturl_len, &path, &path_len, 
&domain, &domain_len, 
+                              &portlist, &portlist_len, &secure, &httponly, 
&version, &version_len) == FAILURE) {
+        return;
+    }
+
+    if (php_setcookie2(name, name_len, value, value_len, maxage, maxage_len, 
discard, comment, comment_len, commenturl, 
+                       commenturl_len, path, path_len, domain, domain_len, 
portlist, portlist_len, secure, 1, 
+                       httponly, version, version_len TSRMLS_CC) == SUCCESS) {
+        RETVAL_TRUE;
+    } else {
+        RETVAL_FALSE;
+    }
+}
+/* }}} */
+
 /* {{{ proto bool setrawcookie(string name [, string value [, int expires [, 
string path [, string domain [, bool secure[, bool httponly]]]]]])
    Send a cookie with no url encoding of the value */
 PHP_FUNCTION(setrawcookie)
Index: ext/standard/head.h
===================================================================
RCS file: /repository/php-src/ext/standard/head.h,v
retrieving revision 1.28.2.1.2.2
diff -u -w -r1.28.2.1.2.2 head.h
--- ext/standard/head.h 1 Jan 2007 09:36:08 -0000       1.28.2.1.2.2
+++ ext/standard/head.h 25 Aug 2007 05:23:07 -0000
@@ -24,11 +24,13 @@
 extern PHP_RINIT_FUNCTION(head);
 PHP_FUNCTION(header);
 PHP_FUNCTION(setcookie);
+PHP_FUNCTION(setcookie2);
 PHP_FUNCTION(setrawcookie);
 PHP_FUNCTION(headers_sent);
 PHP_FUNCTION(headers_list);
 
 PHPAPI int php_header(TSRMLS_D);
 PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, 
time_t expires, char *path, int path_len, char *domain, int domain_len, int 
secure, int url_encode, int httponly TSRMLS_DC);
+PHPAPI int php_setcookie2(char *name, int name_len, char *value, int 
value_len, char *maxage, int maxage_len, int discard, char *comment, int 
comment_len, char *commenturl, int commenturl_len, char *path, int path_len, 
char *domain, int domain_len, char *portlist, int portlist_len, int secure, int 
url_encode, int httponly, char *version, int version_len TSRMLS_DC);
 
 #endif

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to