(bug 8737) Fix warnings caused by incorrect use of `/dev/null` when piping process...
[lhc/web/wiklou.git] / includes / Parser.php
index 3f9b1d6..9f79d67 100644 (file)
@@ -12,7 +12,7 @@
  * changes in an incompatible way, so the parser cache
  * can automatically discard old data.
  */
-define( 'MW_PARSER_VERSION', '1.6.1' );
+define( 'MW_PARSER_VERSION', '1.6.2' );
 
 define( 'RLH_FOR_UPDATE', 1 );
 
@@ -97,7 +97,8 @@ class Parser
         * @private
         */
        # Persistent:
-       var $mTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables;
+       var $mTagHooks, $mTransparentTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables,
+               $mImageParams, $mImageParamsMagicArray;
        
        # Cleared with clearState():
        var $mOutput, $mAutonumber, $mDTopen, $mStripState;
@@ -128,6 +129,7 @@ class Parser
         */
        function Parser() {
                $this->mTagHooks = array();
+               $this->mTransparentTagHooks = array();
                $this->mFunctionHooks = array();
                $this->mFunctionSynonyms = array( 0 => array(), 1 => array() );
                $this->mFirstCall = true;
@@ -308,7 +310,7 @@ class Parser
                $fixtags = array(
                        # french spaces, last one Guillemet-left
                        # only if there is something before the space
-                       '/(.) (?=\\?|:|;|!|\\302\\273)/' => '\\1 \\2',
+                       '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1 \\2',
                        # french spaces, Guillemet-right
                        '/(\\302\\253) /' => '\\1 ',
                );
@@ -329,6 +331,26 @@ class Parser
 
                wfRunHooks( 'ParserBeforeTidy', array( &$this, &$text ) );
 
+//!JF Move to its own function
+
+               $uniq_prefix = $this->mUniqPrefix;
+                $matches = array();
+               $elements = array_keys( $this->mTransparentTagHooks );
+                $text = Parser::extractTagsAndParams( $elements, $text, $matches, $uniq_prefix );
+
+                foreach( $matches as $marker => $data ) {
+                        list( $element, $content, $params, $tag ) = $data;
+                        $tagName = strtolower( $element );
+                        if( isset( $this->mTransparentTagHooks[$tagName] ) ) {
+                                $output = call_user_func_array( $this->mTransparentTagHooks[$tagName],
+                                        array( $content, $params, $this ) );
+                        } else {
+                               $output = $tag;
+                       }
+                       $this->mStripState->general->setPair( $marker, $output );
+               }
+               $text = $this->mStripState->unstripGeneral( $text );
+
                $text = Sanitizer::normalizeCharReferences( $text );
 
                if (($wgUseTidy and $this->mOptions->mTidy) or $wgAlwaysUseTidy) {
@@ -400,12 +422,15 @@ class Parser
         * Expand templates and variables in the text, producing valid, static wikitext.
         * Also removes comments.
         */
-       function preprocess( $text, $title, $options ) {
+       function preprocess( $text, $title, $options, $revid = null ) {
                wfProfileIn( __METHOD__ );
                $this->clearState();
                $this->setOutputType( OT_PREPROCESS );
                $this->mOptions = $options;
                $this->mTitle = $title;
+               if( $revid !== null ) {
+                       $this->mRevisionId = $revid;
+               }
                wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$this->mStripState ) );
                $text = $this->strip( $text, $this->mStripState );
                wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$this->mStripState ) );
@@ -482,7 +507,7 @@ class Parser
                                $inside     = $p[4];
                        }
 
