1 /*
2 * $Id$
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21 package org.apache.struts.util;
22
23 import java.io.UnsupportedEncodingException;
24 import java.net.URLEncoder;
25 import java.nio.charset.Charset;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33 * General purpose utility methods related to generating a servlet response in
34 * the Struts controller framework.
35 */
36 public class ResponseUtils {
37
38 // ------------------------------------------------------- Static Variables
39
40 /**
41 * The {@code Log} instance for this class.
42 */
43 private static final Logger LOG =
44 LoggerFactory.getLogger(ResponseUtils.class);
45
46 /**
47 * Pattern to find XML-Entity-Patterns.
48 *
49 * <p>Valid patterns are:</p>
50 *
51 * <ul>
52 * <li>&[a-zA-Z][a-z-A-Z0-9]*; - &amp;, &quot;</li>
53 * <li>&#[0-9]+; - &#32;, &#64;</li>
54 * <li>&#x[a-fA-F0-9]+; - &#x20, &3f</li>
55 * </ul>
56 *
57 * <p>See also <a href="https://www.w3.org/TR/xml11/#sec-references">
58 * XML-Reference 1.1</a>.</p>
59 */
60 private static final Pattern XML_ENTITY_PATTERN =
61 Pattern.compile("&(?:[a-zA-Z][a-zA-Z\\d]*|#\\d+|#x[a-fA-F\\d]+);");
62
63 /**
64 * The message resources for this package.
65 */
66 protected static MessageResources messages =
67 MessageResources.getMessageResources(
68 "org.apache.struts.util.LocalStrings");
69
70 // ----------------------------------------------------------- Constructors
71
72 /**
73 * Class is not instanceable.
74 */
75 protected ResponseUtils() {
76 }
77
78 // --------------------------------------------------------- Public Methods
79
80 /**
81 * Filter the specified string for characters that are sensitive to HTML
82 * interpreters, returning the string with these characters replaced by
83 * the corresponding character entities.
84 *
85 * @param value The string to be filtered and returned
86 *
87 * @return String The filtered string
88 */
89 public static String filter(String value) {
90 if (value == null || value.isEmpty()) {
91 return value;
92 }
93
94 final int length = value.length();
95
96 StringBuilder result = null;
97 String filtered;
98 Matcher entityMatcher = null;
99
100 for (int i = 0; i < length; i++) {
101 filtered = null;
102
103 switch (value.charAt(i)) {
104 case '<':
105 filtered = "<";
106
107 break;
108
109 case '>':
110 filtered = ">";
111
112 break;
113
114 case '&':
115 if (entityMatcher == null) {
116 entityMatcher = XML_ENTITY_PATTERN.matcher(value);
117 }
118 entityMatcher.region(i, length);
119 if (entityMatcher.lookingAt()) {
120 filtered = value.substring(entityMatcher.start(), entityMatcher.end());
121 i += filtered.length() - 1;
122 if (result == null) {
123 continue;
124 }
125 } else {
126 filtered = "&";
127 }
128
129 break;
130
131 case '"':
132 filtered = """;
133
134 break;
135
136 case '\'':
137 filtered = "'";
138
139 break;
140
141 default:
142 break;
143 }
144
145 if (result == null) {
146 if (filtered != null) {
147 result = new StringBuilder(length + 50);
148
149 if (i > 0) {
150 result.append(value.substring(0, i));
151 }
152
153 result.append(filtered);
154 }
155 } else {
156 if (filtered == null) {
157 result.append(value.charAt(i));
158 } else {
159 result.append(filtered);
160 }
161 }
162 }
163
164 return result == null ? value : result.toString();
165 }
166
167 /**
168 * URLencodes a string assuming the character encoding is UTF-8.
169 *
170 * @param url The url to encode
171 *
172 * @return String The encoded url in UTF-8
173 */
174 public static String encodeURL(String url) {
175 return encodeURL(url, "UTF-8");
176 }
177
178 /**
179 * Use the URLEncoder.encode() with the given encoding. When the
180 * encoding charset didn't found, then it will be tried with the
181 * default-charset.
182 *
183 * @param url The url to encode
184 * @param enc The character encoding the urlencode is performed on.
185 *
186 * @return String The encoded url.
187 */
188 public static String encodeURL(String url, String enc) {
189 String str = null;
190
191 try {
192 if (enc == null || enc.isEmpty()) {
193 enc = "UTF-8";
194 }
195
196 str = URLEncoder.encode(url, enc);
197 } catch (UnsupportedEncodingException e) {
198 LOG.debug("The named encoding is not supported: {}", enc, e);
199
200 try {
201 str = URLEncoder.encode(url, Charset.defaultCharset().toString());
202 } catch (UnsupportedEncodingException e1) {
203 // Should never happen - the system should always have the platform default
204 LOG.debug("The default-encoding is not supported: {}", enc, e1);
205
206 str = url;
207 }
208 }
209
210 return str;
211 }
212 }