/** * 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.helper; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import at.gv.egiz.pdfas.PdfAsFactory; import at.gv.egiz.pdfas.api.PdfAs; import at.gv.egiz.pdfas.api.commons.Constants; import at.gv.egiz.pdfas.api.commons.DynamicSignatureLifetimeEnum; import at.gv.egiz.pdfas.api.commons.DynamicSignatureProfile; import at.gv.egiz.pdfas.api.exceptions.PdfAsException; import at.gv.egiz.pdfas.api.internal.PdfAsInternal; import at.gv.egiz.pdfas.api.io.DataSink; import at.gv.egiz.pdfas.api.sign.SignParameters; import at.gv.egiz.pdfas.api.sign.SignResult; import at.gv.egiz.pdfas.api.sign.SignatureDetailInformation; import at.gv.egiz.pdfas.exceptions.ErrorCode; import at.gv.egiz.pdfas.web.FormFields; import at.gv.egiz.pdfas.web.PDFContainer; import at.gv.egiz.pdfas.web.io.ByteArrayDataSink; import at.gv.egiz.pdfas.web.servlets.ProvidePDFServlet; import at.gv.egiz.pdfas.web.session.SessionAttributes; import at.gv.egiz.pdfas.web.session.SignSessionInformation; import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; /** * @author wprinz * */ public class SignServletHelper { /** * The log. */ private static Log log = LogFactory.getLog(SignServletHelper.class); public 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); } /** * Prepares the sign. * *

* This prepares the data for both being signed or being previewed. *

* * @param si * The SessionInformation to be prepared. * @throws PdfAsException */ public static SignatureDetailInformation prepareSign(PdfAs pdfAs, SignSessionInformation si) throws PdfAsException { log.debug("prepareSign:"); //$NON-NLS-1$ SignParameters signParameters = new SignParameters(); signParameters.setDocument(si.pdfDataSource); signParameters.setSignatureDevice(si.connector); signParameters.setSignaturePositioning(si.pos); signParameters.setSignatureProfileId(si.type); signParameters.setSignatureType(si.mode); DataSink sink = new ByteArrayDataSink(); signParameters.setOutput(sink); // dynamically switch to pdf/a /*if (si.pdfa) { DynamicSignatureProfile dsp = pdfAs.createDynamicSignatureProfile( signParameters.getSignatureProfileId(), DynamicSignatureLifetimeEnum.AUTO ); dsp.setPropertyRaw("key.SIG_PDFA1B_VALID", "true"); dsp.setPropertyRaw("table.main.Style.font", "TTF:DejaVuSansCondensed.ttf,8"); dsp.setPropertyRaw("table.main.Style.valuefont", "TTF:DejaVuSansMono.ttf,8"); dsp.apply(); signParameters.setSignatureProfileId(dsp.getName()); }*/ SignatureDetailInformation signatureDetail = pdfAs.prepareSign(signParameters); si.sdi = signatureDetail; si.signParameters = signParameters; // PdfASID algorithm = FormFields.translateSignatureModeToPdfASID(si.mode); // Signator signator = SignatorFactory.createSignator(algorithm); // tzefferer: modified // si.iui = signator.prepareSign(si.pdf, si.type, null, // ConnectorFactory.needsSIG_ID(si.connector)); // si.si = signator.prepareSign(si.pdfDataSource, si.type, si.pos, null); // end modify log.debug("prepareSign finished."); //$NON-NLS-1$ return signatureDetail; } /** * Finishes the sign. * *

* For non local connectors this concludes the sign process, signs the * document and returns the result. For local connectors this initializes the * local sign process and redirects to following servlets. *

* * @param si * The SessionInformation. * @param request * The servlet request for dispatching. * @param response * The servlet response for dispatching. * @param context * The servlet context for dispatching. * @throws IOException * f. e. * @throws ServletException * f. e. * @throws PdfAsException */ public static void finishSign(SignSessionInformation si, HttpServletRequest request, HttpServletResponse response, ServletContext context) throws IOException, ServletException, PdfAsException { log.debug("finishSign:"); //$NON-NLS-1$ PdfAs pdfAs = ApiHelper.getPdfAsFromContext(context); PdfAsInternal pdfAsInternal = ApiHelper.getPdfAsInternalFromContext(context); // check if document is empty if (si.sdi.getSignatureData() == null || si.sdi.getSignatureData().getLength() == 0) { throw new PDFDocumentException(ErrorCode.NO_TEXTUAL_CONTENT, "Unable to extract and textual content."); } log.debug("connector = " + si.connector); //$NON-NLS-1$ if (LocalRequestHelper.isConnectorLocal(si.connector)) { log.debug("Connector is local -> dispatching to local processing."); //$NON-NLS-1$ String dispatch_to = LocalRequestHelper.processLocalSign(pdfAsInternal, si, request, response); dispatch(request, response, dispatch_to, context); return; } log.debug("Connector is not local -> finishing the sign."); //$NON-NLS-1$ ByteArrayDataSink data = new ByteArrayDataSink(); si.signParameters.setOutput(data); SignResult signResult = pdfAs.sign(si.signParameters, si.sdi); si.signResult = signResult; si.signedPdf = data.getData(); // PdfASID algorithm = FormFields.translateSignatureModeToPdfASID(si.mode); // Signator signator = SignatorFactory.createSignator(algorithm); // // log.debug("RequestURL = " + request.getRequestURL()); // log.debug("ContextPath = " + request.getContextPath()); // String host = request.getServerName(); // TODO TR: Web-Applikation verwendet in Loc-Ref-Variante ext. Referenz, um performanter zu sein; // nachfolend auskommentieren, wenn anstatt SwA-Connector LocRef-Connector verwendet wird // URL signature_data_URL = new URL(WebUtils.addJSessionID(LocalRequestHelper.getLocalContextAddress(request, response) + "/RetrieveSignatureData", request)); // URL signature_data_URL = new URL(WebUtils.buildRetrieveSignatureDataURL(request, response)); // String signature_data_url = response.encodeURL(signature_data_URL.toString()); // // Connector c = ConnectorChooser.chooseWebConnectorForSign(si.connector, si.type, signature_data_url); // SignSignatureObject signSignatureObject = c.doSign(si.si.getSignatureData()); // // si.si.setSignSignatureObject(signSignatureObject); // si.output = TempDirHelper.createTempDataSink(si.filename + "_signed.pdf"); // signator.finishSign(si.si, si.output); returnSignResponse(si, request, response); log.debug("finishSign finished."); //$NON-NLS-1$ } /** * Returns the data in the SignResult with proper content disposition. * * @param si * SessionInformation. * @param bs * @parem request The servlet request. * @param response * The servlet response. * @throws IOException * The IO Exception. */ public static void returnSignResponse(SignSessionInformation si, HttpServletRequest request, HttpServletResponse response) throws IOException { // SignResult sign_result = si.sign_result; String file_name = formatFileNameForSignResult(si.filename, si.output.getMimeType()); // tzefferer: added condition if (si.exappinf == null) { // The name parameter is actually deprecated in favour of // Content-Disposition filename // Unfortunately Acrobat reader does recognize neither of these parameters // with its inline save-as. It always takes the page name. response.setContentType(si.output.getMimeType() + "; name=\"" + file_name + "\""); if (si.download_inline) { response.addHeader("Content-Disposition", "inline; filename=\"" + file_name + "\""); } else { response.addHeader("Content-Disposition", "attachment; filename=\"" + file_name + "\""); } IOUtils.write(si.signedPdf, response.getOutputStream()); // tzefferer: added else-block } else { /* * The following code handles an external invocation of pdf-as. External invocation is done by * redirecting the user to the Sign-Servlet using the parameters defined in class * at.knowcenter.wag.egov.egiz.web.FormFields. * e.g. http://localhost:48080/pdf-as/Sign?preview=false&connector=bku&mode=textual&sig_type=SIGNATURBLOCK_DE&inline=false&filename=test.pdf&num-bytes=45916&pdf-url=http%3A%2F%2Flocalhost%3A8080%2Fmyapp%2FProvidePDF&pdf-id=1956507909008215134&invoke-app-url=https%3A%2F%2Flocalhost%3A8443%2Fmyapp%2FReturnSignedPDF&invoke-app-error-url=https%3A%2F%2Flocalhost%3A8443%2Fmyapp%2Fpdfaserror.do&session-id=9085B85B364BEC31E7D38047FE54577D&locale=de */ log.debug("External webapp invocation detected."); byte [] signed_pdf = si.signedPdf; HttpSession session = request.getSession(); PDFContainer entry = new PDFContainer(signed_pdf, si.exappinf.pdf_id, si.plainPDFDigest); ProvidePDFServlet.signedDocuments.add(entry); // notify webapp... String invoke_url = si.exappinf.invoke_url; String providePDFServlet = "ProvidePDF"; String pdf_id = String.valueOf(si.exappinf.pdf_id); String session_id = si.exappinf.session_id; log.debug("External application has to be notified. Building url from callback url \"" + invoke_url + "\"."); // build URL int ind = invoke_url.indexOf("?"); // fixed by tknall: must not presume that invoke_url contains "?" String sep = "&"; if (ind == -1) { ind = invoke_url.length(); sep = "?"; } String query = invoke_url.substring(0, ind) + ";jsessionid=" + session_id + invoke_url.substring(ind) + sep + FormFields.FIELD_PDF_URL + "=" + providePDFServlet + "&" + FormFields.FIELD_PDF_ID + "=" + pdf_id + "&" + FormFields.FIELD_FILE_LENGTH + "=" + signed_pdf.length + "&" + FormFields.FIELD_PDFAS_SESSION_ID + "=" + session.getId(); /* * Using the external web-interface of pdf-as (as described above) pdf-as should be run within * an iframe. In case of a signature performed with a local citizen card software or with the * server bku the result has to be provided outside the iframe. To break out of the iframe a * helper jsp (redirect_to_parent) has to be used that redirects the user to the parent * window. */ disableBrowserCacheForResponse(response); //rpiazzi added signature devices mobile and mobiletest if (Constants.SIGNATURE_DEVICE_BKU.equals(si.connector) || Constants.SIGNATURE_DEVICE_MOC.equals(si.connector) || Constants.SIGNATURE_DEVICE_MOBILE.equals(si.connector) || Constants.SIGNATURE_DEVICE_MOBILETEST.equals(si.connector)) { log.debug("Pdf-as is supposed to run within an iframe."); log.debug("Putting external application notify url (\"" + query + "\") in session (" + session.getId() + ") for later use."); session.setAttribute(SessionAttributes.PARENT_WEBAPP_REDIRECT_URL, query); String redirectHelper = response.encodeRedirectURL(request.getContextPath() + "/jsp/redirect_to_parent.jsp"); log.debug("Redirecting to " + redirectHelper); log.debug("The browser will finally be redirected outside the iframe to " + query + " in order to notify the external application."); response.sendRedirect(redirectHelper); } else { log.debug("Notifying external application by redirecting to \"" + query + "\"."); response.sendRedirect(query); } } } public static void disableBrowserCacheForResponse(HttpServletResponse response) { log.debug("Disabling browser cache for HttpServletResponse."); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma","no-cache"); response.setDateHeader("Expires", -1); } /** * The Mime Type for XML. */ public static final String XML_MIME_TYPE = "text/xml"; //$NON-NLS-1$ /** * Formats the file name according to the SignResult. * * @param file_name * The file name. * @param sign_result * The sign result. * @return Returns the formatted file name. */ public static String formatFileNameForSignResult(String file_name, String mimeType) { String output = file_name + "_signed"; if (mimeType.equals(XML_MIME_TYPE)) { output += ".xml"; } else { output += ".pdf"; } return output; } public static void finishLocalSign(PdfAs pdfAs, PdfAsInternal pdfAsInternal, SignSessionInformation si) throws PdfAsException { ByteArrayDataSink data = new ByteArrayDataSink(); si.signParameters.setOutput(data); SignResult signResult = pdfAsInternal.finishLocalSign(pdfAs, si.signParameters, si.sdi, si.localBKUParams, false, si.xmlResponse); si.signResult = signResult; si.output = data; si.outputAvailable = true; si.signedPdf = data.getData(); } }