-                       $marker = "$uniq_prefix-$element-" . sprintf('%08X', $n++) . '-QINU';
+                       $marker = "$uniq_prefix-$element-" . sprintf('%08X', $n++) . "-QINU\x07";
                        $stripped .= $marker;
 
                        if ( $close === '/>' ) {
@@ -589,7 +614,8 @@ class Parser
                                        $output = Xml::escapeTagsOnly( $content );
                                        break;
                                case 'math':
-                                       $output = $wgContLang->armourMath( MathRenderer::renderMath( $content ) );
+                                       $output = $wgContLang->armourMath(
+                                               MathRenderer::renderMath( $content, $params ) );
                                        break;
                                case 'gallery':
                                        $output = $this->renderImageGallery( $content, $params );
@@ -727,7 +753,7 @@ class Parser
                $descriptorspec = array(
                        0 => array('pipe', 'r'),
                        1 => array('pipe', 'w'),
-                       2 => array('file', '/dev/null', 'a')  // FIXME: this line in UNIX-specific, it generates a warning on Windows, because /dev/null is not a valid Windows file.
+                       2 => array('file', wfGetNull(), 'a')
                );
                $pipes = array();
                $process = proc_open("$wgTidyBin -config $wgTidyConf $wgTidyOpts$opts", $descriptorspec, $pipes);
@@ -1002,7 +1028,7 @@ class Parser
                $text = strtr( $text, array( '<noinclude>' => '', '</noinclude>' => '') );
                $text = StringUtils::delimiterReplace( '<includeonly>', '</includeonly>', '', $text );
 
-               $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ) );
+               $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ), array(), array_keys( $this->mTransparentTagHooks ) );
 
                $text = $this->replaceVariables( $text, $args );
                wfRunHooks( 'InternalParseBeforeLinks', array( &$this, &$text, &$this->mStripState ) );
@@ -1799,11 +1825,15 @@ class Parser
                                $this->mOutput->addImage( $nt->getDBkey() );
                                continue;
                        } elseif( $ns == NS_SPECIAL ) {
-                               $s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix );
+                               if( SpecialPage::exists( $nt->getDBkey() ) ) {
+                                       $s .= $this->makeKnownLinkHolder( $nt, $text, '', $trail, $prefix );
+                               } else {
+                                       $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix );
+                               }
                                continue;
                        } elseif( $ns == NS_IMAGE ) {
-                               $img = new Image( $nt );
-                               if( $img->exists() ) {
+                               $img = wfFindFile( $nt );
+                               if( $img ) {
                                        // Force a blue link if the file exists; may be a remote
                                        // upload on the shared repository, and we want to see its
                                        // auto-generated page.
@@ -1920,12 +1950,18 @@ class Parser
                wfProfileIn( $fname );
                $ret = $target; # default return value is no change
 
-               # bug 7425
-               $target = trim( $target );
-
                # Some namespaces don't allow subpages,
                # so only perform processing if subpages are allowed
                if( $this->areSubpagesAllowed() ) {
+                       $hash = strpos( $target, '#' );
+                       if( $hash !== false ) {
+                               $suffix = substr( $target, $hash );
+                               $target = substr( $target, 0, $hash );
+                       } else {
+                               $suffix = '';
+                       }
+                       # bug 7425
+                       $target = trim( $target );
                        # Look at the first character
                        if( $target != '' && $target{0} == '/' ) {
                                # / at end means we don't want the slash to be shown
@@ -1937,9 +1973,9 @@ class Parser
                                        $noslash = substr( $target, 1 );
                                }
 
-                               $ret = $this->mTitle->getPrefixedText(). '/' . trim($noslash);
+                               $ret = $this->mTitle->getPrefixedText(). '/' . trim($noslash) . $suffix;
                                if( '' === $text ) {
-                                       $text = $target;
+                                       $text = $target . $suffix;
                                } # this might be changed for ugliness reasons
                        } else {
                                # check for .. subpage backlinks
@@ -1957,13 +1993,14 @@ class Parser
                                                if( substr( $nodotdot, -1, 1 ) == '/' ) {
                                                        $nodotdot = substr( $nodotdot, 0, -1 );
                                                        if( '' === $text ) {
-                                                               $text = $nodotdot;
+                                                               $text = $nodotdot . $suffix;
                                                        }
                                                }
                                                $nodotdot = trim( $nodotdot );
                                                if( $nodotdot != '' ) {
                                                        $ret .= '/' . $nodotdot;
                                                }
+                                               $ret .= $suffix;
                                        }
                                }
                        }
@@ -2409,6 +2446,8 @@ class Parser
                        $oldtz = getenv( 'TZ' );
                        putenv( 'TZ='.$wgLocaltimezone );
                }
+               
+               wfSuppressWarnings(); // E_STRICT system time bitching
                $localTimestamp = date( 'YmdHis', $ts );
                $localMonth = date( 'm', $ts );
                $localMonthName = date( 'n', $ts );
