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