Kill undefined variable warnings.
[lhc/web/wiklou.git] / includes / Sanitizer.php
index e2f0f27..13cde25 100644 (file)
@@ -20,7 +20,8 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
- * @addtogroup Parser
+ * @file
+ * @ingroup Parser
  */
 
 /**
@@ -327,9 +328,12 @@ $wgHtmlEntityAliases = array(
 
 /**
  * XHTML sanitizer for MediaWiki
- * @addtogroup Parser
+ * @ingroup Parser
  */
 class Sanitizer {
+       const NONE = 0;
+       const INITIAL_NONLETTER = 1;
+
        /**
         * Cleans up HTML, removes dangerous tags and attributes, and
         * removes HTML comments
@@ -380,7 +384,7 @@ class Sanitizer {
                        $htmlelements = array_merge( $htmlsingle, $htmlpairs, $htmlnest );
 
                        # Convert them all to hashtables for faster lookup
-                       $vars = array( 'htmlpairs', 'htmlsingle', 'htmlsingleonly', 'htmlnest', 'tabletags', 
+                       $vars = array( 'htmlpairs', 'htmlsingle', 'htmlsingleonly', 'htmlnest', 'tabletags',
                                'htmllist', 'listtags', 'htmlsingleallowed', 'htmlelements' );
                        foreach ( $vars as $var ) {
                                $$var = array_flip( $$var );
@@ -416,7 +420,7 @@ class Sanitizer {
                                                                $optstack = array();
                                                                array_push ($optstack, $ot);
                                                                while ( ( ( $ot = @array_pop( $tagstack ) ) != $t ) &&
-                                                                               isset( $htmlsingleallowed[$ot] ) ) 
+                                                                               isset( $htmlsingleallowed[$ot] ) )
                                                                {
                                                                        array_push ($optstack, $ot);
                                                                }
@@ -579,7 +583,7 @@ class Sanitizer {
                return Sanitizer::validateAttributes( $attribs,
                        Sanitizer::attributeWhitelist( $element ) );
        }
-       
+
        /**
         * Take an array of attribute names and values and normalize or discard
         * illegal values for the given whitelist.
@@ -621,12 +625,11 @@ class Sanitizer {
                }
                return $out;
        }
-       
+
        /**
-        * Merge two sets of HTML attributes.
-        * Conflicting items in the second set will override those
-        * in the first, except for 'class' attributes which will be
-        * combined.
+        * Merge two sets of HTML attributes.  Conflicting items in the second set
+        * will override those in the first, except for 'class' attributes which
+        * will be combined (if they're both strings).
         *
         * @todo implement merging for other attributes such as style
         * @param array $a
@@ -635,20 +638,16 @@ class Sanitizer {
         */
        static function mergeAttributes( $a, $b ) {
                $out = array_merge( $a, $b );
-               if( isset( $a['class'] )
-                       && isset( $b['class'] )
-                       && $a['class'] !== $b['class'] ) {
-                       
-                       $out['class'] = implode( ' ',
-                               array_unique(
-                                       preg_split( '/\s+/',
-                                               $a['class'] . ' ' . $b['class'],
-                                               -1,
-                                               PREG_SPLIT_NO_EMPTY ) ) );
+               if( isset( $a['class'] ) && isset( $b['class'] )
+               && is_string( $a['class'] ) && is_string( $b['class'] )
+               && $a['class'] !== $b['class'] ) {
+                       $classes = preg_split( '/\s+/', "{$a['class']} {$b['class']}",
+                               -1, PREG_SPLIT_NO_EMPTY );
+                       $out['class'] = implode( ' ', array_unique( $classes ) );
                }
                return $out;
        }
-       
+
        /**
         * Pick apart some CSS and check it for forbidden or unsafe structures.
         * Returns a sanitized string, or false if it was just too evil.
@@ -663,7 +662,7 @@ class Sanitizer {
 
                // Remove any comments; IE gets token splitting wrong
                $stripped = StringUtils::delimiterReplace( '/*', '*/', ' ', $stripped );
-               
+
                $value = $stripped;
 
                // ... and continue checks
@@ -675,7 +674,7 @@ class Sanitizer {
                        # haxx0r
                        return false;
                }
-               
+
                return $value;
        }
 
@@ -722,7 +721,7 @@ class Sanitizer {
         * @return HTML-encoded text fragment
         */
        static function encodeAttribute( $text ) {
-               $encValue = htmlspecialchars( $text );
+               $encValue = htmlspecialchars( $text, ENT_QUOTES );
 
                // Whitespace is normalized during attribute decoding,
                // so if we've been passed non-spaces we must encode them
@@ -778,20 +777,29 @@ class Sanitizer {
         *                                                          name attributes
         * @see http://www.w3.org/TR/html401/struct/links.html#h-12.2.3 Anchors with the id attribute
         *
-        * @static
-        *
-        * @param string $id
+        * @param string $id    Id to validate
+        * @param int    $flags Currently only two values: Sanitizer::INITIAL_NONLETTER
+        *                      (default) permits initial non-letter characters,
+        *                      such as if you're adding a prefix to them.
+        *                      Sanitizer::NONE will prepend an 'x' if the id
+        *                      would otherwise start with a nonletter.
         * @return string
         */
-       static function escapeId( $id ) {
+       static function escapeId( $id, $flags = Sanitizer::INITIAL_NONLETTER ) {
                static $replace = array(
                        '%3A' => ':',
                        '%' => '.'
                );
 
                $id = urlencode( Sanitizer::decodeCharReferences( strtr( $id, ' ', '_' ) ) );
+               $id = str_replace( array_keys( $replace ), array_values( $replace ), $id );
 
-               return str_replace( array_keys( $replace ), array_values( $replace ), $id );
+               if( ~$flags & Sanitizer::INITIAL_NONLETTER
+               && !preg_match( '/[a-zA-Z]/', $id[0] ) ) {
+                       // Initial character must be a letter!
+                       $id = "x$id";
+               }
+               return $id;
        }
 
        /**
@@ -813,6 +821,22 @@ class Sanitizer {
                        $class ), '_');
        }
 
+       /**
+        * Given HTML input, escape with htmlspecialchars but un-escape entites.
+        * This allows (generally harmless) entities like   to survive.
+        *
+        * @param  string $html String to escape
+        * @return string Escaped input
+        */
+       static function escapeHtmlAllowEntities( $html ) {
+               # It seems wise to escape ' as well as ", as a matter of course.  Can't
+               # hurt.
+               $html = htmlspecialchars( $html, ENT_QUOTES );
+               $html = str_replace( '&', '&', $html );
+               $html = Sanitizer::normalizeCharReferences( $html );
+               return $html;
+       }
+
        /**
         * Regex replace callback for armoring links against further processing.
         * @param array $matches
@@ -831,7 +855,7 @@ class Sanitizer {
         * @param string
         * @return array
         */
-       static function decodeTagAttributes( $text ) {
+       public static function decodeTagAttributes( $text ) {
                $attribs = array();
 
                if( trim( $text ) == '' ) {
@@ -908,7 +932,7 @@ class Sanitizer {
                        self::normalizeWhitespace(
                                Sanitizer::normalizeCharReferences( $text ) ) );
        }
-       
+
        private static function normalizeWhitespace( $text ) {
                return preg_replace(
                        '/\r\n|[\x20\x0d\x0a\x09]/',
@@ -960,8 +984,8 @@ class Sanitizer {
 
        /**
         * If the named entity is defined in the HTML 4.0/XHTML 1.0 DTD,
-        * return the named entity reference as is. If the entity is a 
-        * MediaWiki-specific alias, returns the HTML equivalent. Otherwise, 
+        * return the named entity reference as is. If the entity is a
+        * MediaWiki-specific alias, returns the HTML equivalent. Otherwise,
         * returns HTML-escaped text of pseudo-entity source (eg &foo;)
         *
         * @param string $name
@@ -1207,7 +1231,7 @@ class Sanitizer {
                        # 11.2.6
                        'td'         => array_merge( $common, $tablecell, $tablealign ),
                        'th'         => array_merge( $common, $tablecell, $tablealign ),
-                       
+
                        # 13.2
                        # Not usually allowed, but may be used for extension-style hooks
                        # such as <math> when it is rasterized
@@ -1238,7 +1262,7 @@ class Sanitizer {
                        'rb'         => $common,
                        'rt'         => $common, #array_merge( $common, array( 'rbspan' ) ),
                        'rp'         => $common,
-                       
+
                        # MathML root element, where used for extensions
                        # 'title' may not be 100% valid here; it's XHTML
                        # http://www.w3.org/TR/REC-MathML/
@@ -1288,7 +1312,7 @@ class Sanitizer {
                return $out;
        }
 
-       static function cleanUrl( $url, $hostname=true ) {
+       static function cleanUrl( $url ) {
                # Normalize any HTML entities in input. They will be
                # re-escaped by makeExternalLink().
                $url = Sanitizer::decodeCharReferences( $url );
@@ -1331,5 +1355,3 @@ class Sanitizer {
        }
 
 }
-
-