@@ -2421,20 +2460,21 @@ class Parser
                if ( isset( $wgLocaltimezone ) ) {
                        putenv( 'TZ='.$oldtz );
                }
+               wfRestoreWarnings();
 
                switch ( $index ) {
                        case 'currentmonth':
-                               return $varCache[$index] = $wgContLang->formatNum( date( 'm', $ts ) );
+                               return $varCache[$index] = $wgContLang->formatNum( gmdate( 'm', $ts ) );
                        case 'currentmonthname':
-                               return $varCache[$index] = $wgContLang->getMonthName( date( 'n', $ts ) );
+                               return $varCache[$index] = $wgContLang->getMonthName( gmdate( 'n', $ts ) );
                        case 'currentmonthnamegen':
-                               return $varCache[$index] = $wgContLang->getMonthNameGen( date( 'n', $ts ) );
+                               return $varCache[$index] = $wgContLang->getMonthNameGen( gmdate( 'n', $ts ) );
                        case 'currentmonthabbrev':
-                               return $varCache[$index] = $wgContLang->getMonthAbbreviation( date( 'n', $ts ) );
+                               return $varCache[$index] = $wgContLang->getMonthAbbreviation( gmdate( 'n', $ts ) );
                        case 'currentday':
-                               return $varCache[$index] = $wgContLang->formatNum( date( 'j', $ts ) );
+                               return $varCache[$index] = $wgContLang->formatNum( gmdate( 'j', $ts ) );
                        case 'currentday2':
-                               return $varCache[$index] = $wgContLang->formatNum( date( 'd', $ts ) );
+                               return $varCache[$index] = $wgContLang->formatNum( gmdate( 'd', $ts ) );
                        case 'localmonth':
                                return $varCache[$index] = $wgContLang->formatNum( $localMonth );
                        case 'localmonthname':
@@ -2448,25 +2488,25 @@ class Parser
                        case 'localday2':
                                return $varCache[$index] = $wgContLang->formatNum( $localDay2 );
                        case 'pagename':
-                               return $this->mTitle->getText();
+                               return wfEscapeWikiText( $this->mTitle->getText() );
                        case 'pagenamee':
                                return $this->mTitle->getPartialURL();
                        case 'fullpagename':
-                               return $this->mTitle->getPrefixedText();
+                               return wfEscapeWikiText( $this->mTitle->getPrefixedText() );
                        case 'fullpagenamee':
                                return $this->mTitle->getPrefixedURL();
                        case 'subpagename':
-                               return $this->mTitle->getSubpageText();
+                               return wfEscapeWikiText( $this->mTitle->getSubpageText() );
                        case 'subpagenamee':
                                return $this->mTitle->getSubpageUrlForm();
                        case 'basepagename':
-                               return $this->mTitle->getBaseText();
+                               return wfEscapeWikiText( $this->mTitle->getBaseText() );
                        case 'basepagenamee':
                                return wfUrlEncode( str_replace( ' ', '_', $this->mTitle->getBaseText() ) );
                        case 'talkpagename':
                                if( $this->mTitle->canTalk() ) {
                                        $talkPage = $this->mTitle->getTalkPage();
-                                       return $talkPage->getPrefixedText();
+                                       return wfEscapeWikiText( $talkPage->getPrefixedText() );
                                } else {
                                        return '';
                                }
@@ -2479,7 +2519,7 @@ class Parser
                                }
                        case 'subjectpagename':
                                $subjPage = $this->mTitle->getSubjectPage();
-                               return $subjPage->getPrefixedText();
+                               return wfEscapeWikiText( $subjPage->getPrefixedText() );
                        case 'subjectpagenamee':
                                $subjPage = $this->mTitle->getSubjectPage();
                                return $subjPage->getPrefixedUrl();
@@ -2508,19 +2548,19 @@ class Parser
                        case 'subjectspacee':
                                return( wfUrlencode( $this->mTitle->getSubjectNsText() ) );
                        case 'currentdayname':
-                               return $varCache[$index] = $wgContLang->getWeekdayName( date( 'w', $ts ) + 1 );
+                               return $varCache[$index] = $wgContLang->getWeekdayName( gmdate( 'w', $ts ) + 1 );
                        case 'currentyear':
