From d9fce2a683f6f46b07e81d7781190db18be84d91 Mon Sep 17 00:00:00 2001 From: Platonides Date: Sun, 28 Nov 2010 21:59:16 +0000 Subject: [PATCH] Fix bug discovered in r77171 from the user data in If-Modified-Since passed by resource loader. Added in wfTimestamp reading support for the three http date formats. Increased conformance reading rfc2822 dates (read support added in r71750/r71751). We may not want full compliance with rfc2822, though. The only provider of rfc2822 dates is probably http and that uses a subset (it's rfc822 + 4-digit years from rfc1123). Make wfTimestamp() return false in case of a wrong input, according to CR. --- includes/GlobalFunctions.php | 30 ++++++-- .../tests/phpunit/includes/GlobalTest.php | 74 +++++++++++++++++++ 2 files changed, 98 insertions(+), 6 deletions(-) diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index e79363a10d..07af8019c6 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -1981,7 +1981,7 @@ define( 'TS_ISO_8601_BASIC', 9 ); * function will autodetect which format is supplied and act * accordingly. * @param $ts Mixed: the timestamp to convert or 0 for the current timestamp - * @return String: in the format specified in $outputtype + * @return Mixed: String / false The same date in the format specified in $outputtype or false */ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { $uts = 0; @@ -2014,14 +2014,23 @@ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { # TS_POSTGRES } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.\d\d\d$/',$ts,$da)) { # TS_DB2 - } elseif ( preg_match( '/^[A-Z][a-z]{2}, \d\d [A-Z][a-z]{2} \d{4} \d\d:\d\d:\d\d/', $ts ) ) { - # TS_RFC2822 + } elseif ( preg_match( '/^[ \t\r\n]*([A-Z][a-z]{2},[ \t\r\n]*)?' . # Day of week + '\d\d?[ \t\r\n]*[A-Z][a-z]{2}[ \t\r\n]*\d{2}(?:\d{2})?' . # dd Mon yyyy + '[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d/S', $ts ) ) { # hh:mm:ss + # TS_RFC2822, accepting a trailing comment. See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html / r77171 + # The regex is a superset of rfc2822 for readability + $strtime = strtok( $ts, ';' ); + } elseif ( preg_match( '/^[A-Z][a-z]{5,8}, \d\d-[A-Z][a-z]{2}-\d{2} \d\d:\d\d:\d\d/', $ts ) ) { + # TS_RFC850 + $strtime = $ts; + } elseif ( preg_match( '/^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d{1,2} \d\d:\d\d:\d\d \d{4}/', $ts ) ) { + # asctime $strtime = $ts; } else { # Bogus value; fall back to the epoch... wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n"); - return "Invalid date"; + return null; } @@ -2050,9 +2059,17 @@ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { (int)$da[4], (int)$da[5], (int)$da[6]); $d = date_create( $ds, new DateTimeZone( 'GMT' ) ); - } else { + } elseif ( $strtime ) { $d = date_create( $strtime, new DateTimeZone( 'GMT' ) ); + } else { + return false; } + + if ( !$d ) { + wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n"); + return false; + } + $output = $d->format( $formats[$outputtype] ); } else { if ( count( $da ) ) { @@ -2065,7 +2082,8 @@ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { } if ( $uts === false ) { - return "Can't handle date"; + wfDebug("wfTimestamp() can't parse the timestamp (non 32-bit time? Update php): $outputtype; $ts\n"); + return false; } if ( TS_UNIX == $outputtype ) { diff --git a/maintenance/tests/phpunit/includes/GlobalTest.php b/maintenance/tests/phpunit/includes/GlobalTest.php index a4aa2e30ec..43eb28bf4c 100644 --- a/maintenance/tests/phpunit/includes/GlobalTest.php +++ b/maintenance/tests/phpunit/includes/GlobalTest.php @@ -195,6 +195,51 @@ class GlobalTest extends PHPUnit_Framework_TestCase { '20010115T123456Z', wfTimestamp( TS_ISO_8601_BASIC, '2001-01-15 12:34:56' ), 'TS_DB to TS_ISO_8601_BASIC' ); + + # rfc2822 section 3.3 + + $this->assertEquals( + 'Mon, 15 Jan 2001 12:34:56 GMT', + wfTimestamp( TS_RFC2822, '20010115123456' ), + 'TS_MW to TS_RFC2822' ); + + $this->assertEquals( + '20010115123456', + wfTimestamp( TS_MW, 'Mon, 15 Jan 2001 12:34:56 GMT' ), + 'TS_RFC2822 to TS_MW' ); + + $this->assertEquals( + '20010115123456', + wfTimestamp( TS_MW, ' Mon, 15 Jan 2001 12:34:56 GMT' ), + 'TS_RFC2822 with leading space to TS_MW' ); + + $this->assertEquals( + '20010115123456', + wfTimestamp( TS_MW, '15 Jan 2001 12:34:56 GMT' ), + 'TS_RFC2822 without optional day-of-week to TS_MW' ); + + # FWS = ([*WSP CRLF] 1*WSP) / obs-FWS ; Folding white space + # obs-FWS = 1*WSP *(CRLF 1*WSP) ; Section 4.2 + $this->assertEquals( + '20010115123456', + wfTimestamp( TS_MW, 'Mon, 15 Jan 2001 12:34:56 GMT' ), + 'TS_RFC2822 to TS_MW' ); + + # WSP = SP / HTAB ; rfc2234 + $this->assertEquals( + '20010115123456', + wfTimestamp( TS_MW, "Mon, 15 Jan\x092001 12:34:56 GMT" ), + 'TS_RFC2822 with HTAB to TS_MW' ); + + $this->assertEquals( + '20010115123456', + wfTimestamp( TS_MW, "Mon, 15 Jan\x09 \x09 2001 12:34:56 GMT" ), + 'TS_RFC2822 with HTAB and SP to TS_MW' ); + + $this->assertEquals( + '19941106084937', + wfTimestamp( TS_MW, "Sun, 6 Nov 94 08:49:37 GMT" ), + 'TS_RFC2822 with obsolete year to TS_MW' ); } /** @@ -274,6 +319,35 @@ class GlobalTest extends PHPUnit_Framework_TestCase { 'ISO 8601:2004 [[year 0]], also called [[1 BC]]'); } + function testHttpDate() { + # The Resource Loader uses wfTimestamp() to convert timestamps + # from If-Modified-Since header. + # Thus it must be able to parse all rfc2616 date formats + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1 + + $this->assertEquals( + '19941106084937', + wfTimestamp( TS_MW, 'Sun, 06 Nov 1994 08:49:37 GMT' ), + 'RFC 822 date' ); + + $this->assertEquals( + '19941106084937', + wfTimestamp( TS_MW, 'Sunday, 06-Nov-94 08:49:37 GMT' ), + 'RFC 850 date' ); + + $this->assertEquals( + '19941106084937', + wfTimestamp( TS_MW, 'Sun Nov 6 08:49:37 1994' ), + "ANSI C's asctime() format" ); + + // See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html and r77171 + $this->assertEquals( + '20101122141242', + wfTimestamp( TS_MW, 'Mon, 22 Nov 2010 14:12:42 GMT; length=52626' ), + "Netscape extension to HTTP/1.0" ); + + } + function testBasename() { $sets = array( '' => '', -- 2.20.1