• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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