1   /**
2    * Copyright (c) 2000-2007 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.filters.strip;
24  
25  import com.liferay.portal.kernel.util.GetterUtil;
26  import com.liferay.portal.kernel.util.JavaConstants;
27  import com.liferay.portal.kernel.util.ParamUtil;
28  import com.liferay.util.Http;
29  import com.liferay.util.SystemProperties;
30  import com.liferay.util.servlet.ServletResponseUtil;
31  
32  import java.io.IOException;
33  
34  import javax.servlet.Filter;
35  import javax.servlet.FilterChain;
36  import javax.servlet.FilterConfig;
37  import javax.servlet.ServletException;
38  import javax.servlet.ServletRequest;
39  import javax.servlet.ServletResponse;
40  import javax.servlet.http.HttpServletRequest;
41  import javax.servlet.http.HttpServletResponse;
42  
43  import org.apache.commons.logging.Log;
44  import org.apache.commons.logging.LogFactory;
45  
46  /**
47   * <a href="StripFilter.java.html"><b><i>View Source</i></b></a>
48   *
49   * @author Brian Wing Shun Chan
50   *
51   */
52  public class StripFilter implements Filter {
53  
54      public static final boolean USE_FILTER = GetterUtil.getBoolean(
55          SystemProperties.get(StripFilter.class.getName()), true);
56  
57      public static final String ENCODING = GetterUtil.getString(
58          SystemProperties.get("file.encoding"), "UTF-8");
59  
60      public void init(FilterConfig config) {
61      }
62  
63      public void doFilter(
64              ServletRequest req, ServletResponse res, FilterChain chain)
65          throws IOException, ServletException {
66  
67          if (_log.isDebugEnabled()) {
68              if (USE_FILTER) {
69                  _log.debug("Strip is enabled");
70              }
71              else {
72                  _log.debug("Strip is disabled");
73              }
74          }
75  
76          HttpServletRequest httpReq = (HttpServletRequest)req;
77          HttpServletResponse httpRes = (HttpServletResponse)res;
78  
79          String completeURL = Http.getCompleteURL(httpReq);
80  
81          if (USE_FILTER && isStrip(httpReq) && !isInclude(httpReq) &&
82              !isAlreadyFiltered(httpReq)) {
83  
84              if (_log.isDebugEnabled()) {
85                  _log.debug("Stripping " + completeURL);
86              }
87  
88              httpReq.setAttribute(_ALREADY_FILTERED, Boolean.TRUE);
89  
90              StripResponse stripResponse = new StripResponse(httpRes);
91  
92              chain.doFilter(req, stripResponse);
93  
94              String contentType = GetterUtil.getString(
95                  stripResponse.getContentType());
96  
97              byte[] oldByteArray = stripResponse.getData();
98  
99              if ((oldByteArray != null) && (oldByteArray.length > 0)) {
100                 byte[] newByteArray = new byte[oldByteArray.length];
101                 int newByteArrayPos = 0;
102 
103                 if (_log.isDebugEnabled()) {
104                     _log.debug("Stripping content of type " + contentType);
105                 }
106 
107                 if (contentType.toLowerCase().indexOf("text/") != -1) {
108                     boolean ignore = false;
109                     char prevChar = '\n';
110 
111                     for (int i = 0; i < oldByteArray.length; i++) {
112                         byte b = oldByteArray[i];
113                         char c = (char)b;
114 
115                         if (c == '<') {
116 
117                             // Ignore text inside certain HTML tags.
118 
119                             if (!ignore) {
120 
121                                 // Check for <pre>
122 
123                                 if ((i + 4) < oldByteArray.length) {
124                                     char c1 = (char)oldByteArray[i + 1];
125                                     char c2 = (char)oldByteArray[i + 2];
126                                     char c3 = (char)oldByteArray[i + 3];
127                                     char c4 = (char)oldByteArray[i + 4];
128 
129                                     if (((c1 == 'p') || (c1 == 'P')) &&
130                                         ((c2 == 'r') || (c2 == 'R')) &&
131                                         ((c3 == 'e') || (c3 == 'E')) &&
132                                         ((c4 == '>'))) {
133 
134                                         ignore = true;
135                                     }
136                                 }
137 
138                                 // Check for <textarea
139 
140                                 if (!ignore &&
141                                     ((i + 9) < oldByteArray.length)) {
142 
143                                     char c1 = (char)oldByteArray[i + 1];
144                                     char c2 = (char)oldByteArray[i + 2];
145                                     char c3 = (char)oldByteArray[i + 3];
146                                     char c4 = (char)oldByteArray[i + 4];
147                                     char c5 = (char)oldByteArray[i + 5];
148                                     char c6 = (char)oldByteArray[i + 6];
149                                     char c7 = (char)oldByteArray[i + 7];
150                                     char c8 = (char)oldByteArray[i + 8];
151                                     char c9 = (char)oldByteArray[i + 9];
152 
153                                     if (((c1 == 't') || (c1 == 'T')) &&
154                                         ((c2 == 'e') || (c2 == 'E')) &&
155                                         ((c3 == 'x') || (c3 == 'X')) &&
156                                         ((c4 == 't') || (c4 == 'T')) &&
157                                         ((c5 == 'a') || (c5 == 'A')) &&
158                                         ((c6 == 'r') || (c6 == 'R')) &&
159                                         ((c7 == 'e') || (c7 == 'E')) &&
160                                         ((c8 == 'a') || (c8 == 'A')) &&
161                                         ((c9 == ' '))) {
162 
163                                         ignore = true;
164                                     }
165                                 }
166                             }
167                             else if (ignore) {
168 
169                                 // Check for </pre>
170 
171                                 if ((i + 5) < oldByteArray.length) {
172                                     char c1 = (char)oldByteArray[i + 1];
173                                     char c2 = (char)oldByteArray[i + 2];
174                                     char c3 = (char)oldByteArray[i + 3];
175                                     char c4 = (char)oldByteArray[i + 4];
176                                     char c5 = (char)oldByteArray[i + 5];
177 
178                                     if (((c1 == '/')) &&
179                                         ((c2 == 'p') || (c2 == 'P')) &&
180                                         ((c3 == 'r') || (c3 == 'R')) &&
181                                         ((c4 == 'e') || (c4 == 'E')) &&
182                                         ((c5 == '>'))) {
183 
184                                         ignore = false;
185                                     }
186                                 }
187 
188                                 // Check for </textarea>
189 
190                                 if (ignore &&
191                                     ((i + 10) < oldByteArray.length)) {
192 
193                                     char c1 = (char)oldByteArray[i + 1];
194                                     char c2 = (char)oldByteArray[i + 2];
195                                     char c3 = (char)oldByteArray[i + 3];
196                                     char c4 = (char)oldByteArray[i + 4];
197                                     char c5 = (char)oldByteArray[i + 5];
198                                     char c6 = (char)oldByteArray[i + 6];
199                                     char c7 = (char)oldByteArray[i + 7];
200                                     char c8 = (char)oldByteArray[i + 8];
201                                     char c9 = (char)oldByteArray[i + 9];
202                                     char c10 = (char)oldByteArray[i + 10];
203 
204                                     if (((c1 == '/')) &&
205                                         ((c2 == 't') || (c2 == 'T')) &&
206                                         ((c3 == 'e') || (c3 == 'E')) &&
207                                         ((c4 == 'x') || (c4 == 'X')) &&
208                                         ((c5 == 't') || (c5 == 'T')) &&
209                                         ((c6 == 'a') || (c6 == 'A')) &&
210                                         ((c7 == 'r') || (c7 == 'R')) &&
211                                         ((c8 == 'e') || (c8 == 'E')) &&
212                                         ((c9 == 'a') || (c9 == 'A')) &&
213                                         ((c10 == '>'))) {
214 
215                                         ignore = false;
216                                     }
217                                 }
218                             }
219                         }
220 
221                         if ((!ignore) &&
222                             ((c == '\n') || (c == '\r') || (c == '\t'))) {
223 
224                             if ((i + 1) == oldByteArray.length) {
225                             }
226 
227                             if ((prevChar == '\n') || (prevChar == '\r')) {
228                             }
229                             else {
230                                 if (c != '\t') {
231                                     prevChar = c;
232                                 }
233 
234                                 newByteArray[newByteArrayPos++] = b;
235                             }
236                         }
237                         else {
238                             prevChar = c;
239 
240                             newByteArray[newByteArrayPos++] = b;
241                         }
242                     }
243                 }
244                 else {
245                     newByteArray = oldByteArray;
246                     newByteArrayPos = oldByteArray.length;
247                 }
248 
249                 ServletResponseUtil.write(
250                     httpRes, newByteArray, newByteArrayPos);
251             }
252         }
253         else {
254             if (_log.isDebugEnabled()) {
255                 _log.debug("Not stripping " + completeURL);
256             }
257 
258             chain.doFilter(req, res);
259         }
260     }
261 
262     public void destroy() {
263     }
264 
265     protected boolean isAlreadyFiltered(HttpServletRequest req) {
266         if (req.getAttribute(_ALREADY_FILTERED) != null) {
267             return true;
268         }
269         else {
270             return false;
271         }
272     }
273 
274     protected boolean isInclude(HttpServletRequest req) {
275         String uri = (String)req.getAttribute(
276             JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
277 
278         if (uri == null) {
279             return false;
280         }
281         else {
282             return true;
283         }
284     }
285 
286     protected boolean isStrip(HttpServletRequest req) {
287         if (!ParamUtil.get(req, _STRIP, true)) {
288             return false;
289         }
290         else {
291 
292             // The exclusive state is used to stream binary content.
293             // Compressing binary content through a servlet filter is bad on
294             // performance because the user will not start downloading the
295             // content until the entire content is compressed.
296 
297             boolean action = ParamUtil.getBoolean(req, "p_p_action");
298             String windowState = ParamUtil.getString(req, "p_p_state");
299 
300             if (action && windowState.equals("exclusive")) {
301                 return false;
302             }
303             else {
304                 return true;
305             }
306         }
307     }
308 
309     private static final String _ALREADY_FILTERED =
310         StripFilter.class.getName() + "_ALREADY_FILTERED";
311 
312     private static final String _STRIP = "strip";
313 
314     private static Log _log = LogFactory.getLog(StripFilter.class);
315 
316 }