Audit tweaks: extra post checks, markup fixes.
authorBrion Vibber <brion@users.mediawiki.org>
Mon, 21 Feb 2005 01:56:50 +0000 (01:56 +0000)
committerBrion Vibber <brion@users.mediawiki.org>
Mon, 21 Feb 2005 01:56:50 +0000 (01:56 +0000)
16 files changed:
includes/Article.php
includes/DifferenceEngine.php
includes/ImagePage.php
includes/Linker.php
includes/SpecialBlockip.php
includes/SpecialContributions.php
includes/SpecialEmailuser.php
includes/SpecialIpblocklist.php
includes/SpecialLockdb.php
includes/SpecialMovepage.php
includes/SpecialPreferences.php
includes/SpecialUndelete.php
includes/SpecialUnlockdb.php
includes/SpecialUpload.php
includes/User.php
languages/Language.php

index cd97451..d869c8b 100644 (file)
@@ -1214,7 +1214,9 @@ class Article {
                        return;
                }
 
-               $confirm = $wgRequest->getBool( 'wpConfirmProtect' ) && $wgRequest->wasPosted();
+               $confirm = $wgRequest->getBool( 'wpConfirmProtect' ) &&
+                       $wgRequest->wasPosted() &&
+                       $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
                $moveonly = $wgRequest->getBool( 'wpMoveOnly' );
                $reason = $wgRequest->getText( 'wpReasonProtect' );
 
