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