Fix bug discovered in r77171 from the user data in If-Modified-Since passed by resour...
authorPlatonides <platonides@users.mediawiki.org>
Sun, 28 Nov 2010 21:59:16 +0000 (21:59 +0000)
committerPlatonides <platonides@users.mediawiki.org>
Sun, 28 Nov 2010 21:59:16 +0000 (21:59 +0000)
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
maintenance/tests/phpunit/includes/GlobalTest.php

index e79363a..07af801 100644 (file)
@@ -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 ) {
index a4aa2e3..43eb28b 100644 (file)
@@ -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(
                        '' => '',