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