/* * Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.agiletec.aps.tags; import java.io.CharArrayWriter; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.agiletec.aps.system.RequestContext; import com.agiletec.aps.system.SystemConstants; import com.agiletec.aps.system.services.page.IPage; import com.agiletec.aps.system.services.page.Widget; import com.agiletec.aps.util.ApsProperties; /** * Tag for widget "InternalServlet". * Publish a function erogated throw a internal Servlet; the servlet is invoked by a path specificated * by the tag attribute "actionPath" or by the widget parameter of the same name. * @author M.Casari - E.Santoboni */ public class InternalServletTag extends TagSupport { private static final Logger _logger = LoggerFactory.getLogger(InternalServletTag.class); /** * Internal class that wrappers the response, extending the * javax.servlet.http.HttpServletResponseWrapper class to * define a proprietary output channel. * It is used to retrieve the content response after having * made an 'include' in the RequestDispatcher */ public class ResponseWrapper extends HttpServletResponseWrapper { public ResponseWrapper(HttpServletResponse response) { super(response); _output = new CharArrayWriter(); } @Override public PrintWriter getWriter() { return new PrintWriter(_output); } @Override public void sendRedirect(String path) throws IOException { this._redirectPath = path; } @Override public void addCookie(Cookie cookie) { super.addCookie(cookie); int len = (null == this._cookiesToAdd) ? 0 : this._cookiesToAdd.length; Cookie[] newCookiesToAdd = new Cookie[len + 1]; if (null != this._cookiesToAdd) { for (int i=0; i < len; i++) { newCookiesToAdd[i] = this._cookiesToAdd[i]; } } newCookiesToAdd[len] = cookie; this._cookiesToAdd = newCookiesToAdd; } protected Cookie[] getCookiesToAdd() { return _cookiesToAdd; } public boolean isRedirected() { return (_redirectPath != null); } public String getRedirectPath() { return _redirectPath; } @Override public String toString() { return _output.toString(); } private String _redirectPath; private CharArrayWriter _output; private Cookie[] _cookiesToAdd; } /** * Invokes the widget configured in the current page. * @throws JspException in case of error that occurred in both this method * or in one of the included JSPs */ @Override public int doEndTag() throws JspException { int result = super.doEndTag(); ServletRequest req = this.pageContext.getRequest(); RequestContext reqCtx = (RequestContext) req.getAttribute(RequestContext.REQCTX); try { IPage page = (IPage) reqCtx.getExtraParam(SystemConstants.EXTRAPAR_CURRENT_PAGE); ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse)this.pageContext.getResponse()); String output = this.buildWidgetOutput(page, responseWrapper); if (responseWrapper.isRedirected()) { String redirect = responseWrapper.getRedirectPath(); reqCtx.addExtraParam(SystemConstants.EXTRAPAR_EXTERNAL_REDIRECT, redirect); result = SKIP_PAGE; } else { this.pageContext.getOut().print(output); } } catch (Throwable t) { _logger.error("Error in widget preprocessing", t); String msg = "Error in widget preprocessing"; throw new JspException(msg, t); } return result; } protected String buildWidgetOutput(IPage page, ResponseWrapper responseWrapper) throws JspException { String output = null; ServletRequest req = this.pageContext.getRequest(); RequestContext reqCtx = (RequestContext) req.getAttribute(RequestContext.REQCTX); try { Widget widget = (Widget) reqCtx.getExtraParam(SystemConstants.EXTRAPAR_CURRENT_WIDGET); this.includeWidget(reqCtx, responseWrapper, widget); Cookie[] cookies = responseWrapper.getCookiesToAdd(); if (null != cookies) { for (int i = 0; i < cookies.length; i++) { Cookie cookie = cookies[i]; reqCtx.getResponse().addCookie(cookie); } } output = responseWrapper.toString(); responseWrapper.getWriter().close(); } catch (Throwable t) { String msg = "Error building widget output"; throw new JspException(msg, t); } return output; } protected void includeWidget(RequestContext reqCtx, ResponseWrapper responseWrapper, Widget widget) throws ServletException, IOException { HttpServletRequest request = reqCtx.getRequest(); try { String actionPath = this.extractIntroActionPath(reqCtx, widget); if (!this.isStaticAction()) { String requestActionPath = request.getParameter(REQUEST_PARAM_ACTIONPATH); String currentFrameActionPath = request.getParameter(REQUEST_PARAM_FRAMEDEST); Integer currentFrame = (Integer) reqCtx.getExtraParam(SystemConstants.EXTRAPAR_CURRENT_FRAME); if (requestActionPath != null && currentFrameActionPath != null && currentFrame.toString().equals(currentFrameActionPath)) { if (this.isAllowedRequestPath(requestActionPath) && !this.isRecursivePath(requestActionPath, request)) { actionPath = requestActionPath; } } } reqCtx.addExtraParam(EXTRAPAR_STATIC_ACTION, this.isStaticAction()); RequestDispatcher requestDispatcher = request.getRequestDispatcher(actionPath); requestDispatcher.include(request, responseWrapper); } catch (Throwable t) { _logger.error("Error including widget", t); RequestDispatcher requestDispatcher = request.getRequestDispatcher("/WEB-INF/aps/jsp/system/internalServlet_error.jsp"); requestDispatcher.include(request, responseWrapper); } } protected boolean isAllowedRequestPath(String requestActionPath) { String rapLowerCase = requestActionPath.toLowerCase(); if (rapLowerCase.contains("web-inf") || rapLowerCase.contains("meta-inf") || rapLowerCase.contains("../") || rapLowerCase.contains("%2e%2e%2f") || rapLowerCase.endsWith(".txt") || rapLowerCase.contains("<") || rapLowerCase.endsWith("%3c") || rapLowerCase.endsWith("%00") || rapLowerCase.endsWith("'") || rapLowerCase.endsWith("\"")) { return false; } return true; } protected boolean isRecursivePath(String requestActionPath, HttpServletRequest request) { String contextPath = request.getContextPath(); if (!requestActionPath.contains(contextPath)) { return false; } String prefix = contextPath + "/pages/"; return (requestActionPath.contains(".wp") || requestActionPath.contains(".page") || requestActionPath.contains(prefix)); } /** * Extract the init Action Path. * Return the tag attribute (if set), else the widget parameter. * @param reqCtx The request context. * @param widget The current widget. * @return The init Action Path */ protected String extractIntroActionPath(RequestContext reqCtx, Widget widget) { String actionPath = this.getActionPath(); if (null == this.getActionPath()) { ApsProperties config = widget.getConfig(); if (widget.getType().isLogic()) { config = widget.getType().getConfig(); } if (null != config) { actionPath = config.getProperty(CONFIG_PARAM_ACTIONPATH); } } if (null == actionPath || actionPath.trim().length() == 0) { IPage page = (IPage) reqCtx.getExtraParam(SystemConstants.EXTRAPAR_CURRENT_PAGE); throw new RuntimeException("Null init action path : page " + page.getCode()); } return actionPath; } @Override public void release() { super.release(); this.setActionPath(null); this.setStaticAction(false); } public String getActionPath() { return _actionPath; } public void setActionPath(String actionPath) { this._actionPath = actionPath; } public boolean isStaticAction() { return _staticAction; } public void setStaticAction(boolean staticAction) { this._staticAction = staticAction; } private String _actionPath; private boolean _staticAction; public static final String CONFIG_PARAM_ACTIONPATH = "actionPath"; public static final String REQUEST_PARAM_ACTIONPATH = "internalServletActionPath"; public static final String REQUEST_PARAM_FRAMEDEST = "internalServletFrameDest"; public static final String EXTRAPAR_STATIC_ACTION = "internalServletStaticAction"; }