@@ -1266,7 +1268,7 @@ class Article {
         * Output protection confirmation dialog
         */
        function confirmProtect( $par, $reason, $limit = 'sysop'  ) {
-               global $wgOut;
+               global $wgOut, $wgUser;
 
                wfDebug( "Article::confirmProtect\n" );
 
@@ -1295,6 +1297,7 @@ class Article {
                }
 
                $confirm = htmlspecialchars( wfMsg( 'confirm' ) );
+               $token = htmlspecialchars( $wgUser->editToken() );
 
                $wgOut->addHTML( "
 <form id='protectconfirm' method='post' action=\"{$formaction}\">
@@ -1337,6 +1340,7 @@ class Article {
                        </td>
                </tr>
        </table>
+       <input type='hidden' name='wpEditToken' value=\"{$token}\" />
 </form>\n" );
 
                $wgOut->returnToMain( false );
@@ -1355,7 +1359,9 @@ class Article {
        function delete() {
                global $wgUser, $wgOut, $wgMessageCache, $wgRequest;
                $fname = 'Article::delete';
-               $confirm = $wgRequest->getBool( 'wpConfirm' ) && $wgRequest->wasPosted();
+               $confirm = $wgRequest->getBool( 'wpConfirm' ) &&
+                       $wgRequest->wasPosted() &&
+                       $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
                $reason = $wgRequest->getText( 'wpReason' );
 
                # This code desperately needs to be totally rewritten
@@ -1464,7 +1470,7 @@ class Article {
         * Output deletion confirmation dialog
         */
        function confirmDelete( $par, $reason ) {
-               global $wgOut;
+               global $wgOut, $wgUser;
 
                wfDebug( "Article::confirmDelete\n" );
 
@@ -1478,6 +1484,7 @@ class Article {
                $confirm = htmlspecialchars( wfMsg( 'confirm' ) );
                $check = htmlspecialchars( wfMsg( 'confirmcheck' ) );
                $delcom = htmlspecialchars( wfMsg( 'deletecomment' ) );
+               $token = htmlspecialchars( $wgUser->editToken() );
 
                $wgOut->addHTML( "
 <form id='deleteconfirm' method='post' action=\"{$formaction}\">
@@ -1508,6 +1515,7 @@ class Article {
                        </td>
                </tr>
        </table>
+       <input type='hidden' name='wpEditToken' value=\"{$token}\" />
 </form>\n" );
 
                $wgOut->returnToMain( false );
@@ -1663,6 +1671,13 @@ class Article {
                        $wgOut->readOnlyPage( $this->getContent( true ) );
                        return;
                }
+               if( !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ),
+                       array( $this->mTitle->getPrefixedText(),
+                               $wgRequest->getVal( 'from' ) )  ) ) {
+                       $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+                       $wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
+                       return;
+               }
                $dbw =& wfGetDB( DB_MASTER );
 
                # Enhanced rollback, marks edits rc_bot=1
index 7d910aa..603fadf 100644 (file)
@@ -117,7 +117,9 @@ class DifferenceEngine {
                        'target=' . urlencode($this->mNewUser) );
                if ( !$this->mNewid && $wgUser->isAllowed('rollback') ) {
                        $rollback = '&nbsp;&nbsp;&nbsp;<strong>[' . $sk->makeKnownLinkObj( $wgTitle, wfMsg( 'rollbacklink' ),
-                               'action=rollback&from=' . urlencode($this->mNewUser) ) . ']</strong>';
+                               'action=rollback&from=' . urlencode($this->mNewUser) .
+                               '&token=' . urlencode( $wgUser->editToken( array( $wgTitle->getPrefixedText(), $this->mNewUser ) ) ) ) .
+                               ']</strong>';
                } else {
                        $rollback = '';
                }
index d5cf1c0..be57183 100644 (file)
@@ -212,7 +212,11 @@ class ImagePage extends Article {
                
                # Deleting old images doesn't require confirmation
                if ( !is_null( $oldimage ) || $confirm ) {
-                       $this->doDelete();
+                       if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) {
+                               $this->doDelete();
+                       } else {
+                               $wgOut->fatalError( wfMsg( 'sessionfailure' ) );
+                       }
                        return;
                }
                
@@ -233,12 +237,19 @@ class ImagePage extends Article {
                $fname = 'ImagePage::doDelete';
 
                $reason = $wgRequest->getVal( 'wpReason' );
-               $image = $wgRequest->getVal( 'image' );
                $oldimage = $wgRequest->getVal( 'oldimage' );
                
                $dbw =& wfGetDB( DB_MASTER );
 
                if ( !is_null( $oldimage ) ) {
+                       if ( strlen( $oldimage ) < 16 ) {
+                               $wgOut->unexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
+                               return;
+                       }
+                       if ( strstr( $oldimage, "/" ) || strstr( $oldimage, "\\" ) ) {
+                               $wgOut->unexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
+                               return;
+                       }
                        # Squid purging
                        if ( $wgUseSquid ) {
                                $urlArr = Array(
@@ -250,9 +261,7 @@ class ImagePage extends Article {
                        $dbw->delete( 'oldimage', array( 'oi_archive_name' => $oldimage ) );
                        $deleted = $oldimage;
                } else {
-                       if ( is_null ( $image ) ) {
-                               $image = $this->mTitle->getDBkey();
-                       }
+                       $image = $this->mTitle->getDBkey();
                        $dest = wfImageDir( $image );
                        $archive = wfImageDir( $image );
                        
@@ -342,7 +351,7 @@ class ImagePage extends Article {
 
        function revert()
        {
-               global $wgOut, $wgRequest;
+               global $wgOut, $wgRequest, $wgUser;
                global $wgUseSquid, $wgInternalServer, $wgDeferredUpdateList;
 
                $oldimage = $wgRequest->getText( 'oldimage' );
@@ -367,6 +376,10 @@ class ImagePage extends Article {
                        $wgOut->sysopRequired();
                        return;
                }
+               if( !$wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) {
+                       $wgOut->errorpage( 'internalerror', 'sessionfailure' );
+                       return;
+               }               
                $name = substr( $oldimage, 15 );
 
                $dest = wfImageDir( $name );
@@ -450,11 +463,13 @@ class ImageHistoryList {
                } else {
                        $url = htmlspecialchars( wfImageArchiveUrl( $img ) );
                        if( $wgUser->getID() != 0 && $wgTitle->userCanEdit() ) {
+                               $token = urlencode( $wgUser->editToken( $img ) );
                                $rlink = $this->skin->makeKnownLink( $wgTitle->getPrefixedText(),
                                           wfMsg( 'revertimg' ), 'action=revert&oldimage=' .
-                                          urlencode( $img ) );
+                                          urlencode( $img ) . "&wpEditToken=$token" );
                                $dlink = $this->skin->makeKnownLink( $wgTitle->getPrefixedText(),
-                                          $del, 'action=delete&oldimage=' . urlencode( $img ) );
+                                          $del, 'action=delete&oldimage=' . urlencode( $img ) .
+                                          "&wpEditToken=$token" );
                        } else {
                                # Having live active links for non-logged in users
                                # means that bots and spiders crawling our site can
index ee36c91..b02c88b 100644 (file)
@@ -551,9 +551,7 @@ class Linker {
                $url  = $img->getViewURL();
 
                #$label = htmlspecialchars( $label );
-               $alt = preg_replace( '/<[^>]*>/', '', $label);
-               $alt = preg_replace('/&(?!:amp;|#[Xx][0-9A-fa-f]+;|#[0-9]+;|[a-zA-Z0-9]+;)/', '&amp;', $alt);
-               $alt = str_replace( array('<', '>', '"'), array('&lt;', '&gt;', '&quot;'), $alt );
+               $alt = Sanitizer::stripAllTags( $label );
 
                $width = $height = 0;
                if ( $img->exists() )
@@ -637,28 +635,36 @@ class Linker {
                return $this->makeMediaLinkObj( $nt, $alt );
        }
 
-       /** @todo document */
-       function makeMediaLinkObj( $nt, $alt = '', $nourl=false ) {             
-               if ( ! isset( $nt ) )
-               {
+       /**
+        * Create a direct link to a given uploaded file.
+        *
+        * @param Title  $title
+        * @param string $text   pre-sanitized HTML
+        * @param bool   $nourl  Mask absolute URLs, so the parser doesn't
+        *                       linkify them (it is currently not context-aware)
+        * @return string HTML
+        *
+        * @access public
+        * @todo Handle invalid or missing images better.
+        */
+       function makeMediaLinkObj( $title, $text = '', $nourl=false ) {
+               if( is_null( $title ) ) {
                        ### HOTFIX. Instead of breaking, return empty string.
-                       $s = $alt;
+                       return $text;
                } else {
-                       $name = $nt->getDBKey();        
-                       $img   = Image::newFromTitle( $nt );
-                       $url = $img->getURL();
-                       # $nourl can be set by the parser
-                       # this is a hack to mask absolute URLs, so the parser doesn't
-                       # linkify them (it is currently not context-aware)
-                       # 2004-10-25
-                       if ($nourl) { $url=str_replace("http://","http-noparse://",$url) ; }
-                       if ( empty( $alt ) ) {
-                               $alt = preg_replace( '/\.(.+?)^/', '', $name );
+                       $name = $title->getDBKey();     
+                       $img  = Image::newFromTitle( $title );
+                       $url  = $img->getURL();
+                       if( $nourl ) {
+                               $url = str_replace( "http://", "http-noparse://", $url );
+                       }
+                       $alt = htmlspecialchars( $title->getText() );
+                       if( $text == '' ) {
+                               $text = $alt;
                        }
                        $u = htmlspecialchars( $url );
-                       $s = "<a href=\"{$u}\" class='internal' title=\"{$alt}\">{$alt}</a>";                   
+                       return "<a href=\"{$u}\" class='internal' title=\"{$alt}\">{$text}</a>";                        
                }
-               return $s;
        }
 
        /** @todo document */
@@ -778,7 +784,10 @@ class Linker {
         * parameter level defines if we are on an indentation level
         */
        function tocLine( $anchor, $tocline, $tocnumber, $level ) {
-               return "\n<li class='toclevel-$level'><a href=\"#" . $anchor . '"><span class="tocnumber">' . $tocnumber . '</span> <span class="toctext">' . $tocline . '</span></a>';
+               return "\n<li class='toclevel-$level'><a href=\"#" .
+                       $anchor . '"><span class="tocnumber">' .
+                       $tocnumber . '</span> <span class="toctext">' .
+                       $tocline . '</span></a>';
        }
 
        /** @todo document */
index bed961b..1671b43 100644 (file)
@@ -19,9 +19,14 @@ function wfSpecialBlockip() {
        $ipb = new IPBlockForm();
 
        $action = $wgRequest->getVal( 'action' );
-       if ( 'success' == $action ) { $ipb->showSuccess(); }
-       else if ( $wgRequest->wasPosted() && 'submit' == $action ) { $ipb->doSubmit(); }
-       else { $ipb->showForm( '' ); }
+       if ( 'success' == $action ) {
+               $ipb->showSuccess();
+       } else if ( $wgRequest->wasPosted() && 'submit' == $action &&
+               $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+               $ipb->doSubmit();
+       } else {
+               $ipb->showForm( '' );
+       }
 }
 
 /**
@@ -66,6 +71,7 @@ class IPBlockForm {
                $scBlockAddress = htmlspecialchars( $this->BlockAddress );
                $scBlockExpiry = htmlspecialchars( $this->BlockExpiry );
                $scBlockReason = htmlspecialchars( $this->BlockReason );
+               $token = htmlspecialchars( $wgUser->editToken() );
                
                $wgOut->addHTML( "
 <form id=\"blockip\" method=\"post\" action=\"{$action}\">
@@ -106,6 +112,7 @@ class IPBlockForm {
                        </td>
                </tr>
        </table>
+       <input type='hidden' name='wpEditToken' value=\"{$token}\" />
 </form>\n" );
 
        }
index 6834568..efb4f0c 100644 (file)
@@ -187,6 +187,8 @@ function ucListEdit( $sk, $ns, $t, $ts, $topmark, $comment, $isminor, $isnew, $t
                
                if( $wgUser->isAllowed('rollback') ) {
                        $extraRollback = $wgRequest->getBool( 'bot' ) ? '&bot=1' : '';
+                       $extraRollback .= '&token=' . urlencode(
+                               $wgUser->editToken( array( $page->getPrefixedText(), $target ) ) );
                        # $target = $wgRequest->getText( 'target' );
                        $topmarktext .= ' ['. $sk->makeKnownLinkObj( $page,
                                $messages['rollbacklink'],
index 92b938e..8845e93 100644 (file)
@@ -57,9 +57,14 @@ function wfSpecialEmailuser( $par ) {
 
        $f = new EmailUserForm( $nu->getName() . " <{$address}>", $target );
 
-       if ( "success" == $action ) { $f->showSuccess(); }
-       else if ( "submit" == $action && $wgRequest->wasPosted() ) { $f->doSubmit(); }
-       else { $f->showForm(); }
+       if ( "success" == $action ) {
+               $f->showSuccess();
+       } else if ( "submit" == $action && $wgRequest->wasPosted() &&
+               $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+               $f->doSubmit();
+       } else {
+               $f->showForm();
+       }
 }
 
 /**
@@ -103,6 +108,7 @@ class EmailUserForm {
                $titleObj = Title::makeTitle( NS_SPECIAL, "Emailuser" );
                $action = $titleObj->escapeLocalURL( "target=" .
                        urlencode( $this->target ) . "&action=submit" );
+               $token = $wgUser->editToken();
 
                $wgOut->addHTML( "
 <form id=\"emailuser\" method=\"post\" action=\"{$action}\">
@@ -126,6 +132,7 @@ class EmailUserForm {
 <td>&nbsp;</td><td align='left'>
 <input type='submit' name=\"wpSend\" value=\"{$ems}\" />
 </td></tr></table>
+<input type='hidden' name='wpEditToken' value=\"$token\" />
 </form>\n" );
 
        }
index 8f1129b..c186786 100644 (file)
@@ -20,7 +20,8 @@ function wfSpecialIpblocklist() {
        if ( "success" == $action ) {
                $msg = wfMsg( "ipusuccess", htmlspecialchars( $ip ) );
                $ipu->showList( $msg );
-       } else if ( "submit" == $action && $wgRequest->wasPosted() ) {
+       } else if ( "submit" == $action && $wgRequest->wasPosted() &&
+               $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
                if ( ! $wgUser->isAllowed('block') ) {
                        $wgOut->sysopRequired();
                        return;
@@ -63,6 +64,7 @@ class IPUnblockForm {
                        $wgOut->setSubtitle( wfMsg( "formerror" ) );
                        $wgOut->addHTML( "<p class='error'>{$err}</p>\n" );
                }
+               $token = htmlspecialchars( $wgUser->editToken() );
                
                $wgOut->addHTML( "
 <form id=\"unblockip\" method=\"post\" action=\"{$action}\">
@@ -86,6 +88,7 @@ class IPUnblockForm {
                        </td>
                </tr>
        </table>
+       <input type='hidden' name='wpEditToken' value=\"{$token}\" />
 </form>\n" );
 
        }
index db07f45..bc07b04 100644 (file)
@@ -19,9 +19,14 @@ function wfSpecialLockdb()
        $action = $wgRequest->getVal( 'action' );
        $f = new DBLockForm();
 
-       if ( "success" == $action ) { $f->showSuccess(); }
-       else if ( "submit" == $action && $wgRequest->wasPosted() ) { $f->doSubmit(); }
-       else { $f->showForm( "" ); }
+       if ( "success" == $action ) {
+               $f->showSuccess();
+       } else if ( "submit" == $action && $wgRequest->wasPosted() &&
+               $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+               $f->doSubmit();
+       } else {
+               $f->showForm( "" );
+       }
 }
 
 /**
@@ -53,6 +58,7 @@ class DBLockForm {
                $elr = htmlspecialchars( wfMsg( "enterlockreason" ) );
                $titleObj = Title::makeTitle( NS_SPECIAL, "Lockdb" );
                $action = $titleObj->escapeLocalURL( "action=submit" );
+               $token = htmlspecialchars( $wgUser->editToken() );
 
                $wgOut->addHTML( <<<END
 <form id="lockdb" method="post" action="{$action}">
@@ -72,6 +78,7 @@ class DBLockForm {
                </td>
        </tr>
 </table>
+<input type="hidden" name="wpEditToken" value="{$token}" />
 </form>
 END
 );
index 89922b4..140b254 100644 (file)
@@ -29,9 +29,14 @@ function wfSpecialMovepage() {
 
        $f = new MovePageForm();
 
-       if ( 'success' == $action ) { $f->showSuccess(); }
-       else if ( 'submit' == $action && $wgRequest->wasPosted() ) { $f->doSubmit(); }
-       else { $f->showForm( '' ); }
+       if ( 'success' == $action ) {
+               $f->showSuccess();
+       } else if ( 'submit' == $action && $wgRequest->wasPosted()
+               && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+               $f->doSubmit();
+       } else {
+               $f->showForm( '' );
+       }
 }
 
 /**
@@ -82,6 +87,7 @@ class MovePageForm {
 
                $titleObj = Title::makeTitle( NS_SPECIAL, 'Movepage' );
                $action = $titleObj->escapeLocalURL( 'action=submit' );
+               $token = htmlspecialchars( $wgUser->editToken() );
 
                if ( $err != '' ) {
                        $wgOut->setSubtitle( wfMsg( 'formerror' ) );
@@ -119,6 +125,7 @@ class MovePageForm {
                        </td>
                </tr>
        </table>
+       <input type='hidden' name='wpEditToken' value=\"{$token}\" />
 </form>\n" );
 
        }
index 58036a9..e2c16f6 100644 (file)
@@ -39,7 +39,7 @@ class PreferencesForm {
         * Load some values
         */
        function PreferencesForm( &$request ) { 
-               global $wgLang, $wgContLang, $wgAllowRealName;
+               global $wgLang, $wgContLang, $wgUser, $wgAllowRealName;
                
                $this->mQuickbar = $request->getVal( 'wpQuickbar' );
                $this->mOldpass = $request->getVal( 'wpOldpass' );
@@ -67,7 +67,9 @@ class PreferencesForm {
                $this->mAction = $request->getVal( 'action' );
                $this->mReset = $request->getCheck( 'wpReset' );
                $this->mPosted = $request->wasPosted();
-               $this->mSaveprefs = $request->getCheck( 'wpSaveprefs' ) && $this->mPosted;
+               $this->mSaveprefs = $request->getCheck( 'wpSaveprefs' ) &&
+                       $this->mPosted &&
+                       $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
 
                # User toggles  (the big ugly unsorted list of checkboxes)
                $this->mToggles = array();
@@ -686,6 +688,7 @@ class PreferencesForm {
                }
                $wgOut->addHTML( "</fieldset>\n\n" );
 
+               $token = htmlspecialchars( $wgUser->editToken() );
                $wgOut->addHTML( "
        <div id='prefsubmit'>
        <div>
@@ -696,6 +699,7 @@ class PreferencesForm {
        
        </div>
        
+       <input type='hidden' name='wpEditToken' value=\"{$token}\" />
        </form>\n" );
        }
 }
index dbfe7cc..9d874d4 100644 (file)
@@ -275,10 +275,13 @@ class UndeleteForm {
        var $mTargetTimestamp;
 
        function UndeleteForm( &$request, $par = "" ) {
+               global $wgUser;
                $this->mAction = $request->getText( 'action' );
                $this->mTarget = $request->getText( 'target' );
                $this->mTimestamp = $request->getText( 'timestamp' );
-               $this->mRestore = $request->getCheck( 'restore' ) && $request->wasPosted();
+               $this->mRestore = $request->getCheck( 'restore' ) &&
+                       $request->wasPosted() &&
+                       $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
                if( $par != "" ) {
                        $this->mTarget = $par;
                }
@@ -376,11 +379,13 @@ class UndeleteForm {
                $action = $titleObj->escapeLocalURL( "action=submit" );
                $encTarget = htmlspecialchars( $this->mTarget );
                $button = htmlspecialchars( wfMsg("undeletebtn") );
+               $token = htmlspecialchars( $wgUser->editToken() );
                
                $wgOut->addHTML("
        <form id=\"undelete\" method=\"post\" action=\"{$action}\">
        <input type=\"hidden\" name=\"target\" value=\"{$encTarget}\" />
        <input type=\"submit\" name=\"restore\" value=\"{$button}\" />
+       <input type='hidden' name='wpEditToken' value=\"{$token}\" />
        ");
 
                # Show relevant lines from the deletion log:
index 222a324..9184ab0 100644 (file)
@@ -18,9 +18,14 @@ function wfSpecialUnlockdb() {
        $action = $wgRequest->getVal( 'action' );
        $f = new DBUnlockForm();
 
-       if ( "success" == $action ) { $f->showSuccess(); }
-       else if ( "submit" == $action && $wgRequest->wasPosted() ) { $f->doSubmit(); }
-       else { $f->showForm( "" ); }
+       if ( "success" == $action ) {
+               $f->showSuccess();
+       } else if ( "submit" == $action && $wgRequest->wasPosted() &&
+               $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+               $f->doSubmit();
+       } else {
+               $f->showForm( "" );
+       }
 }
 
 /**
@@ -44,6 +49,7 @@ class DBUnlockForm {
                $lb = htmlspecialchars( wfMsg( "unlockbtn" ) );
                $titleObj = Title::makeTitle( NS_SPECIAL, "Unlockdb" );
                $action = $titleObj->escapeLocalURL( "action=submit" );
+               $token = htmlspecialchars( $wgUser->editToken() );
 
                $wgOut->addHTML( <<<END
 
@@ -62,6 +68,7 @@ class DBUnlockForm {
                </td>
        </tr>
 </table>
+<input type="hidden" name="wpEditToken" value="{$token}" />
 </form>
 END
 );
index 2a9bd39..75b12a3 100644 (file)
@@ -595,7 +595,8 @@ class UploadForm {
         * @return bool
         */
        function verify( $tmpfile, $extension ) {
-               if( $this->triggersIEbug( $tmpfile ) ) {
+               if( $this->triggersIEbug( $tmpfile ) ||
+                   $this->triggersSafariBug( $tmpfile ) ) {
                        return false;
                }
                
@@ -681,10 +682,18 @@ class UploadForm {
         */
        function triggersIEbug( $filename ) {
                $file = fopen( $filename, 'rb' );
-               $chunk = strtolower( fread( $file, 200 ) );
+               $chunk = strtolower( fread( $file, 256 ) );
                fclose( $file );
                
-               $tags = array( '<html', '<head', '<body', '<script' );
+               $tags = array(
+                       '<body',
+                       '<head',
+                       '<html',
+                       '<img',
+                       '<pre',
+                       '<script',
+                       '<table',
+                       '<title' );
                foreach( $tags as $tag ) {
                        if( false !== strpos( $chunk, $tag ) ) {
                                return true;
@@ -692,5 +701,35 @@ class UploadForm {
                }
                return false;
        }
+
+       /**
+        * Apple's Safari browser performs some unsafe file type autodetection
+        * which can cause legitimate files to be interpreted as HTML if the
+        * web server is not correctly configured to send the right content-type
+        * (or if you're really uploading plain text and octet streams!)
+        *
+        * Returns true if Safari would mistake the given file for HTML
+        * when served with a generic content-type.
+        *
+        * @param string $filename
+        * @return bool
+        */
+       function triggersSafariBug( $filename ) {
+               $file = fopen( $filename, 'rb' );
+               $chunk = strtolower( fread( $file, 1024 ) );
+               fclose( $file );
+               
+               $tags = array(
+                       '<html',
+                       '<script',
+                       '<title' );
+               foreach( $tags as $tag ) {
+                       if( false !== strpos( $chunk, $tag ) ) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+       
 }
 ?>
index 27bb449..81f8130 100644 (file)
@@ -1210,15 +1210,22 @@ class User {
         * login credentials aren't being hijacked with a foreign form
         * submission.
         *
+        * @param mixed $salt - Optional function-specific data for hash.
+        *                      Use a string or an array of strings.
         * @return string
         * @access public
         */
-       function editToken() {
+       function editToken( $salt = '' ) {
                if( !isset( $_SESSION['wsEditToken'] ) ) {
                        $token = dechex( mt_rand() ) . dechex( mt_rand() );
                        $_SESSION['wsEditToken'] = $token;
+               } else {
+                       $token = $_SESSION['wsEditToken'];
+               }
+               if( is_array( $salt ) ) {
+                       $salt = implode( '|', $salt );
                }
-               return $_SESSION['wsEditToken'];
+               return md5( $token . $salt );
        }
        
        /**
@@ -1227,14 +1234,13 @@ class User {
         * user's own login session, not a form submission from a third-party
         * site.
         *
-        * @param string $val
+        * @param string $val - the input value to compare
+        * @param string $salt - Optional function-specific data for hash
         * @return bool
         * @access public
         */
-       function matchEditToken( $val ) {
-               if( !isset( $_SESSION['wsEditToken'] ) ) 
-                       return false;
-               return $_SESSION['wsEditToken'] == $val;
+       function matchEditToken( $val, $salt = '' ) {
+               return ( $val == $this->editToken( $salt ) );
        }
 }
 
index fb32735..99d33c6 100644 (file)
@@ -1300,6 +1300,9 @@ Last edit was by [[User:$3|$3]] ([[User talk:$3|Talk]]). ",
 #   only shown if there is an edit comment
 'editcomment' => "The edit comment was: \"<i>$1</i>\".",
 'revertpage'   => "Reverted edit of $2, changed back to last version by $1",
+'sessionfailure' => 'There seems to be a problem with your login session;
+this action has been canceled as a precaution against session hijacking.
+Please hit "back" and reload the page you came from, then try again.',
 'protectlogpage' => 'Protection_log',
 'protectlogtext' => "Below is a list of page locks/unlocks.
 See [[Project:Protected page]] for more information.",