From 290f6b341d9b9a0e946edd0007c40c8aca15f9c3 Mon Sep 17 00:00:00 2001 From: Andrey Andreev Date: Wed, 5 Dec 2012 13:52:35 +0200 Subject: [PATCH 1/7] Issue 23955: setcookie() to send Max-Age attribute Signed-off-by: Andrey Andreev --- ext/session/session.c | 12 ++++++++---- ext/standard/head.c | 18 ++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/ext/session/session.c b/ext/session/session.c index 0c08d496816cf..bb8a5f72c4960 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -1153,10 +1153,11 @@ static int php_session_cache_limiter(TSRMLS_D) /* {{{ */ ********************* */ #define COOKIE_SET_COOKIE "Set-Cookie: " -#define COOKIE_EXPIRES "; expires=" -#define COOKIE_PATH "; path=" -#define COOKIE_DOMAIN "; domain=" -#define COOKIE_SECURE "; secure" +#define COOKIE_EXPIRES "; Expires=" +#define COOKIE_MAX_AGE "; Max-Age=" +#define COOKIE_PATH "; Path=" +#define COOKIE_DOMAIN "; Domain=" +#define COOKIE_SECURE "; Secure" #define COOKIE_HTTPONLY "; HttpOnly" static void php_session_send_cookie(TSRMLS_D) /* {{{ */ @@ -1201,6 +1202,9 @@ static void php_session_send_cookie(TSRMLS_D) /* {{{ */ smart_str_appends(&ncookie, COOKIE_EXPIRES); smart_str_appends(&ncookie, date_fmt); efree(date_fmt); + + smart_str_appends(&ncookie, COOKIE_MAX_AGE); + smart_str_appends(&ncookie, PS(cookie_lifetime)); } } diff --git a/ext/standard/head.c b/ext/standard/head.c index dfeb0e0f18ad4..a13bc6d503f9e 100644 --- a/ext/standard/head.c +++ b/ext/standard/head.c @@ -80,7 +80,7 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t char *dt; 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 cannot contain any of the following '=,; \\t\\r\\n\\013\\014'" ); return FAILURE; @@ -111,19 +111,19 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t cookie = emalloc(len + 100); if (value && value_len == 0) { - /* + /* * MSIE doesn't delete a cookie when you set it to a null value * so in order to force cookies to be deleted, even on MSIE, we * pick an expiry date in the past */ dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, 1, 0 TSRMLS_CC); - snprintf(cookie, len + 100, "Set-Cookie: %s=deleted; expires=%s", name, dt); + snprintf(cookie, len + 100, "Set-Cookie: %s=deleted; Expires=%s; Max-Age=0", name, dt); efree(dt); } else { snprintf(cookie, len + 100, "Set-Cookie: %s=%s", name, value ? encoded_value : ""); if (expires > 0) { const char *p; - strlcat(cookie, "; expires=", len + 100); + strlcat(cookie, "; Expires=", len + 100); dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, expires, 0 TSRMLS_CC); /* check to make sure that the year does not exceed 4 digits in length */ p = zend_memrchr(dt, '-', strlen(dt)); @@ -136,6 +136,8 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t } strlcat(cookie, dt, len + 100); efree(dt); + strlcat(cookie, "; Max-Age=", len + 100); + strlcat(cookie, expires, len + 100); } } @@ -144,18 +146,18 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t } if (path && path_len > 0) { - strlcat(cookie, "; path=", len + 100); + strlcat(cookie, "; Path=", len + 100); strlcat(cookie, path, len + 100); } if (domain && domain_len > 0) { - strlcat(cookie, "; domain=", len + 100); + strlcat(cookie, "; Domain=", len + 100); strlcat(cookie, domain, len + 100); } if (secure) { - strlcat(cookie, "; secure", len + 100); + strlcat(cookie, "; Secure", len + 100); } if (httponly) { - strlcat(cookie, "; httponly", len + 100); + strlcat(cookie, "; HttpOnly", len + 100); } ctr.line = cookie; From e1618d4306f0bf481baab81ade4650668f38b143 Mon Sep 17 00:00:00 2001 From: Andrey Andreev Date: Wed, 5 Dec 2012 14:24:00 +0200 Subject: [PATCH 2/7] setcookie() accepts a UNIX timestamp, not TS delta --- ext/standard/head.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ext/standard/head.c b/ext/standard/head.c index a13bc6d503f9e..2f403efa60d02 100644 --- a/ext/standard/head.c +++ b/ext/standard/head.c @@ -123,6 +123,7 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t snprintf(cookie, len + 100, "Set-Cookie: %s=%s", name, value ? encoded_value : ""); if (expires > 0) { const char *p; + int tsdelta; strlcat(cookie, "; Expires=", len + 100); dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, expires, 0 TSRMLS_CC); /* check to make sure that the year does not exceed 4 digits in length */ @@ -136,8 +137,13 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t } strlcat(cookie, dt, len + 100); efree(dt); - strlcat(cookie, "; Max-Age=", len + 100); - strlcat(cookie, expires, len + 100); + + tsdelta = expires - (long)time(NULL); + if (tsdelta && tsdelta > 0) { + strlcat(cookie, "; Max-Age=", len + 100); + strlcat(cookie, expires, len + 100); + } + efree(tsdelta); } } From 7917c9ba94a9524f398d16802e517316ef2a0490 Mon Sep 17 00:00:00 2001 From: Andrey Andreev Date: Thu, 6 Dec 2012 11:31:25 +0200 Subject: [PATCH 3/7] Another two fixes --- ext/session/session.c | 2 +- ext/standard/head.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/session/session.c b/ext/session/session.c index bb8a5f72c4960..c3e4b57f9fd56 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -1204,7 +1204,7 @@ static void php_session_send_cookie(TSRMLS_D) /* {{{ */ efree(date_fmt); smart_str_appends(&ncookie, COOKIE_MAX_AGE); - smart_str_appends(&ncookie, PS(cookie_lifetime)); + smart_str_appends(&ncookie, (char) PS(cookie_lifetime)); } } diff --git a/ext/standard/head.c b/ext/standard/head.c index 2f403efa60d02..eac9325731681 100644 --- a/ext/standard/head.c +++ b/ext/standard/head.c @@ -141,7 +141,7 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t tsdelta = expires - (long)time(NULL); if (tsdelta && tsdelta > 0) { strlcat(cookie, "; Max-Age=", len + 100); - strlcat(cookie, expires, len + 100); + strlcat(cookie, (char) tsdelta, len + 100); } efree(tsdelta); } From 0907512172e4781943aff4329c770ae7ae72a639 Mon Sep 17 00:00:00 2001 From: Andrey Andreev Date: Thu, 6 Dec 2012 11:37:53 +0200 Subject: [PATCH 4/7] Allow negative values --- ext/standard/head.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ext/standard/head.c b/ext/standard/head.c index eac9325731681..bf5d5aa13ad4a 100644 --- a/ext/standard/head.c +++ b/ext/standard/head.c @@ -139,10 +139,8 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t efree(dt); tsdelta = expires - (long)time(NULL); - if (tsdelta && tsdelta > 0) { - strlcat(cookie, "; Max-Age=", len + 100); - strlcat(cookie, (char) tsdelta, len + 100); - } + strlcat(cookie, "; Max-Age=", len + 100); + strlcat(cookie, (char) tsdelta, len + 100); efree(tsdelta); } } From 07fb196a6d4bb0e2ba5f4727996c5fb0ec2734ec Mon Sep 17 00:00:00 2001 From: Andrey Andreev Date: Fri, 7 Dec 2012 14:22:17 +0200 Subject: [PATCH 5/7] Really fix and test the code --- ext/session/session.c | 4 +- ext/standard/head.c | 7 +-- ext/standard/tests/network/setcookie.phpt | 70 +++++++++++++++++++++++ 3 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 ext/standard/tests/network/setcookie.phpt diff --git a/ext/session/session.c b/ext/session/session.c index c3e4b57f9fd56..57f1b48a41df2 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -1203,8 +1203,10 @@ static void php_session_send_cookie(TSRMLS_D) /* {{{ */ smart_str_appends(&ncookie, date_fmt); efree(date_fmt); + char maxage[12]; + sprintf(maxage, "%li", PS(cookie_lifetime)); smart_str_appends(&ncookie, COOKIE_MAX_AGE); - smart_str_appends(&ncookie, (char) PS(cookie_lifetime)); + smart_str_appends(&ncookie, maxage); } } diff --git a/ext/standard/head.c b/ext/standard/head.c index bf5d5aa13ad4a..fdc8852af91b0 100644 --- a/ext/standard/head.c +++ b/ext/standard/head.c @@ -123,7 +123,6 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t snprintf(cookie, len + 100, "Set-Cookie: %s=%s", name, value ? encoded_value : ""); if (expires > 0) { const char *p; - int tsdelta; strlcat(cookie, "; Expires=", len + 100); dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, expires, 0 TSRMLS_CC); /* check to make sure that the year does not exceed 4 digits in length */ @@ -138,10 +137,10 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t strlcat(cookie, dt, len + 100); efree(dt); - tsdelta = expires - (long)time(NULL); + char tsdelta[12]; + sprintf(tsdelta, "%li", (long) difftime(expires, time(NULL))); strlcat(cookie, "; Max-Age=", len + 100); - strlcat(cookie, (char) tsdelta, len + 100); - efree(tsdelta); + strlcat(cookie, tsdelta, len + 100); } } diff --git a/ext/standard/tests/network/setcookie.phpt b/ext/standard/tests/network/setcookie.phpt new file mode 100644 index 0000000000000..b742682456f37 --- /dev/null +++ b/ext/standard/tests/network/setcookie.phpt @@ -0,0 +1,70 @@ +--TEST-- +setcookie() tests +--DESCRIPTION-- +--INI-- +date.timezone=UTC +--FILE-- + count($headers)) +{ + echo "Less headers are being sent than expected - aborting"; + return; +} + +do +{ + if (strncmp(current($headers), 'Set-Cookie:', 11) !== 0) + { + continue; + } + + if (current($headers) === current($expected)) + { + $i--; + } + else + { + echo "Header mismatch:\n\tExpected: " + .current($expected) + ."\n\tReceived: ".current($headers)."\n"; + } + + next($expected); +} +while (next($headers) !== FALSE); + +echo ($i === 0) + ? 'OK' + : 'A total of '.$i.' errors found.'; +--EXPECTHEADERS-- + +--EXPECT-- +OK \ No newline at end of file From 34b574acd1b41586b07b228d8bf8fb3905e924f3 Mon Sep 17 00:00:00 2001 From: Andrey Andreev Date: Fri, 28 Dec 2012 17:54:16 +0200 Subject: [PATCH 6/7] Apply improvements suggested by @smalishev, @KalleZ --- ext/session/session.c | 4 +--- ext/standard/head.c | 12 ++++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/ext/session/session.c b/ext/session/session.c index 57f1b48a41df2..d480a47f17da2 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -1203,10 +1203,8 @@ static void php_session_send_cookie(TSRMLS_D) /* {{{ */ smart_str_appends(&ncookie, date_fmt); efree(date_fmt); - char maxage[12]; - sprintf(maxage, "%li", PS(cookie_lifetime)); smart_str_appends(&ncookie, COOKIE_MAX_AGE); - smart_str_appends(&ncookie, maxage); + smart_str_append_long(&ncookie, PS(cookie_lifetime)); } } diff --git a/ext/standard/head.c b/ext/standard/head.c index fdc8852af91b0..8d1acb4b7c08b 100644 --- a/ext/standard/head.c +++ b/ext/standard/head.c @@ -40,11 +40,11 @@ PHP_FUNCTION(header) { zend_bool rep = 1; sapi_header_line ctr = {0}; - + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bl", &ctr.line, &ctr.line_len, &rep, &ctr.response_code) == FAILURE) return; - + sapi_header_op(rep ? SAPI_HEADER_REPLACE:SAPI_HEADER_ADD, &ctr TSRMLS_CC); } /* }}} */ @@ -123,6 +123,7 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t snprintf(cookie, len + 100, "Set-Cookie: %s=%s", name, value ? encoded_value : ""); if (expires > 0) { const char *p; + char tsdelta[13]; strlcat(cookie, "; Expires=", len + 100); dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, expires, 0 TSRMLS_CC); /* check to make sure that the year does not exceed 4 digits in length */ @@ -137,8 +138,7 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t strlcat(cookie, dt, len + 100); efree(dt); - char tsdelta[12]; - sprintf(tsdelta, "%li", (long) difftime(expires, time(NULL))); + snprintf(tsdelta, sizeof(tsdelta), "%li", (long) difftime(expires, time(NULL))); strlcat(cookie, "; Max-Age=", len + 100); strlcat(cookie, tsdelta, len + 100); } @@ -242,11 +242,11 @@ PHP_FUNCTION(headers_sent) ZVAL_LONG(arg2, line); case 1: zval_dtor(arg1); - if (file) { + if (file) { ZVAL_STRING(arg1, file, 1); } else { ZVAL_STRING(arg1, "", 1); - } + } break; } From 9f0d8c317a751c9e8fdb70f3db7ae23d0ef17374 Mon Sep 17 00:00:00 2001 From: Andrey Andreev Date: Sat, 29 Dec 2012 23:11:35 +0200 Subject: [PATCH 7/7] Revert attributes case-changes --- ext/session/session.c | 8 ++++---- ext/standard/head.c | 12 ++++++------ ext/standard/tests/network/setcookie.phpt | 14 +++++++------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ext/session/session.c b/ext/session/session.c index d480a47f17da2..e7197328b1f02 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -1153,11 +1153,11 @@ static int php_session_cache_limiter(TSRMLS_D) /* {{{ */ ********************* */ #define COOKIE_SET_COOKIE "Set-Cookie: " -#define COOKIE_EXPIRES "; Expires=" +#define COOKIE_EXPIRES "; expires=" #define COOKIE_MAX_AGE "; Max-Age=" -#define COOKIE_PATH "; Path=" -#define COOKIE_DOMAIN "; Domain=" -#define COOKIE_SECURE "; Secure" +#define COOKIE_PATH "; path=" +#define COOKIE_DOMAIN "; domain=" +#define COOKIE_SECURE "; secure" #define COOKIE_HTTPONLY "; HttpOnly" static void php_session_send_cookie(TSRMLS_D) /* {{{ */ diff --git a/ext/standard/head.c b/ext/standard/head.c index 8d1acb4b7c08b..dea7b8f3e32c6 100644 --- a/ext/standard/head.c +++ b/ext/standard/head.c @@ -117,14 +117,14 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t * pick an expiry date in the past */ dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, 1, 0 TSRMLS_CC); - snprintf(cookie, len + 100, "Set-Cookie: %s=deleted; Expires=%s; Max-Age=0", name, dt); + snprintf(cookie, len + 100, "Set-Cookie: %s=deleted; expires=%s; Max-Age=0", name, dt); efree(dt); } else { snprintf(cookie, len + 100, "Set-Cookie: %s=%s", name, value ? encoded_value : ""); if (expires > 0) { const char *p; char tsdelta[13]; - strlcat(cookie, "; Expires=", len + 100); + strlcat(cookie, "; expires=", len + 100); dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, expires, 0 TSRMLS_CC); /* check to make sure that the year does not exceed 4 digits in length */ p = zend_memrchr(dt, '-', strlen(dt)); @@ -149,18 +149,18 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t } if (path && path_len > 0) { - strlcat(cookie, "; Path=", len + 100); + strlcat(cookie, "; path=", len + 100); strlcat(cookie, path, len + 100); } if (domain && domain_len > 0) { - strlcat(cookie, "; Domain=", len + 100); + strlcat(cookie, "; domain=", len + 100); strlcat(cookie, domain, len + 100); } if (secure) { - strlcat(cookie, "; Secure", len + 100); + strlcat(cookie, "; secure", len + 100); } if (httponly) { - strlcat(cookie, "; HttpOnly", len + 100); + strlcat(cookie, "; httponly", len + 100); } ctr.line = cookie; diff --git a/ext/standard/tests/network/setcookie.phpt b/ext/standard/tests/network/setcookie.phpt index b742682456f37..bf04ec78de13c 100644 --- a/ext/standard/tests/network/setcookie.phpt +++ b/ext/standard/tests/network/setcookie.phpt @@ -23,13 +23,13 @@ $expected = array( 'Set-Cookie: name=value', 'Set-Cookie: name=space+value', 'Set-Cookie: name=value', - 'Set-Cookie: name=value; Expires='.date('D, d-M-Y H:i:s', $tsp).' GMT; Max-Age=5', - 'Set-Cookie: name=value; Expires='.date('D, d-M-Y H:i:s', $tsn).' GMT; Max-Age=-6', - 'Set-Cookie: name=value; Expires='.date('D, d-M-Y H:i:s', $tsc).' GMT; Max-Age=0', - 'Set-Cookie: name=value; Path=/path/', - 'Set-Cookie: name=value; Domain=domain.tld', - 'Set-Cookie: name=value; Secure', - 'Set-Cookie: name=value; HttpOnly' + 'Set-Cookie: name=value; expires='.date('D, d-M-Y H:i:s', $tsp).' GMT; Max-Age=5', + 'Set-Cookie: name=value; expires='.date('D, d-M-Y H:i:s', $tsn).' GMT; Max-Age=-6', + 'Set-Cookie: name=value; expires='.date('D, d-M-Y H:i:s', $tsc).' GMT; Max-Age=0', + 'Set-Cookie: name=value; path=/path/', + 'Set-Cookie: name=value; domain=domain.tld', + 'Set-Cookie: name=value; secure', + 'Set-Cookie: name=value; httponly' ); $headers = headers_list();