156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/* 256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Copyright (C) 2010 Google Inc. 356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Licensed under the Apache License, Version 2.0 (the "License"); 556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * you may not use this file except in compliance with the License. 656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * You may obtain a copy of the License at 756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * http://www.apache.org/licenses/LICENSE-2.0 956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 1056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Unless required by applicable law or agreed to in writing, software 1156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * distributed under the License is distributed on an "AS IS" BASIS, 1256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * See the License for the specific language governing permissions and 1456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * limitations under the License. 1556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 1656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 1756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpackage com.google.clearsilver.jsilver.syntax; 1856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 1956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.autoescape.AutoEscapeContext; 2056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.autoescape.EscapeMode; 2156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.exceptions.JSilverAutoEscapingException; 2256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter; 2356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AAltCommand; 2456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AAutoescapeCommand; 2556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ACallCommand; 2656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AContentTypeCommand; 2756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ACsOpenPosition; 2856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ADataCommand; 2956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ADefCommand; 3056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AEscapeCommand; 3156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AEvarCommand; 3256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AHardIncludeCommand; 3356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AHardLincludeCommand; 3456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AIfCommand; 3556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AIncludeCommand; 3656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ALincludeCommand; 3756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ALvarCommand; 3856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.ANameCommand; 3956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AStringExpression; 4056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AUvarCommand; 4156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.AVarCommand; 4256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.Node; 4356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.PCommand; 4456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.PPosition; 4556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.Start; 4656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.TCsOpen; 4756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.TString; 4856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.syntax.node.Token; 4956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 5056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/** 5156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Run a context parser (currently only HTML parser) over the AST, determine nodes that need 5256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * escaping, and apply the appropriate escaping command to those nodes. The parser is fed literal 5356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * data (from DataCommands), which it uses to track the context. When variables (e.g. VarCommand) 5456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * are encountered, we query the parser for its current context, and apply the appropriate escaping 5556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * command. 5656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 5756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpublic class AutoEscaper extends DepthFirstAdapter { 5856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 5956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private AutoEscapeContext autoEscapeContext; 6056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private boolean skipAutoEscape; 6156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private final EscapeMode escapeMode; 6256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private final String templateName; 6356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private boolean contentTypeCalled; 6456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 6556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 6656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Create an AutoEscaper, which will apply the specified escaping mode. If templateName is 6756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * non-null, it will be used while displaying error messages. 6856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 6956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @param mode 7056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @param templateName 7156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 7256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public AutoEscaper(EscapeMode mode, String templateName) { 7356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson this.templateName = templateName; 7456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (mode.equals(EscapeMode.ESCAPE_NONE)) { 7556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new JSilverAutoEscapingException("AutoEscaper called when no escaping is required", 7656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson templateName); 7756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 7856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson escapeMode = mode; 7956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (mode.isAutoEscapingMode()) { 8056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson autoEscapeContext = new AutoEscapeContext(mode, templateName); 8156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson skipAutoEscape = false; 8256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } else { 8356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson autoEscapeContext = null; 8456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 8556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 8656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 8756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 8856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Create an AutoEscaper, which will apply the specified escaping mode. When possible, use 8956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * #AutoEscaper(EscapeMode, String) instead. It specifies the template being auto escaped, which 9056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * is useful when displaying error messages. 9156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 9256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @param mode 9356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 9456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public AutoEscaper(EscapeMode mode) { 9556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson this(mode, null); 9656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 9756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 9856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 9956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseStart(Start start) { 10056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (!escapeMode.isAutoEscapingMode()) { 10156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // For an explicit EscapeMode like {@code EscapeMode.ESCAPE_HTML}, we 10256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // do not need to parse the rest of the tree. Instead, we just wrap the 10356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // entire tree in a <?cs escape ?> node. 10456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson handleExplicitEscapeMode(start); 10556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } else { 10656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson AutoEscapeContext.AutoEscapeState startState = autoEscapeContext.getCurrentState(); 10756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // call super.caseStart, which will make us visit the rest of the tree, 10856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // so we can determine the appropriate escaping to apply for each 10956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // variable. 11056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson super.caseStart(start); 11156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson AutoEscapeContext.AutoEscapeState endState = autoEscapeContext.getCurrentState(); 11256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (!autoEscapeContext.isPermittedStateChangeForIncludes(startState, endState)) { 11356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // If template contains a content-type command, the escaping context 11456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // was intentionally changed. Such a change in context is fine as long 11556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // as the current template is not included inside another. There is no 11656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // way to verify that the template is not an include template however, 11756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // so ignore the error and depend on developers doing the right thing. 11856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (contentTypeCalled) { 11956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return; 12056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 12156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // We do not permit templates to end in a different context than they start in. 12256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // This is so that an included template does not modify the context of 12356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // the template that includes it. 12456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new JSilverAutoEscapingException("Template starts in context " + startState 12556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson + " but ends in different context " + endState, templateName); 12656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 12756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 12856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 12956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 13056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private void handleExplicitEscapeMode(Start start) { 13156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson AStringExpression escapeExpr = 13256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson new AStringExpression(new TString("\"" + escapeMode.getEscapeCommand() + "\"")); 13356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 13456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson PCommand node = start.getPCommand(); 13556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson AEscapeCommand escape = 13656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson new AEscapeCommand(new ACsOpenPosition(new TCsOpen("<?cs ", 0, 0)), escapeExpr, 13756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson (PCommand) node.clone()); 13856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 13956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson node.replaceBy(escape); 14056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 14156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 14256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 14356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseADataCommand(ADataCommand node) { 14456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson String data = node.getData().getText(); 14556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson autoEscapeContext.setCurrentPosition(node.getData().getLine(), node.getData().getPos()); 14656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson autoEscapeContext.parseData(data); 14756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 14856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 14956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 15056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseADefCommand(ADefCommand node) { 15156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Ignore the entire defcommand subtree, don't even parse it. 15256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 15356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 15456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 15556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseAIfCommand(AIfCommand node) { 15656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson setCurrentPosition(node.getPosition()); 15756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 15856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /* 15956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Since AutoEscaper is being applied while building the AST, and not during rendering, the html 16056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * context of variables is sometimes ambiguous. For instance: <?cs if: X ?><script><?cs /if ?> 16156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs var: MyVar ?> 16256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 16356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Here MyVar may require js escaping or html escaping depending on whether the "if" condition 16456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * is true or false. 16556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 16656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * To avoid such ambiguity, we require all branches of a conditional statement to end in the 16756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * same context. So, <?cs if: X ?><script>X <?cs else ?><script>Y<?cs /if ?> is fine but, 16856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 16956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <?cs if: X ?><script>X <?cs elif: Y ?><script>Y<?cs /if ?> is not. 17056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 17156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson AutoEscapeContext originalEscapedContext = autoEscapeContext.cloneCurrentEscapeContext(); 17256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Save position of the start of if statement. 17356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson int line = autoEscapeContext.getLineNumber(); 17456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson int column = autoEscapeContext.getColumnNumber(); 17556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 17656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (node.getBlock() != null) { 17756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson node.getBlock().apply(this); 17856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 17956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson AutoEscapeContext.AutoEscapeState ifEndState = autoEscapeContext.getCurrentState(); 18056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // restore original context before executing else block 18156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson autoEscapeContext = originalEscapedContext; 18256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 18356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Interestingly, getOtherwise() is not null even when the if command 18456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // has no else branch. In such cases, getOtherwise() contains a 18556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Noop command. 18656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // In practice this does not matter for the checks being run here. 18756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (node.getOtherwise() != null) { 18856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson node.getOtherwise().apply(this); 18956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 19056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson AutoEscapeContext.AutoEscapeState elseEndState = autoEscapeContext.getCurrentState(); 19156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 19256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (!ifEndState.equals(elseEndState)) { 19356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson throw new JSilverAutoEscapingException("'if/else' branches have different ending contexts " 19456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson + ifEndState + " and " + elseEndState, templateName, line, column); 19556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 19656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 19756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 19856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 19956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseAEscapeCommand(AEscapeCommand node) { 20056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson boolean saved_skip = skipAutoEscape; 20156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson skipAutoEscape = true; 20256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson node.getCommand().apply(this); 20356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson skipAutoEscape = saved_skip; 20456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 20556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 20656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 20756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseACallCommand(ACallCommand node) { 20856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson saveAutoEscapingContext(node, node.getPosition()); 20956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 21056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 21156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 21256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseALvarCommand(ALvarCommand node) { 21356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson saveAutoEscapingContext(node, node.getPosition()); 21456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 21556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 21656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 21756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseAEvarCommand(AEvarCommand node) { 21856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson saveAutoEscapingContext(node, node.getPosition()); 21956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 22056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 22156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 22256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseALincludeCommand(ALincludeCommand node) { 22356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson saveAutoEscapingContext(node, node.getPosition()); 22456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 22556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 22656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 22756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseAIncludeCommand(AIncludeCommand node) { 22856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson saveAutoEscapingContext(node, node.getPosition()); 22956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 23056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 23156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 23256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseAHardLincludeCommand(AHardLincludeCommand node) { 23356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson saveAutoEscapingContext(node, node.getPosition()); 23456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 23556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 23656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 23756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseAHardIncludeCommand(AHardIncludeCommand node) { 23856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson saveAutoEscapingContext(node, node.getPosition()); 23956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 24056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 24156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 24256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseAVarCommand(AVarCommand node) { 24356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson applyAutoEscaping(node, node.getPosition()); 24456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 24556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 24656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 24756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseAAltCommand(AAltCommand node) { 24856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson applyAutoEscaping(node, node.getPosition()); 24956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 25056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 25156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 25256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseANameCommand(ANameCommand node) { 25356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson applyAutoEscaping(node, node.getPosition()); 25456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 25556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 25656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 25756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseAUvarCommand(AUvarCommand node) { 25856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Let parser know that was some text that it has not seen 25956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson setCurrentPosition(node.getPosition()); 26056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson autoEscapeContext.insertText(); 26156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 26256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 26356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson /** 26456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Handles a <?cs content-type: "content type" ?> command. 26556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * 26656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * This command is used when the auto escaping context of a template cannot be determined from its 26756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * contents - for example, a CSS stylesheet or a javascript source file. Note that <?cs 26856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * content-type: ?> command is not required for all javascript and css templates. If the 26956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * template contains a <script> or <style> tag (or is included from another template 27056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * within the right tag), auto escaping will recognize the tag and switch context accordingly. On 27156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * the other hand, if the template serves a resource that is loaded via a <script src= > or 27256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * <link rel > command, the explicit <?cs content-type: ?> command would be required. 27356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */ 27456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 27556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseAContentTypeCommand(AContentTypeCommand node) { 27656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson setCurrentPosition(node.getPosition()); 27756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson String contentType = node.getString().getText(); 27856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Strip out quotes around the string 27956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson contentType = contentType.substring(1, contentType.length() - 1); 28056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson autoEscapeContext.setContentType(contentType); 28156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson contentTypeCalled = true; 28256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 28356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 28456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private void applyAutoEscaping(PCommand node, PPosition position) { 28556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson setCurrentPosition(position); 28656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (skipAutoEscape) { 28756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return; 28856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 28956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 29056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson AStringExpression escapeExpr = new AStringExpression(new TString("\"" + getEscaping() + "\"")); 29156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson AEscapeCommand escape = new AEscapeCommand(position, escapeExpr, (PCommand) node.clone()); 29256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 29356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson node.replaceBy(escape); 29456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Now that we have determined the correct escaping for this variable, 29556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // let parser know that there was some text that it has not seen. The 29656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // parser may choose to update its state based on this. 29756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson autoEscapeContext.insertText(); 29856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 29956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 30056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 30156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private void setCurrentPosition(PPosition position) { 30256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson // Will eventually call caseACsOpenPosition 30356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson position.apply(this); 30456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 30556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 30656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson @Override 30756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson public void caseACsOpenPosition(ACsOpenPosition node) { 30856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson Token token = node.getCsOpen(); 30956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson autoEscapeContext.setCurrentPosition(token.getLine(), token.getPos()); 31056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 31156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 31256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private void saveAutoEscapingContext(Node node, PPosition position) { 31356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson setCurrentPosition(position); 31456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson if (skipAutoEscape) { 31556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return; 31656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 31756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson EscapeMode mode = autoEscapeContext.getEscapeModeForCurrentState(); 31856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson AStringExpression escapeStrategy = 31956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson new AStringExpression(new TString("\"" + mode.getEscapeCommand() + "\"")); 32056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson AAutoescapeCommand command = 32156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson new AAutoescapeCommand(position, escapeStrategy, (PCommand) node.clone()); 32256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson node.replaceBy(command); 32356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson autoEscapeContext.insertText(); 32456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 32556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson 32656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson private String getEscaping() { 32756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson return autoEscapeContext.getEscapingFunctionForCurrentState(); 32856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson } 32956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson} 330