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