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 38 static bool isValidFirstVariableNameCharacter(const UChar& character) 39 { 40 return WTF::isASCIIAlpha(character) 41 || character == '_'; 42 } 43 44 static bool isValidVariableNameCharacter(const UChar& character) 45 { 46 return WTF::isASCIIAlpha(character) 47 || WTF::isASCIIDigit(character) 48 || character == '_'; 49 } 50 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 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 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 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