File indexing completed on 2024-12-22 05:36:21

0001 <?php
0002 
0003 /**
0004  * Validates the attributes of a token. Doesn't manage required attributes
0005  * very well. The only reason we factored this out was because RemoveForeignElements
0006  * also needed it besides ValidateAttributes.
0007  */
0008 class HTMLPurifier_AttrValidator
0009 {
0010 
0011     /**
0012      * Validates the attributes of a token, mutating it as necessary.
0013      * that has valid tokens
0014      * @param HTMLPurifier_Token $token Token to validate.
0015      * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
0016      * @param HTMLPurifier_Context $context Instance of HTMLPurifier_Context
0017      */
0018     public function validateToken($token, $config, $context)
0019     {
0020         $definition = $config->getHTMLDefinition();
0021         $e =& $context->get('ErrorCollector', true);
0022 
0023         // initialize IDAccumulator if necessary
0024         $ok =& $context->get('IDAccumulator', true);
0025         if (!$ok) {
0026             $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
0027             $context->register('IDAccumulator', $id_accumulator);
0028         }
0029 
0030         // initialize CurrentToken if necessary
0031         $current_token =& $context->get('CurrentToken', true);
0032         if (!$current_token) {
0033             $context->register('CurrentToken', $token);
0034         }
0035 
0036         if (!$token instanceof HTMLPurifier_Token_Start &&
0037             !$token instanceof HTMLPurifier_Token_Empty
0038         ) {
0039             return;
0040         }
0041 
0042         // create alias to global definition array, see also $defs
0043         // DEFINITION CALL
0044         $d_defs = $definition->info_global_attr;
0045 
0046         // don't update token until the very end, to ensure an atomic update
0047         $attr = $token->attr;
0048 
0049         // do global transformations (pre)
0050         // nothing currently utilizes this
0051         foreach ($definition->info_attr_transform_pre as $transform) {
0052             $attr = $transform->transform($o = $attr, $config, $context);
0053             if ($e) {
0054                 if ($attr != $o) {
0055                     $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
0056                 }
0057             }
0058         }
0059 
0060         // do local transformations only applicable to this element (pre)
0061         // ex. <p align="right"> to <p style="text-align:right;">
0062         foreach ($definition->info[$token->name]->attr_transform_pre as $transform) {
0063             $attr = $transform->transform($o = $attr, $config, $context);
0064             if ($e) {
0065                 if ($attr != $o) {
0066                     $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
0067                 }
0068             }
0069         }
0070 
0071         // create alias to this element's attribute definition array, see
0072         // also $d_defs (global attribute definition array)
0073         // DEFINITION CALL
0074         $defs = $definition->info[$token->name]->attr;
0075 
0076         $attr_key = false;
0077         $context->register('CurrentAttr', $attr_key);
0078 
0079         // iterate through all the attribute keypairs
0080         // Watch out for name collisions: $key has previously been used
0081         foreach ($attr as $attr_key => $value) {
0082 
0083             // call the definition
0084             if (isset($defs[$attr_key])) {
0085                 // there is a local definition defined
0086                 if ($defs[$attr_key] === false) {
0087                     // We've explicitly been told not to allow this element.
0088                     // This is usually when there's a global definition
0089                     // that must be overridden.
0090                     // Theoretically speaking, we could have a
0091                     // AttrDef_DenyAll, but this is faster!
0092                     $result = false;
0093                 } else {
0094                     // validate according to the element's definition
0095                     $result = $defs[$attr_key]->validate(
0096                         $value,
0097                         $config,
0098                         $context
0099                     );
0100                 }
0101             } elseif (isset($d_defs[$attr_key])) {
0102                 // there is a global definition defined, validate according
0103                 // to the global definition
0104                 $result = $d_defs[$attr_key]->validate(
0105                     $value,
0106                     $config,
0107                     $context
0108                 );
0109             } else {
0110                 // system never heard of the attribute? DELETE!
0111                 $result = false;
0112             }
0113 
0114             // put the results into effect
0115             if ($result === false || $result === null) {
0116                 // this is a generic error message that should replaced
0117                 // with more specific ones when possible
0118                 if ($e) {
0119                     $e->send(E_ERROR, 'AttrValidator: Attribute removed');
0120                 }
0121 
0122                 // remove the attribute
0123                 unset($attr[$attr_key]);
0124             } elseif (is_string($result)) {
0125                 // generally, if a substitution is happening, there
0126                 // was some sort of implicit correction going on. We'll
0127                 // delegate it to the attribute classes to say exactly what.
0128 
0129                 // simple substitution
0130                 $attr[$attr_key] = $result;
0131             } else {
0132                 // nothing happens
0133             }
0134 
0135             // we'd also want slightly more complicated substitution
0136             // involving an array as the return value,
0137             // although we're not sure how colliding attributes would
0138             // resolve (certain ones would be completely overriden,
0139             // others would prepend themselves).
0140         }
0141 
0142         $context->destroy('CurrentAttr');
0143 
0144         // post transforms
0145 
0146         // global (error reporting untested)
0147         foreach ($definition->info_attr_transform_post as $transform) {
0148             $attr = $transform->transform($o = $attr, $config, $context);
0149             if ($e) {
0150                 if ($attr != $o) {
0151                     $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
0152                 }
0153             }
0154         }
0155 
0156         // local (error reporting untested)
0157         foreach ($definition->info[$token->name]->attr_transform_post as $transform) {
0158             $attr = $transform->transform($o = $attr, $config, $context);
0159             if ($e) {
0160                 if ($attr != $o) {
0161                     $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
0162                 }
0163             }
0164         }
0165 
0166         $token->attr = $attr;
0167 
0168         // destroy CurrentToken if we made it ourselves
0169         if (!$current_token) {
0170             $context->destroy('CurrentToken');
0171         }
0172 
0173     }
0174 
0175 
0176 }
0177 
0178 // vim: et sw=4 sts=4