// Copyright 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.enterprise.connector.servlet;
import com.google.enterprise.connector.common.SecurityUtils;
import com.google.enterprise.connector.instantiator.ExtendedConfigureResponse;
import com.google.enterprise.connector.logging.NDC;
import com.google.enterprise.connector.manager.Context;
import com.google.enterprise.connector.manager.Manager;
import com.google.enterprise.connector.spi.ConfigureResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* An abstract class for Connector Manager GET servlets.
* It contains an abstract method "processDoGet".
*/
public abstract class ConnectorManagerGetServlet extends HttpServlet {
private static final Logger LOGGER =
Logger.getLogger(ConnectorManagerGetServlet.class.getName());
/**
* This abstract method processes servlet-specific GET request,
* make servlet-specific call to the connector manager and write the
* XML response body.
*
* @param connectorName String the parameter for connector name
* @param lang String the parameter for language
* @param manager Manager
* @param out PrintWriter where the XML response body is written
*/
protected abstract void processDoGet(
String connectorName, String lang, Manager manager, PrintWriter out);
/**
* Returns the XML response for a given request.
*
* @param req
* @param res
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException {
// Make sure this requester is OK
if (!RemoteAddressFilter.getInstance()
.allowed(RemoteAddressFilter.Access.BLACK, req.getRemoteAddr())) {
res.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
res.setContentType(ServletUtil.MIMETYPE_XML);
res.setCharacterEncoding("UTF-8");
PrintWriter out = res.getWriter();
String connectorName = req.getParameter(ServletUtil.XMLTAG_CONNECTOR_NAME);
NDC.pushAppend("Config " + connectorName);
try {
if (connectorName == null || connectorName.length() < 1) {
ServletUtil.writeResponse(out, new ConnectorMessageCode(
ConnectorMessageCode.RESPONSE_NULL_CONNECTOR));
LOGGER.warning(ServletUtil.LOG_RESPONSE_NULL_CONNECTOR);
return;
}
String lang = req.getParameter(ServletUtil.QUERY_PARAM_LANG);
if (lang == null || lang.length() < 1) {
lang = null;
}
Manager manager = Context.getInstance().getManager();
processDoGet(connectorName, lang, manager, out);
} finally {
out.close();
NDC.pop();
}
}
/**
* Returns the XML response for a given request.
* Simply call doGet
*
* @param req
* @param res
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse res)
throws IOException {
doGet(req, res);
}
public static void writeConfigureResponse(PrintWriter out,
ConnectorMessageCode status, ConfigureResponse configRes) {
writeConfigureResponse(out, status, configRes, true);
}
/**
* Write connector configuration to XML response
*
* @param out PrintWriter servlet response writer
* @param status ConnectorMessageCode the servlet call status
* @param configRes ConfigureResponse
* @param doObfuscate set to true to obfuscate any sensitive values in the
* form snippet contained in the response.
*/
public static void writeConfigureResponse(PrintWriter out,
ConnectorMessageCode status, ConfigureResponse configRes,
boolean doObfuscate) {
if (configRes != null && LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest("CONFIGURE RESPONSE: message = " + configRes.getMessage());
// TODO: Add a separate logger for formSnippet (since they are huge).
LOGGER.finest("CONFIGURE RESPONSE: formSnippet = "
+ ((configRes.getFormSnippet() == null) ? "null" : "[...]"
// ServletUtil.filterSensitiveData(configRes.getFormSnippet())
));
LOGGER.finest("CONFIGURE RESPONSE: configData = "
+ SecurityUtils.getMaskedMap(configRes.getConfigData()));
if (configRes instanceof ExtendedConfigureResponse) {
LOGGER.finest("CONFIGURE RESPONSE: configXML = "
+ ((ExtendedConfigureResponse) configRes).getConfigXml());
}
}
ServletUtil.writeRootTag(out, false);
// Have to check the configRes for a well formed HTML snippet before
// committing to given status.
String formSnippet = null;
if (configRes != null && configRes.getFormSnippet() != null &&
configRes.getFormSnippet().length() > 0) {
formSnippet = configRes.getFormSnippet();
// formSnippet is required to contain XHTML at this point.
if (doObfuscate) {
formSnippet = ServletUtil.filterSensitiveData(formSnippet);
}
// formSnippet may now contain XHTML or HTML, depending on if
// filterSensitiveData made any modifications.
if (formSnippet == null) {
// Form snippet was not well formed. Change status to reflect XML
// parsing error.
status = new ConnectorMessageCode(
ConnectorMessageCode.ERROR_PARSING_XML_REQUEST);
configRes = null;
} else {
// Remove CDATA sections and properly encode their old contents. This is
// necessary for both the XHTML and HTML cases, since Xalan as shipped
// with Java improperly includes CDATA sections in HTML.
formSnippet = ServletUtil.removeNestedMarkers(formSnippet);
// Now handle any ']]>' within the content by turning them into ]]>.
// These characters are completely valid within the (X)HTML, but will
// not be once we place the (X)HTML within the XML document in a CDATA.
// Note that this does change the meaning of the snippet within the XML,
// but 1) the snippet will be parsed a second time by the browser, in
// which case a > would become > and 2) the GSA proactively reverses
// the process by replacing ]]> with ]]> before sending the HTML to
// the browser.
//
// The XML-way of handling this problem is by stopping and restarting
// the CDATA section around the ]]> such that the ]] are in one section
// and the > is in another. However, this doesn't work because the GSA
// expects only a single CDATA section and would ignore any later
// sections.
formSnippet = ServletUtil.escapeEndMarkers(formSnippet);
}
}
// Now write out the response.
ServletUtil.writeMessageCode(out, status);
if (configRes != null) {
ServletUtil.writeXMLTag(
out, 1, ServletUtil.XMLTAG_CONFIGURE_RESPONSE, false);
if (formSnippet != null) {
ServletUtil.writeXMLElement(
out, 2, ServletUtil.XMLTAG_FORM_SNIPPET,
ServletUtil.XML_CDATA_START + formSnippet
+ ServletUtil.XML_CDATA_END);
}
if (configRes instanceof ExtendedConfigureResponse) {
String configXml =
((ExtendedConfigureResponse) configRes).getConfigXml();
if (configXml != null) {
ServletUtil.writeXMLElement(
out, 2, ServletUtil.XMLTAG_CONNECTOR_CONFIG_XML,
ServletUtil.XML_CDATA_START
+ ServletUtil.escapeEndMarkers(configXml)
+ ServletUtil.XML_CDATA_END);
}
}
if (configRes.getMessage() != null &&
configRes.getMessage().length() > 0) {
ServletUtil.writeXMLElement(
out, 2, ServletUtil.XMLTAG_MESSAGE, configRes.getMessage());
}
ServletUtil.writeXMLTag(
out, 1, ServletUtil.XMLTAG_CONFIGURE_RESPONSE, true);
}
ServletUtil.writeRootTag(out, true);
}
}