1
19
20 package com.liferay.portal.servlet.filters.strip;
21
22 import com.liferay.portal.kernel.log.Log;
23 import com.liferay.portal.kernel.log.LogFactoryUtil;
24 import com.liferay.portal.kernel.portlet.LiferayWindowState;
25 import com.liferay.portal.kernel.util.ByteArrayMaker;
26 import com.liferay.portal.kernel.util.CharPool;
27 import com.liferay.portal.kernel.util.GetterUtil;
28 import com.liferay.portal.kernel.util.HttpUtil;
29 import com.liferay.portal.kernel.util.JavaConstants;
30 import com.liferay.portal.kernel.util.ParamUtil;
31 import com.liferay.portal.kernel.util.StringPool;
32 import com.liferay.portal.kernel.util.Validator;
33 import com.liferay.portal.servlet.filters.BasePortalFilter;
34 import com.liferay.portal.util.MinifierUtil;
35 import com.liferay.util.servlet.ServletResponseUtil;
36
37 import java.io.IOException;
38
39 import javax.servlet.FilterChain;
40 import javax.servlet.ServletException;
41 import javax.servlet.http.HttpServletRequest;
42 import javax.servlet.http.HttpServletResponse;
43
44
51 public class StripFilter extends BasePortalFilter {
52
53 public static final String SKIP_FILTER =
54 StripFilter.class.getName() + "SKIP_FILTER";
55
56 protected boolean hasMarker(byte[] oldByteArray, int pos, char[] marker) {
57 if ((pos + marker.length) >= oldByteArray.length) {
58 return false;
59 }
60
61 for (int i = 0; i < marker.length; i++) {
62 char c = marker[i];
63
64 char oldC = (char)oldByteArray[pos + i + 1];
65
66 if ((c != oldC) &&
67 (Character.toUpperCase(c) != oldC)) {
68
69 return false;
70 }
71 }
72
73 return true;
74 }
75
76 protected boolean isAlreadyFiltered(HttpServletRequest request) {
77 if (request.getAttribute(SKIP_FILTER) != null) {
78 return true;
79 }
80 else {
81 return false;
82 }
83 }
84
85 protected boolean isInclude(HttpServletRequest request) {
86 String uri = (String)request.getAttribute(
87 JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
88
89 if (uri == null) {
90 return false;
91 }
92 else {
93 return true;
94 }
95 }
96
97 protected boolean isStrip(HttpServletRequest request) {
98 if (!ParamUtil.getBoolean(request, _STRIP, true)) {
99 return false;
100 }
101 else {
102
103
107 String lifecycle = ParamUtil.getString(request, "p_p_lifecycle");
108
109 if ((lifecycle.equals("1") &&
110 LiferayWindowState.isExclusive(request)) ||
111 lifecycle.equals("2")) {
112
113 return false;
114 }
115 else {
116 return true;
117 }
118 }
119 }
120
121 protected void processFilter(
122 HttpServletRequest request, HttpServletResponse response,
123 FilterChain filterChain)
124 throws IOException, ServletException {
125
126 String completeURL = HttpUtil.getCompleteURL(request);
127
128 if (isStrip(request) && !isInclude(request) &&
129 !isAlreadyFiltered(request)) {
130
131 if (_log.isDebugEnabled()) {
132 _log.debug("Stripping " + completeURL);
133 }
134
135 request.setAttribute(SKIP_FILTER, Boolean.TRUE);
136
137 StripResponse stripResponse = new StripResponse(response);
138
139 processFilter(
140 StripFilter.class, request, stripResponse, filterChain);
141
142 String contentType = GetterUtil.getString(
143 stripResponse.getContentType()).toLowerCase();
144
145 byte[] oldByteArray = stripResponse.getData();
146
147 if ((oldByteArray != null) && (oldByteArray.length > 0)) {
148 byte[] newByteArray = null;
149 int newByteArrayPos = 0;
150
151 if (_log.isDebugEnabled()) {
152 _log.debug("Stripping content of type " + contentType);
153 }
154
155 if (contentType.indexOf("text/") != -1) {
156 Object[] value = strip(oldByteArray);
157
158 newByteArray = (byte[])value[0];
159 newByteArrayPos = (Integer)value[1];
160 }
161 else {
162 newByteArray = oldByteArray;
163 newByteArrayPos = oldByteArray.length;
164 }
165
166 ServletResponseUtil.write(
167 response, newByteArray, newByteArrayPos);
168 }
169 }
170 else {
171 if (_log.isDebugEnabled()) {
172 _log.debug("Not stripping " + completeURL);
173 }
174
175 processFilter(StripFilter.class, request, response, filterChain);
176 }
177 }
178
179 protected Object[] strip(byte[] oldByteArray) throws IOException {
180 byte[] newByteArray = new byte[oldByteArray.length];
181 int newByteArrayPos = 0;
182
183 int state = _STATE_NORMAL;
184
185 boolean removeStartingWhitespace = true;
186
187 ByteArrayMaker scriptBytes = new ByteArrayMaker();
188 ByteArrayMaker styleBytes = new ByteArrayMaker();
189
190 for (int i = 0; i < oldByteArray.length; i++) {
191 byte b = oldByteArray[i];
192
193 char c = (char)b;
194
195 if (c == CharPool.LESS_THAN) {
196 if (state == _STATE_NORMAL) {
197 if (hasMarker(oldByteArray, i, _MARKER_PRE_OPEN) ||
198 hasMarker(oldByteArray, i, _MARKER_TEXTAREA_OPEN)) {
199
200 state = _STATE_IGNORE;
201 }
202 else if (hasMarker(oldByteArray, i, _MARKER_DIV_CLOSE) ||
203 hasMarker(oldByteArray, i, _MARKER_FORM_CLOSE) ||
204 hasMarker(oldByteArray, i, _MARKER_LI_CLOSE) ||
205 hasMarker(oldByteArray, i, _MARKER_SCRIPT_CLOSE) ||
206 hasMarker(oldByteArray, i, _MARKER_STYLE_CLOSE) ||
207 hasMarker(oldByteArray, i, _MARKER_TABLE_CLOSE) ||
208 hasMarker(oldByteArray, i, _MARKER_TD_CLOSE) ||
209 hasMarker(oldByteArray, i, _MARKER_TD_OPEN) ||
210 hasMarker(oldByteArray, i, _MARKER_TR_CLOSE) ||
211 hasMarker(oldByteArray, i, _MARKER_TR_OPEN) ||
212 hasMarker(oldByteArray, i, _MARKER_UL_CLOSE)) {
213
214 state = _STATE_FOUND_ELEMENT;
215 }
216 else if (hasMarker(oldByteArray, i, _MARKER_SCRIPT_OPEN)) {
217 state = _STATE_MINIFY_SCRIPT;
218 }
219 else if (hasMarker(oldByteArray, i, _MARKER_STYLE_OPEN)) {
220 state = _STATE_MINIFY_STYLE;
221 }
222 }
223 else if (state == _STATE_IGNORE) {
224 if (hasMarker(oldByteArray, i, _MARKER_PRE_CLOSE) ||
225 hasMarker(oldByteArray, i, _MARKER_TEXTAREA_CLOSE)) {
226
227 state = _STATE_NORMAL;
228 }
229 }
230 else if (state == _STATE_MINIFY_SCRIPT) {
231 if (hasMarker(oldByteArray, i, _MARKER_SCRIPT_CLOSE)) {
232 state = _STATE_NORMAL;
233
234 String scriptContent = scriptBytes.toString(
235 StringPool.UTF8);
236
237 scriptBytes = new ByteArrayMaker();
238
239 scriptContent = scriptContent.substring(
240 _SCRIPT_TYPE_JAVASCRIPT.length()).trim();
241
242 if (Validator.isNull(scriptContent)) {
243 i += _MARKER_SCRIPT_CLOSE.length;
244
245 continue;
246 }
247
248 scriptContent = MinifierUtil.minifyJavaScript(
249 scriptContent);
250
251 if (Validator.isNull(scriptContent)) {
252 i += _MARKER_SCRIPT_CLOSE.length;
253
254 continue;
255 }
256
257 scriptContent = _SCRIPT_TYPE_JAVASCRIPT + scriptContent;
258
259 byte[] scriptContentBytes = scriptContent.getBytes(
260 StringPool.UTF8);
261
262 for (byte curByte : scriptContentBytes) {
263 newByteArray[newByteArrayPos++] = curByte;
264 }
265
266 state = _STATE_FOUND_ELEMENT;
267 }
268 }
269 else if (state == _STATE_MINIFY_STYLE) {
270 if (hasMarker(oldByteArray, i, _MARKER_STYLE_CLOSE)) {
271 state = _STATE_NORMAL;
272
273 String styleContent = styleBytes.toString(
274 StringPool.UTF8);
275
276 styleBytes = new ByteArrayMaker();
277
278 styleContent = styleContent.substring(
279 _STYLE_TYPE_CSS.length()).trim();
280
281 if (Validator.isNull(styleContent)) {
282 i += _MARKER_STYLE_CLOSE.length;
283
284 continue;
285 }
286
287 styleContent = MinifierUtil.minifyCss(styleContent);
288
289 if (Validator.isNull(styleContent)) {
290 i += _MARKER_STYLE_CLOSE.length;
291
292 continue;
293 }
294
295 styleContent = _STYLE_TYPE_CSS + styleContent;
296
297 byte[] styleContentBytes = styleContent.getBytes(
298 StringPool.UTF8);
299
300 for (byte curByte : styleContentBytes) {
301 newByteArray[newByteArrayPos++] = curByte;
302 }
303
304 state = _STATE_FOUND_ELEMENT;
305 }
306 }
307 }
308 else if (c == CharPool.GREATER_THAN) {
309 if (state == _STATE_FOUND_ELEMENT) {
310 state = _STATE_NORMAL;
311
312 newByteArray[newByteArrayPos++] = b;
313
314 while ((i + 1) < oldByteArray.length) {
315 char nextChar = (char)oldByteArray[i + 1];
316
317 if (Validator.isWhitespace(nextChar)) {
318 i++;
319 }
320 else {
321 break;
322 }
323 }
324
325 continue;
326 }
327 }
328
329 if (state == _STATE_NORMAL) {
330 if ((i + 1) < oldByteArray.length) {
331 if (removeStartingWhitespace) {
332 if (Validator.isWhitespace(c)) {
333 continue;
334 }
335 else {
336 removeStartingWhitespace = false;
337 }
338 }
339
340 if ((c == CharPool.NEW_LINE) ||
341 (c == CharPool.RETURN) ||
342 (c == CharPool.TAB)) {
343
344 char nextChar = (char)oldByteArray[i + 1];
345
346 if ((nextChar == CharPool.NEW_LINE) ||
347 (nextChar == CharPool.RETURN) ||
348 (nextChar == CharPool.TAB)) {
349
350 continue;
351 }
352 }
353 }
354 }
355
356 if (state == _STATE_MINIFY_SCRIPT) {
357 scriptBytes.write(b);
358 }
359 else if (state == _STATE_MINIFY_STYLE) {
360 styleBytes.write(b);
361 }
362 else {
363 newByteArray[newByteArrayPos++] = b;
364 }
365 }
366
367 if (newByteArrayPos > 1) {
368 for (int i = newByteArrayPos - 1; i > 0; i--) {
369 byte b = newByteArray[i];
370
371 char c = (char)b;
372
373 if (Validator.isWhitespace(c)) {
374 newByteArrayPos--;
375 }
376 else {
377 break;
378 }
379 }
380 }
381
382 if (state == _STATE_MINIFY_SCRIPT) {
383 _log.error("Missing </script>");
384 }
385 else if (state == _STATE_MINIFY_STYLE) {
386 _log.error("Missing </style>");
387 }
388
389 return new Object[] {newByteArray, newByteArrayPos};
390 }
391
392 private static final char[] _MARKER_DIV_CLOSE = "/div>".toCharArray();
393
394 private static final char[] _MARKER_FORM_CLOSE = "/form>".toCharArray();
395
396 private static final char[] _MARKER_LI_CLOSE = "/li>".toCharArray();
397
398 private static final char[] _MARKER_PRE_CLOSE = "/pre>".toCharArray();
399
400 private static final char[] _MARKER_PRE_OPEN = "pre>".toCharArray();
401
402 private static final char[] _MARKER_SCRIPT_OPEN =
403 "script type=\"text/javascript\">".toCharArray();
404
405 private static final char[] _MARKER_SCRIPT_CLOSE = "/script>".toCharArray();
406
407 private static final char[] _MARKER_STYLE_OPEN =
408 "style type=\"text/css\">".toCharArray();
409
410 private static final char[] _MARKER_STYLE_CLOSE = "/style>".toCharArray();
411
412 private static final char[] _MARKER_TABLE_CLOSE = "/table>".toCharArray();
413
414 private static final char[] _MARKER_TD_CLOSE = "/td>".toCharArray();
415
416 private static final char[] _MARKER_TD_OPEN = "td>".toCharArray();
417
418 private static final char[] _MARKER_TR_CLOSE = "/tr>".toCharArray();
419
420 private static final char[] _MARKER_TR_OPEN = "tr>".toCharArray();
421
422 private static final char[] _MARKER_TEXTAREA_CLOSE =
423 "/textarea>".toCharArray();
424
425 private static final char[] _MARKER_TEXTAREA_OPEN =
426 "textarea ".toCharArray();
427
428 private static final char[] _MARKER_UL_CLOSE = "/ul>".toCharArray();
429
430 private static final String _SCRIPT_TYPE_JAVASCRIPT =
431 "<script type=\"text/javascript\">";
432
433 private static final int _STATE_FOUND_ELEMENT = 3;
434
435 private static final int _STATE_IGNORE = 1;
436
437 private static final int _STATE_MINIFY_SCRIPT = 4;
438
439 private static final int _STATE_MINIFY_STYLE = 5;
440
441 private static final int _STATE_NORMAL = 0;
442
443 private static final String _STYLE_TYPE_CSS = "<style type=\"text/css\">";
444
445 private static final String _STRIP = "strip";
446
447 private static Log _log = LogFactoryUtil.getLog(StripFilter.class);
448
449 }