/**
* Copyright 2006 by Know-Center, Graz, Austria
* PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a
* joint initiative of the Federal Chancellery Austria and Graz University of
* Technology.
*
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
* the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
* http://www.osor.eu/eupl/
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*
* This product combines work with different licenses. See the "NOTICE" text
* file for details on the various modules and licenses.
* The "NOTICE" text file is part of the distribution. Any derivative works
* that you distribute must include a readable copy of the "NOTICE" text file.
*/
package at.gv.egiz.pdfas.web.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import at.gv.egiz.pdfas.api.PdfAs;
import at.gv.egiz.pdfas.api.commons.Constants;
import at.gv.egiz.pdfas.api.commons.SignatureInformation;
import at.gv.egiz.pdfas.api.exceptions.PdfAsException;
import at.gv.egiz.pdfas.api.internal.LocalBKUParams;
import at.gv.egiz.pdfas.api.internal.PdfAsInternal;
import at.gv.egiz.pdfas.api.verify.VerifyResult;
import at.gv.egiz.pdfas.api.verify.VerifyResults;
import at.gv.egiz.pdfas.exceptions.external.ExternalErrorException;
import at.gv.egiz.pdfas.exceptions.web.SessionExpiredException;
import at.gv.egiz.pdfas.framework.logging.StatisticData;
import at.gv.egiz.pdfas.framework.logging.StatisticLogFactory;
import at.gv.egiz.pdfas.framework.logging.StatisticLogger;
import at.gv.egiz.pdfas.web.LocalRequest;
import at.gv.egiz.pdfas.web.helper.ApiHelper;
import at.gv.egiz.pdfas.web.helper.LocalRequestHelper;
import at.gv.egiz.pdfas.web.helper.SessionHelper;
import at.gv.egiz.pdfas.web.helper.SignServletHelper;
import at.gv.egiz.pdfas.web.helper.SigningTimeHelper;
import at.gv.egiz.pdfas.web.session.SessionAttributes;
import at.gv.egiz.pdfas.web.session.SignSessionInformation;
import at.gv.egiz.pdfas.web.session.VerifySessionInformation;
import at.knowcenter.wag.egov.egiz.cfg.SettingsReader;
import at.knowcenter.wag.egov.egiz.exceptions.ConnectorException;
import at.knowcenter.wag.egov.egiz.exceptions.SignatureException;
/**
* @author wprinz
*
*/
public class DataURLServlet extends HttpServlet {
/**
* SVUID.
*/
private static final long serialVersionUID = -5846618335843762752L;
/**
* The log.
*/
private static Log log = LogFactory.getLog(DataURLServlet.class);
// stat Log
private static StatisticLogger statLog = StatisticLogFactory.getLog(Constants.STATISTIC_LOGGER_NAME);
protected void dispatch(HttpServletRequest request,
HttpServletResponse response, String resource)
throws ServletException, IOException {
dispatch(request, response, resource, getServletContext());
}
protected static void dispatch(HttpServletRequest request,
HttpServletResponse response, String resource,
ServletContext context) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
RequestDispatcher disp = context.getRequestDispatcher(resource);
disp.forward(request, response);
}
protected void dispatchToResults(VerifyResults results,
HttpServletRequest request, HttpServletResponse response,
String backToListURL) throws ServletException, IOException {
request.setAttribute("results", results);
request.setAttribute("btlurl", backToListURL);
dispatch(request, response, "/jsp/results.jsp");
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
private static void temporaryRedirect(String redirectURL,
HttpServletResponse response) throws IOException {
String encodedRedirect = response.encodeRedirectURL(redirectURL);
response.addHeader("Location", encodedRedirect);
response.setContentType("text/xml");
response.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT);
String nop = "";
PrintWriter pw = response.getWriter();
response.setCharacterEncoding("UTF-8");
response.setContentLength(nop.getBytes("UTF-8").length);
log.debug("Redirecting via NullOperationRequest to " + encodedRedirect
+ ".");
pw.println(nop);
pw.flush();
pw.close();
}
/**
* @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse)
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
log.debug("Data URL is accessed."); //$NON-NLS-1$
Object sessionObject = null;
try {
sessionObject = SessionHelper.getSession(request);
// obsolete since EncodingFilter is set in web.xml
checkRequestCharacterEncoding(request);
if (sessionObject instanceof SignSessionInformation) {
SignSessionInformation si = (SignSessionInformation) sessionObject;
log.debug("Vor process sign:...");
processSign(request, response, si);
log.debug("Nach process sign...");
} else {
VerifySessionInformation si = (VerifySessionInformation) sessionObject;
processVerify(request, response, si);
}
} catch (PdfAsException e) {
if (statLog.isEnabled()) {
String ua = request.getHeader("User-Agent");
long endTime = System.currentTimeMillis();
StatisticData statisticData = new StatisticData();
statisticData.setException(new SessionExpiredException("Session lost?"));
statisticData.setUserAgent(ua);
if (sessionObject != null) {
statisticData.setException(e);
if (sessionObject instanceof SignSessionInformation) {
SignSessionInformation si = (SignSessionInformation) sessionObject;
statisticData.setOperation("SIGN").setSignatureMode(si.mode).setConnector(si.connector)
.setFileSize(si.pdfDataSource.getLength()).setDuration(endTime - si.startTime)
.setSignatureProfileId(si.type);
} else {
VerifySessionInformation si = (VerifySessionInformation) sessionObject;
statisticData.setOperation("VERIFY").setSignatureMode(si.mode).setConnector(si.connector)
.setFileSize(si.inputDataSource.getLength()).setDuration(endTime - si.startTime)
.setSignatureProfileId(si.type);
}
}
statLog.log(statisticData);
}
log.error(e);
if (e instanceof ExternalErrorException) {
HttpSession session = request.getSession(true);
session.setAttribute(SignServlet.ERROR_WITHIN_IFRAME, "false");
log.debug("Attribute ERROR_WITHIN_IFRAME: " + session.getAttribute(SignServlet.ERROR_WITHIN_IFRAME));
}
SignServlet.prepareDispatchToErrorPage(e, request);
dispatch(request, response, "/jsp/error.jsp");
}
log.debug("DataURL access finished."); //$NON-NLS-1$
}
protected void checkRequestCharacterEncoding(HttpServletRequest request)
throws UnsupportedEncodingException {
if (request.getCharacterEncoding() == null
|| request.getCharacterEncoding().length() <= 0) //$NON-NLS-1$
{
log.info("The BKU didn't set a character encoding for the request."); //$NON-NLS-1$
log.info("Manually setting character encoding to UTF-8"); //$NON-NLS-1$
request.setCharacterEncoding("UTF-8"); //$NON-NLS-1$
}
}
protected boolean isNullResponse(String xml_response) {
return xml_response != null
&& xml_response.indexOf("NullOperationResponse") != -1;
}
private static String retrieveXMLResponse(HttpServletRequest request)
throws ServletException {
log.debug("Trying to fetch XMLResponse...");
String xml_response = null;
if (ServletFileUpload.isMultipartContent(request)) {
log.debug("Response is multipart.");
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
try {
List items = upload.parseRequest(request);
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
if (item.isFormField()
&& "XMLResponse".equals(item.getFieldName())) {
log.debug("XMLResponse part found.");
xml_response = item.getString();
break;
}
}
} catch (FileUploadException e) {
throw new ServletException(e);
}
} else {
xml_response = request.getParameter("XMLResponse");
}
log.debug("XMLResponse = " + xml_response);
return xml_response;
}
protected void processSign(HttpServletRequest request,
HttpServletResponse response, SignSessionInformation si)
throws ServletException, IOException, PdfAsException {
log.trace("processSign");
String xml_response = retrieveXMLResponse(request);
PdfAsInternal pdfAsInternal = ApiHelper
.getPdfAsInternalFromContext(getServletContext());
String server = request.getHeader("server");
String userAgent = request.getHeader("user-agent");
String signatureLayout = request
.getHeader(Constants.BKU_HEADER_SIGNATURE_LAYOUT);
String connector = si.connector;
String url = "";
if (si.connector.equals(Constants.SIGNATURE_DEVICE_BKU)) {
url = SettingsReader.getInstance().getSetting("bku.sign.url");
} else if (si.connector.equals(Constants.SIGNATURE_DEVICE_MOC)) {
url = SettingsReader.getInstance().getSetting("moc.sign.url");
} else if (si.connector.equals(Constants.SIGNATURE_DEVICE_MOBILE)) {
url = SettingsReader.getInstance().getSetting("mobile.sign.url");
} else {
url = SettingsReader.getInstance()
.getSetting("mobiletest.sign.url");
}
log.debug("Url ist: " + url);
log.debug("Server ist: " + server);
log.debug("UserAgent: " + userAgent);
log.debug("Layout ist: " + signatureLayout);
// rpiazzi added
// When choosing local CCS (a-trust 1.3.3.3 and higher) it seems that
// more requests to this servlet are sent from
// CCS. Therefore the first request (with no information about CCS in
// the headers) has to be ignored...
if (((server == null) && (userAgent == null)
&& (signatureLayout == null) || (xml_response == null))) {
log.debug("Received response with none of the following header fields: \"server\", \"user-agent\", \""
+ Constants.BKU_HEADER_SIGNATURE_LAYOUT + "\"");
log.debug("This is probably the empty servlet call when local CCS and a-trust CCS version >1.3.3.2 is choosen. In this case the right call of this servlet will follow soon!");
log.debug("Server is: " + server);
log.debug("UserAgent is: " + userAgent);
log.debug("SignatureLayout is: " + signatureLayout);
log.debug("xml_response is: " + xml_response);
}
// end added
else {
LocalBKUParams bkuParams = new LocalBKUParams(server, userAgent,
signatureLayout);
si.localBKUParams = bkuParams;
pdfAsInternal.verifyBKUSupport(bkuParams);
if (isNullResponse(xml_response)) {
log.debug("Received a NullOperationResponse -> answering with the first request."); //$NON-NLS-1$
// assert si.outputAvailable == false;
// assert si.xmlResponse == null;
log.debug("There are still requests to be performed -> answering with request."); //$NON-NLS-1$
LocalRequest local_request = si.localRequest;
String request_string = local_request.getRequestString();
log.debug("request = " + request_string);
response.setContentType("text/xml");
response.setCharacterEncoding("UTF-8");
response.getWriter().println(request_string);
} else if (xml_response != null) {
log.debug("Received a normal response -> storing the response."); //$NON-NLS-1$
si.xmlResponse = xml_response;
log.debug("All requests have been processed -> processing the responses."); //$NON-NLS-1$
// Sign
if (!si.outputAvailable) {
PdfAs pdfAs = ApiHelper
.getPdfAsFromContext(getServletContext());
SignServletHelper.finishLocalSign(pdfAs, pdfAsInternal, si);
SigningTimeHelper.checkSigningTimeAgainstHostTime(si.sdi
.getSignDate());
si.outputAvailable = true;
}
if (si.output.getMimeType().equals("text/xml")
&& si.outputAvailable) {
// For "detached" signatures, the return value (data sink)
// is the response xml,
// but when passed through the BKU it is interpreted as
// another request
// which will generate a return code 1501
// Then PDF-AS would answer with the response as well
// generating
// another 1501 and so forth.
// Therefor return it as TXT.
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
response.getWriter()
.println(
"Das detached XML kann nicht direkt durch die BKU geschliffen werden, weil diese es als Request interpretieren w\u00FCrde. Daher das XML als Text:");
response.getWriter().println(
new String(si.signedPdf, "UTF-8"));
if (statLog.isEnabled()) {
long endTime = System.currentTimeMillis();
StatisticData statisticData = new StatisticData("SIGN", si.connector,
si.pdfDataSource.getLength(), si.mode, endTime - si.startTime, userAgent);
statisticData.setSignatureProfileId(si.type);
statLog.log(statisticData);
}
} else {
// tzefferer: If PDF-AS has been called by an external
// web-application, we do not
// redirect to download.jsp but return the sign-response
// immediately
if (si.exappinf != null) {
log.debug("Entering external application interface mode. Skipping redirection to download page.");
// afitzek
if (statLog.isEnabled()) {
long endTime = System.currentTimeMillis();
StatisticData statisticData = new StatisticData("SIGN EXTERNAL", si.connector,
si.pdfDataSource.getLength(), si.mode, endTime - si.startTime, userAgent);
statisticData.setSignatureProfileId(si.type);
statLog.log(statisticData);
}
SignServletHelper.returnSignResponse(si, request,
response);
// Not needed due to redirection of returnSignResponse.
// Just to clarify that there must not be any code after
// returnSignResponse.
return;
} else {
log.debug("Preparing download page.");
HttpSession session = request.getSession(true);
log.debug("Putting signed document into session ("
+ session.getId() + ").");
session.setAttribute(
SessionAttributes.SIGNED_PDF_DOCUMENT, si);
String downloadURL = response
.encodeRedirectURL(LocalRequestHelper
.getLocalContextAddress(request,
response)
+ "/ProvidePDF");
/*
* afitzek: changing log message to info level to see in
* log files if signature process was ok
*/
log.info("Creating download URL \"" + downloadURL
+ "\".");
session.setAttribute(
SessionAttributes.DOWNLOAD_URL_FOR_SIGNED_PDF_DOCUMENT,
downloadURL);
// afitzek
if (statLog.isEnabled()) {
long endTime = System.currentTimeMillis();
StatisticData statisticData = new StatisticData("SIGN", si.connector,
si.pdfDataSource.getLength(), si.mode, endTime - si.startTime, userAgent);
statisticData.setSignatureProfileId(si.type);
statLog.log(statisticData);
}
temporaryRedirect(
response.encodeRedirectURL(LocalRequestHelper
.getLocalContextAddress(request,
response)
+ "/jsp/download.jsp"), response);
// Not needed due to temporaryRedirect.
// Just to clarify that there must not be any code after
// temporaryRedirect.
return;
}
// do not insert any code within this else block !
}
} else {
log.debug("No XMLResponse found. Do nothing.");
}
}
}
protected void processVerify(HttpServletRequest request,
HttpServletResponse response, VerifySessionInformation si)
throws ServletException, IOException, ConnectorException,
SignatureException {
log.trace("processVerify");
String userAgent = request.getHeader("user-agent");
String xml_response = request.getParameter("XMLResponse"); //$NON-NLS-1$
log.debug("xml_response = " + xml_response); //$NON-NLS-1$
if (isNullResponse(xml_response)) {
log.debug("Received a NullOperationResponse -> answering with the first request."); //$NON-NLS-1$
// assert si.currentLocalOperation.current_operation == 0;
} else {
log.debug("Recieved a normal response -> storing the response."); //$NON-NLS-1$
si.currentLocalOperation.finishCurrentOperation(xml_response);
}
if (!si.currentLocalOperation.isFinished()) {
log.debug("There are still requests to be performed -> answering with request #" + si.currentLocalOperation.current_operation); //$NON-NLS-1$
LocalRequest local_request = si.currentLocalOperation
.getCurrentLocalRequest();
String request_string = local_request.getRequestString();
response.setContentType("text/xml");
response.setCharacterEncoding("UTF-8");
response.getWriter().println(request_string);
} else {
log.debug("All requests have been processed -> processing the responses."); //$NON-NLS-1$
PdfAsInternal pdfAsInternal = ApiHelper
.getPdfAsInternalFromContext(getServletContext());
final ArrayList resList = new ArrayList();
for (int i = 0; i < si.currentLocalOperation.response_xmls.length; i++) {
SignatureInformation sigInfo = (SignatureInformation) si.currentLocalOperation.signaturesToBeverified
.get(i);
VerifyResult result = pdfAsInternal.finishLocalVerify(sigInfo,
si.connector, si.type,
"loc ref content not needed here",
si.currentLocalOperation.response_xmls[i]);
resList.add(result);
}
si.currentLocalOperation = null;
URL btlURL = new URL(LocalRequestHelper.getLocalContextAddress(
request, response) + "/jsp/verifylist.jsp");
String backToListURL = response.encodeURL(btlURL.toString());
VerifyResults results = new VerifyResults() {
public List getResults() {
return resList;
}
};
if (statLog.isEnabled()) {
long endTime = System.currentTimeMillis();
StatisticData statisticData = new StatisticData("VERIFY", si.connector, si.inputDataSource.getLength());
statisticData.setDuration(endTime - si.startTime);
statisticData.setUserAgent(request.getHeader("User-Agent"));
statLog.log(statisticData);
}
dispatchToResults(results, request, response, backToListURL);
}
}
}