/*******************************************************************************
* Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
*
* This file is part of the OpenWGA server platform.
*
* OpenWGA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, a special exception is granted by the copyright holders
* of OpenWGA called "OpenWGA plugin exception". You should have received
* a copy of this exception along with OpenWGA in file COPYING.
* If not, see <http://www.openwga.com/gpl-plugin-exception>.
*
* OpenWGA 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenWGA in file COPYING.
* If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package de.innovationgate.wgpublisher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.URL;
import java.net.URLDecoder;
import java.rmi.RemoteException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.activation.DataSource;
import javax.activation.URLDataSource;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.log4j.Logger;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentFactory;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.DOMWriter;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.jdom.IllegalDataException;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.Dom4JDriver;
import de.innovationgate.utils.Base64;
import de.innovationgate.utils.TemporaryFile;
import de.innovationgate.utils.TransientList;
import de.innovationgate.utils.TransientMap;
import de.innovationgate.utils.UIDGenerator;
import de.innovationgate.utils.URLBuilder;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.utils.Zipper;
import de.innovationgate.utils.io.ByteArrayDataSource;
import de.innovationgate.webgate.api.WGACLEntry;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGCSSJSModule;
import de.innovationgate.webgate.api.WGContent;
import de.innovationgate.webgate.api.WGContentKey;
import de.innovationgate.webgate.api.WGDatabase;
import de.innovationgate.webgate.api.WGDocument;
import de.innovationgate.webgate.api.WGException;
import de.innovationgate.webgate.api.WGFactory;
import de.innovationgate.webgate.api.WGIllegalArgumentException;
import de.innovationgate.webgate.api.WGLanguage;
import de.innovationgate.webgate.api.WGLanguageChooser;
import de.innovationgate.webgate.api.WGScriptModule;
import de.innovationgate.webgate.api.WGStructEntry;
import de.innovationgate.webgate.api.WGTMLModule;
import de.innovationgate.webgate.api.WGUnavailableException;
import de.innovationgate.webgate.api.WGUserProfile;
import de.innovationgate.webgate.api.jdbc.ACLEntry;
import de.innovationgate.webgate.api.utils.MasterSessionTask;
import de.innovationgate.wga.common.Constants;
import de.innovationgate.wga.common.beans.csconfig.v1.MediaKey;
import de.innovationgate.wga.common.beans.csconfig.v1.PublisherOption;
import de.innovationgate.wga.config.PersonalisationConfiguration;
import de.innovationgate.wga.model.BrowsingSecurity;
import de.innovationgate.wga.modules.ModuleDefinition;
import de.innovationgate.wga.modules.options.OptionConversionException;
import de.innovationgate.wga.modules.options.OptionDefinition;
import de.innovationgate.wga.modules.options.OptionReader;
import de.innovationgate.wga.modules.types.ContentStorePublisherOptionsModuleType;
import de.innovationgate.wga.modules.types.WGAServerOptionsModuleType;
import de.innovationgate.wgpublisher.WGACore.UserAgentVerifier;
import de.innovationgate.wgpublisher.WGACore.WGAConfigurationOptionReader;
import de.innovationgate.wgpublisher.cache.FileCache;
import de.innovationgate.wgpublisher.lang.LanguageBehaviour;
import de.innovationgate.wgpublisher.lang.LanguageBehaviourTools;
import de.innovationgate.wgpublisher.lang.RequestLanguageChooser;
import de.innovationgate.wgpublisher.lang.WebTMLLanguageChooser;
import de.innovationgate.wgpublisher.log.WGALoggerWrapper;
import de.innovationgate.wgpublisher.log.WGARequestInformation;
import de.innovationgate.wgpublisher.lucene.LuceneManager;
import de.innovationgate.wgpublisher.modules.poptions.ContentStorePublisherOptionsCollector;
import de.innovationgate.wgpublisher.modules.serveroptions.VariousModuleDefinition;
import de.innovationgate.wgpublisher.plugins.WGAPlugin;
import de.innovationgate.wgpublisher.scheduler.Job;
import de.innovationgate.wgpublisher.url.TitlePathManager;
import de.innovationgate.wgpublisher.url.WGAURLBuilder;
import de.innovationgate.wgpublisher.url.TitlePathManager.RemainingPathElementException;
import de.innovationgate.wgpublisher.url.TitlePathManager.TitlePath;
import de.innovationgate.wgpublisher.webtml.utils.AjaxActionDefinition;
import de.innovationgate.wgpublisher.webtml.utils.AjaxInfo;
import de.innovationgate.wgpublisher.webtml.utils.HttpErrorException;
import de.innovationgate.wgpublisher.webtml.utils.TMLUserProfile;
public class WGPDispatcher extends HttpServlet {
private static final String BYTERANGE_BOUNDARY = "##BYTERANGE##";
public static final String COOKIE_WGPID = "WGPID";
public static final String COOKIE_SECURE_WGPID = "SecureWGPID";
public static final String REQUESTTYPE_TML = "tml";
public static final String REQUESTTYPE_STATICTML = "statictml";
private static final Pattern ACCEPT_RANGE_PATTERN = Pattern.compile("(\\d+)?\\-(\\d+)?");
private static class AcceptRange {
public int from;
int to;
public AcceptRange(int from, int to) {
this.from = from;
this.to = to;
}
}
public static class URLID {
private String _resourceId = null;
private String _language = null;
private String _suffix = null;
private String _completeId;
private boolean _completeFormat = true;
private WGDatabase _db;
public URLID(String id, WGDatabase contentDb) throws WGAPIException {
_db = contentDb;
if (id == null || id.trim().equals("")) {
throw new WGIllegalArgumentException("The given id is empty or null");
}
List tokens = WGUtils.deserializeCollection(id, WGContentKey.TOKEN_DIVIDER);
Collections.reverse(tokens);
if (tokens.size() < 3) {
_completeFormat = false;
}
int contentStartsAt = 0;
if (tokens.size() >= 2) {
_suffix = (String) tokens.get(0);
contentStartsAt = 1;
}
if (tokens.size() >= 3) {
_language = (String) tokens.get(1);
contentStartsAt = 2;
if (_language.equals(DEFAULT_LANGUAGE_TOKEN)) {
_language = contentDb.getDefaultLanguage();
}
}
List contentIdTokens = tokens.subList(contentStartsAt, tokens.size());
Collections.reverse(contentIdTokens);
_resourceId = WGUtils.serializeCollection(contentIdTokens, WGContentKey.TOKEN_DIVIDER);
StringBuffer completeId = new StringBuffer();
completeId.append(_resourceId);
if (_language != null) {
completeId.append(".");
completeId.append(_language);
}
if (_suffix != null) {
completeId.append(".");
completeId.append(_suffix);
}
_completeId = completeId.toString();
}
public boolean isCompleteFormat() {
return _completeFormat;
}
public void setCompleteFormat(boolean complete) {
_completeFormat = complete;
}
public String getResourceId() {
return _resourceId;
}
public String getLanguage() {
return _language;
}
public String getSuffix() {
return _suffix;
}
public String getCompleteId() {
return _completeId;
}
public WGContentKey asContentKey() throws WGAPIException {
if (isCompleteFormat()) {
return WGContentKey.parse(_completeId, _db);
}
else {
return null;
}
}
public int getSuffixVersion() {
int version = 0;
try {
version = Integer.parseInt(_suffix);
}
catch (NumberFormatException e) {
// Everything ok. All non-numeric versions (p, mediakey) will
// get parsed as version 0
}
return version;
}
@Override
public String toString() {
return _completeId;
}
}
public class WebTMLDebugger {
private Transformer _debugModulesTransformer;
private Transformer _debugTagsTransformer = null;
private Transformer getDebugModulesTransformer(boolean throwAway) throws TransformerConfigurationException, TransformerFactoryConfigurationError {
if (_debugModulesTransformer == null || throwAway) {
_debugModulesTransformer = TransformerFactory.newInstance().newTransformer(new StreamSource(getServletContext().getResourceAsStream("/tmldebugModules.xsl")));
}
return _debugModulesTransformer;
}
private Transformer getDebugTagsTransformer(boolean throwAway) throws TransformerConfigurationException, TransformerFactoryConfigurationError {
if (_debugTagsTransformer == null || throwAway) {
_debugTagsTransformer = TransformerFactory.newInstance().newTransformer(new StreamSource(getServletContext().getResourceAsStream("/tmlDebugTags.xsl")));
}
return _debugTagsTransformer;
}
/**
* @param request
* @param response
* @param session
* @throws HttpErrorException
* @throws IOException
* @throws ServletException
*/
private void performDebugMode(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws HttpErrorException, ServletException, IOException {
if (!getCore().isAdminLoggedIn(request)) {
throw new HttpErrorException(HttpServletResponse.SC_FORBIDDEN, "You must be logged in to the wga admin page to use WebTML debugging", null);
}
String command = request.getParameter("command");
Boolean debugModeEnabled = (Boolean) session.getAttribute(WGACore.ATTRIB_TMLDEBUG);
if (debugModeEnabled == null) {
debugModeEnabled = new Boolean(false);
}
Boolean resultTracingEnabled = (Boolean) session.getAttribute(WGACore.ATTRIB_TMLDEBUG_TRACE_RESULTS);
if (resultTracingEnabled == null) {
resultTracingEnabled = new Boolean(false);
}
Boolean optionsTracingEnabled = (Boolean) session.getAttribute(WGACore.ATTRIB_TMLDEBUG_TRACE_OPTIONS);
if (optionsTracingEnabled == null) {
optionsTracingEnabled = new Boolean(false);
}
Boolean tmlscriptOptimizationDisabled = (Boolean) session.getAttribute(WGACore.ATTRIB_TMLDEBUG_DISABLE_TMLSCRIPT_OPTIMIZATION);
if (tmlscriptOptimizationDisabled == null) {
tmlscriptOptimizationDisabled = new Boolean(false);
}
if (command == null) {
request.getRequestDispatcher("/tmlDebugFrameset.jsp").include(request, response);
return;
}
if (command.equalsIgnoreCase("toggledebug")) {
debugModeEnabled = new Boolean(!debugModeEnabled.booleanValue());
session.setAttribute(WGACore.ATTRIB_TMLDEBUG, debugModeEnabled);
command = "status";
}
else if (command.equalsIgnoreCase("toggleresulttrace")) {
resultTracingEnabled = new Boolean(!resultTracingEnabled.booleanValue());
session.setAttribute(WGACore.ATTRIB_TMLDEBUG_TRACE_RESULTS, resultTracingEnabled);
command = "status";
}
else if (command.equalsIgnoreCase("toggleoptionstrace")) {
optionsTracingEnabled = new Boolean(!optionsTracingEnabled.booleanValue());
session.setAttribute(WGACore.ATTRIB_TMLDEBUG_TRACE_OPTIONS, optionsTracingEnabled);
command = "status";
}
else if (command.equalsIgnoreCase("toggletmlscriptoptimization")) {
tmlscriptOptimizationDisabled = new Boolean(!tmlscriptOptimizationDisabled.booleanValue());
session.setAttribute(WGACore.ATTRIB_TMLDEBUG_DISABLE_TMLSCRIPT_OPTIMIZATION, tmlscriptOptimizationDisabled);
command = "status";
}
else if (command.equalsIgnoreCase("clearlist")) {
List debugDocuments = WGACore.getDebugDocumentsList(session);
debugDocuments.clear();
command = "list";
}
if (command.equalsIgnoreCase("toolbar")) {
request.getRequestDispatcher("/tmlDebugToolbar.jsp").include(request, response);
}
else if (command.equalsIgnoreCase("status")) {
response.getWriter().write("TMLScript stack traces are switched " + (tmlscriptOptimizationDisabled.booleanValue() ? "ON" : "OFF") + "\n");
if (debugModeEnabled.booleanValue() == true) {
response.getWriter().write("WebTML debug mode is switched ON\n");
response.getWriter().write("Result tracing is switched " + (resultTracingEnabled.booleanValue() ? "ON" : "OFF") + "\n");
response.getWriter().write("Options tracing is switched " + (optionsTracingEnabled.booleanValue() ? "ON" : "OFF") + "\n");
}
else {
response.getWriter().write("WebTML debug mode is switched OFF\n");
}
}
else if (command.equalsIgnoreCase("list")) {
if (debugModeEnabled.booleanValue() == true) {
sendDebugDocList(request, response, session);
}
else {
throw new HttpErrorException(HttpServletResponse.SC_BAD_REQUEST, "WebTML debug mode is not enabled. First enable it via tmldebug?command=on", null);
}
}
else if (command.equalsIgnoreCase("showModules")) {
showTMLModules(request, response, session);
}
else if (command.equalsIgnoreCase("showTags")) {
showTMLTags(request, response, session);
}
else if (command.equalsIgnoreCase("show")) {
sendDebugDoc(request, response, session);
}
else {
throw new HttpErrorException(HttpServletResponse.SC_BAD_REQUEST, "Unknown debug command: " + command, null);
}
}
/**
* @param request
* @param response
* @param session
* @throws HttpErrorException
* @throws IOException
*/
private void sendDebugDoc(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws HttpErrorException, IOException {
String urlStr = request.getParameter("url");
String indexStr = request.getParameter("index");
if (urlStr != null) {
urlStr = URLDecoder.decode(urlStr, "UTF-8");
List debugDocuments = WGACore.getDebugDocumentsList(session);
Document debugDoc;
for (int idx = 0; idx < debugDocuments.size(); idx++) {
debugDoc = (Document) debugDocuments.get(idx);
if (debugDoc.getRootElement().attributeValue("url", "").equals(urlStr)) {
indexStr = String.valueOf(idx);
break;
}
}
}
if (indexStr != null) {
int index = Integer.valueOf(indexStr).intValue();
List debugDocuments = WGACore.getDebugDocumentsList(session);
if (index == -1) {
index = debugDocuments.size() - 1;
}
if (index >= debugDocuments.size()) {
throw new HttpErrorException(HttpServletResponse.SC_BAD_REQUEST, "Index out of range: " + index + " where maximum index is " + (debugDocuments.size() - 1), null);
}
else {
Document doc = (Document) debugDocuments.get(index);
response.setContentType("text/xml");
XMLWriter writer = new XMLWriter(response.getWriter());
writer.write(doc);
}
}
else {
throw new HttpErrorException(HttpServletResponse.SC_BAD_REQUEST, "You must include either parameter index or url to address the debug document to show", null);
}
}
/**
* @param response
* @param session
* @throws IOException
*/
private void sendDebugDocList(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {
List debugDocuments = WGACore.getDebugDocumentsList(session);
Document debugDoc;
response.setContentType("text/html");
Writer out = response.getWriter();
out.write("<html><body>");
out.write("<h2>Debug data for session " + session.getId());
out.write("<table width='100%' border='1'>");
out.write("<thead><th>index</th><th>url</th><th>started</th><th>ended</th></thead>");
out.write("<tbody>");
for (int idx = debugDocuments.size() - 1; idx > -1; idx--) {
debugDoc = (Document) debugDocuments.get(idx);
out.write("<tr>");
out.write("<td>" + String.valueOf(idx) + "</td>");
out.write("<td><a target=\"blank\" href=\"" + getContextPath() + "/tmlDebug?command=showModules&index=" + idx + "\">" + debugDoc.selectSingleNode("/tmldebugdocument/@url").getText()
+ "</a></td>");
out.write("<td>" + debugDoc.selectSingleNode("/tmldebugdocument/@started").getText() + "");
out.write("<td>");
Attribute endedElement = (Attribute) debugDoc.selectSingleNode("/tmldebugdocument/@ended");
if (endedElement != null) {
out.write(endedElement.getText());
}
else {
out.write(" ");
}
out.write("</td></tr>");
}
out.write("</tbody></table>");
out.write("<button onclick=\"location.href='" + request.getContextPath() + "/tmlDebug?command=clearlist'\">Clear list</button>");
out.write("</body></html>");
}
private void showTMLModules(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws HttpErrorException, IOException {
String urlStr = request.getParameter("url");
String indexStr = request.getParameter("index");
if (urlStr != null) {
urlStr = URLDecoder.decode(urlStr, "UTF-8");
List debugDocuments = WGACore.getDebugDocumentsList(session);
Document debugDoc;
for (int idx = 0; idx < debugDocuments.size(); idx++) {
debugDoc = (Document) debugDocuments.get(idx);
if (debugDoc.getRootElement().attributeValue("url", "").equals(urlStr)) {
indexStr = String.valueOf(idx);
break;
}
}
}
response.setContentType("text/html");
if (indexStr != null) {
int index = Integer.valueOf(indexStr).intValue();
List debugDocuments = WGACore.getDebugDocumentsList(session);
if (index == -1) {
index = debugDocuments.size() - 1;
}
if (index >= debugDocuments.size()) {
throw new HttpErrorException(HttpServletResponse.SC_BAD_REQUEST, "Index out of range: " + index + " where maximum index is " + (debugDocuments.size() - 1), null);
}
else {
Document doc = (Document) debugDocuments.get(index);
doc.getRootElement().addAttribute("index", String.valueOf(index));
try {
DOMWriter domWriter = new DOMWriter();
org.w3c.dom.Document domDocument = domWriter.write(doc);
Transformer trans = getDebugModulesTransformer(request.getParameter("throwAway") != null);
trans.transform(new DOMSource(domDocument), new StreamResult(response.getOutputStream()));
}
catch (TransformerConfigurationException e) {
response.sendError(500, e.getMessageAndLocation());
e.printStackTrace();
}
catch (TransformerFactoryConfigurationError e) {
response.sendError(500, e.getMessage());
e.printStackTrace();
}
catch (TransformerException e) {
response.sendError(500, e.getMessageAndLocation());
e.printStackTrace();
}
catch (IOException e) {
response.sendError(500, e.getMessage());
e.printStackTrace();
}
catch (DocumentException e) {
response.sendError(500, e.getMessage());
e.printStackTrace();
}
}
}
else {
throw new HttpErrorException(HttpServletResponse.SC_BAD_REQUEST, "You must include either parameter index or url to address the debug document to show", null);
}
}
private void showTMLTags(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws HttpErrorException, IOException {
String urlStr = request.getParameter("url");
String indexStr = request.getParameter("index");
if (urlStr != null) {
urlStr = URLDecoder.decode(urlStr, "UTF-8");
List debugDocuments = WGACore.getDebugDocumentsList(session);
Document debugDoc;
for (int idx = 0; idx < debugDocuments.size(); idx++) {
debugDoc = (Document) debugDocuments.get(idx);
if (debugDoc.getRootElement().attributeValue("url", "").equals(urlStr)) {
indexStr = String.valueOf(idx);
break;
}
}
}
response.setContentType("text/html");
if (indexStr != null) {
int index = Integer.valueOf(indexStr).intValue();
List debugDocuments = WGACore.getDebugDocumentsList(session);
if (index == -1) {
index = debugDocuments.size() - 1;
}
if (index >= debugDocuments.size()) {
throw new HttpErrorException(HttpServletResponse.SC_BAD_REQUEST, "Index out of range: " + index + " where maximum index is " + (debugDocuments.size() - 1), null);
}
else {
Document doc = (Document) debugDocuments.get(index);
Element element = (Element) doc.selectSingleNode(request.getParameter("root"));
doc = DocumentFactory.getInstance().createDocument(element.createCopy());
doc.getRootElement().addAttribute("index", String.valueOf(index));
try {
DOMWriter domWriter = new DOMWriter();
org.w3c.dom.Document domDocument = domWriter.write(doc);
Transformer trans = getDebugTagsTransformer(request.getParameter("throwAway") != null);
trans.transform(new DOMSource(domDocument), new StreamResult(response.getOutputStream()));
}
catch (TransformerConfigurationException e) {
response.sendError(500, e.getMessageAndLocation());
e.printStackTrace();
}
catch (TransformerFactoryConfigurationError e) {
response.sendError(500, e.getMessage());
e.printStackTrace();
}
catch (TransformerException e) {
response.sendError(500, e.getMessageAndLocation());
e.printStackTrace();
}
catch (IOException e) {
response.sendError(500, e.getMessage());
e.printStackTrace();
}
catch (DocumentException e) {
response.sendError(500, e.getMessage());
e.printStackTrace();
}
}
}
else {
throw new HttpErrorException(HttpServletResponse.SC_BAD_REQUEST, "You must include either parameter index or url to address the debug document to show", null);
}
}
}
public class TemporaryDownload implements HttpSessionBindingListener {
private String _name;
private TemporaryFile _tempFile;
public TemporaryDownload(String name, TemporaryFile file) {
super();
this._name = name;
this._tempFile = file;
}
public TemporaryFile getTempFile() {
return _tempFile;
}
public String getName() {
return _name;
}
protected void finalize() throws Throwable {
_tempFile.delete();
}
public void valueBound(HttpSessionBindingEvent arg0) {
}
public void valueUnbound(HttpSessionBindingEvent arg0) {
_tempFile.delete();
}
}
public class RequestInformation {
private Date _date;
private boolean committed = false;
public RequestInformation() {
_date = new Date();
}
/**
* @return Returns the date.
*/
public Date getDate() {
return _date;
}
/**
* @param date
* The date to set.
*/
public void setDate(Date date) {
_date = date;
}
/**
* @return Returns the committed.
*/
public boolean isCommitted() {
return committed;
}
/**
* @param committed
* The committed to set.
*/
public void setCommitted(boolean committed) {
this.committed = committed;
}
}
WGACore _core;
private Logger _log;
public static final String SESSION_VARS = "Vars";
// Session attributes set by this program
public static final String SESSION_LOGINS = "Logins";
public static final String SESSION_PROFILENAME = "Profilename:";
public static final String SESSION_PROFILENAME_INDIVIDUALDB = "ProfilenameDB:";
// Session attribute where temporary downloads are registered
public static final String SESSION_TEMPORARYDOWNLOADS = "TemporaryDownloads";
// Other storage
private boolean _servePages = false;
private int _listenPort = -1;
private String _contextPath = null;
private WebTMLDebugger _tmlDebugger;
protected WGAConfigurationOptionReader _variousServerOptionReader;
public static final String DEFAULT_LANGUAGE_TOKEN = "int";
// Config flags and infos
public void doPut(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
this.doPost(request, response);
}
public void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
if (!isServePages()) {
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Website is currently updating configuration. Please try again later.");
return;
}
String path = request.getServletPath() + (request.getPathInfo() != null ? request.getPathInfo() : "");
try {
if (path.equals("/login")) {
doLogin(request, response);
return;
}
else if (path.equals("/logout")) {
String domain = request.getParameter("domain");
_core.logout(domain, request.getSession());
sendRedirect(response, request.getParameter("redirect"));
}
else if (path.equals("/ajaxform")) {
this.dispatchAjaxForm(request, response);
}
else {
this.doGet(request, response);
}
}
catch (Exception exc) {
if (!(exc instanceof HttpErrorException)) {
_log.error("Error in request processing", exc);
}
request.setAttribute(WGACore.ATTRIB_EXCEPTION, exc);
throw new ServletException(exc);
}
catch (Error err) {
_log.error("Error in request processing", err);
request.setAttribute(WGACore.ATTRIB_EXCEPTION, err);
throw new ServletException(err);
}
}
/**
* retrieve posted formdata, parse request, store posted data in
* temp-sessionkey, render javascript-callbackfunction to submit sessionkey
* to clientside ajax call
*
* @param request
* @param response
*/
private void dispatchAjaxForm(HttpServletRequest request, HttpServletResponse response) {
// skip logging
WGARequestInformation info = (WGARequestInformation) request.getAttribute(WGARequestInformation.REQUEST_ATTRIBUTENAME);
if (info != null) {
info.setType(WGARequestInformation.TYPE_AJAXFORM);
}
if (FileUpload.isMultipartContent(request)) {
DiskFileUpload dfu = new DiskFileUpload();
// F000037B2
if (getCore().getCharacterEncoding() != null) {
dfu.setHeaderEncoding(getCore().getCharacterEncoding());
}
try {
// parse request
List fileItems = dfu.parseRequest(request);
Iterator iter = fileItems.iterator();
String formaction = null;
String ajaxcallid = null;
String ajaxgraydiv = null;
String ajaxmode = null;
while (iter.hasNext()) {
FileItem fi = (FileItem) iter.next();
if (fi.isFormField()) {
String fieldValue = null;
if (_core.getCharacterEncoding() != null) {
fieldValue = fi.getString(_core.getCharacterEncoding());
}
else {
fieldValue = fi.getString();
}
if (fi.getFieldName().equals("$formaction")) {
formaction = fieldValue;
}
else if (fi.getFieldName().equals("$ajaxcallid")) {
ajaxcallid = fieldValue;
}
else if (fi.getFieldName().equals("$ajaxgraydiv")) {
ajaxgraydiv = fieldValue;
}
else if (fi.getFieldName().equals("$ajaxmode")) {
ajaxmode = fieldValue;
}
}
}
if (formaction == null || ajaxcallid == null) {
getCore().getLog().error("Could not retrieve 'formaction' or 'ajaxcallid' from ajaxform-data.");
return;
}
else {
// generate temp session key
String sessionKey = UIDGenerator.generateUID();
TransientList transientList = new TransientList(fileItems);
// store posted data transient on session - bc. fileItems
// are not serializable
request.getSession().setAttribute(sessionKey, transientList);
// check if session is new
if (request.getSession().isNew()) {
// server create new session in an ajax form call
// set flag on session and later render
// Event.SESSION_IS_NEW_EVENT
// in ajax call to notify client
// the sessionflag is handled and removed in root-tag
request.getSession().setAttribute(sessionKey + ".event.SessionWasNew", new Boolean(true));
}
// create action definition
AjaxActionDefinition actionDef = new AjaxActionDefinition(formaction, ajaxcallid);
actionDef.setTmlformSessionKey(sessionKey);
if (ajaxgraydiv != null && !ajaxgraydiv.equals("#null#")) {
actionDef.setGraydiv(Boolean.valueOf(ajaxgraydiv).booleanValue());
}
if (ajaxmode != null && !ajaxmode.equals("#null#")) {
actionDef.setMode(ajaxmode);
}
// render response
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.write("<script type=\"text/javascript\">");
out.write("parent.WGA.ajax.formCallback(" + actionDef.toJavaScriptObject() + ");");
out.write("</script>");
}
}
catch (FileUploadException e) {
getCore().getLog().error("Error parsing multipart-data from ajaxcall: " + e.getMessage());
}
catch (IOException e) {
getCore().getLog().error("Error creating ajaxcall response.", e);
}
}
}
private void doLogin(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws HttpErrorException, IOException, MalformedURLException,
ServletException, WGAPIException {
// Get the necessary fields
String domain = request.getParameter("domain");
String username = request.getParameter("username");
String password = request.getParameter("password");
String redirect = request.getParameter("redirect");
String referer = request.getHeader("Referer");
// Some basic validating
if (domain == null) {
throw new HttpErrorException(500, "Missing parameter \"domain\". (Hint: Field name must be lower case)", null);
}
if (username == null) {
throw new HttpErrorException(500, "Missing parameter \"username\". (Hint: Field name must be lower case)", null);
}
if (password == null) {
throw new HttpErrorException(500, "Missing parameter \"password\". (Hint: Field name must be lower case)", null);
}
if (username == null || password == null) {
request.setAttribute(WGACore.ATTRIB_LOGINERROR, "Invalid login for domain " + domain + ". Username or password is null.");
username = "Anonymous";
password = "";
}
boolean isLoginSuccessful = false;
try {
if (WGACore.DOMAIN_ADMINLOGINS.equals(domain)) {
if (!_core.isAdministrativePort(request.getLocalPort())) {
throw new HttpErrorException(HttpServletResponse.SC_FORBIDDEN, "Access to administrative applications is disabled", null);
}
isLoginSuccessful = _core.doAdminLogin(username, password, request);
}
else {
isLoginSuccessful = _core.login(username, password, domain, request, response);
}
}
catch (LoginException e) {
throw new ServletException("Login Error", e);
}
// React on login success
if (isLoginSuccessful) {
if (redirect != null) {
sendRedirect(response, redirect);
}
else if (referer != null) {
request.setAttribute(WGACore.ATTRIB_LOGINERROR, "No redirect specified.");
de.innovationgate.utils.URLBuilder builder = new de.innovationgate.utils.URLBuilder(new java.net.URL(referer));
builder.setParameter("loginerror", "1");
sendRedirect(response, builder.rebuild(false));
}
else {
response.setStatus(HttpServletResponse.SC_OK);
}
}
else {
if (WGACore.DOMAIN_ADMINLOGINS.equals(domain)) {
request.setAttribute(WGACore.ATTRIB_LOGINERROR, "Invalid administrative login. Please verify username and password.");
}
else {
request.setAttribute(WGACore.ATTRIB_LOGINERROR, "Invalid login for domain " + domain + ". Please verify username and password.");
}
if (request.getParameter("flag") != null) {
response.setContentType("text/html");
request.getRequestDispatcher("login.jsp").include(request, response);
}
else if (referer != null) {
de.innovationgate.utils.URLBuilder builder = new de.innovationgate.utils.URLBuilder(new java.net.URL(referer));
builder.setParameter("loginerror", "1");
sendRedirect(response, builder.rebuild(false).toString());
}
else {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid login");
}
}
return;
}
public void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
if (!isServePages()) {
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Website is currently updating configuration. Please try again later.");
return;
}
Date startDate = new Date();
if (this._contextPath == null) {
this._contextPath = request.getContextPath();
this._listenPort = request.getServerPort();
}
try {
// Retrieve session
javax.servlet.http.HttpSession session = request.getSession();
if (session.isNew()) {
session.setAttribute(SESSION_VARS, new HashMap());
}
// Parse request
WGPRequestPath path = parseRequest(request, response);
request.setAttribute(WGACore.ATTRIB_REQUESTPATH, path);
// If database login failed exit immediately
if (!path.isProceedRequest()) {
return;
}
// Set access logging for this request
if (path.getDatabase() != null) {
String accessLoggingEnabled = (String) path.getDatabase().getAttribute(WGACore.DBATTRIB_ENABLE_ACCESSLOGGING);
if (accessLoggingEnabled != null) {
WGARequestInformation info = (WGARequestInformation) request.getAttribute(WGARequestInformation.REQUEST_ATTRIBUTENAME);
if (info != null) {
info.setLoggingEnabled(Boolean.parseBoolean(accessLoggingEnabled));
}
}
}
int iPathType = path.getPathType();
// Treatment of special URL types
String dbKey = path.getDatabaseKey();
if (iPathType == WGPRequestPath.TYPE_INVALID) {
throw new HttpErrorException(404, "Invalid path: " + path.getBasePath(), dbKey);
}
if (iPathType == WGPRequestPath.TYPE_INVALID_DB) {
throw new HttpErrorException(404, "Specified database '" + dbKey + "' is unknown", null);
}
if (iPathType == WGPRequestPath.TYPE_UNKNOWN_CONTENT) {
sendNoContentNotification(path, request, response, path.getDatabase());
return;
}
if (iPathType == WGPRequestPath.TYPE_GOTO_HOMEPAGE) {
iPathType = determineHomepage(request, path, iPathType);
}
if (iPathType == WGPRequestPath.TYPE_UNAVAILABLE_DB) {
throw new HttpErrorException(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "The website is currently unavailable", path.getDatabaseKey());
}
if (iPathType == WGPRequestPath.TYPE_UNDEFINED_HOMEPAGE) {
throw new HttpErrorException(HttpServletResponse.SC_NOT_FOUND, "No home page was defined for database '" + path.getDatabaseKey() + "'. Please specify an explicit content path.", path
.getDatabaseKey());
}
if (iPathType == WGPRequestPath.TYPE_TMLDEBUG) {
_tmlDebugger.performDebugMode(request, response, session);
return;
}
if (iPathType == WGPRequestPath.TYPE_JOBLOG) {
sendJobLog(request, response, session);
return;
}
if (iPathType == WGPRequestPath.TYPE_LOGOUT) {
WGDatabase db = (WGDatabase) _core.getContentdbs().get(dbKey);
String domain = (String) db.getAttribute(WGACore.DBATTRIB_DOMAIN);
_core.logout(domain, session);
removeSessionCookie(response, request.getSession(), db);
iPathType = WGPRequestPath.TYPE_REDIRECT;
}
if (iPathType == WGPRequestPath.TYPE_FAVICON) {
String faviconPath = determineFavicon(request);
if (faviconPath != null) {
iPathType = WGPRequestPath.TYPE_REDIRECT;
path.setResourcePath(faviconPath);
}
else {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Favicon not defined");
return;
}
}
// Treatment of base URL Types
if (iPathType == WGPRequestPath.TYPE_REDIRECT) {
String url = path.getResourcePath();
if (path.appendQueryString() == true && request.getQueryString() != null && !request.getQueryString().equals("")) {
if (url.indexOf("?") != -1) {
url += "&" + request.getQueryString();
}
else {
url += "?" + request.getQueryString();
}
}
if (path.isPermanentRedirect()) {
sendPermanentRedirect(response, url);
}
else {
sendRedirect(response, url);
}
}
else if (iPathType != WGPRequestPath.TYPE_RESOURCE && iPathType != WGPRequestPath.TYPE_STATICTML && !_core.getContentdbs().containsKey(path.getDatabaseKey())) {
throw new HttpErrorException(404, "Database '" + dbKey + "' is unknown", null);
}
else {
String requestMethod = request.getMethod().toLowerCase();
switch (iPathType) {
case (WGPRequestPath.TYPE_TML):
case (WGPRequestPath.TYPE_TITLE_PATH):
if (path.isCompletePath() || !(requestMethod.equals("get") || requestMethod.equals("head"))) {
if (!path.isCompletePath()) {
_log.warn("Received " + request.getMethod() + " request to incomplete URL '" + path.getCompleteURL()
+ "' which cannot be redirected. Please use complete URLs on this HTTP method for consistent behaviour.");
}
dispatchTmlRequest(path, request, response, startDate);
}
else {
sendRedirect(response, path.expandToCompletePath(request));
}
break;
case (WGPRequestPath.TYPE_FILE):
dispatchFileRequest(path, request, response);
break;
case (WGPRequestPath.TYPE_CSS):
case (WGPRequestPath.TYPE_JS):
dispatchCssjsRequest(path, request, response);
break;
case (WGPRequestPath.TYPE_RESOURCE):
dispatchResourceRequest(path, request, response);
break;
case (WGPRequestPath.TYPE_STATICTML):
dispatchStaticTmlRequest(path, request, response);
break;
default:
throw new HttpErrorException(500, "Invalid url format", dbKey);
}
}
// moved from finally block to ensure errorpage can be displayed
commitResponse(response);
}
catch (HttpErrorException exc) {
request.setAttribute(WGACore.ATTRIB_EXCEPTION, exc);
if (!response.isCommitted()) {
// throw exception to display errorpage - with senderror() the
// applicationserver use the buildin errorpage
if (exc.getCode() == HttpServletResponse.SC_NOT_FOUND || exc.getCode() == HttpServletResponse.SC_FORBIDDEN) {
_log.warn(exc.getLogMessage(request));
response.sendError(exc.getCode(), exc.getMessage());
}
else {
throw new ServletException(exc);
}
}
else {
_log.warn(exc.getLogMessage(request));
}
}
catch (SocketException exc) {
_log.warn("Socket Exception: " + exc.getMessage());
}
catch (Exception exc) {
_log.error("Exception in processing of request URL " + String.valueOf(request.getRequestURL()), exc);
request.setAttribute(WGACore.ATTRIB_EXCEPTION, exc);
throw new ServletException(exc);
}
catch (Error err) {
_log.error("Error in processing of request URL " + String.valueOf(request.getRequestURL()), err);
request.setAttribute(WGACore.ATTRIB_EXCEPTION, err);
throw new ServletException(err);
}
finally {
WGARequestInformation reqInfo = (WGARequestInformation) request.getAttribute(WGARequestInformation.REQUEST_ATTRIBUTENAME);
if (reqInfo != null) {
reqInfo.setCommited(true);
}
}
}
private void sendPermanentRedirect(HttpServletResponse response, String url) {
response.setStatus(301);
response.setHeader( "Location", url);
response.setHeader( "Connection", "close");
}
private WGPRequestPath parseRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws HttpErrorException, WGException, IOException {
return new WGPRequestPath(request, response, this, _core);
}
private int determineHomepage(javax.servlet.http.HttpServletRequest request, WGPRequestPath path, int iPathType) throws UnsupportedEncodingException, HttpErrorException {
WGDatabase db = (WGDatabase) _core.getContentdbs().get(path.getDatabaseKey());
WGAURLBuilder urlBuilder = _core.retrieveURLBuilder(request, db);
String homepage;
try {
homepage = urlBuilder.buildHomepageURL(db, request);
if (homepage != null) {
path.setResourcePath(homepage);
return WGPRequestPath.TYPE_REDIRECT;
}
}
catch (WGException e) {
getCore().getLog().error("Error determining homepage", e);
throw new HttpErrorException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), path.getDatabaseKey());
}
return WGPRequestPath.TYPE_UNDEFINED_HOMEPAGE;
}
public String determineFavicon(HttpServletRequest request) {
String faviconPath = getCore().getWgaConfiguration().getFavicon();
if (faviconPath != null) {
if (faviconPath.indexOf("://") == -1 && !faviconPath.startsWith("/")) {
faviconPath = "/" + faviconPath;
}
return faviconPath;
}
else {
return null;
}
}
private void commitResponse(javax.servlet.http.HttpServletResponse response) {
if (!response.isCommitted()) {
try {
response.flushBuffer();
}
catch (IOException e) {
}
}
}
/**
* @param request
* @param response
* @param session
*/
private void sendJobLog(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {
if (!isAdminLoggedIn(request)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "You must be logged in as WGA administrator!");
return;
}
String jobToShow = request.getParameter("name");
Job job = getCore().getScheduler().getJob(jobToShow);
if (job == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unknown job: " + jobToShow);
return;
}
response.setContentType("text/html");
Writer out = response.getWriter();
out.write("<HTML><HEAD>");
out.write("\n<script language=\"javascript\">\nvar running=" + Boolean.valueOf(job.isRunning()).toString() + ";\n</script>\n");
/*
* if (job.isRunning()) {
* out.write("<META HTTP-EQUIV=\"refresh\" CONTENT=\"3\"/>"); } else {
* out.write("<META HTTP-EQUIV=\"refresh\" CONTENT=\"6\"/>"); }
*/
out.write("</HEAD>");
out.write("<BODY style=\"background-color:white; font-family:sans-serif; font-size:10pt\">");
String log = job.getLog();
LineNumberReader reader = new LineNumberReader(new StringReader(log));
String line;
while ((line = reader.readLine()) != null) {
out.write(line);
out.write("<BR/>");
}
if (!job.isRunning() && job.getEndMessage() != null) {
out.write("<p>");
out.write("<table border=\"1\" cellpadding=\"5\"><tr><td>");
out.write(job.getEndMessage());
out.write("</td></tr></table>");
out.write("</p>");
}
out.write("<a id=\"bottomLink\" name=\"bottom\"> </a></BODY></HTML>");
}
private void dispatchTmlRequest(WGPRequestPath path, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Date startDate) throws java.lang.Exception {
boolean ajax = false;
String completeUrl = path.getCompleteURL();
HttpSession session = request.getSession();
// Fetch the database
WGDatabase database = path.getDatabase();
if (database == null) {
throw new HttpErrorException(404, "No database of key " + path.getDatabaseKey(), null);
}
// Determine requested mime type and type key
String mediaKey = path.getMediaKey();
if (mediaKey == null) {
mediaKey = database.getAttribute(WGACore.DBATTRIB_DEFAULT_MEDIAKEY).toString();
}
MediaKey mediaKeyObj = _core.getMediaKey(mediaKey);
String mimeType = mediaKeyObj.getMimeType();
response.setContentType(mimeType);
// Look if a session cookie has to be set
_core.setSessionCookie(request, response, database);
// Determine the content for this request
WGContent content = path.getContent();
// Context request, if content key filled or we have a title path
if (content != null) {
if (!content.mayBePublished(isBrowserInterface(session) || isAuthoringMode(database.getDbReference(), session), WGContent.DISPLAYTYPE_NONE)) {
sendNoContentNotification(path, request, response, database);
return;
}
if (content.isVirtual()) {
if (isBrowserInterface(session)) {
if (!content.getStatus().equals(WGContent.STATUS_DRAFT) && request.getParameter("forceVLink") == null) {
String url = getVirtualContentURL(request, database, path, content);
sendRedirect(response, url);
return;
}
}
else {
sendRedirect(response, buildVirtualLink(content, request, path.getMediaKey(), path.getLayoutKey()));
return;
}
}
}
// Contextless request. We use a dummy content
else {
content = database.getDummyContent(path.getRequestLanguage());
}
// Test browsability of content
if (!content.isDummy() && getBrowsingSecurity(database) <= BrowsingSecurity.NO_BROWSING) {
throw new HttpErrorException(java.net.HttpURLConnection.HTTP_FORBIDDEN, "Browsing not allowed in database '" + path.getDatabaseKey() + "'", path.getDatabaseKey());
}
// Drop cache if requested by url param
if (request.getQueryString() != null && request.getQueryString().toLowerCase().indexOf("dropcache") != -1 && isAdminLoggedIn(request)) {
content.dropCache();
}
// Personalize
TMLUserProfile tmlUserProfile = null;
try {
if (_core.isPersonalisationEnabled()) {
tmlUserProfile = this.fetchUserProfile(request, response, database, session);
if (tmlUserProfile != null && !tmlUserProfile.getprofile().isDeleted()) {
if (!isBrowserInterface(session)) {
this.registerHit(tmlUserProfile.getprofile(), database, content);
}
}
}
}
catch (WGAPIException e) {
_log.error("Unable to personalize tmlrequest.", e);
}
// Set context attributes for tml
request.setAttribute(WGACore.ATTRIB_MAINCONTEXT, content);
request.setAttribute(WGACore.ATTRIB_WGPPATH, path.getPublisherURL());
request.setAttribute(WGACore.ATTRIB_TAGIDS, WGUtils.createSynchronizedMap());
request.setAttribute(WGACore.ATTRIB_TMLCONTEXTS, WGUtils.createSynchronizedMap());
request.setAttribute(WGACore.ATTRIB_REQUESTURL, completeUrl);
request.setAttribute(WGACore.ATTRIB_MIMETYPE, mimeType);
request.setAttribute(WGACore.ATTRIB_MEDIAKEY, mediaKey);
request.setAttribute(WGACore.ATTRIB_REQUESTTYPE, REQUESTTYPE_TML);
if (mediaKeyObj.isBinary()) {
request.setAttribute(WGACore.ATTRIB_SERVLETRESPONSE, response);
}
// Determine tml design for this request
WGTMLModule tmlLib = null;
if (path.getLayoutKey() != null) {
tmlLib = (WGTMLModule) database.getDesignObject(WGDocument.TYPE_TML, path.getLayoutKey(), mediaKey);
if (tmlLib != null && tmlLib.isDirectAccessAllowed() == false) {
throw new HttpErrorException(java.net.HttpURLConnection.HTTP_FORBIDDEN, "This design is not allowed for direct access: " + tmlLib.getName() + " (" + tmlLib.getMediaKey() + ")", path
.getDatabaseKey());
}
}
else {
WGStructEntry entry = content.getStructEntry();
if (entry == null) {
throw new HttpErrorException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Content " + content.getContentKey().toString() + " has no struct entry", path.getDatabaseKey());
}
tmlLib = entry.getOuterLayout(mediaKey);
}
if (tmlLib == null || tmlLib.isDummy()) {
if (path.getLayoutKey() != null) {
throw new HttpErrorException(404, "No WebTML layout '" + path.getLayoutKey() + "' for media key '" + mediaKey + "' available in app '" + database.getDbReference() + "'", path.getDatabaseKey());
}
else {
throw new HttpErrorException(500, "Outer layout of struct entry '" + content.getStructEntry().getTitle() + "(" + content.getStructEntry().getStructKey() + ")"
+ "' not available for media key '" + mediaKey + "'", path.getDatabaseKey());
}
}
request.setAttribute(WGACore.ATTRIB_OUTER_DESIGN, tmlLib.getName());
// TML Cache control
if (tmlLib.isCacheable()) {
response.setHeader("Cache-Control", "must-revalidate");
long lastModified;
// determine lastModified
// - last modified of binary response depends only on resource
// change date
// - last change date of textual response additionally depends on
// character encoding change date
if (isBinary(response)) {
lastModified = getCore().getDeployer().getLastChangedOrDeployed(database).getTime();
}
else {
lastModified = Math.max(getCore().getDeployer().getLastChangedOrDeployed(database).getTime(), _core.getCharacterEncodingLastModified());
lastModified = Math.max(lastModified, _core.getDesignEncodingLastModified(database.getDbReference()));
}
// Test modified since
if (browserCacheIsValid(request, lastModified)) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
else {
response.setDateHeader("Last-Modified", lastModified);
response.setHeader("ETag", '"' + String.valueOf(lastModified) + '"');
}
}
else {
response.setHeader("Pragma", "No-Cache");
if (mediaKeyObj.isBinary()) {
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
}
else {
response.setHeader("Cache-Control", "No-Cache");
}
}
// check if we have an ajaxcall
String encAjaxInfo = request.getParameter("$ajaxInfo");
if (encAjaxInfo != null) {
tmlLib = processAjaxCall(request, database, encAjaxInfo);
ajax = true;
}
// Update usage statistics
getCore().getUsageStatistics().addRequestStatistic(request, session, database, tmlUserProfile);
// Dispatch to jsp
String targetJSP = _core.getDeployer().locateTmlResource(tmlLib);
try {
if (targetJSP != null) {
if (!"HEAD".equalsIgnoreCase(request.getMethod())) {
if (mediaKeyObj.isBinary()) {
if (request instanceof RenderServletRequestWrapper) {
HttpServletRequestWrapper wrapper = (HttpServletRequestWrapper) request;
wrapper.getRequest().getRequestDispatcher(targetJSP).include(wrapper.getRequest(), new DummyServletResponse(response));
}
else {
this.getServletContext().getRequestDispatcher(targetJSP).include(request, new DummyServletResponse(response));
}
}
else {
if (request instanceof RenderServletRequestWrapper) {
HttpServletRequestWrapper wrapper = (HttpServletRequestWrapper) request;
wrapper.getRequest().getRequestDispatcher(targetJSP).include(wrapper.getRequest(), response);
}
else {
this.getServletContext().getRequestDispatcher(targetJSP).include(request, response);
}
}
}
}
else {
throw new HttpErrorException(500, "tml design not active: " + tmlLib.getName(), path.getDatabaseKey());
}
// Eventually do redirect
if (request.getAttribute(WGACore.ATTRIB_REDIRECT) != null) {
if (!ajax) { // On AJAX requests the redirect is performed by Root.tmlEndTag()
if (!session.isNew()) { // on new sessions we must not reset the response (#00000147)
response.reset();
}
response.sendRedirect(String.valueOf(request.getAttribute(WGACore.ATTRIB_REDIRECT)));
}
}
// Eventually save profile
if (tmlUserProfile != null && tmlUserProfile.isSavedOnEnd() && !tmlUserProfile.isSaved()) {
// To keep profile storage operation from delaying the response
commitResponse(response);
if (!tmlUserProfile.getprofile().save()) {
getCore().getLog().error("Unable to save profile on request end");
}
}
}
catch (Exception t) {
throw t;
}
finally {
// Log
// WGALoggerWrapper logger = (WGALoggerWrapper)
// database.getAttribute(WGACore.DBATTRIB_LOGGER);
// if (logger != null) {
// logger.logRequest(mimeType, path, request, content,
// tmlUserProfile, tmlLib);
// }
WGARequestInformation info = (WGARequestInformation) request.getAttribute(WGARequestInformation.REQUEST_ATTRIBUTENAME);
if (info != null) {
if (content == null || content.isDummy()) {
info.setType(WGARequestInformation.TYPE_TML);
}
else {
info.setType(WGARequestInformation.TYPE_CONTENT);
}
info.setDatabase(database);
info.setMimeType(mimeType);
info.setPath(path);
info.setContent(content);
info.setProfile(tmlUserProfile);
info.setDesign(tmlLib);
info.setAjax(ajax);
}
}
}
public static boolean isAuthoringMode(String dbReference, HttpSession session) {
if (session == null) {
return false;
}
String attName = WGACore.ATTRIB_AUTHORINGMODE + dbReference;
Boolean bi = (Boolean) session.getAttribute(attName);
if (bi != null && bi.booleanValue() == true) {
return true;
}
else {
return false;
}
}
private WGTMLModule processAjaxCall(javax.servlet.http.HttpServletRequest request, WGDatabase database, String encAjaxInfo) throws HttpErrorException, WGException {
WGTMLModule tmlLib;
AjaxInfo ajaxInfo = null;
try {
// decrypt
byte[] zipped = this.getCore().getDesEncrypter().decrypt(encAjaxInfo);
if (zipped == null) {
this.getCore().getLog().error("Retrieved AjaxInfo could not be decrypted. Maybe the ajax call was defined for another WGA instance.");
throw new HttpErrorException(HttpURLConnection.HTTP_INTERNAL_ERROR, "Retrieved AjaxInfo could not be decrypted. Maybe the ajax call was defined for another WGA instance.", null);
}
// unzip
String unzipped = Zipper.unzip(zipped);
// deserialize
ajaxInfo = (AjaxInfo) new XStream(new Dom4JDriver()).fromXML(unzipped);
// put ajaxInfo on request so root tag can retrieve it
request.setAttribute(WGACore.ATTRIB_AJAXINFO, ajaxInfo);
}
catch (HttpErrorException e) {
throw e;
}
catch (Exception e) {
this.getCore().getLog().error("Retrieved AjaxInfo could not be restored.", e);
throw new HttpErrorException(HttpURLConnection.HTTP_INTERNAL_ERROR, "Retrieved AjaxInfo could not be restored. Check log for details.", null);
}
// fetch correct db for ajax (if designdb on include tag is present use
// design db, if not use current db)
WGDatabase ajaxDB = null;
String ajaxDesignDBKey = ajaxInfo.getDesignDB();
if (ajaxDesignDBKey != null) {
// fetch design db
try {
ajaxDB = _core.openContentDB(ajaxDesignDBKey, request);
}
catch (WGUnavailableException e) {
throw new HttpErrorException(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "The website is currently unavailable", ajaxDesignDBKey);
}
catch (de.innovationgate.wgpublisher.AuthenticationException e) {
throw new HttpErrorException(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage(), null);
}
catch (AccessException e) {
throw new HttpErrorException(HttpServletResponse.SC_FORBIDDEN, e.getMessage(), null);
}
if (ajaxDB == null) {
throw new HttpErrorException(HttpServletResponse.SC_NOT_FOUND, "No database of key " + ajaxDesignDBKey, null);
}
else if (ajaxDB.isSessionOpen() == false) {
throw new HttpErrorException(HttpServletResponse.SC_FORBIDDEN, "Database '" + ajaxDesignDBKey + "' could not be opened by ajaxCall.", null);
}
}
else {
ajaxDB = database;
}
// search for tml module
tmlLib = (WGTMLModule) ajaxDB.getDesignObject(WGDocument.TYPE_TML, ajaxInfo.getTmlmodule(), ajaxInfo.getMediaKey());
if (tmlLib == null) {
throw new HttpErrorException(HttpServletResponse.SC_NOT_FOUND, "No WebTML layout '" + ajaxInfo.getTmlmodule() + "' for media key '" + ajaxInfo.getMediaKey() + "' available in app '" + ajaxDB.getDbReference() + "'", ajaxDB
.getDbReference());
}
return tmlLib;
}
private void sendNoContentNotification(WGPRequestPath path, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, WGDatabase database)
throws IOException, HttpErrorException {
if (request.getQueryString() != null && request.getQueryString().toLowerCase().indexOf("login") != -1) {
sendRedirect(response, getLoginURL(request, database, path.getCompleteURL()));
}
else {
if (isBrowserInterface(request.getSession()) && (path.getPathType() == WGPRequestPath.TYPE_TML || path.getPathType() == WGPRequestPath.TYPE_UNKNOWN_CONTENT)) {
String url = getNoContentNotificationURL(request, database, path);
sendRedirect(response, url);
}
else {
if (path.getTitlePathURL() != null) {
throw new HttpErrorException(404, "Content on path '" + path.getTitlePathURL().getPath() + "' does not exist or is not visible for user '"
+ database.getSessionContext().getUser() + "'", path.getDatabaseKey());
}
else {
throw new HttpErrorException(404, "Content of name/id '" + path.getContentKey() + "' does not exist or is not visible for user '" + database.getSessionContext().getUser() + "'",
path.getDatabaseKey());
}
}
}
}
private void sendNoFileContainerNotification(WGPRequestPath path, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, WGDatabase database)
throws IOException, HttpErrorException {
if (request.getQueryString() != null && request.getQueryString().toLowerCase().indexOf("login") != -1) {
sendRedirect(response, getLoginURL(request, database, path.getCompleteURL()));
}
else {
throw new HttpErrorException(404, "No file container of name " + path.getContainerKey(), path.getDatabaseKey());
}
}
private void removeSessionCookie(javax.servlet.http.HttpServletResponse response, HttpSession session, WGDatabase database) {
if (!database.hasFeature(WGDatabase.FEATURE_SESSIONTOKEN)) {
return;
}
String cookieName = (String) database.getAttribute(WGACore.DBATTRIB_SESSIONCOOKIE);
if (cookieName == null) {
return;
}
Cookie sessionCookie = new Cookie(cookieName, "");
sessionCookie.setMaxAge(0);
sessionCookie.setPath("/");
String sessionCookieDomain = (String) database.getAttribute(WGACore.DBATTRIB_SESSIONCOOKIEDOMAIN);
if (sessionCookieDomain != null) {
sessionCookie.setDomain(sessionCookieDomain);
}
response.addCookie(sessionCookie);
session.removeAttribute(WGACore.SESSION_COOKIESET + cookieName);
}
protected void sendRedirect(javax.servlet.http.HttpServletResponse response, String virtualLink) throws IOException {
// send redirect always via j2ee method
// B00004862
response.sendRedirect(response.encodeRedirectURL(virtualLink));
}
/**
* Method isBrowserInterface.
*
* @param request
* @return boolean
*/
public static boolean isBrowserInterface(HttpSession session) {
if (session == null) {
return false;
}
Boolean bi = (Boolean) session.getAttribute(WGACore.ATTRIB_BROWSERINTERFACE);
if (bi != null && bi.booleanValue() == true) {
return true;
}
else {
return false;
}
}
public static String buildContentURLID(WGContent content, String mediaKey, boolean isBI) throws WGAPIException {
String language = content.getLanguage().getName();
if (!LanguageBehaviourTools.isMultiLanguageDB(content.getDatabase()) && language.equals(content.getDatabase().getDefaultLanguage())) {
language = DEFAULT_LANGUAGE_TOKEN;
}
StringBuffer idBuffer = new StringBuffer();
String uniqueName = content.getStructEntry().getUniqueName();
if (WGUtils.isEmpty(uniqueName)) {
uniqueName = content.getUniqueName();
}
if (!WGUtils.isEmpty(uniqueName) && !isBI && content.getStatus().equals(WGContent.STATUS_RELEASE) && content.getDatabase().getBooleanAttribute(WGACore.DBATTRIB_CREATE_NAME_URLS, true)) {
idBuffer.append(uniqueName).append(".");
idBuffer.append(language).append(".");
idBuffer.append(mediaKey);
}
else {
idBuffer.append(String.valueOf(content.getStructKey())).append(".");
idBuffer.append(language).append(".");
if (content.getStatus().equals(WGContent.STATUS_RELEASE)) {
idBuffer.append(mediaKey);
}
else {
idBuffer.append(content.getVersion());
}
}
return idBuffer.toString();
}
public static String buildLayoutURLID(WGDatabase db, String layoutKey, String language, String mediaKey) throws WGAPIException {
if (db != null && !LanguageBehaviourTools.isMultiLanguageDB(db) && language.equals(db.getDefaultLanguage())) {
language = DEFAULT_LANGUAGE_TOKEN;
}
StringBuffer idBuffer = new StringBuffer();
idBuffer.append(layoutKey).append(".");
idBuffer.append(language).append(".");
idBuffer.append(mediaKey);
return idBuffer.toString();
}
/**
* retrieves a relative url from session attribute
* WGACore.ATTRIB_NO_CONTENT_NOTIFCATION_URL to use as
* NoContentNotificationURL in sendNoContentNotification(...) default is
* "/statictml/#DBKEY#/dispNoContent?key=#KEY#" parameters #DBKEY# and
* #TKEY# are replaced by the corresponding values,
*
* @param request
* @param database
* @param path
* @return URL starting with getContextPath();
*/
private String getNoContentNotificationURL(HttpServletRequest request, WGDatabase database, WGPRequestPath path) {
// default URL used by old BI
String defaultURL = "/statictml/#DBKEY#/dispNoContent?key=#KEY#";
String relativeURL = null;
if (request.getSession().getAttribute(WGACore.ATTRIB_NO_CONTENT_NOTIFCATION_URL) != null) {
relativeURL = (String) request.getSession().getAttribute(WGACore.ATTRIB_NO_CONTENT_NOTIFCATION_URL);
}
else {
relativeURL = defaultURL;
}
// replace variables in relative URL
relativeURL = relativeURL.replaceAll("#DBKEY#", database.getAttribute(WGACore.DBATTRIB_DBKEY).toString());
relativeURL = relativeURL.replaceAll("#KEY#", path.getContentKey().toString());
StringBuffer url = new StringBuffer();
url.append(getContextPath());
url.append(relativeURL);
return url.toString();
}
/**
* retrieves a relative url from session attribute
* WGACore.ATTRIB_VIRTUAL_CONTENT_URL to use instead of the default static
* tml for vitual content in BI default is
* "/statictml/#DBKEY#/dispVirtual/#CONTENTKEY_URL_MODE#" parameters
* #DBKEY#, #CONTENTKEY_URL_MODE#, #CONTENTKEY_UNIQUE_MODE# and #STRUCTKEY#
* are replaced by the corresponding values
*
* @param request
* @param database
* @param path
* @param content
* @return URL starting with getContextPath();
* @throws WGAPIException
*/
private String getVirtualContentURL(HttpServletRequest request, WGDatabase database, WGPRequestPath path, WGContent content) throws WGAPIException {
// default URL used by old BI
String defaultURL = "/statictml/#DBKEY#/dispVirtual/#CONTENTKEY_URL_MODE#";
String relativeURL = null;
if (request.getSession().getAttribute(WGACore.ATTRIB_VIRTUAL_CONTENT_URL) != null) {
relativeURL = (String) request.getSession().getAttribute(WGACore.ATTRIB_VIRTUAL_CONTENT_URL);
}
else {
relativeURL = defaultURL;
}
// replace variables in relative URL
relativeURL = relativeURL.replaceAll("#DBKEY#", database.getAttribute(WGACore.DBATTRIB_DBKEY).toString());
relativeURL = relativeURL.replaceAll("#CONTENTKEY_URL_MODE#", content.getContentKey(false).toString());
relativeURL = relativeURL.replaceAll("#CONTENTKEY_UNIQUE_MODE#", content.getContentKey(true).toString());
relativeURL = relativeURL.replaceAll("#STRUCTKEY#", content.getStructKey().toString());
StringBuffer url = new StringBuffer();
url.append(getContextPath());
url.append(relativeURL);
return url.toString();
}
/**
* Method registerHit.
*
* @param userProfile
* @param request
* @param path
* @param database
* @param content
*/
public void registerHit(WGUserProfile userProfile, WGDatabase database, WGContent content) {
try {
Integer statisticsMode = Integer.valueOf((String) _core.readPublisherOptionOrDefault(userProfile.getDatabase(), WGACore.DBATTRIB_PERSSTATMODE));
if (statisticsMode == null || statisticsMode.intValue() != Constants.PERSSTATMODE_HIT) {
return;
}
userProfile.addHit();
userProfile.setLastAccess(new Date());
if (database != null) {
userProfile.setDBLogin(database.getSessionContext().getUser());
}
userProfile.save();
}
catch (Exception e) {
_log.error("Unable to register hit in user profile", e);
}
}
private void registerSession(WGUserProfile userProfile, HttpServletRequest request, HttpSession session, boolean created) {
Integer statisticsMode = Integer.valueOf((String) _core.readPublisherOptionOrDefault(userProfile.getDatabase(), WGACore.DBATTRIB_PERSSTATMODE));
if (!created && (statisticsMode == null || statisticsMode.intValue() == Constants.PERSSTATMODE_OFF)) {
return;
}
try {
userProfile.addNewSession(session);
userProfile.setLastAccess(new Date());
String userAgent = request.getHeader("User-Agent");
if (userAgent != null) {
userProfile.setClient(userAgent);
}
Enumeration langsIt = request.getLocales();
List langs = new ArrayList();
while (langsIt.hasMoreElements()) {
langs.add(((Locale) langsIt.nextElement()).getLanguage());
}
userProfile.setLanguages(langs);
userProfile.save();
}
catch (WGAPIException e) {
_log.error("Unable to register session in user profile.", e);
}
}
public TMLUserProfile fetchUserProfile(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, WGDatabase database, HttpSession session)
throws WGAPIException {
String domain = (String) database.getAttribute(WGACore.DBATTRIB_DOMAIN);
if (domain == null) {
return null;
}
// Tr to get pers db from domain
WGDatabase persDB = null;
try {
persDB = _core.openPersonalisationDB(domain, session);
}
catch (WGAPIException e1) {
_core.getLog().error("Error opening personalisation db for domain '" + domain + "'", e1);
throw e1;
}
// Try to use content database as pers db or use an external self
// personalisation db
if (persDB == null) {
if (database.hasFeature(WGDatabase.FEATURE_SELF_PERSONALIZABLE)) {
persDB = database;
}
else {
persDB = (WGDatabase) database.getAttribute(WGACore.DBATTRIB_EXTERNAL_SELF_PERSONALISATION_DB);
if (persDB != null) {
if (!persDB.isSessionOpen()) {
persDB.openSession();
}
}
else {
return null;
}
}
}
// Try to retrieve previously stored user profile in session
String wgpid = (String) session.getAttribute(SESSION_PROFILENAME_INDIVIDUALDB + database.getDbReference());
if (wgpid != null) {
WGUserProfile profile = persDB.getUserProfile(wgpid);
if (profile != null) {
TMLUserProfile tmlProfile = new TMLUserProfile(profile, getCore());
request.setAttribute(WGACore.ATTRIB_PROFILE + database.getDbReference(), tmlProfile);
return tmlProfile;
}
}
// Fetch user profile using database's persmode. If not available we
// take the default value for the option
String persModeStr = (String) _core.readPublisherOptionOrDefault(database, WGACore.DBATTRIB_PERSMODE);
Integer persMode;
if (persModeStr != null) {
persMode = Integer.valueOf(persModeStr);
}
// Backup for sure, which should not happen as the option is defined for
// all databases
else {
persMode = Constants.PERSMODE_AUTO;
}
// If in Mode AUTO, the User Profile is retrieved automatically
// (otherwise it is set in the login process)
WGUserProfile userProfile = null;
boolean created = false;
if (persMode == Constants.PERSMODE_AUTO) {
boolean secureMode = database.getBooleanAttribute(WGACore.DBATTRIB_SECURE_APP, false);
String cookieName = COOKIE_WGPID;
if (secureMode) {
cookieName = COOKIE_SECURE_WGPID;
}
wgpid = this.findCookie(request.getCookies(), cookieName);
if (wgpid != null) {
userProfile = persDB.getUserProfile(wgpid);
}
if (userProfile == null) {
userProfile = createUserProfile((String) request.getHeader("User-Agent"), persDB, wgpid, Constants.PERSMODE_AUTO);
created = true;
}
if (userProfile != null && !userProfile.isDummy()) {
Cookie wgpidCookie = new Cookie(cookieName, userProfile.getName());
wgpidCookie.setPath("/");
wgpidCookie.setSecure(secureMode);
wgpidCookie.setMaxAge(60 * 60 * 24 * 365);
response.addCookie(wgpidCookie);
}
}
else if (persMode == Constants.PERSMODE_LOGIN) {
if (database.getSessionContext().isAnonymous()) {
// no profile for anonymous
return null;
}
String userName = database.getSessionContext().getUser();
userProfile = persDB.getUserProfile(userName);
if (userProfile == null) {
userProfile = createUserProfile((String) request.getHeader("User-Agent"), persDB, userName, Constants.PERSMODE_LOGIN);
created = true;
}
}
// If valid user profile, set in session object and register new session
if (userProfile != null && !userProfile.isDeleted()) {
// Drop cache so we can be sure to be up-to-date with the backend
// database
// (Since backend changes - maybe from other cluster nodes - are not
// registered for user profiles)
userProfile.dropCache();
session.setAttribute(SESSION_PROFILENAME_INDIVIDUALDB + database.getDbReference(), userProfile.getName());
TMLUserProfile tmlProfile = new TMLUserProfile(userProfile, getCore());
request.setAttribute(WGACore.ATTRIB_PROFILE + database.getDbReference(), tmlProfile);
this.registerSession(userProfile, request, session, created);
return tmlProfile;
}
else {
session.removeAttribute(SESSION_PROFILENAME_INDIVIDUALDB + database.getDbReference());
return null;
}
}
private WGUserProfile createUserProfile(String userAgent, WGDatabase persDB, String wgpid, int persMode) throws WGAPIException {
WGUserProfile userProfile;
UserAgentVerifier userAgentVerifier = getCore().getUserAgentVerifier();
if (userAgentVerifier.isValidUserAgent(userAgent) == true) {
userProfile = persDB.createUserProfile(wgpid, persMode);
}
else {
userProfile = persDB.getDummyProfile(wgpid);
}
if (userProfile != null && !userProfile.isDummy()) {
TMLUserProfile.prepareNewProfile(userProfile);
userProfile.save();
}
return userProfile;
}
private String findCookie(Cookie[] cookies, String cookieName) {
if (cookies == null) {
return null;
}
Cookie cookie;
for (int idx = 0; idx < cookies.length; idx++) {
cookie = cookies[idx];
if (cookie.getName().equalsIgnoreCase(cookieName)) {
return cookie.getValue();
}
}
return null;
}
public static String getCompleteRequestURL(javax.servlet.http.HttpServletRequest request) {
StringBuffer requestURL = request.getRequestURL();
if (request.getQueryString() != null) {
requestURL.append("?").append(request.getQueryString());
}
return requestURL.toString();
}
public static WGContent getContentByAnyKey(String contentKey, WGDatabase database, HttpServletRequest req) throws WGAPIException {
if (contentKey == null || contentKey.trim().equals("")) {
return null;
}
HttpSession session = req.getSession();
boolean isBI = isBrowserInterface(session) || isAuthoringMode(database.getDbReference(), session);
// Inner getContentByAnyKey
return getContentByAnyKey(contentKey, database, new RequestLanguageChooser(database, req), isBI);
}
public static WGContent getContentByAnyKey(String contentKey, WGDatabase database, WGLanguageChooser languageChooser, boolean isBI) throws WGAPIException {
try {
URLID urlid = new URLID(contentKey, database);
return getContentByAnyKey(urlid, database, languageChooser, isBI);
}
catch (WGIllegalArgumentException e) {
return null;
}
}
public static WGContent getContentByAnyKey(URLID id, WGDatabase database, WGLanguageChooser languageChooser, boolean isBI) throws WGAPIException {
WGLanguage langObj;
// Block with various attempts to search content by this key
WGContent content = null;
// Try to retrieve content via content key - Fastest way
WGContentKey key = id.asContentKey();
if (key != null) {
content = database.getContentByKey(key);
if (content != null) {
return content;
}
}
// Try to interpret struct key as unique name
if (id.getSuffixVersion() == 0 && id.getLanguage() != null) {
content = database.getContentByName(id.getResourceId(), id.getLanguage());
if (content != null) {
return content;
}
}
// From here on we are sure to have no URLID of complete format. We will
// use the complete id string from here on to make any sense of it.
id.setCompleteFormat(false);
String completeId = id.getCompleteId();
// Try to use key as struct key only. If found, will try to find a
// content for this struct from the languages list
WGStructEntry entry = null;
Object structKey = database.parseStructKey(completeId);
if (structKey != null) {
entry = database.getStructEntryByKey(structKey);
if (entry != null) {
content = languageChooser.selectContentForPage(entry, isBI);
if (content != null) {
return content;
}
}
}
// Try to use key as name only
content = languageChooser.selectContentForName(database, completeId, isBI);
if (content != null) {
return content;
}
// Try to retrieve via Name-Language syntax
int dividerPos = completeId.lastIndexOf("-");
if (content == null && dividerPos != -1) {
String realName = completeId.substring(0, dividerPos);
String realLanguage = completeId.substring(dividerPos + 1).toLowerCase();
// Cutoff trailing suffix which might be there for browser
// compatibility
if (realLanguage.lastIndexOf(".") != -1) {
realLanguage = realLanguage.substring(0, realLanguage.lastIndexOf("."));
}
if (realLanguage.equals(DEFAULT_LANGUAGE_TOKEN)) {
realLanguage = database.getDefaultLanguage();
}
content = database.getContentByName(realName, realLanguage);
if (content != null) {
return content;
}
}
// Last Try :-) Fetch by name only, so browser language
// configuration cannot prevent you from fetching by name only
// However, when the name is used by multiple language versions, the
// first one is returned
if (content == null) {
content = database.getAnyContentByName(completeId);
if (content != null) {
return content;
}
}
return null;
}
private void dispatchFileRequest(WGPRequestPath path, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws java.lang.Exception {
WGARequestInformation info = (WGARequestInformation) request.getAttribute(WGARequestInformation.REQUEST_ATTRIBUTENAME);
// Fetch database
WGDatabase database = path.getDatabase();
if (info != null) {
info.setType(WGARequestInformation.TYPE_FILE);
if (database != null) {
info.setDatabase(database);
}
}
// Parse container key - This may be a path when we have the /~file/
// syntax. We use different parts depending on the type of container
String containerKey = path.getContainerKey();
List containerKeyElems = WGUtils.deserializeCollection(containerKey, "/");
String containerKeyLastElement = (String) containerKeyElems.get(containerKeyElems.size() - 1);
// Fetch the file container
WGDocument fileContainer = database.getFileContainer(containerKeyLastElement);
if (fileContainer == null) {
// Container addressed by some kind of content key?
WGContent content = getContentByAnyKey(containerKeyLastElement, database, request);
// Container addressed by Title path?
if (content == null) {
TitlePathManager tpm = (TitlePathManager) database.getAttribute(WGACore.DBATTRIB_TITLEPATHMANAGER);
if (tpm != null && tpm.isGenerateTitlePathURLs()) {
TitlePathManager.TitlePath url = tpm.parseTitlePathURL(containerKeyElems);
if (url != null) {
path.setTitlePathURL(url);
content = path.getContentByTitlePath(request);
}
}
}
if (content == null || !content.mayBePublished(isBrowserInterface(request.getSession()) || isAuthoringMode(database.getDbReference(), request.getSession()), WGContent.DISPLAYTYPE_NONE)) {
sendNoFileContainerNotification(path, request, response, database);
return;
}
fileContainer = content;
}
if (info != null && fileContainer != null && fileContainer instanceof WGContent) {
info.setContent((WGContent) fileContainer);
}
String fileName = path.getFileName();
if (fileName == null || fileName.equals("")) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No file name in file URL: " + path.getCompleteURL());
return;
}
ExternalFileServingConfig externalFileServingConfig = _core.getExternalFileServingConfig();
if (externalFileServingConfig.isEnabled() && database.getBooleanAttribute(WGACore.DBATTRIB_EXTERNAL_FILE_SERVING_ENABLED, false)) {
// check if file is anonymous accessible
boolean isAnonymousAccessible = database.isAnonymousAccessible();
if (isAnonymousAccessible) {
// perform further document level checks
if (fileContainer != null && fileContainer instanceof WGContent) {
// check status
if (!((WGContent) fileContainer).getStatus().equals(WGContent.STATUS_RELEASE)) {
isAnonymousAccessible = false;
}
// check readers
if (isAnonymousAccessible) {
isAnonymousAccessible = ((WGContent) fileContainer).getReaders().size() == 0;
}
}
}
if (isAnonymousAccessible && fileContainer.getFileSize(fileName) >= externalFileServingConfig.getThreshold()) {
// file is anonymous accessible and above external serving
// threshold
dispatchFileExternalImpl(externalFileServingConfig, path, request, response, database, fileContainer);
}
else {
dispatchFileDefaultImpl(path, request, response, database, fileContainer);
}
}
else {
dispatchFileDefaultImpl(path, request, response, database, fileContainer);
}
}
private void dispatchFileExternalImpl(ExternalFileServingConfig config, WGPRequestPath path, HttpServletRequest request, HttpServletResponse response, final WGDatabase database,
final WGDocument fileContainer) throws WGAPIException, IOException, HttpErrorException, WGException {
final String filename = path.getFileName();
File dbRoot = config.getRootForDB(database.getDbReference());
if (!dbRoot.exists()) {
dbRoot.mkdir();
}
File externalCacheFolder = new File(dbRoot, fileContainer.getDocumentKey().replaceAll("/", ":"));
if (!externalCacheFolder.exists()) {
externalCacheFolder.mkdir();
}
if (!externalCacheFolder.exists()) {
// fallback to normal dispatch
_core.getLog().warn("Unable to create external file cache directory '" + externalCacheFolder.getAbsolutePath() + "'. Performing file dispatch in default mode.");
dispatchFileDefaultImpl(path, request, response, database, fileContainer);
}
else {
final File cachedFile = new File(externalCacheFolder, path.getFileName());
if (isExternalCacheUpToDate(cachedFile, fileContainer, filename)) {
// perform redirect
response.sendRedirect(config.getRootURL() + database.getDbReference() + "/" + fileContainer.getDocumentKey().replaceAll("/", ":") + "/" + filename);
}
else {
// cache file
Thread cacheTask = new Thread() {
@Override
public void run() {
synchronized (WGPDispatcher.class.getName() + ".filecache." + cachedFile.getAbsolutePath().intern()) {
try {
database.openSession();
// perform synchronized up to date check
if (!isExternalCacheUpToDate(cachedFile, fileContainer, filename)) {
File temp = new File(cachedFile.getParent(), UIDGenerator.generateUID() + ".tmp");
WGDocument container = database.getDocumentByDocumentKey(fileContainer.getDocumentKey());
InputStream in = null;
OutputStream out = null;
try {
in = container.getFileData(filename);
out = new FileOutputStream(temp);
WGUtils.inToOut(in, out, 1024);
}
finally {
if (in != null) {
try {
in.close();
}
catch (IOException e) {
}
}
if (out != null) {
try {
out.close();
}
catch (IOException e) {
}
}
}
temp.renameTo(cachedFile);
cachedFile.setLastModified(fileContainer.getFileLastModified(filename).getTime());
}
}
catch (Throwable e) {
_core.getLog().error("Unable to create cache for external file serving.", e);
}
finally {
WGFactory.getInstance().closeSessions();
}
}
}
};
cacheTask.start();
dispatchFileDefaultImpl(path, request, response, database, fileContainer);
}
}
}
private boolean isExternalCacheUpToDate(File cachedFile, WGDocument fileContainer, String filename) throws WGAPIException {
if (cachedFile.exists() && WGUtils.cutoffTimeMillis(cachedFile.lastModified()) == WGUtils.cutoffTimeMillis(fileContainer.getFileLastModified(filename).getTime())
&& cachedFile.length() == fileContainer.getFileSize(filename)) {
return true;
}
else {
return false;
}
}
private void dispatchFileDefaultImpl(WGPRequestPath path, HttpServletRequest request, HttpServletResponse response, WGDatabase database, WGDocument fileContainer) throws IOException,
WGAPIException, HttpErrorException, WGException {
response.setDateHeader("Date", System.currentTimeMillis());
// Set expiration time
int fileExpirationMinutes = ((Integer) _core.readPublisherOptionOrDefault(database, WGACore.DBATTRIB_FILEEXPIRATION_MINUTES)).intValue();
if (fileContainer instanceof WGContent && ((WGContent) fileContainer).getStatus().equals(WGContent.STATUS_DRAFT)) {
fileExpirationMinutes = 0;
}
if (fileExpirationMinutes > 0) {
int fileExpirationSeconds = fileExpirationMinutes * 60;
response.setHeader("Cache-Control", "max-age=" + fileExpirationSeconds + ", must-revalidate");
}
String fileName = path.getFileName();
// determine content type
String contentType = this.getServletContext().getMimeType(fileName);
if (contentType == null) {
contentType = "application/octet-stream";
}
response.setContentType(contentType);
// Create publishing file object, which will provide the data
PublishingFile publishingFile = new PublishingFile(this, fileContainer, fileName);
if (!publishingFile.isPublishable()) {
throw new HttpErrorException(403, "Files from this container may not be published", path.getDatabaseKey());
}
// Optionally inject scaling information to file object
String maxHeightStr = request.getParameter("maxheight");
if (maxHeightStr != null) {
try {
int maxHeight = Integer.parseInt(maxHeightStr);
publishingFile.setScaleMaxHeight(maxHeight);
}
catch (NumberFormatException e) {
getCore().getLog().error("Unparseable scaling height: " + maxHeightStr);
}
}
String maxWidthStr = request.getParameter("maxwidth");
if (maxWidthStr != null) {
try {
int maxWidth = Integer.parseInt(maxWidthStr);
publishingFile.setScaleMaxWidth(maxWidth);
}
catch (NumberFormatException e) {
getCore().getLog().error("Unparseable scaling widthW: " + maxWidthStr);
}
}
// determine lastModified
// - last modified of binary response depends only on resource change
// date
// - last change date of textual response additionally depends on
// character encoding change date
long lastModified;
if (isBinary(response)) {
lastModified = publishingFile.getLastModifiedTime();
}
else {
lastModified = Math.max(publishingFile.getLastModifiedTime(), _core.getCharacterEncodingLastModified());
lastModified = Math.max(lastModified, _core.getDesignEncodingLastModified(database.getDbReference()));
}
// force download if requested by designer
Boolean forceDownload = Boolean.parseBoolean(request.getParameter("forcedownload"));
if (forceDownload) {
response.setHeader("Content-disposition", "attachment");
}
// Handle browser cache
if (browserCacheIsValid(request, lastModified)) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
else {
response.setDateHeader("Last-Modified", lastModified);
response.setHeader("ETag", '"' + String.valueOf(lastModified) + '"');
}
// Allow accept ranges on all CS with optimized file handling and binary responses
if (database.getContentStoreVersion() >= WGDatabase.CSVERSION_WGA4_1 && isBinary(response)) {
response.setHeader("Accept-Ranges", "bytes");
}
if (contentType.startsWith("application/")) {
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
}
// Look if file is cached - If so, send it and exit
FileCache fileCache = (FileCache) database.getAttribute(WGACore.DBATTRIB_FILECACHE);
byte[] data = fileCache.getFile(publishingFile, lastModified);
if (data != null) {
try {
// B000041DA
ByteArrayDataSource dataIn = new ByteArrayDataSource(data, publishingFile.getFileName(), contentType);
String designEncoding = (String) database.getAttribute(WGACore.DBATTRIB_DESIGN_ENCODING);
if (designEncoding != null) {
writeData(dataIn, request, response, designEncoding, data.length, database.getDbReference(), true);
}
else {
writeData(dataIn, request, response, null, data.length, database.getDbReference(), true);
}
}
catch (java.net.SocketException exc) {
_log.warn("Dispatch of cached file request failed bc. of socket error: " + exc.getMessage());
}
catch (java.io.IOException exc) {
if (!exc.getClass().getName().equals("org.apache.catalina.connector.ClientAbortException")) {
_log.warn("Dispatch of cached file request failed bc. of IO error: " + exc.getMessage());
}
}
return;
}
// In domino native implementations: Change to Master login to retrieve
// data (Workaround for Domino API Bug)
String databaseTypeName = database.getTypeName();
if (databaseTypeName.equals("domino/wgacontentstore/local") || databaseTypeName.equals("domino/custom/local")) {
WGFactory.getInstance().closeSessions();
database = _core.openContentDB(path.getDatabaseKey(), null, true);
}
long fileSize = publishingFile.getFileSize();
// Look if file size is below cache threshold - if so, collect data and
// put into cache, then serve
long threshold = fileCache.getThreshold();
if (fileSize != -1 && threshold >= fileSize) {
// Put into cache
InputStream inputStream = publishingFile.getInputStream();
try {
ByteArrayOutputStream outCache = new ByteArrayOutputStream((int) fileSize);
WGUtils.inToOut(inputStream, outCache, 2048);
data = outCache.toByteArray();
fileCache.putFile(publishingFile, data, lastModified);
}
catch (java.net.SocketException exc) {
_log.warn("Caching of file request failed bc. of socket error: " + exc.getMessage());
}
catch (java.io.IOException exc) {
if (!exc.getClass().getName().equals("org.apache.catalina.connector.ClientAbortException")) {
_log.warn("Caching of file request failed bc. of IO error: " + exc.getMessage());
}
}
finally {
if (inputStream != null) {
try {
inputStream.close();
}
catch (Exception e) {
}
}
}
// Writing from cache to out
try {
// B000041DA
ByteArrayDataSource dataIn = new ByteArrayDataSource(data, publishingFile.getFileName(), contentType);
String designEncoding = (String) database.getAttribute(WGACore.DBATTRIB_DESIGN_ENCODING);
if (designEncoding != null) {
writeData(dataIn, request, response, designEncoding, data.length, database.getDbReference(), true);
}
else {
writeData(dataIn, request, response, null, data.length, database.getDbReference(), true);
}
}
catch (java.net.SocketException exc) {
_log.warn("Dispatch of file request failed bc. of socket error: " + exc.getMessage());
}
catch (java.io.IOException exc) {
if (!exc.getClass().getName().equals("org.apache.catalina.connector.ClientAbortException")) {
_log.warn("Dispatch of file request failed bc. of IO error: " + exc.getMessage());
}
}
return;
}
// File is above threshold and not in cache - serve from database
try {
// B000041DA
String designEncoding = (String) database.getAttribute(WGACore.DBATTRIB_DESIGN_ENCODING);
if (designEncoding != null) {
writeData(publishingFile, request, response, designEncoding, (int) fileSize, database.getDbReference(), database.getContentStoreVersion() >= WGDatabase.CSVERSION_WGA4_1);
}
else {
writeData(publishingFile, request, response, null, (int) fileSize, database.getDbReference(), database.getContentStoreVersion() >= WGDatabase.CSVERSION_WGA4_1);
}
}
catch (java.net.SocketException exc) {
_log.warn("Dispatch of file request failed bc. of socket error: " + exc.getMessage());
}
catch (java.io.IOException exc) {
if (!exc.getClass().getName().equals("org.apache.catalina.connector.ClientAbortException")) {
_log.warn("Dispatch of file request failed bc. of IO error: " + exc.getMessage());
}
}
}
private void dispatchResourceRequest(WGPRequestPath path, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws Exception {
WGARequestInformation info = (WGARequestInformation) request.getAttribute(WGARequestInformation.REQUEST_ATTRIBUTENAME);
if (info != null) {
info.setType(WGARequestInformation.TYPE_RESOURCE);
}
// determine mimeType
String resourcePath = path.getResourcePath();
String mimeType = this.getServletContext().getMimeType(resourcePath);
if (mimeType == null) {
mimeType = "application/octet-stream";
}
response.setContentType(mimeType);
File file = null;
boolean isTemporaryDownload = false;
if (path.getPathCommand().equals(WGPRequestPath.PATHCMD_TEMP_DOWNLOAD)) {
String tempFileName = path.getResourcePath().substring(path.getResourcePath().lastIndexOf("/") + 1);
Map temporaryDownloads = (Map) request.getSession().getAttribute(SESSION_TEMPORARYDOWNLOADS);
if (temporaryDownloads != null) {
TemporaryDownload tempDownload = (TemporaryDownload) temporaryDownloads.get(tempFileName);
if (tempDownload != null) {
file = tempDownload.getTempFile().getFile();
isTemporaryDownload = true;
}
}
}
else {
file = new File(this.getServletContext().getRealPath("/"), path.getResourcePath());
}
if (file == null || !file.exists() || !file.isFile()) {
throw new HttpErrorException(404, "File not found: " + path.getResourcePath(), null);
}
// / Set expiration time
int fileExpirationMinutes = getCore().getWgaConfiguration().getCacheExpirationForStaticResources();
if (fileExpirationMinutes > 0) {
int fileExpirationSeconds = fileExpirationMinutes * 60;
response.setHeader("Cache-Control", "max-age=" + fileExpirationSeconds + ", must-revalidate");
}
// determine lastModified
// - last modified of binary response depends only on resource change
// date
// - last change date of textual response additionally depends on
// character encoding change date
long lastModified;
if (isBinary(response)) {
lastModified = file.lastModified();
}
else {
lastModified = Math.max(file.lastModified(), _core.getCharacterEncodingLastModified());
}
if (browserCacheIsValid(request, lastModified)) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
else {
response.setDateHeader("Last-Modified", lastModified);
response.setHeader("ETag", '"' + String.valueOf(lastModified) + '"');
}
if (mimeType.startsWith("application/")) {
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
}
// Dispatch to file resource
try {
if (!"HEAD".equalsIgnoreCase(request.getMethod())) {
DataSource in = null;
int fileSize = 0;
// We allow direct file dispatching only for temporary files. In
// all other cases we use the servlet
// context which will take care of file system security.
if (isTemporaryDownload) {
response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());
in = new URLDataSource(file.toURL());
fileSize = (int) file.length();
}
else {
in = new URLDataSource(this.getServletContext().getResource(resourcePath));
}
if (in == null) {
throw new HttpErrorException(404, "File not found: " + resourcePath, null);
}
// B000041DA
// write data to response - use UTF-8 when reading characters -
// WGA resources are UTF-8 encoded
writeData(in, request, response, "UTF-8", fileSize, null, true);
}
}
catch (java.net.SocketException exc) {
_log.warn("Dispatch of resource request failed bc. of socket error: " + exc.getMessage());
}
catch (java.io.IOException exc) {
_log.warn("Dispatch of resource request failed bc. of IO error: " + exc.getMessage());
}
}
/**
* writes the given data to the response based on response.getContentType()
* either reader/writer (for textual content) or binary streams are used
*
* @param data
* - the data as inputstream to write - implicit closed after
* method call
* @param request
* @param response
* @param characterEncoding
* - encoding to use when reading character based from given
* data, if null platform encoding is used
* @throws IOException
* @throws HttpErrorException
*/
private void writeData(DataSource data, HttpServletRequest request, HttpServletResponse response, String characterEncoding, int size, String dbHint, boolean allowRanges) throws IOException,
HttpErrorException {
// Parse accept ranges if requested
List<AcceptRange> ranges = new ArrayList<AcceptRange>();
String rangeHeader = request.getHeader("Range");
if (rangeHeader != null && allowRanges && isBinary(response) && size > 0) {
if (rangeHeader.startsWith("bytes=")) {
rangeHeader = rangeHeader.substring("bytes=".length());
String[] subranges = rangeHeader.split("\\s*,\\s*");
for (String r : subranges) {
Matcher matcher = ACCEPT_RANGE_PATTERN.matcher(r);
if (matcher.matches()) {
String from = matcher.group(1);
String to = matcher.group(2);
AcceptRange newRange;
try {
newRange = new AcceptRange(from != null ? Integer.parseInt(from) : -1, to != null ? Integer.parseInt(to) : -1);
if (newRange.from == -1) {
newRange.from = size - newRange.to;
newRange.to = size - 1;
}
else if (newRange.to == -1) {
newRange.to = size - 1;
}
}
catch (NumberFormatException e) {
response.addHeader("Content-Range", "bytes */" + size);
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return;
}
if ((newRange.from == -1 && newRange.to == -1) || newRange.to > size) {
response.addHeader("Content-Range", "bytes */" + size);
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return;
}
ranges.add(newRange);
}
else {
response.addHeader("Content-Range", "bytes */" + size);
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return;
}
}
}
else {
response.addHeader("Content-Range", "bytes */" + size);
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return;
}
}
if (ranges.isEmpty()) {
// We can only set the raw length on binary resources bc.
// transcoding textual response can change the size
if (isBinary(response) && size > 0) {
response.setContentLength(size);
}
if (!"HEAD".equalsIgnoreCase(request.getMethod())) {
writeWholeData(data, response, characterEncoding, dbHint);
}
}
else {
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
if (ranges.size() == 1) {
AcceptRange r = ranges.get(0);
response.addHeader("Content-Range", "bytes " + r.from + "-" + r.to + "/" + size);
response.setContentLength(r.to - r.from + 1);
}
else {
response.setContentType("multipart/byteranges; boundary=" + BYTERANGE_BOUNDARY);
}
if (!"HEAD".equalsIgnoreCase(request.getMethod())) {
writeRangesData(data, ranges, response, dbHint, size);
}
}
}
private void writeWholeData(DataSource data, HttpServletResponse response, String characterEncoding, String dbHint) throws IOException, HttpErrorException, UnsupportedEncodingException {
Reader reader = null;
InputStream in = data.getInputStream();
if (in == null) {
throw new HttpErrorException(404, "File not found: " + data.getName(), dbHint);
}
try {
if (!isBinary(response)) {
// this is a textual reponse
if (characterEncoding != null) {
// read data in given encoding
reader = new InputStreamReader(in, characterEncoding);
}
else {
// use platform encoding
reader = new InputStreamReader(in);
}
WGUtils.inToOut(reader, response.getWriter(), 2048);
}
else {
WGUtils.inToOut(in, response.getOutputStream(), 2048);
}
}
finally {
if (reader != null) {
try {
reader.close();
}
catch (IOException e) {
}
}
if (in != null) {
try {
in.close();
}
catch (IOException e) {
}
}
}
}
private void writeRangesData(DataSource data, List<AcceptRange> ranges, HttpServletResponse response, String dbHint, long size) throws IOException, HttpErrorException, UnsupportedEncodingException {
ServletOutputStream out = response.getOutputStream();
for (AcceptRange range : ranges) {
InputStream in = data.getInputStream();
if (in == null) {
throw new HttpErrorException(404, "File not found: " + data.getName(), dbHint);
}
if (ranges.size() != 1) {
out.println();
out.println("--" + BYTERANGE_BOUNDARY);
out.println("Content-Type: " + data.getContentType());
out.println("Content-Range: bytes " + range.from + "-" + range.to + "/" + size);
out.println();
}
if (range.from > 0) {
in.skip(range.from);
}
try {
WGUtils.inToOutLimited(in, out, range.to - range.from + 1, 2048);
out.flush();
}
finally {
if (in != null) {
try {
in.close();
}
catch (IOException e) {
}
}
}
}
if (ranges.size() != 1) {
out.println();
out.print("--" + BYTERANGE_BOUNDARY + "--");
out.flush();
}
}
private void dispatchCssjsRequest(WGPRequestPath path, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws java.lang.Exception {
WGARequestInformation info = (WGARequestInformation) request.getAttribute(WGARequestInformation.REQUEST_ATTRIBUTENAME);
if (info != null) {
info.setType(WGARequestInformation.TYPE_SCRIPT);
}
// Fetch the database
WGDatabase database = path.getDatabase();
// Fetch the library
String codeType;
switch (path.getPathType()) {
case WGPRequestPath.TYPE_CSS:
codeType = WGScriptModule.CODETYPE_CSS;
break;
case WGPRequestPath.TYPE_JS:
codeType = WGScriptModule.CODETYPE_JS;
break;
default:
throw new HttpErrorException(500, "Invalid path type to dispatch a css/js request: " + path.getPathType(), path.getDatabaseKey());
}
WGCSSJSModule lib = database.getScriptModule(path.getCssjsKey(), codeType);
if (lib == null) {
throw new HttpErrorException(404, "No css/js resource of name " + path.getCssjsKey(), path.getDatabaseKey());
}
// determine mime type and encoding
String mimeType;
String libType = lib.getCodeType();
if (libType.equals(WGCSSJSModule.CODETYPE_CSS)) {
mimeType = "text/css";
}
else if (libType.equals(WGCSSJSModule.CODETYPE_JS)) {
mimeType = "text/javascript";
}
else if (libType.equals(WGCSSJSModule.CODETYPE_VBS)) {
mimeType = "text/vbscript";
}
else {
mimeType = "text/" + libType;
}
response.setContentType(mimeType);
// Set expiration time
int fileExpirationMinutes = ((Integer) _core.readPublisherOptionOrDefault(database, WGACore.DBATTRIB_FILEEXPIRATION_MINUTES)).intValue();
if (fileExpirationMinutes > 0) {
int fileExpirationSeconds = fileExpirationMinutes * 60;
response.setHeader("Cache-Control", "max-age=" + fileExpirationSeconds + ", must-revalidate");
}
// determine lastModified
// - last modified of binary response depends only on resource change
// date
// - last change date of textual response additionally depends on
// character encoding change date
long lastModified;
if (isBinary(response)) {
lastModified = lib.getLastModified().getTime();
}
else {
lastModified = Math.max(lib.getLastModified().getTime(), _core.getCharacterEncodingLastModified());
lastModified = Math.max(lastModified, _core.getDesignEncodingLastModified(database.getDbReference()));
}
if (browserCacheIsValid(request, lastModified)) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
else {
response.setDateHeader("Last-Modified", lastModified);
response.setHeader("ETag", '"' + String.valueOf(lastModified) + '"');
}
// If this is a head request we are finished now
if ("HEAD".equalsIgnoreCase(request.getMethod())) {
return;
}
String code = lib.getCode();
// response.setContentLength(code.length());
try {
java.io.Writer out = response.getWriter();
out.write(code);
}
catch (java.net.SocketException exc) {
_log.warn("Dispatch of css/js request failed bc. of socket error: " + exc.getMessage());
}
catch (java.io.IOException exc) {
_log.warn("Dispatch of css/js request failed bc. of IO error: " + exc.getMessage());
}
}
/**
* @see GenericServlet#init(ServletConfig)
*/
public void init(ServletConfig config) throws ServletException {
super.init(config);
_log = Logger.getLogger("wga.publisher");
Thread.currentThread().setName("WGPDispatcher.init");
this.getServletContext().setAttribute(WGACore.ATTRIB_DISPATCHER, this);
this._core = (WGACore) getServletContext().getAttribute(WGACore.ATTRIB_CORE);
if (this._core == null) {
_log.fatal("Could not connect to WGACore");
}
_tmlDebugger = new WebTMLDebugger();
_variousServerOptionReader = _core.getConfigOptionReader("/serverOptions", WGAServerOptionsModuleType.class, VariousModuleDefinition.class);
this.setServePages(true);
_log.debug("WebGate Anywhere Publisher initialized");
}
public WGACore getCore() {
return _core;
}
/**
* @see GenericServlet#destroy()
*/
public void destroy() {
shutdown();
super.destroy();
}
public void shutdown() {
this.setServePages(false);
}
public String getLoginURL(HttpServletRequest req, WGDatabase database, String sourceURL) throws UnsupportedEncodingException {
// / Retrieve LoginPage publisher option. If it is not set it defaults
// to /login.jsp�
String url = (String) database.getAttribute(WGACore.DBATTRIB_LOGIN_PAGE);
if (url == null || url.equals("")) {
url = "/login.jsp";
}
StringBuffer loginURL = qualifyWGAURL(url, req, database);
String parameterIntroducor = "?";
if (url.indexOf("?") != -1) {
parameterIntroducor = "&";
}
loginURL.append(parameterIntroducor).append("domain=").append(database.getAttribute(WGACore.DBATTRIB_DOMAIN).toString()).append("&redirect=").append(
java.net.URLEncoder.encode(sourceURL, getCore().getCharacterEncoding()));
return loginURL.toString();
}
public String getAdminLoginURL(HttpServletRequest req, String sourceURL) throws UnsupportedEncodingException {
StringBuffer loginURL = new StringBuffer();
loginURL.append(getPublisherURL(req)).append("/login.jsp");
String parameterIntroducor = "?";
if (loginURL.indexOf("?") != -1) {
parameterIntroducor = "&";
}
loginURL.append(parameterIntroducor).append("domain=").append(WGACore.DOMAIN_ADMINLOGINS).append("&redirect=").append(java.net.URLEncoder.encode(sourceURL, getCore().getCharacterEncoding()));
return loginURL.toString();
}
/**
* Takes a URL, and qualifies it to be a complete path, given the following
* schema: - If the url contains double slashes "//" it is considered to be
* a complete path already and returned unmodified - If the url starts with
* a slash "/", it is considered to be starting with a database key and it
* is only completed by the application context path - If the url does not
* start with a slash, it is considered to be a relative path for the given
* database. It is completed to the context path and the dbs database key
*
* @param url
* The url to qualify
* @param req
* A request object, used to determine the application context
* path
* @param database
* A database, used to determine a database key
* @return A string buffer containing the qualified URL. It can be used to
* add further information to the path
*/
public StringBuffer qualifyWGAURL(String url, HttpServletRequest req, WGDatabase database) {
StringBuffer loginURL = new StringBuffer();
// If this is no complete url, we will prepend the publisher url and
// conditionally dbkey, if the LoginPage does not begin with "/"
if (url.indexOf("//") == -1) {
loginURL.append(getPublisherURL(req, false));
if (!url.startsWith("/")) {
if (database == null) {
throw new IllegalArgumentException("Cannot qualify URL '" + url + "' when database parameter is not given");
}
loginURL.append("/").append(database.getDbReference()).append("/");
}
}
loginURL.append(url);
return loginURL;
}
public String getPublisherURL(javax.servlet.http.HttpServletRequest request, boolean absolute) {
if (request != null && absolute) {
String protocol = request.getProtocol().substring(0, request.getProtocol().indexOf("/")).toLowerCase();
if (protocol.equals("http") && request.isSecure()) {
protocol = "https";
}
int port = request.getServerPort();
if (port != -1) {
if (protocol.equals("http") && port == 80) {
port = -1;
} else if (protocol.equals("https") && port == 443) {
port = -1;
}
}
return protocol + "://" + request.getServerName() + (port != -1 ? ":" + port : "") + request.getContextPath();
}
else {
return request.getContextPath();
}
}
public String getPublisherURL(javax.servlet.http.HttpServletRequest request) {
return this.getPublisherURL(request, false);
}
private void dispatchStaticTmlRequest(WGPRequestPath path, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws java.lang.Exception {
// do not static requests
WGARequestInformation info = (WGARequestInformation) request.getAttribute(WGARequestInformation.REQUEST_ATTRIBUTENAME);
if (info != null) {
info.setType(WGARequestInformation.TYPE_STATIC);
}
// Fetch the database
WGDatabase database = path.getDatabase();
String contentKey = path.getContentKey();
String jspName = path.getResourcePath();
if (database != null) {
WGContent content = null;
LanguageBehaviour langBehaviour = LanguageBehaviourTools.retrieve(database);
// Determine the content for this request
if (contentKey != null) {
content = getContentByAnyKey(contentKey, database, request);
if (content == null) {
if (request.getQueryString() != null && request.getQueryString().toLowerCase().indexOf("login") != -1) {
sendRedirect(response, getLoginURL(request, database, path.getCompleteURL()));
}
else {
throw new HttpErrorException(404, "No content of name/id " + contentKey, path.getDatabaseKey());
}
return;
}
if (!content.isVisible() && !isBrowserInterface(request.getSession())) {
throw new HttpErrorException(404, "No content of name/id " + contentKey, path.getDatabaseKey());
}
}
else {
WGLanguage lang = langBehaviour.requestSelectDatabaseLanguage(database, request);
content = database.getDummyContent(lang.getName());
}
// Test browsability of content
if (!content.isDummy() && getBrowsingSecurity(database) <= BrowsingSecurity.NO_BROWSING) {
throw new HttpErrorException(java.net.HttpURLConnection.HTTP_FORBIDDEN, "Browsing not allowed in database '" + path.getDatabaseKey() + "'", path.getDatabaseKey());
}
request.setAttribute(WGACore.ATTRIB_MAINCONTEXT, content);
request.setAttribute(WGACore.ATTRIB_MEDIAKEY, database.getAttribute(WGACore.DBATTRIB_DEFAULT_MEDIAKEY));
}
// Set context attributes for jsps
String completeURL = path.getCompleteURL();
request.setAttribute(WGACore.ATTRIB_WGPPATH, this.getPublisherURL(request));
request.setAttribute(WGACore.ATTRIB_TAGIDS, WGUtils.createSynchronizedMap());
request.setAttribute(WGACore.ATTRIB_TMLCONTEXTS, WGUtils.createSynchronizedMap());
request.setAttribute(WGACore.ATTRIB_REQUESTURL, completeURL);
request.setAttribute(WGACore.ATTRIB_REQUESTTYPE, REQUESTTYPE_STATICTML);
String mimeType = null;
if (contentKey != null) {
mimeType = this.getServletContext().getMimeType(jspName);
}
else {
mimeType = this.getServletContext().getMimeType(request.getRequestURI());
}
if (mimeType == null) {
mimeType = "text/html";
}
request.setAttribute(WGACore.ATTRIB_MIMETYPE, mimeType);
response.setContentType(mimeType);
String targetJSP = jspName + ".jsp";
if (targetJSP != null) {
if (!"HEAD".equalsIgnoreCase(request.getMethod())) {
this.getServletContext().getRequestDispatcher(targetJSP).include(request, response);
}
}
else {
throw new HttpErrorException(500, "no static tml page: " + targetJSP, path.getDatabaseKey());
}
// Eventually do redirect
if (request.getAttribute(WGACore.ATTRIB_REDIRECT) != null) {
response.reset();
response.sendRedirect(String.valueOf(request.getAttribute(WGACore.ATTRIB_REDIRECT)));
}
}
public int getBrowsingSecurity(WGDatabase db) {
String securityStr = (String) db.getAttribute(WGACore.DBATTRIB_BROWSING_SECURITY);
if (securityStr != null) {
return Integer.parseInt(securityStr);
}
String allowBrowsing = (String) db.getAttribute(WGACore.DBATTRIB_ALLOW_BROWSING);
if (allowBrowsing != null && Boolean.parseBoolean(allowBrowsing) == false) {
return BrowsingSecurity.NO_BROWSING;
}
return BrowsingSecurity.FULL_ACCESS;
}
/**
* @see GenericServlet#getServletInfo()
*/
public String getServletInfo() {
return WGACore.getReleaseString();
}
/**
* Gets the contextPath
*
* @return Returns a String
*/
public String getContextPath() {
return _contextPath;
}
/**
* Sets the contextPath
*
* @param contextPath
* The contextPath to set
*/
public void setContextPath(String contextPath) {
this._contextPath = contextPath;
}
/**
* Gets the listenPort
*
* @return Returns a int
*/
public int getListenPort() {
return _listenPort;
}
/**
* Returns the servePages.
*
* @return boolean
*/
public boolean isServePages() {
return _servePages;
}
/**
* @deprecated use isAdminLoggedIn(HttpServletRequest) instead
* @param session
* @return
*/
public boolean isAdminLoggedIn(HttpSession session) {
return isAdminLoggedIn(session, null);
}
/**
* @deprecated use isAdminLoggedIn(HttpServletRequest) instead
* @param session
* @param request
* @return
*/
public boolean isAdminLoggedIn(HttpSession session, HttpServletRequest request) {
if (request != null) {
return getCore().isAdminLoggedIn(request);
}
else {
return getCore().isAdminLoggedIn(session);
}
}
public boolean isAdminLoggedIn(HttpServletRequest request) {
return getCore().isAdminLoggedIn(request);
}
/**
* Sets the servePages.
*
* @param servePages
* The servePages to set
*/
public void setServePages(boolean servePages) {
this._servePages = servePages;
}
public static String buildVirtualLink(WGContent content, ServletRequest request, String mediakey, String layout) throws WGAPIException {
// External urls are just put out without modification
String vlinkType = content.getVirtualLinkType();
if (vlinkType.equals(WGContent.VIRTUALLINKTYPE_EXTERNAL)) {
return content.getVirtualLink();
}
// Build base url
WGPRequestPath path = (WGPRequestPath) request.getAttribute(WGACore.ATTRIB_REQUESTPATH);
StringBuffer url = new StringBuffer();
url.append(path.getPublisherURL()).append("/");
String dbKey = content.getDatabase().getAttribute(WGACore.DBATTRIB_DBKEY).toString();
url.append(dbKey).append("/");
// Vary detail path by link type
if (vlinkType.equals(WGContent.VIRTUALLINKTYPE_INTERNAL_FILE)) {
url.append("file/");
url.append(content.getContentKey(false).toString());
url.append("/");
url.append(content.getVirtualLink());
return url.toString();
}
else if (vlinkType.equals(WGContent.VIRTUALLINKTYPE_CONTENT)) {
// Extract content key
List elements = WGUtils.deserializeCollection(content.getVirtualLink(), "/");
String contentKey = (String) elements.get(elements.size() - 1);
// If available append media and layout keys
if (mediakey != null) {
url.append(mediakey).append("/");
}
if (layout != null) {
url.append(layout).append("/");
}
url.append(contentKey);
return url.toString();
}
else if (vlinkType.equals(WGContent.VIRTUALLINKTYPE_FILE)) {
// Extract file container and filename information
List elements = WGUtils.deserializeCollection(content.getVirtualLink(), "/");
if (elements.size() < 2) {
throw new IllegalArgumentException("Virtual link of type '" + vlinkType + "' in " + content.getDocumentKey() + "' has too few elements (< 2)");
}
String containerName = (String) elements.get(elements.size() - 2);
String fileName = (String) elements.get(elements.size() - 1);
// Build url
url.append("file/");
url.append(containerName);
url.append("/");
url.append(fileName);
return url.toString();
}
else if (vlinkType.equals(WGContent.VIRTUALLINKTYPE_EXTERNAL_FILE)) {
List elements = WGUtils.deserializeCollection(content.getVirtualLink(), "/");
if (elements.size() < 2) {
throw new IllegalArgumentException("Virtual link of type '" + vlinkType + "' in " + content.getDocumentKey() + "' has too few elements (< 2)");
}
String containerName = (String) elements.get(0);
String fileName = (String) elements.get(1);
// Build url
url.append("file/");
url.append(containerName);
url.append("/");
url.append(fileName);
return url.toString();
}
else if (vlinkType.equals(WGContent.VIRTUALLINKTYPE_UNIQUENAME)) {
String uname = content.getVirtualLink();
url.append(uname);
return url.toString();
}
// Fallback. If unknown link type, just put out virtual link
else {
return content.getVirtualLink();
}
}
/**
* @param code
* @param message
* @param dbHint
* @deprecated instead throw HttpErrorException directly
* @throws HttpErrorException
*/
public void sendError(int code, String message, String dbHint) throws HttpErrorException {
throw new HttpErrorException(code, message, dbHint);
}
/**
* @param code
* @param message
* @deprecated instead throw HttpErrorException directly
* @throws HttpErrorException
*/
public void sendError(int code, String message) throws HttpErrorException {
sendError(code, message, null);
}
public synchronized String addTemporaryDownload(HttpSession session, TemporaryFile file) {
String name = UIDGenerator.generateUID();
Map temporaryDownloads = (Map) session.getAttribute(SESSION_TEMPORARYDOWNLOADS);
if (temporaryDownloads == null) {
temporaryDownloads = new TransientMap(new HashMap());
session.setAttribute(SESSION_TEMPORARYDOWNLOADS, temporaryDownloads);
}
TemporaryDownload tempDownload = new TemporaryDownload(name, file);
temporaryDownloads.put(name, tempDownload);
return name;
}
public synchronized String addTemporaryDownload(HttpSession session, File file) throws IOException {
InputStream in = new FileInputStream(file);
TemporaryFile tempFile = new TemporaryFile(file.getName(), in, WGFactory.getTempDir());
return addTemporaryDownload(session, tempFile);
}
private boolean browserCacheIsValid(HttpServletRequest request, long lastModified) {
String currentETag = String.valueOf(lastModified);
String browserETagsStr = (String) request.getHeader("If-None-Match");
if (browserETagsStr != null) {
Iterator browserETags = WGUtils.deserializeCollection(browserETagsStr, ",", true, new Character('"')).iterator();
while (browserETags.hasNext()) {
String rawETag = (String) browserETags.next();
String eTag = rawETag.substring(rawETag.indexOf('"') + 1, rawETag.lastIndexOf('"'));
if (eTag.equals(currentETag)) {
return true;
}
}
}
long modSince = request.getDateHeader("If-Modified-Since");
if (modSince != -1 && modSince >= WGUtils.cutoffTimeMillis(lastModified)) {
return true;
}
return false;
}
private boolean isBinary(HttpServletResponse response) {
String mimeType = response.getContentType();
if (mimeType != null && mimeType.toLowerCase().indexOf("text") != -1) {
return false;
}
else {
return true;
}
}
}