You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

179 lines
6.4KB

  1. <?php
  2. /**
  3. * Validates the attributes of a token. Doesn't manage required attributes
  4. * very well. The only reason we factored this out was because RemoveForeignElements
  5. * also needed it besides ValidateAttributes.
  6. */
  7. class HTMLPurifier_AttrValidator
  8. {
  9. /**
  10. * Validates the attributes of a token, mutating it as necessary.
  11. * that has valid tokens
  12. * @param HTMLPurifier_Token $token Token to validate.
  13. * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
  14. * @param HTMLPurifier_Context $context Instance of HTMLPurifier_Context
  15. */
  16. public function validateToken($token, $config, $context)
  17. {
  18. $definition = $config->getHTMLDefinition();
  19. $e =& $context->get('ErrorCollector', true);
  20. // initialize IDAccumulator if necessary
  21. $ok =& $context->get('IDAccumulator', true);
  22. if (!$ok) {
  23. $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
  24. $context->register('IDAccumulator', $id_accumulator);
  25. }
  26. // initialize CurrentToken if necessary
  27. $current_token =& $context->get('CurrentToken', true);
  28. if (!$current_token) {
  29. $context->register('CurrentToken', $token);
  30. }
  31. if (!$token instanceof HTMLPurifier_Token_Start &&
  32. !$token instanceof HTMLPurifier_Token_Empty
  33. ) {
  34. return;
  35. }
  36. // create alias to global definition array, see also $defs
  37. // DEFINITION CALL
  38. $d_defs = $definition->info_global_attr;
  39. // don't update token until the very end, to ensure an atomic update
  40. $attr = $token->attr;
  41. // do global transformations (pre)
  42. // nothing currently utilizes this
  43. foreach ($definition->info_attr_transform_pre as $transform) {
  44. $attr = $transform->transform($o = $attr, $config, $context);
  45. if ($e) {
  46. if ($attr != $o) {
  47. $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
  48. }
  49. }
  50. }
  51. // do local transformations only applicable to this element (pre)
  52. // ex. <p align="right"> to <p style="text-align:right;">
  53. foreach ($definition->info[$token->name]->attr_transform_pre as $transform) {
  54. $attr = $transform->transform($o = $attr, $config, $context);
  55. if ($e) {
  56. if ($attr != $o) {
  57. $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
  58. }
  59. }
  60. }
  61. // create alias to this element's attribute definition array, see
  62. // also $d_defs (global attribute definition array)
  63. // DEFINITION CALL
  64. $defs = $definition->info[$token->name]->attr;
  65. $attr_key = false;
  66. $context->register('CurrentAttr', $attr_key);
  67. // iterate through all the attribute keypairs
  68. // Watch out for name collisions: $key has previously been used
  69. foreach ($attr as $attr_key => $value) {
  70. // call the definition
  71. if (isset($defs[$attr_key])) {
  72. // there is a local definition defined
  73. if ($defs[$attr_key] === false) {
  74. // We've explicitly been told not to allow this element.
  75. // This is usually when there's a global definition
  76. // that must be overridden.
  77. // Theoretically speaking, we could have a
  78. // AttrDef_DenyAll, but this is faster!
  79. $result = false;
  80. } else {
  81. // validate according to the element's definition
  82. $result = $defs[$attr_key]->validate(
  83. $value,
  84. $config,
  85. $context
  86. );
  87. }
  88. } elseif (isset($d_defs[$attr_key])) {
  89. // there is a global definition defined, validate according
  90. // to the global definition
  91. $result = $d_defs[$attr_key]->validate(
  92. $value,
  93. $config,
  94. $context
  95. );
  96. } else {
  97. // system never heard of the attribute? DELETE!
  98. $result = false;
  99. }
  100. // put the results into effect
  101. if ($result === false || $result === null) {
  102. // this is a generic error message that should replaced
  103. // with more specific ones when possible
  104. if ($e) {
  105. $e->send(E_ERROR, 'AttrValidator: Attribute removed');
  106. }
  107. // remove the attribute
  108. unset($attr[$attr_key]);
  109. } elseif (is_string($result)) {
  110. // generally, if a substitution is happening, there
  111. // was some sort of implicit correction going on. We'll
  112. // delegate it to the attribute classes to say exactly what.
  113. // simple substitution
  114. $attr[$attr_key] = $result;
  115. } else {
  116. // nothing happens
  117. }
  118. // we'd also want slightly more complicated substitution
  119. // involving an array as the return value,
  120. // although we're not sure how colliding attributes would
  121. // resolve (certain ones would be completely overriden,
  122. // others would prepend themselves).
  123. }
  124. $context->destroy('CurrentAttr');
  125. // post transforms
  126. // global (error reporting untested)
  127. foreach ($definition->info_attr_transform_post as $transform) {
  128. $attr = $transform->transform($o = $attr, $config, $context);
  129. if ($e) {
  130. if ($attr != $o) {
  131. $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
  132. }
  133. }
  134. }
  135. // local (error reporting untested)
  136. foreach ($definition->info[$token->name]->attr_transform_post as $transform) {
  137. $attr = $transform->transform($o = $attr, $config, $context);
  138. if ($e) {
  139. if ($attr != $o) {
  140. $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
  141. }
  142. }
  143. }
  144. $token->attr = $attr;
  145. // destroy CurrentToken if we made it ourselves
  146. if (!$current_token) {
  147. $context->destroy('CurrentToken');
  148. }
  149. }
  150. }
  151. // vim: et sw=4 sts=4