-                               return $varCache[$index] = $wgContLang->formatNum( date( 'Y', $ts ), true );
+                               return $varCache[$index] = $wgContLang->formatNum( gmdate( 'Y', $ts ), true );
                        case 'currenttime':
                                return $varCache[$index] = $wgContLang->time( wfTimestamp( TS_MW, $ts ), false, false );
                        case 'currenthour':
-                               return $varCache[$index] = $wgContLang->formatNum( date( 'H', $ts ), true );
+                               return $varCache[$index] = $wgContLang->formatNum( gmdate( 'H', $ts ), true );
                        case 'currentweek':
                                // @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
                                // int to remove the padding
-                               return $varCache[$index] = $wgContLang->formatNum( (int)date( 'W', $ts ) );
+                               return $varCache[$index] = $wgContLang->formatNum( (int)gmdate( 'W', $ts ) );
                        case 'currentdow':
-                               return $varCache[$index] = $wgContLang->formatNum( date( 'w', $ts ) );
+                               return $varCache[$index] = $wgContLang->formatNum( gmdate( 'w', $ts ) );
                        case 'localdayname':
                                return $varCache[$index] = $wgContLang->getWeekdayName( $localDayOfWeek + 1 );
                        case 'localyear':
@@ -3260,13 +3300,25 @@ class Parser
         * Fetch the unparsed text of a template and register a reference to it.
         */
        function fetchTemplateAndtitle( $title ) {
-               $text = false;
+               $text = $skip = false;
                $finalTitle = $title;
                // Loop to fetch the article, with up to 1 redirect
                for ( $i = 0; $i < 2 && is_object( $title ); $i++ ) {
-                       $rev = Revision::newFromTitle( $title );
-                       $this->mOutput->addTemplate( $title, $title->getArticleID() );
-                       if ( $rev ) {
+                       # Give extensions a chance to select the revision instead
+                       $id = false; // Assume current
+                       wfRunHooks( 'BeforeParserFetchTemplateAndtitle', array( &$this, &$title, &$skip, &$id ) );
+                       
+                       if( $skip ) {
+                               $text = false;
+                               $this->mOutput->addTemplate( $title, $title->getArticleID(), null );
+                               break;
+                       }
+                       $rev = $id ? Revision::newFromId( $id ) : Revision::newFromTitle( $title );
+                       $rev_id = $rev ? $rev->getId() : 0;
+                       
+                       $this->mOutput->addTemplate( $title, $title->getArticleID(), $rev_id );
+                       
+                       if( $rev ) {
                                $text = $rev->getText();
                        } elseif( $title->getNamespace() == NS_MEDIAWIKI ) {
                                global $wgLang;
@@ -3386,7 +3438,13 @@ class Parser
        }
 
        /**
-        * Detect __TOC__ magic word and set a placeholder
+        * Find the first __TOC__ magic word and set a <!--MWTOC-->
+        * placeholder that will then be replaced by the real TOC in
+        * ->formatHeadings, this works because at this points real
+        * comments will have already been discarded by the sanitizer.
+        *
+        * Any additional __TOC__ magic words left over will be discarded
+        * as there can only be one TOC on the page.
         */
        function stripToc( $text ) {
                # if the string __NOTOC__ (not case-sensitive) occurs in the HTML,
@@ -3778,11 +3836,16 @@ class Parser
         * @private
         */
        function getUserSig( &$user ) {
+               global $wgMaxSigChars;
+               
                $username = $user->getName();
                $nickname = $user->getOption( 'nickname' );
                $nickname = $nickname === '' ? $username : $nickname;
-
-               if( $user->getBoolOption( 'fancysig' ) !== false ) {
+               
+               if( mb_strlen( $nickname ) > $wgMaxSigChars ) {
+                       $nickname = $username;
+                       wfDebug( __METHOD__ . ": $username has overlong signature.\n" );
+               } elseif( $user->getBoolOption( 'fancysig' ) !== false ) {
                        # Sig. might contain markup; validate this
                        if( $this->validateSig( $nickname ) !== false ) {
                                # Validated; clean up (if needed) and return it
@@ -3922,6 +3985,14 @@ class Parser
                return $oldVal;
        }
 
+       function setTransparentTagHook( $tag, $callback ) {
+               $tag = strtolower( $tag );
+               $oldVal = isset( $this->mTransparentTagHooks[$tag] ) ? $this->mTransparentTagHooks[$tag] : null;
+               $this->mTransparentTagHooks[$tag] = $callback;
+
+               return $oldVal;
+       }
+
        /**
         * Create a function, e.g. {{sum:1|2|3}}
         * The callback function should have the form:
@@ -4037,6 +4108,8 @@ class Parser
                                        $this->mOutput->addLink( $title, $id );
                                } elseif ( $linkCache->isBadLink( $pdbk ) ) {
                                        $colours[$pdbk] = 0;
+                               } elseif ( $title->getNamespace() == NS_SPECIAL && !SpecialPage::exists( $pdbk ) ) {
+                                       $colours[$pdbk] = 0;
                                } else {
                                        # Not in the link cache, add it to the query
                                        if ( !isset( $current ) ) {
@@ -4118,7 +4191,7 @@ class Parser
                                }
 
                                // process categories, check if a category exists in some variant
-                               foreach( $categories as $category){
+                               foreach( $categories as $category ){
                                        $variants = $wgContLang->convertLinkToAllVariants($category);
                                        foreach($variants as $variant){
                                                if($variant != $category){
@@ -4339,7 +4412,9 @@ class Parser
                $ig->setShowBytes( false );
                $ig->setShowFilename( false );
                $ig->setParsing();
+               $ig->setAttributes( Sanitizer::validateTagAttributes( $params, 'table' ) );
                $ig->useSkin( $this->mOptions->getSkin() );
+               $ig->mRevisionId = $this->mRevisionId;
 
                if( isset( $params['caption'] ) ) {
                        $caption = $params['caption'];
@@ -4356,6 +4431,8 @@ class Parser
                if( isset( $params['heights'] ) ) {
                        $ig->setHeights( $params['heights'] );
                }
+               
+               wfRunHooks( 'BeforeParserrenderImageGallery', array( &$this, &$ig ) );
 
                $lines = explode( "\n", $text );
                foreach ( $lines as $line ) {
@@ -4387,7 +4464,7 @@ class Parser
                        );
                        $html = $pout->getText();
 
-                       $ig->add( new Image( $nt ), $html );
+                       $ig->add( $nt, $html );
 
                        # Only add real images (bug #5586)
                        if ( $nt->getNamespace() == NS_IMAGE ) {
@@ -4397,10 +4474,50 @@ class Parser
                return $ig->toHTML();
        }
 
+       function getImageParams( $handler ) {
+               if ( $handler ) {
+                       $handlerClass = get_class( $handler );
+               } else {
+                       $handlerClass = '';
+               }
+               if ( !isset( $this->mImageParams[$handlerClass]  ) ) {
+                       // Initialise static lists
+                       static $internalParamNames = array(
+                               'horizAlign' => array( 'left', 'right', 'center', 'none' ),
+                               'vertAlign' => array( 'baseline', 'sub', 'super', 'top', 'text-top', 'middle', 
+                                       'bottom', 'text-bottom' ),
+                               'frame' => array( 'thumbnail', 'manualthumb', 'framed', 'frameless', 
+                                       'upright', 'border' ),
+                       );
+                       static $internalParamMap;
+                       if ( !$internalParamMap ) {
+                               $internalParamMap = array();
+                               foreach ( $internalParamNames as $type => $names ) {
+                                       foreach ( $names as $name ) {
+                                               $magicName = str_replace( '-', '_', "img_$name" );
+                                               $internalParamMap[$magicName] = array( $type, $name );
+                                       }
+                               }
+                       }
+
+                       // Add handler params
+                       $paramMap = $internalParamMap;
+                       if ( $handler ) {
+                               $handlerParamMap = $handler->getParamMap();
+                               foreach ( $handlerParamMap as $magic => $paramName ) {
+                                       $paramMap[$magic] = array( 'handler', $paramName );
+                               }
+                       }
+                       $this->mImageParams[$handlerClass] = $paramMap;
+                       $this->mImageParamsMagicArray[$handlerClass] = new MagicWordArray( array_keys( $paramMap ) );
+               }
+               return array( $this->mImageParams[$handlerClass], $this->mImageParamsMagicArray[$handlerClass] );
+       }
+
        /**
         * Parse image options text and use it to make an image
         */
-       function makeImage( $nt, $options ) {
+       function makeImage( $title, $options ) {
                # @TODO: let the MediaHandler specify its transform parameters
                #
                # Check if the options text is of the form "options|alt text"
@@ -4412,7 +4529,7 @@ class Parser
                #  * ___px              scale to ___ pixels width, no aligning. e.g. use in taxobox
                #  * center             center the image
                #  * framed             Keep original image size, no magnify-button.
-               #  * frameless          like 'thumbnail' but without frame, enlarge-icon and caption. User preference thumb width is used
+               #  * frameless          like 'thumb' but without a frame. Keeps user preferences for width
                #  * upright            reduce width for upright images, rounded to full __0 px
                #  * border             draw a 1px border around the image
                # vertical-align values (no % or length right now):
@@ -4424,81 +4541,55 @@ class Parser
                #  * middle
                #  * bottom
                #  * text-bottom
+               
+               $parts = array_map( 'trim', explode( '|', $options) );
+               $sk = $this->mOptions->getSkin();
 
+               # Give extensions a chance to select the file revision for us
+               $skip = $time = false;
+               wfRunHooks( 'BeforeParserMakeImageLinkObj', array( &$this, &$title, &$skip, &$time ) );
 
-               $part = array_map( 'trim', explode( '|', $options) );
-
-               $mwAlign = array();
-               $alignments = array( 'left', 'right', 'center', 'none', 'baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom' );
-               foreach ( $alignments as $alignment ) {
-                       $mwAlign[$alignment] =& MagicWord::get( 'img_'.$alignment );
+               if ( $skip ) {
+                       return $sk->makeLinkObj( $title );
                }
-               $mwThumb  =& MagicWord::get( 'img_thumbnail' );
-               $mwManualThumb =& MagicWord::get( 'img_manualthumb' );
-               $mwWidth  =& MagicWord::get( 'img_width' );
-               $mwFramed =& MagicWord::get( 'img_framed' );
-               $mwFrameless =& MagicWord::get( 'img_frameless' );
-               $mwUpright =& MagicWord::get( 'img_upright' );
-               $mwBorder =& MagicWord::get( 'img_border' );
-               $mwPage   =& MagicWord::get( 'img_page' );
-               $caption = '';
 
-               $params = array();
-               $framed = $thumb = false;
-               $frameless = false;
-               $upright = false;
-               $upright_factor = 0;
-               $border = false;
-               $manual_thumb = '' ;
-               $align = $valign = '';
-               $sk = $this->mOptions->getSkin();
+               # Get parameter map
+               $file = wfFindFile( $title, $time );
+               $handler = $file ? $file->getHandler() : false;
 
-               foreach( $part as $val ) {
-                       if ( !is_null( $mwThumb->matchVariableStartToEnd($val) ) ) {
-                               $thumb=true;
-                       } elseif ( !is_null( $match = $mwUpright->matchVariableStartToEnd( $val ) ) ) {
-                               $upright = true;
-                               $upright_factor = floatval( $match );
-                       } elseif ( !is_null( $mwBorder->matchVariableStartToEnd( $val ) ) ) {
-                               $border = true;
-                       } elseif ( !is_null( $mwFrameless->matchVariableStartToEnd( $val ) ) ) {
-                               $frameless = true;
-                       } elseif ( ! is_null( $match = $mwManualThumb->matchVariableStartToEnd($val) ) ) {
-                               # use manually specified thumbnail
-                               $thumb=true;
-                               $manual_thumb = $match;
+               list( $paramMap, $mwArray ) = $this->getImageParams( $handler );
+
+               # Process the input parameters
+               $caption = '';
+               $params = array( 'frame' => array(), 'handler' => array(), 
+                       'horizAlign' => array(), 'vertAlign' => array() );
+               foreach( $parts as $part ) {
+                       list( $magicName, $value ) = $mwArray->matchVariableStartToEnd( $part );
+                       if ( isset( $paramMap[$magicName] ) ) {
+                               list( $type, $paramName ) = $paramMap[$magicName];
+                               $params[$type][$paramName] = $value;
                        } else {
-                               foreach( $alignments as $alignment ) {
-                                       if ( ! is_null( $mwAlign[$alignment]->matchVariableStartToEnd($val) ) ) {
-                                               switch ( $alignment ) {
-                                                       case 'left': case 'right': case 'center': case 'none':
-                                                               $align = $alignment; break;
-                                                       default:
-                                                               $valign = $alignment;
-                                               }
-                                               continue 2;
-                                       }
-                               }
-                               if ( ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
-                                       # Select a page in a multipage document
-                                       $params['page'] = $match;
-                               } elseif ( !isset( $params['width'] ) && ! is_null( $match = $mwWidth->matchVariableStartToEnd($val) ) ) {
-                                       wfDebug( "img_width match: $match\n" );
-                                       # $match is the image width in pixels
-                                       $m = array();
-                                       if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $match, $m ) ) {
-                                                $params['width'] = intval( $m[1] );
-                                                $params['height'] = intval( $m[2] );
-                                       } else {
-                                               $params['width'] = intval($match);
-                                       }
-                               } elseif ( ! is_null( $mwFramed->matchVariableStartToEnd($val) ) ) {
-                                       $framed=true;
-                               } else {
-                                       $caption = $val;
+                               $caption = $part;
+                       }
+               }
+
+               # Process alignment parameters
+               if ( $params['horizAlign'] ) {
+                       $params['frame']['align'] = key( $params['horizAlign'] );
+               }
+               if ( $params['vertAlign'] ) {
+                       $params['frame']['valign'] = key( $params['vertAlign'] );
+               }
+
+               # Validate the handler parameters
+               if ( $handler ) {
+                       foreach ( $params['handler'] as $name => $value ) {
+                               if ( !$handler->validateParam( $name, $value ) ) {
+                                       unset( $params['handler'][$name] );
                                }
                        }
                }
+
                # Strip bad stuff out of the alt text
                $alt = $this->replaceLinkHoldersText( $caption );
 
@@ -4508,8 +4599,18 @@ class Parser
                $alt = $this->mStripState->unstripBoth( $alt );
                $alt = Sanitizer::stripAllTags( $alt );
 
+               $params['frame']['alt'] = $alt;
+               $params['frame']['caption'] = $caption;
+
                # Linker does the rest
-               return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $params, $framed, $thumb, $manual_thumb, $valign, $upright, $upright_factor, $border, $frameless );
+               $ret = $sk->makeImageLink2( $title, $file, $params['frame'], $params['handler'] );
+
+               # Give the handler a chance to modify the parser object
+               if ( $handler ) {
+                       $handler->parserTransformHook( $this, $file );
+               }
+
+               return $ret;
        }
 
        /**
@@ -4548,7 +4649,7 @@ class Parser
        /**#@+
         * Accessor
         */
-       function getTags() { return array_keys( $this->mTagHooks ); }
+       function getTags() { return array_merge( array_keys($this->mTransparentTagHooks), array_keys( $this->mTagHooks ) ); }
        /**#@-*/
 
 
@@ -4568,6 +4669,10 @@ class Parser
         *                for "replace", the whole page with the section replaced.
         */
        private function extractSections( $text, $section, $mode, $newtext='' ) {
+               # I.... _hope_ this is right.
+               # Otherwise, sometimes we don't have things initialized properly.
+               $this->clearState();
+               
                # strip NOWIKI etc. to avoid confusion (true-parameter causes HTML
                # comments to be stripped as well)
                $stripState = new StripState;
@@ -4585,7 +4690,7 @@ class Parser
                # now that we can be sure that no pseudo-sections are in the source,
                # split it up by section
                $uniq = preg_quote( $this->uniqPrefix(), '/' );
-               $comment = "(?:$uniq-!--.*?QINU)";
+               $comment = "(?:$uniq-!--.*?QINU\x07)";
                $secs = preg_split(
                        "/
                        (
@@ -4748,7 +4853,6 @@ class Parser
                                        : $this->mTitle->getPrefixedText();
                }
        }
-
 }
 
 /**
@@ -4801,5 +4905,3 @@ class StripState {
                return $text;
        }
 }
-
-?>