1   /**
2    * Copyright (c) 2000-2008 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.log.Log;
26  import com.liferay.portal.kernel.log.LogFactoryUtil;
27  import com.liferay.portal.kernel.servlet.BaseFilter;
28  import com.liferay.portal.kernel.util.GetterUtil;
29  import com.liferay.portal.kernel.util.JavaConstants;
30  import com.liferay.portal.kernel.util.ParamUtil;
31  import com.liferay.util.Http;
32  import com.liferay.util.SystemProperties;
33  import com.liferay.util.servlet.ServletResponseUtil;
34  
35  import java.io.IOException;
36  
37  import javax.servlet.FilterChain;
38  import javax.servlet.ServletException;
39  import javax.servlet.ServletRequest;
40  import javax.servlet.ServletResponse;
41  import javax.servlet.http.HttpServletRequest;
42  import javax.servlet.http.HttpServletResponse;
43  
44  /**
45   * <a href="StripFilter.java.html"><b><i>View Source</i></b></a>
46   *
47   * @author Brian Wing Shun Chan
48   * @author Raymond Augé
49   *
50   */
51  public class StripFilter extends BaseFilter {
52  
53      public static final boolean USE_FILTER = GetterUtil.getBoolean(
54          SystemProperties.get(StripFilter.class.getName()), true);
55  
56      public static final String ENCODING = GetterUtil.getString(
57          SystemProperties.get("file.encoding"), "UTF-8");
58  
59      public void doFilter(
60              ServletRequest req, ServletResponse res, FilterChain chain)
61          throws IOException, ServletException {
62  
63          if (_log.isDebugEnabled()) {
64              if (USE_FILTER) {
65                  _log.debug("Strip is enabled");
66              }
67              else {
68                  _log.debug("Strip is disabled");
69              }
70          }
71  
72          HttpServletRequest httpReq = (HttpServletRequest)req;
73          HttpServletResponse httpRes = (HttpServletResponse)res;
74  
75          String completeURL = Http.getCompleteURL(httpReq);
76  
77          if (USE_FILTER && isStrip(httpReq) && !isInclude(httpReq) &&
78              !isAlreadyFiltered(httpReq)) {
79  
80              if (_log.isDebugEnabled()) {
81                  _log.debug("Stripping " + completeURL);
82              }
83  
84              httpReq.setAttribute(_ALREADY_FILTERED, Boolean.TRUE);
85  
86              StripResponse stripResponse = new StripResponse(httpRes);
87  
88              doFilter(StripFilter.class, req, stripResponse, chain);
89  
90              String contentType = GetterUtil.getString(
91                  stripResponse.getContentType());
92  
93              byte[] oldByteArray = stripResponse.getData();
94  
95              if ((oldByteArray != null) && (oldByteArray.length > 0)) {
96                  byte[] newByteArray = new byte[oldByteArray.length];
97                  int newByteArrayPos = 0;
98  
99                  if (_log.isDebugEnabled()) {
100                     _log.debug("Stripping content of type " + contentType);
101                 }
102 
103                 if (contentType.toLowerCase().indexOf("text/") != -1) {
104                     boolean ignore = false;
105                     char prevChar = '\n';
106 
107                     for (int i = 0; i < oldByteArray.length; i++) {
108                         byte b = oldByteArray[i];
109                         char c = (char)b;
110 
111                         if (c == '<') {
112 
113                             // Ignore text inside certain HTML tags.
114 
115                             if (!ignore) {
116 
117                                 // Check for <pre>
118 
119                                 if ((i + 4) < oldByteArray.length) {
120                                     char c1 = (char)oldByteArray[i + 1];
121                                     char c2 = (char)oldByteArray[i + 2];
122                                     char c3 = (char)oldByteArray[i + 3];
123                                     char c4 = (char)oldByteArray[i + 4];
124 
125                                     if (((c1 == 'p') || (c1 == 'P')) &&
126                                         ((c2 == 'r') || (c2 == 'R')) &&
127                                         ((c3 == 'e') || (c3 == 'E')) &&
128                                         ((c4 == '>'))) {
129 
130                                         ignore = true;
131                                     }
132                                 }
133 
134                                 // Check for <textarea
135 
136                                 if (!ignore &&
137                                     ((i + 9) < oldByteArray.length)) {
138 
139                                     char c1 = (char)oldByteArray[i + 1];
140                                     char c2 = (char)oldByteArray[i + 2];
141                                     char c3 = (char)oldByteArray[i + 3];
142                                     char c4 = (char)oldByteArray[i + 4];
143                                     char c5 = (char)oldByteArray[i + 5];
144                                     char c6 = (char)oldByteArray[i + 6];
145                                     char c7 = (char)oldByteArray[i + 7];
146                                     char c8 = (char)oldByteArray[i + 8];
147                                     char c9 = (char)oldByteArray[i + 9];
148 
149                                     if (((c1 == 't') || (c1 == 'T')) &&
150                                         ((c2 == 'e') || (c2 == 'E')) &&
151                                         ((c3 == 'x') || (c3 == 'X')) &&
152                                         ((c4 == 't') || (c4 == 'T')) &&
153                                         ((c5 == 'a') || (c5 == 'A')) &&
154                                         ((c6 == 'r') || (c6 == 'R')) &&
155                                         ((c7 == 'e') || (c7 == 'E')) &&
156                                         ((c8 == 'a') || (c8 == 'A')) &&
157                                         ((c9 == ' '))) {
158 
159                                         ignore = true;
160                                     }
161                                 }
162                             }
163                             else if (ignore) {
164 
165                                 // Check for </pre>
166 
167                                 if ((i + 5) < oldByteArray.length) {
168                                     char c1 = (char)oldByteArray[i + 1];
169                                     char c2 = (char)oldByteArray[i + 2];
170                                     char c3 = (char)oldByteArray[i + 3];
171                                     char c4 = (char)oldByteArray[i + 4];
172                                     char c5 = (char)oldByteArray[i + 5];
173 
174                                     if (((c1 == '/')) &&
175                                         ((c2 == 'p') || (c2 == 'P')) &&
176                                         ((c3 == 'r') || (c3 == 'R')) &&
177                                         ((c4 == 'e') || (c4 == 'E')) &&
178                                         ((c5 == '>'))) {
179 
180                                         ignore = false;
181                                     }
182                                 }
183 
184                                 // Check for </textarea>
185 
186                                 if (ignore &&
187                                     ((i + 10) < oldByteArray.length)) {
188 
189                                     char c1 = (char)oldByteArray[i + 1];
190                                     char c2 = (char)oldByteArray[i + 2];
191                                     char c3 = (char)oldByteArray[i + 3];
192                                     char c4 = (char)oldByteArray[i + 4];
193                                     char c5 = (char)oldByteArray[i + 5];
194                                     char c6 = (char)oldByteArray[i + 6];
195                                     char c7 = (char)oldByteArray[i + 7];
196                                     char c8 = (char)oldByteArray[i + 8];
197                                     char c9 = (char)oldByteArray[i + 9];
198                                     char c10 = (char)oldByteArray[i + 10];
199 
200                                     if (((c1 == '/')) &&
201                                         ((c2 == 't') || (c2 == 'T')) &&
202                                         ((c3 == 'e') || (c3 == 'E')) &&
203                                         ((c4 == 'x') || (c4 == 'X')) &&
204                                         ((c5 == 't') || (c5 == 'T')) &&
205                                         ((c6 == 'a') || (c6 == 'A')) &&
206                                         ((c7 == 'r') || (c7 == 'R')) &&
207                                         ((c8 == 'e') || (c8 == 'E')) &&
208                                         ((c9 == 'a') || (c9 == 'A')) &&
209                                         ((c10 == '>'))) {
210 
211                                         ignore = false;
212                                     }
213                                 }
214                             }
215                         }
216 
217                         if ((!ignore) &&
218                             ((c == '\n') || (c == '\r') || (c == '\t'))) {
219 
220                             if ((i + 1) == oldByteArray.length) {
221                             }
222 
223                             if ((prevChar == '\n') || (prevChar == '\r')) {
224                             }
225                             else {
226                                 if (c != '\t') {
227                                     prevChar = c;
228                                 }
229 
230                                 newByteArray[newByteArrayPos++] = b;
231                             }
232                         }
233                         else {
234                             prevChar = c;
235 
236                             newByteArray[newByteArrayPos++] = b;
237                         }
238                     }
239                 }
240                 else {
241                     newByteArray = oldByteArray;
242                     newByteArrayPos = oldByteArray.length;
243                 }
244 
245                 ServletResponseUtil.write(
246                     httpRes, newByteArray, newByteArrayPos);
247             }
248         }
249         else {
250             if (_log.isDebugEnabled()) {
251                 _log.debug("Not stripping " + completeURL);
252             }
253 
254             doFilter(StripFilter.class, req, res, chain);
255         }
256     }
257 
258     protected boolean isAlreadyFiltered(HttpServletRequest req) {
259         if (req.getAttribute(_ALREADY_FILTERED) != null) {
260             return true;
261         }
262         else {
263             return false;
264         }
265     }
266 
267     protected boolean isInclude(HttpServletRequest req) {
268         String uri = (String)req.getAttribute(
269             JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
270 
271         if (uri == null) {
272             return false;
273         }
274         else {
275             return true;
276         }
277     }
278 
279     protected boolean isStrip(HttpServletRequest req) {
280         if (!ParamUtil.get(req, _STRIP, true)) {
281             return false;
282         }
283         else {
284 
285             // The exclusive state is used to stream binary content.
286             // Compressing binary content through a servlet filter is bad on
287             // performance because the user will not start downloading the
288             // content until the entire content is compressed.
289 
290             boolean action = ParamUtil.getBoolean(req, "p_p_action");
291             String windowState = ParamUtil.getString(req, "p_p_state");
292 
293             if (action && windowState.equals("exclusive")) {
294                 return false;
295             }
296             else {
297                 return true;
298             }
299         }
300     }
301 
302     private static final String _ALREADY_FILTERED =
303         StripFilter.class.getName() + "_ALREADY_FILTERED";
304 
305     private static final String _STRIP = "strip";
306 
307     private static Log _log = LogFactoryUtil.getLog(StripFilter.class);
308 
309 }