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(
217 oldByteArray, i, _MARKER_JAVASCRIPT_OPEN) ||
218 hasMarker(oldByteArray, i, _MARKER_SCRIPT_OPEN)) {
219
220 state = _STATE_MINIFY_SCRIPT;
221 }
222 else if (hasMarker(oldByteArray, i, _MARKER_STYLE_OPEN)) {
223 state = _STATE_MINIFY_STYLE;
224 }
225 }
226 else if (state == _STATE_IGNORE) {
227 if (hasMarker(oldByteArray, i, _MARKER_PRE_CLOSE) ||
228 hasMarker(oldByteArray, i, _MARKER_TEXTAREA_CLOSE)) {
229
230 state = _STATE_NORMAL;
231 }
232 }
233 else if (state == _STATE_MINIFY_SCRIPT) {
234 if (hasMarker(oldByteArray, i, _MARKER_SCRIPT_CLOSE)) {
235 state = _STATE_NORMAL;
236
237 String scriptContent = scriptBytes.toString(
238 StringPool.UTF8);
239
240 scriptBytes = new ByteArrayMaker();
241
242 int pos = scriptContent.indexOf(CharPool.GREATER_THAN);
243
244 scriptContent = scriptContent.substring(pos + 1).trim();
245
246 if (Validator.isNull(scriptContent)) {
247 i += _MARKER_SCRIPT_CLOSE.length;
248
249 continue;
250 }
251
252 scriptContent = MinifierUtil.minifyJavaScript(
253 scriptContent);
254
255 scriptContent =
256 _CDATA_OPEN + scriptContent + _CDATA_CLOSE;
257
258 if (Validator.isNull(scriptContent)) {
259 i += _MARKER_SCRIPT_CLOSE.length;
260
261 continue;
262 }
263
264 scriptContent = _SCRIPT_TYPE_JAVASCRIPT + scriptContent;
265
266 byte[] scriptContentBytes = scriptContent.getBytes(
267 StringPool.UTF8);
268
269 for (byte curByte : scriptContentBytes) {
270 newByteArray[newByteArrayPos++] = curByte;
271 }
272
273 state = _STATE_FOUND_ELEMENT;
274 }
275 }
276 else if (state == _STATE_MINIFY_STYLE) {
277 if (hasMarker(oldByteArray, i, _MARKER_STYLE_CLOSE)) {
278 state = _STATE_NORMAL;
279
280 String styleContent = styleBytes.toString(
281 StringPool.UTF8);
282
283 styleBytes = new ByteArrayMaker();
284
285 styleContent = styleContent.substring(
286 _STYLE_TYPE_CSS.length()).trim();
287
288 if (Validator.isNull(styleContent)) {
289 i += _MARKER_STYLE_CLOSE.length;
290
291 continue;
292 }
293
294 styleContent = MinifierUtil.minifyCss(styleContent);
295
296 if (Validator.isNull(styleContent)) {
297 i += _MARKER_STYLE_CLOSE.length;
298
299 continue;
300 }
301
302 styleContent = _STYLE_TYPE_CSS + styleContent;
303
304 byte[] styleContentBytes = styleContent.getBytes(
305 StringPool.UTF8);
306
307 for (byte curByte : styleContentBytes) {
308 newByteArray[newByteArrayPos++] = curByte;
309 }
310
311 state = _STATE_FOUND_ELEMENT;
312 }
313 }
314 }
315 else if (c == CharPool.GREATER_THAN) {
316 if (state == _STATE_FOUND_ELEMENT) {
317 state = _STATE_NORMAL;
318
319 newByteArray[newByteArrayPos++] = b;
320
321 while ((i + 1) < oldByteArray.length) {
322 char nextChar = (char)oldByteArray[i + 1];
323
324 if (Validator.isWhitespace(nextChar)) {
325 i++;
326 }
327 else {
328 break;
329 }
330 }
331
332 continue;
333 }
334 }
335
336 if (state == _STATE_NORMAL) {
337 if ((i + 1) < oldByteArray.length) {
338 if (removeStartingWhitespace) {
339 if (Validator.isWhitespace(c)) {
340 continue;
341 }
342 else {
343 removeStartingWhitespace = false;
344 }
345 }
346
347 if ((c == CharPool.NEW_LINE) ||
348 (c == CharPool.RETURN) ||
349 (c == CharPool.TAB)) {
350
351 char nextChar = (char)oldByteArray[i + 1];
352
353 if ((nextChar == CharPool.NEW_LINE) ||
354 (nextChar == CharPool.RETURN) ||
355 (nextChar == CharPool.TAB)) {
356
357 continue;
358 }
359 }
360 }
361 }
362
363 if (state == _STATE_MINIFY_SCRIPT) {
364 scriptBytes.write(b);
365 }
366 else if (state == _STATE_MINIFY_STYLE) {
367 styleBytes.write(b);
368 }
369 else {
370 newByteArray[newByteArrayPos++] = b;
371 }
372 }
373
374 if (newByteArrayPos > 1) {
375 for (int i = newByteArrayPos - 1; i > 0; i--) {
376 byte b = newByteArray[i];
377
378 char c = (char)b;
379
380 if (Validator.isWhitespace(c)) {
381 newByteArrayPos--;
382 }
383 else {
384 break;
385 }
386 }
387 }
388
389 if (state == _STATE_MINIFY_SCRIPT) {
390 _log.error("Missing </script>");
391 }
392 else if (state == _STATE_MINIFY_STYLE) {
393 _log.error("Missing </style>");
394 }
395
396 return new Object[] {newByteArray, newByteArrayPos};
397 }
398
399 private static final String _CDATA_CLOSE = "/*]]>*/";
400
401 private static final String _CDATA_OPEN = "/*<![CDATA[*/";
402
403 private static final char[] _MARKER_DIV_CLOSE = "/div>".toCharArray();
404
405 private static final char[] _MARKER_FORM_CLOSE = "/form>".toCharArray();
406
407 private static final char[] _MARKER_JAVASCRIPT_OPEN =
408 "script type=\"text/javascript\">".toCharArray();
409
410 private static final char[] _MARKER_LI_CLOSE = "/li>".toCharArray();
411
412 private static final char[] _MARKER_PRE_CLOSE = "/pre>".toCharArray();
413
414 private static final char[] _MARKER_PRE_OPEN = "pre>".toCharArray();
415
416 private static final char[] _MARKER_SCRIPT_OPEN = "script>".toCharArray();
417
418 private static final char[] _MARKER_SCRIPT_CLOSE = "/script>".toCharArray();
419
420 private static final char[] _MARKER_STYLE_OPEN =
421 "style type=\"text/css\">".toCharArray();
422
423 private static final char[] _MARKER_STYLE_CLOSE = "/style>".toCharArray();
424
425 private static final char[] _MARKER_TABLE_CLOSE = "/table>".toCharArray();
426
427 private static final char[] _MARKER_TD_CLOSE = "/td>".toCharArray();
428
429 private static final char[] _MARKER_TD_OPEN = "td>".toCharArray();
430
431 private static final char[] _MARKER_TR_CLOSE = "/tr>".toCharArray();
432
433 private static final char[] _MARKER_TR_OPEN = "tr>".toCharArray();
434
435 private static final char[] _MARKER_TEXTAREA_CLOSE =
436 "/textarea>".toCharArray();
437
438 private static final char[] _MARKER_TEXTAREA_OPEN =
439 "textarea ".toCharArray();
440
441 private static final char[] _MARKER_UL_CLOSE = "/ul>".toCharArray();
442
443 private static final String _SCRIPT_TYPE_JAVASCRIPT =
444 "<script type=\"text/javascript\">";
445
446 private static final int _STATE_FOUND_ELEMENT = 3;
447
448 private static final int _STATE_IGNORE = 1;
449
450 private static final int _STATE_MINIFY_SCRIPT = 4;
451
452 private static final int _STATE_MINIFY_STYLE = 5;
453
454 private static final int _STATE_NORMAL = 0;
455
456 private static final String _STYLE_TYPE_CSS = "<style type=\"text/css\">";
457
458 private static final String _STRIP = "strip";
459
460 private static Log _log = LogFactoryUtil.getLog(StripFilter.class);
461
462 }