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 &lt;?cs content-type: "content type" ?&gt; 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 &lt;?cs
26856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * content-type: ?&gt; command is not required for all javascript and css templates. If the
26956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * template contains a &lt;script&gt; or &lt;style&gt; 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 &lt;script src= &gt; or
27256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * &lt;link rel &gt; command, the explicit &lt;?cs content-type: ?&gt; 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