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