001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.tags; 018 019import java.io.IOException; 020 021import javax.servlet.jsp.JspException; 022import javax.servlet.jsp.tagext.BodyContent; 023import javax.servlet.jsp.tagext.BodyTag; 024 025import org.springframework.lang.Nullable; 026import org.springframework.util.Assert; 027import org.springframework.web.util.JavaScriptUtils; 028 029/** 030 * The {@code <escapeBody>} tag is used to escape its enclosed body content, 031 * applying HTML escaping and/or JavaScript escaping. 032 * 033 * <p>Provides a "htmlEscape" property for explicitly specifying whether to 034 * apply HTML escaping. If not set, a page-level default (e.g. from the 035 * HtmlEscapeTag) or an application-wide default (the "defaultHtmlEscape" 036 * context-param in web.xml) is used. 037 * 038 * <p>Provides a "javaScriptEscape" property for specifying whether to apply 039 * JavaScript escaping. Can be combined with HTML escaping or used standalone. 040 * 041 * <table> 042 * <caption>Attribute Summary</caption> 043 * <thead> 044 * <tr> 045 * <th>Attribute</th> 046 * <th>Required?</th> 047 * <th>Runtime Expression?</th> 048 * <th>Description</th> 049 * </tr> 050 * </thead> 051 * <tbody> 052 * <tr> 053 * <td>htmlEscape</td> 054 * <td>false</td> 055 * <td>true</td> 056 * <td>Set HTML escaping for this tag, as boolean value. 057 * Overrides the default HTML escaping setting for the current page.</td> 058 * </tr> 059 * <tr> 060 * <td>javaScriptEscape</td> 061 * <td>false</td> 062 * <td>true</td> 063 * <td>Set JavaScript escaping for this tag, as boolean value. 064 * Default is false.</td> 065 * </tr> 066 * </tbody> 067 * </table> 068 * 069 * @author Juergen Hoeller 070 * @since 1.1.1 071 * @see org.springframework.web.util.HtmlUtils 072 * @see org.springframework.web.util.JavaScriptUtils 073 */ 074@SuppressWarnings("serial") 075public class EscapeBodyTag extends HtmlEscapingAwareTag implements BodyTag { 076 077 private boolean javaScriptEscape = false; 078 079 @Nullable 080 private BodyContent bodyContent; 081 082 083 /** 084 * Set JavaScript escaping for this tag, as boolean value. 085 * Default is "false". 086 */ 087 public void setJavaScriptEscape(boolean javaScriptEscape) throws JspException { 088 this.javaScriptEscape = javaScriptEscape; 089 } 090 091 092 @Override 093 protected int doStartTagInternal() { 094 // do nothing 095 return EVAL_BODY_BUFFERED; 096 } 097 098 @Override 099 public void doInitBody() { 100 // do nothing 101 } 102 103 @Override 104 public void setBodyContent(BodyContent bodyContent) { 105 this.bodyContent = bodyContent; 106 } 107 108 @Override 109 public int doAfterBody() throws JspException { 110 try { 111 String content = readBodyContent(); 112 // HTML and/or JavaScript escape, if demanded 113 content = htmlEscape(content); 114 content = (this.javaScriptEscape ? JavaScriptUtils.javaScriptEscape(content) : content); 115 writeBodyContent(content); 116 } 117 catch (IOException ex) { 118 throw new JspException("Could not write escaped body", ex); 119 } 120 return (SKIP_BODY); 121 } 122 123 /** 124 * Read the unescaped body content from the page. 125 * @return the original content 126 * @throws IOException if reading failed 127 */ 128 protected String readBodyContent() throws IOException { 129 Assert.state(this.bodyContent != null, "No BodyContent set"); 130 return this.bodyContent.getString(); 131 } 132 133 /** 134 * Write the escaped body content to the page. 135 * <p>Can be overridden in subclasses, e.g. for testing purposes. 136 * @param content the content to write 137 * @throws IOException if writing failed 138 */ 139 protected void writeBodyContent(String content) throws IOException { 140 Assert.state(this.bodyContent != null, "No BodyContent set"); 141 this.bodyContent.getEnclosingWriter().print(content); 142 } 143 144}