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}