1 /**
2 * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21 #include "config.h"
22
23 #if ENABLE(WML)
24 #include "WMLVariables.h"
25
26 #include "WMLDocument.h"
27 #include <wtf/ASCIICType.h>
28
29 namespace WebCore {
30
31 // WML variables specification, excluding the
32 // pre-WML 1.0 deprecated variable syntax
33 //
34 // varname = ("_" | alpha) ("_" | alpha | digit)*
35 // conv = ":" ("e" ("scape")? | "n" ("oesc")? | "u" ("nesc")?)
36 // var = ("$" varname) | ("$(" varname (conv)? ")")
37
isValidFirstVariableNameCharacter(const UChar & character)38 static bool isValidFirstVariableNameCharacter(const UChar& character)
39 {
40 return WTF::isASCIIAlpha(character)
41 || character == '_';
42 }
43
isValidVariableNameCharacter(const UChar & character)44 static bool isValidVariableNameCharacter(const UChar& character)
45 {
46 return WTF::isASCIIAlpha(character)
47 || WTF::isASCIIDigit(character)
48 || character == '_';
49 }
50
isValidVariableEscapingModeString(const String & mode,WMLVariableEscapingMode & escapeMode)51 static bool isValidVariableEscapingModeString(const String& mode, WMLVariableEscapingMode& escapeMode)
52 {
53 if (mode == "e" || mode == "escape")
54 escapeMode = WMLVariableEscapingEscape;
55 else if (mode == "u" || mode == "unesc")
56 escapeMode = WMLVariableEscapingUnescape;
57 else if (mode == "n" || mode == "noesc")
58 escapeMode = WMLVariableEscapingNone;
59 else
60 return false;
61
62 return true;
63 }
64
isValidVariableName(const String & name)65 bool isValidVariableName(const String& name)
66 {
67 if (name.isEmpty())
68 return false;
69
70 const UChar* characters = name.characters();
71 if (!isValidFirstVariableNameCharacter(characters[0]))
72 return false;
73
74 unsigned length = name.length();
75 for (unsigned i = 1; i < length; ++i) {
76 if (!isValidVariableNameCharacter(characters[i]))
77 return false;
78 }
79
80 return true;
81 }
82
containsVariableReference(const String & text,bool & isValid)83 bool containsVariableReference(const String& text, bool& isValid)
84 {
85 isValid = true;
86 bool foundReference = false;
87 bool finished = false;
88 int currentPosition = 0;
89 const UChar* characters = text.characters();
90
91 while (!finished) {
92 // Find beginning of variable reference
93 int referenceStartPosition = text.find('$', currentPosition);
94 if (referenceStartPosition == -1) {
95 finished = true;
96 break;
97 }
98
99 foundReference = true;
100
101 int nameStartPosition = referenceStartPosition + 1;
102 int nameEndPosition = -1;
103
104 if (characters[nameStartPosition] == '(') {
105 // If the input string contains an open brace, a close brace must exist as well
106 nameEndPosition = text.find(')', nameStartPosition + 1);
107 if (nameEndPosition == -1) {
108 finished = true;
109 isValid = false;
110 break;
111 }
112
113 ++nameStartPosition;
114 } else {
115 int length = text.length();
116 for (nameEndPosition = nameStartPosition; nameEndPosition < length; ++nameEndPosition) {
117 if (!isValidVariableNameCharacter(text[nameEndPosition]))
118 break;
119 }
120 }
121
122 if (nameEndPosition < nameStartPosition) {
123 finished = true;
124 isValid = false;
125 break;
126 }
127
128 // Eventually split of conversion string, and check its syntax afterwards
129 String conversionString;
130 String variableName = text.substring(nameStartPosition, nameEndPosition - nameStartPosition);
131
132 int conversionStringStart = variableName.find(':');
133 if (conversionStringStart != -1) {
134 conversionString = variableName.substring(conversionStringStart + 1, variableName.length() - (conversionStringStart + 1));
135 variableName = variableName.left(conversionStringStart);
136 }
137
138 isValid = isValidVariableName(variableName);
139 if (!isValid) {
140 finished = true;
141 break;
142 }
143
144 if (!conversionString.isEmpty()) {
145 isValid = isValidVariableName(conversionString);
146 if (!isValid) {
147 finished = true;
148 break;
149 }
150
151 WMLVariableEscapingMode escapeMode = WMLVariableEscapingNone;
152 isValid = isValidVariableEscapingModeString(conversionString, escapeMode);
153 if (!isValid) {
154 finished = true;
155 break;
156 }
157 }
158
159 currentPosition = nameEndPosition;
160 }
161
162 return foundReference;
163 }
164
substituteVariableReferences(const String & reference,Document * document,WMLVariableEscapingMode escapeMode)165 String substituteVariableReferences(const String& reference, Document* document, WMLVariableEscapingMode escapeMode)
166 {
167 ASSERT(document);
168
169 if (reference.isEmpty())
170 return reference;
171
172 WMLPageState* pageState = wmlPageStateForDocument(document);
173 if (!pageState)
174 return reference;
175
176 bool isValid = true;
177 String remainingInput = reference;
178 String result;
179
180 while (!remainingInput.isEmpty()) {
181 ASSERT(isValid);
182
183 int start = remainingInput.find("$");
184 if (start == -1) {
185 // Consume all remaining characters, as there's nothing more to substitute
186 result += remainingInput;
187 break;
188 }
189
190 // Consume all characters until the variable reference beginning
191 result += remainingInput.left(start);
192 remainingInput.remove(0, start);
193
194 // Transform adjacent dollar signs into a single dollar sign as string literal
195 if (remainingInput[1] == '$') {
196 result += "$";
197 remainingInput.remove(0, 2);
198 continue;
199 }
200
201 String variableName;
202 String conversionMode;
203
204 if (remainingInput[1] == '(') {
205 int referenceEndPosition = remainingInput.find(")");
206 if (referenceEndPosition == -1) {
207 isValid = false;
208 break;
209 }
210
211 variableName = remainingInput.substring(2, referenceEndPosition - 2);
212 remainingInput.remove(0, referenceEndPosition + 1);
213
214 // Determine variable conversion mode string
215 int pos = variableName.find(':');
216 if (pos != -1) {
217 conversionMode = variableName.substring(pos + 1, variableName.length() - (pos + 1));
218 variableName = variableName.left(pos);
219 }
220 } else {
221 int length = remainingInput.length();
222 int referenceEndPosition = 1;
223
224 for (; referenceEndPosition < length; ++referenceEndPosition) {
225 if (!isValidVariableNameCharacter(remainingInput[referenceEndPosition]))
226 break;
227 }
228
229 variableName = remainingInput.substring(1, referenceEndPosition - 1);
230 remainingInput.remove(0, referenceEndPosition);
231 }
232
233 isValid = isValidVariableName(variableName);
234 if (!isValid)
235 break;
236
237 ASSERT(!variableName.isEmpty());
238
239 String variableValue = pageState->getVariable(variableName);
240 if (variableValue.isEmpty())
241 continue;
242
243 if (containsVariableReference(variableValue, isValid)) {
244 if (!isValid)
245 break;
246
247 variableValue = substituteVariableReferences(variableValue, document, escapeMode);
248 continue;
249 }
250
251 if (!conversionMode.isEmpty()) {
252 // Override default escape mode, if desired
253 WMLVariableEscapingMode specifiedEscapeMode = WMLVariableEscapingNone;
254 if ((isValid = isValidVariableEscapingModeString(conversionMode, specifiedEscapeMode)))
255 escapeMode = specifiedEscapeMode;
256
257 if (!isValid)
258 break;
259 }
260
261 switch (escapeMode) {
262 case WMLVariableEscapingNone:
263 break;
264 case WMLVariableEscapingEscape:
265 variableValue = encodeWithURLEscapeSequences(variableValue);
266 break;
267 case WMLVariableEscapingUnescape:
268 variableValue = decodeURLEscapeSequences(variableValue);
269 break;
270 }
271
272 result += variableValue;
273 ASSERT(isValid);
274 }
275
276 if (!isValid) {
277 reportWMLError(document, WMLErrorInvalidVariableReference);
278 return reference;
279 }
280
281 return result;
282 }
283
284 }
285
286 #endif
287