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