/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/portal/trunk/portal-render-impl/impl/src/java/org/sakaiproject/portal/render/iframe/IFrameToolRenderService.java $
* $Id: IFrameToolRenderService.java 128674 2013-08-20 15:14:33Z csev@umich.edu $
***********************************************************************************
*
* Copyright (c) 2005, 2006, 2007, 2008 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.portal.render.iframe;
import java.io.IOException;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.exception.PermissionException;
import org.sakaiproject.portal.api.Portal;
import org.sakaiproject.portal.api.PortalService;
import org.sakaiproject.portal.api.StoredState;
import org.sakaiproject.portal.render.api.RenderResult;
import org.sakaiproject.portal.render.api.ToolRenderException;
import org.sakaiproject.portal.render.api.ToolRenderService;
import org.sakaiproject.portal.util.ByteArrayServletResponse;
import org.sakaiproject.portal.util.URLUtils;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.ToolConfiguration;
import org.sakaiproject.site.cover.SiteService;
import org.sakaiproject.tool.api.ActiveTool;
import org.sakaiproject.tool.api.Session;
import org.sakaiproject.tool.api.Tool;
import org.sakaiproject.tool.api.ToolException;
import org.sakaiproject.tool.api.ToolSession;
import org.sakaiproject.tool.cover.ActiveToolManager;
import org.sakaiproject.tool.cover.SessionManager;
import org.sakaiproject.util.Web;
/**
* I Frame tool renderer, renders the iframe header to contain the tool content
*
* @author ddwolf
* @since Sakai 2.4
* @version $Rev: 128674 $
*/
public class IFrameToolRenderService implements ToolRenderService
{
private static final Log LOG = LogFactory.getLog(IFrameToolRenderService.class);
private PortalService portalService;
// private static ResourceLoader rb = new ResourceLoader("sitenav");
public boolean preprocess(Portal portal, HttpServletRequest request, HttpServletResponse response,
ServletContext context) throws IOException, ToolRenderException
{
return true;
}
public RenderResult render(Portal portal, ToolConfiguration configuration,
HttpServletRequest request, HttpServletResponse response,
ServletContext context) throws IOException, ToolRenderException
{
final String titleString = Web.escapeHtml(configuration.getTitle());
String toolUrl = ServerConfigurationService.getToolUrl() + "/"
+ Web.escapeUrl(configuration.getId());
StoredState ss = portalService.getStoredState();
LOG.debug("Restoring Iframe [" + ss + "]");
Map parametermap = ss == null ? request.getParameterMap() : ss
.getRequest(request).getParameterMap();
String URLstub = portalService.decodeToolState(parametermap, configuration
.getId());
if (URLstub != null)
{
toolUrl += URLstub;
}
String sakaiPanel = request.getParameter("panel");
if ( sakaiPanel != null && sakaiPanel.matches(".*[\"'<>].*" ) ) sakaiPanel=null;
if ( sakaiPanel == null ) sakaiPanel="Main";
toolUrl = URLUtils.addParameter(toolUrl, "panel", sakaiPanel);
final StringBuilder sb = new StringBuilder();
//SAK-18792 if we don't replace '&' characters we will end up with malformed XML. '&' signifies the beginning
// of an XML "entity" like < >, etc.
toolUrl = toolUrl.replace("&", "&");
// SAK-20462 - Pass through the sakai_action parameter
String sakaiAction = request.getParameter("sakai_action");
if ( sakaiAction != null && sakaiAction.matches(".*[\"'<>].*" ) ) sakaiAction=null;
// Produce the iframe markup
sb.append("<iframe").append(" name=\"").append(
Web.escapeJavascript("Main" + configuration.getId())).append("\"\n")
.append(" id=\"").append(
Web.escapeJavascript("Main" + configuration.getId()))
.append("\"\n title=\"").append(titleString).append(" ").
/* append(Web.escapeHtml(rb.getString("sit.contentporttit"))). */
append("\"").append("\n").append(" class =\"portletMainIframe\"").append(
"\n").append(" height=\"475\"").append("\n").append(
" width=\"100%\"").append("\n").append(" frameborder=\"0\"")
.append("\n").append(" marginwidth=\"0\"").append("\n").append(
" marginheight=\"0\"").append("\n").append(" scrolling=\"auto\"")
.append("\n").append(" src=\"").append(toolUrl);
if ( sakaiAction != null )
{
sb.append( toolUrl.indexOf('?') >=0 ? '&' : '?');
sb.append("sakai_action=").append(Web.escapeHtml(sakaiAction));
}
sb.append("\">") .append("\n").append("</iframe>");
final String[] buffered = bufferContent(portal,request, response, configuration);
RenderResult result = new RenderResult()
{
public String getHead() {
if ( buffered != null ) {
return buffered[0];
}
return "";
}
public String getTitle()
{
return titleString;
}
public String getContent()
{
if ( buffered != null ) {
return buffered[1];
}
return sb.toString();
}
public void setContent(String content)
{
return; // Not allowed
}
public String getJSR168EditUrl()
{
return null;
}
public String getJSR168HelpUrl()
{
return null;
}
};
return result;
}
public boolean accept(Portal portal, ToolConfiguration configuration, HttpServletRequest request,
HttpServletResponse response, ServletContext context)
{
return true;
}
public void reset( ToolConfiguration configuration)
{
}
/**
* @return the portalService
*/
public PortalService getPortalService()
{
return portalService;
}
/**
* @param portalService
* the portalService to set
*/
public void setPortalService(PortalService portalService)
{
this.portalService = portalService;
}
public String[] bufferContent(Portal portal, HttpServletRequest req, HttpServletResponse res,
ToolConfiguration toolConfig)
{
if ( toolConfig == null ) return null;
if (toolConfig.getId() == null) return null;
String tidAllow = ServerConfigurationService
.getString("portal.experimental.iframesuppress");
if (tidAllow == null) return null;
if (tidAllow.indexOf(":all:") < 0)
{
if (tidAllow.indexOf(toolConfig.getToolId()) < 0) return null;
}
ByteArrayServletResponse bufferedResponse = new ByteArrayServletResponse(res);
try
{
boolean retval = doToolBuffer(portal, req, bufferedResponse, toolConfig);
if (!retval) return null;
}
catch (Exception e)
{
return null;
}
String responseStr = bufferedResponse.getInternalBuffer();
if (responseStr == null || responseStr.length() < 1) return null;
String responseStrLower = responseStr.toLowerCase();
int headStart = responseStrLower.indexOf("<head");
headStart = findEndOfTag(responseStrLower, headStart);
int headEnd = responseStrLower.indexOf("</head");
int bodyStart = responseStrLower.indexOf("<body");
bodyStart = findEndOfTag(responseStrLower, bodyStart);
// Some tools (Blogger for example) have multiple
// head-body pairs - browsers seem to not care much about
// this so we will do the same - so tht we can be
// somewhat clean - we search for the "last" end
// body tag - for the normal case there will only be one
int bodyEnd = responseStrLower.lastIndexOf("</body");
// If there is no body end at all or it is before the body
// start tag we simply - take the rest of the response
if (bodyEnd < bodyStart) bodyEnd = responseStrLower.length() - 1;
if (tidAllow.indexOf(":debug:") >= 0)
{
LOG.info("Frameless HS=" + headStart + " HE=" + headEnd + " BS=" + bodyStart
+ " BE=" + bodyEnd);
}
if (bodyEnd > bodyStart && bodyStart > headEnd && headEnd > headStart
&& headStart > 1)
{
String headString = responseStr.substring(headStart + 1, headEnd);
String bodyString = responseStr.substring(bodyStart + 1, bodyEnd);
if (tidAllow.indexOf(":debug:") >= 0)
{
System.out.println(" ---- Head --- ");
System.out.println(headString);
System.out.println(" ---- Body --- ");
System.out.println(bodyString);
}
String[] s = new String[2];
s[0] = headString;
s[1] = bodyString;
return s;
}
return null;
}
private int findEndOfTag(String string, int startPos)
{
if (startPos < 1) return -1;
for (int i = startPos; i < string.length(); i++)
{
if (string.charAt(i) == '>') return i;
}
return -1;
}
private boolean doToolBuffer(Portal portal, HttpServletRequest req, HttpServletResponse res, ToolConfiguration toolConfig) throws ToolException, IOException
{
// Reset the tool state if requested
Session s = SessionManager.getCurrentSession();
ToolSession ts = s.getToolSession(toolConfig.getId());
if (portalService.isResetRequested(req))
{
ts.clearAttributes();
}
// find the tool registered for this
ActiveTool tool = ActiveToolManager.getActiveTool(toolConfig.getToolId());
if (tool == null)
{
return false;
}
// permission check - visit the site (unless the tool is configured to
// bypass)
if (tool.getAccessSecurity() == Tool.AccessSecurity.PORTAL)
{
Site site = null;
try
{
site = SiteService.getSiteVisit(toolConfig.getSiteId());
}
catch (IdUnusedException e)
{
portal.doError(req, res, s, Portal.ERROR_WORKSITE);
return false;
}
catch (PermissionException e)
{
return false;
}
}
// System.out.println("portal.forwardTool siteTool="+siteTool+"
// TCP="+toolContextPath+" TPI="+toolPathInfo);
String option = URLUtils.getSafePathInfo(req);
String[] parts = option.split("/");
String toolContextPath = req.getContextPath()
+ req.getServletPath() + Web.makePath(parts, 1, 3);
String toolPathInfo = Web.makePath(
parts, 3, parts.length);
portal.forwardTool(tool, req, res, toolConfig, toolConfig.getSkin(), toolContextPath,
toolPathInfo);
return true;
}
}