/** * 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); } } }