Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:31905 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 87762 invoked by uid 1010); 25 Aug 2007 06:35:48 -0000 Delivered-To: ezmlm-scan-internals@lists.php.net Delivered-To: ezmlm-internals@lists.php.net Received: (qmail 87746 invoked from network); 25 Aug 2007 06:35:48 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 25 Aug 2007 06:35:48 -0000 X-Host-Fingerprint: 76.97.7.151 unknown Received: from [76.97.7.151] ([76.97.7.151:9064] helo=localhost.localdomain) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id CF/D3-60759-1CDCFC64 for ; Sat, 25 Aug 2007 02:35:47 -0400 Message-ID: To: internals@lists.php.net Date: Sat, 25 Aug 2007 02:35:31 -0400 User-Agent: Thunderbird 2.0.0.0 (Macintosh/20070326) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------040501010700050506010807" X-Posted-By: 76.97.7.151 Subject: [PATCH] Addition of setcookie2() to support RFC 2965 From: ramsey@php.net (Ben Ramsey) --------------040501010700050506010807 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit 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/ --------------040501010700050506010807 Content-Type: text/plain; x-mac-type="0"; x-mac-creator="0"; name="setcookie2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="setcookie2.patch" 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 --------------040501010700050506010807--