From 3d982813b34f6f230baf4a467cdc37ec92a77595 Mon Sep 17 00:00:00 2001 From: netconomy Date: Fri, 17 Aug 2007 06:10:56 +0000 Subject: Performance git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@167 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../commandline/CommandlineConnectorChooser.java | 167 ++++++ .../at/gv/egiz/pdfas/exceptions/ErrorCode.java | 49 ++ .../gv/egiz/pdfas/exceptions/ErrorCodeHelper.java | 43 ++ .../external/ExternalErrorException.java | 43 ++ .../exceptions/framework/SignatorException.java | 40 ++ .../framework/VerificationFilterException.java | 40 ++ .../framework/VerificatorFactoryException.java | 49 ++ .../exceptions/pdf/KZSettingNotFoundException.java | 27 + .../exceptions/pdf/TextExtractionException.java | 18 + .../exceptions/web/SessionExpiredException.java | 48 ++ .../gv/egiz/pdfas/framework/ConnectorFactory.java | 83 +++ .../at/gv/egiz/pdfas/framework/DataManager.java | 35 ++ .../at/gv/egiz/pdfas/framework/DataStrategy.java | 37 ++ .../gv/egiz/pdfas/framework/SignatorFactory.java | 100 ++++ .../pdfas/framework/SignatureHolderHelper.java | 34 ++ .../egiz/pdfas/framework/VerificatorFactory.java | 45 ++ .../pdfas/framework/config/SettingsHelper.java | 37 ++ .../gv/egiz/pdfas/framework/input/DataSource.java | 35 ++ .../pdfas/framework/input/ExtractionStage.java | 66 +++ .../egiz/pdfas/framework/input/PdfDataSource.java | 21 + .../egiz/pdfas/framework/input/TextDataSource.java | 19 + .../gv/egiz/pdfas/framework/output/DataSink.java | 15 + .../sigdevice/SequentialSignatureDevice.java | 25 + .../pdfas/framework/sigdevice/SignatureDevice.java | 16 + .../gv/egiz/pdfas/framework/signator/Signator.java | 56 ++ .../framework/signator/SignatorInformation.java | 43 ++ .../pdfas/framework/verificator/Verificator.java | 53 ++ .../framework/vfilter/VerificationFilter.java | 52 ++ .../vfilter/VerificationFilterParameters.java | 76 +++ .../impl/input/ByteArrayPdfDataSourceImpl.java | 56 ++ .../impl/input/CompoundPdfDataSourceImpl.java | 47 ++ .../pdfas/impl/input/DelimitedInputStream.java | 105 ++++ .../pdfas/impl/input/DelimitedPdfDataSource.java | 44 ++ .../at/gv/egiz/pdfas/impl/input/FileBased.java | 20 + .../impl/input/FileBasedPdfDataSourceImpl.java | 103 ++++ .../impl/input/FileBasedTextDataSourceImpl.java | 124 +++++ .../pdfas/impl/input/IncrementalUpdateParser.java | 49 ++ .../egiz/pdfas/impl/input/TextDataSourceImpl.java | 82 +++ .../pdfas/impl/input/helper/DataSourceHelper.java | 92 ++++ .../egiz/pdfas/impl/output/ByteArrayDataSink.java | 83 +++ .../egiz/pdfas/impl/output/FileBasedDataSink.java | 126 +++++ .../impl/signator/IncrementalUpdateHelper.java | 45 ++ .../impl/signator/SignatorInformationImpl.java | 20 + .../signator/binary/BinarySignatorInformation.java | 51 ++ .../impl/signator/binary/BinarySignator_1_0_0.java | 326 ++++++++++++ .../impl/signator/binary/BinarySignator_1_1_0.java | 69 +++ .../detached/DetachedTextualSignator_1_0_0.java | 142 +++++ .../textual/TextualSignatorInformation.java | 45 ++ .../signator/textual/TextualSignator_1_0_0.java | 149 ++++++ .../signator/textual/TextualSignator_1_1_0.java | 45 ++ .../binary/BinaryVerificator_1_0_0.java | 399 ++++++++++++++ .../binary/BinaryVerificator_1_1_0.java | 24 + .../at/gv/egiz/pdfas/impl/vfilter/Partition.java | 9 + .../pdfas/impl/vfilter/VerificationFilterImpl.java | 575 +++++++++++++++++++++ .../vfilter/VerificationFilterParametersImpl.java | 67 +++ .../helper/VerificationFilterBinaryHelper.java | 152 ++++++ .../vfilter/helper/VerificationFilterHelper.java | 142 +++++ .../helper/VerificationFilterTextHelper.java | 15 + .../impl/vfilter/partition/BinaryPartition.java | 19 + .../impl/vfilter/partition/TextPartition.java | 20 + .../egiz/pdfas/performance/PerformanceCounter.java | 62 +++ .../pdfas/performance/PerformanceCounters.java | 22 + .../egiz/pdfas/performance/PerformanceTimer.java | 48 ++ .../java/at/gv/egiz/pdfas/utils/DataHashUtils.java | 136 +++++ .../java/at/gv/egiz/pdfas/utils/StreamUtils.java | 42 ++ .../gv/egiz/pdfas/web/CurrentLocalOperation.java | 87 ++++ .../gv/egiz/pdfas/web/SignSessionInformation.java | 140 +++++ .../egiz/pdfas/web/VerifySessionInformation.java | 195 +++++++ .../at/gv/egiz/pdfas/web/helper/SessionHelper.java | 48 ++ .../egiz/pdfas/web/helper/SignServletHelper.java | 229 ++++++++ .../at/gv/egiz/pdfas/web/helper/TempDirHelper.java | 242 +++++++++ 71 files changed, 5868 insertions(+) create mode 100644 src/main/java/at/gv/egiz/pdfas/commandline/CommandlineConnectorChooser.java create mode 100644 src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java create mode 100644 src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCodeHelper.java create mode 100644 src/main/java/at/gv/egiz/pdfas/exceptions/external/ExternalErrorException.java create mode 100644 src/main/java/at/gv/egiz/pdfas/exceptions/framework/SignatorException.java create mode 100644 src/main/java/at/gv/egiz/pdfas/exceptions/framework/VerificationFilterException.java create mode 100644 src/main/java/at/gv/egiz/pdfas/exceptions/framework/VerificatorFactoryException.java create mode 100644 src/main/java/at/gv/egiz/pdfas/exceptions/pdf/KZSettingNotFoundException.java create mode 100644 src/main/java/at/gv/egiz/pdfas/exceptions/pdf/TextExtractionException.java create mode 100644 src/main/java/at/gv/egiz/pdfas/exceptions/web/SessionExpiredException.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/ConnectorFactory.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/DataManager.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/DataStrategy.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/SignatorFactory.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/SignatureHolderHelper.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/VerificatorFactory.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/config/SettingsHelper.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/input/DataSource.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/input/ExtractionStage.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/input/PdfDataSource.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/input/TextDataSource.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/output/DataSink.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/sigdevice/SequentialSignatureDevice.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/sigdevice/SignatureDevice.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/signator/Signator.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/signator/SignatorInformation.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/verificator/Verificator.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/vfilter/VerificationFilter.java create mode 100644 src/main/java/at/gv/egiz/pdfas/framework/vfilter/VerificationFilterParameters.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/input/ByteArrayPdfDataSourceImpl.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/input/CompoundPdfDataSourceImpl.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/input/DelimitedInputStream.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/input/DelimitedPdfDataSource.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/input/FileBased.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/input/FileBasedPdfDataSourceImpl.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/input/FileBasedTextDataSourceImpl.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/input/IncrementalUpdateParser.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/input/TextDataSourceImpl.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/input/helper/DataSourceHelper.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/output/ByteArrayDataSink.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/output/FileBasedDataSink.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/signator/IncrementalUpdateHelper.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/signator/SignatorInformationImpl.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignatorInformation.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_1_0.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/signator/detached/DetachedTextualSignator_1_0_0.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/signator/textual/TextualSignatorInformation.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/signator/textual/TextualSignator_1_0_0.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/signator/textual/TextualSignator_1_1_0.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/verificator/binary/BinaryVerificator_1_0_0.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/verificator/binary/BinaryVerificator_1_1_0.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/vfilter/Partition.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterImpl.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterParametersImpl.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterBinaryHelper.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterHelper.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterTextHelper.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/vfilter/partition/BinaryPartition.java create mode 100644 src/main/java/at/gv/egiz/pdfas/impl/vfilter/partition/TextPartition.java create mode 100644 src/main/java/at/gv/egiz/pdfas/performance/PerformanceCounter.java create mode 100644 src/main/java/at/gv/egiz/pdfas/performance/PerformanceCounters.java create mode 100644 src/main/java/at/gv/egiz/pdfas/performance/PerformanceTimer.java create mode 100644 src/main/java/at/gv/egiz/pdfas/utils/DataHashUtils.java create mode 100644 src/main/java/at/gv/egiz/pdfas/utils/StreamUtils.java create mode 100644 src/main/java/at/gv/egiz/pdfas/web/CurrentLocalOperation.java create mode 100644 src/main/java/at/gv/egiz/pdfas/web/SignSessionInformation.java create mode 100644 src/main/java/at/gv/egiz/pdfas/web/VerifySessionInformation.java create mode 100644 src/main/java/at/gv/egiz/pdfas/web/helper/SessionHelper.java create mode 100644 src/main/java/at/gv/egiz/pdfas/web/helper/SignServletHelper.java create mode 100644 src/main/java/at/gv/egiz/pdfas/web/helper/TempDirHelper.java (limited to 'src/main/java/at/gv/egiz/pdfas') diff --git a/src/main/java/at/gv/egiz/pdfas/commandline/CommandlineConnectorChooser.java b/src/main/java/at/gv/egiz/pdfas/commandline/CommandlineConnectorChooser.java new file mode 100644 index 0000000..2bc6a58 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/commandline/CommandlineConnectorChooser.java @@ -0,0 +1,167 @@ +/** + * + */ +package at.gv.egiz.pdfas.commandline; + +import at.gv.egiz.pdfas.framework.ConnectorFactory; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.exceptions.ConnectorException; +import at.knowcenter.wag.egov.egiz.framework.SignatorFactory; +import at.knowcenter.wag.egov.egiz.sig.sigid.HotfixIdFormatter; + +/** + * Encapsulates the logic of choosing the correct connector for the commandline + * that can later be created using the ConnectorFactory. + * + * @author wprinz + */ +public class CommandlineConnectorChooser +{ + /** + * The log. + */ + private static Log log = LogFactory.getLog(CommandlineConnectorChooser.class); + + protected static final String BKU = "bku"; //$NON-NLS-1$ + + protected static final String MOA = "moa"; //$NON-NLS-1$ + + public static boolean needsSigId (String connectorId) + { + return !ConnectorFactory.isMOA(connectorId); + } + + public static String chooseCommandlineConnectorForSign(String connectorType) throws ConnectorException + { + log.debug("Choosing Connector for commandline signation..."); + + log.debug("connector type = " + connectorType); + + if (connectorType.equals(BKU)) + { + log.debug("sig_app is BKU ==> DetachedMultipartBKUConnector"); //$NON-NLS-1$ + + return ConnectorFactory.DETACHED_MULTIPART_BKU_CONNECTOR; + } + if (connectorType.equals(MOA)) + { + // TODO MOA detached signing is not allowed at the commandline + log.warn("Detached MOA is not supported on the commandline. -> choosing Base64 temporarily."); + return ConnectorFactory.ENVELOPING_BASE64_MOA_CONNECTOR; + } + + throw new ConnectorException(300, "Unknown connector type '" + connectorType + "' specified."); + } + + public static String chooseCommandlineConnectorForVerify(String connectorType, PdfASID sig_kz, String sig_id, String profile) throws ConnectorException + { + log.debug("Choosing Connector for Commandline verification..."); + + log.debug("connector type = " + connectorType); + log.debug("sig_kz = " + sig_kz); //$NON-NLS-1$ + log.debug("sig_id = " + sig_id); //$NON-NLS-1$ + + if (sig_kz == null) + { + log.debug("sig_kz is null -> chose an old enveloped base64 connector"); //$NON-NLS-1$ + + return chooseEnvelopingBase64ConnectorOld(connectorType); + } + + log.debug("sig_kz is not null -> one of the newer signatures"); + + if (sig_kz.getVersion().equals(SignatorFactory.VERSION_1_0_0)) + { + log.debug("Version is 1.0.0 -> Base64 Signatur (old or Hotfix)."); + + if (sig_id == null) + { + log.debug("sig_id is null, which means that it is a MOA signature -> choose a hotfix base64 connector (thus it is moa - it doesn't matter)."); + + return chooseEnvelopingBase64ConnectorHotfix(connectorType); + } + + String[] sig_id_parts = sig_id.split("@"); + if (sig_id_parts.length == 2) + { + log.debug("sig_id has 2 @-separated parts -> choosing old base64 connector"); + + return chooseEnvelopingBase64ConnectorOld(connectorType); + } + if (sig_id_parts[0].equals(HotfixIdFormatter.SIG_ID_PREFIX)) + { + log.debug("sig_id prefix is hotfix -> choosing hotfix base64 connector"); + + return chooseEnvelopingBase64ConnectorHotfix(connectorType); + } + + throw new ConnectorException(300, "The SIG_KZ version is 1.0.0, but SIG_ID is neither MOA nor Old base64 nor Hotfix base64 ???'"); + } + if (sig_kz.getVersion().equals(SignatorFactory.VERSION_1_1_0)) + { + log.debug("Version is 1.1.0 -> chose a detached connector."); + + return chooseDetachedMultipartConnector(connectorType); + } + + throw new ConnectorException(310, "The SIG_KZ version '" + sig_kz.getVersion() + "' is unknown."); + } + + protected static String chooseEnvelopingBase64ConnectorOld(String sig_app) throws ConnectorException + { + if (sig_app.equals(BKU)) + { + log.debug("sig_app is BKU ==> OldEnvelopingBase64BKUConnector"); //$NON-NLS-1$ + + return ConnectorFactory.OLD_ENVELOPING_BASE64_BKU_CONNECTOR; + } + if (sig_app.equals(MOA)) + { + log.debug("sig_app is MOA ==> EnvelopingBase64MOAConnector"); //$NON-NLS-1$ + + return ConnectorFactory.ENVELOPING_BASE64_MOA_CONNECTOR; + } + throw new ConnectorException(310, "Unknown sig_app '" + sig_app + "'."); //$NON-NLS-1$ //$NON-NLS-2$ + + } + + protected static String chooseEnvelopingBase64ConnectorHotfix(String sig_app) throws ConnectorException + { + if (sig_app.equals(BKU)) + { + log.debug("sig_app is BKU ==> EnvelopingBase64BKUConnector"); //$NON-NLS-1$ + + return ConnectorFactory.ENVELOPING_BASE64_BKU_CONNECTOR; + } + if (sig_app.equals(MOA)) + { + log.debug("sig_app is MOA ==> EnvelopingBase64MOAConnector"); //$NON-NLS-1$ + + return ConnectorFactory.ENVELOPING_BASE64_MOA_CONNECTOR; + } + throw new ConnectorException(310, "Unknown sig_app '" + sig_app + "'."); //$NON-NLS-1$ //$NON-NLS-2$ + } + + protected static String chooseDetachedMultipartConnector(String sig_app) throws ConnectorException + { + if (sig_app.equals(BKU)) + { + log.debug("sig_app is BKU ==> DetachedMultipartBKUConnector"); //$NON-NLS-1$ + + return ConnectorFactory.DETACHED_MULTIPART_BKU_CONNECTOR; + } + if (sig_app.equals(MOA)) + { + log.debug("sig_app is MOA ==> DetachedMOAConnector"); //$NON-NLS-1$ + + String msg = "A Detached signature cannot be verified with the MOA connector (yet)."; //$NON-NLS-1$ + log.error(msg); + throw new ConnectorException(370, msg); + } + throw new ConnectorException(310, "Unknown sig_app '" + sig_app + "'."); //$NON-NLS-1$ //$NON-NLS-2$ + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java b/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java new file mode 100644 index 0000000..dda4919 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java @@ -0,0 +1,49 @@ +/** + * + */ +package at.gv.egiz.pdfas.exceptions; + +/** + * Contains constants for the error codes. + * + *

+ * In Java 1.5 this would be an enum. + *

+ * + * @author wprinz + */ +public final class ErrorCode +{ + public static final int EXTERNAL_ERROR = 0; + + public static final int SETTING_NOT_FOUND = 100; + public static final int SETTINGS_EXCEPTION = 101; + public static final int KZ_SETTING_NOT_FOUND = 102; + + public static final int DOCUMENT_CANNOT_BE_READ = 201; + public static final int TEXT_EXTRACTION_EXCEPTION = 202; + public static final int CANNOT_WRITE_PDF = 205; + public static final int DOCUMENT_NOT_SIGNED = 206; + public static final int SIGNATURE_TYPES_EXCEPTION = 223; + + public static final int SIGNATURE_COULDNT_BE_CREATED = 300; + public static final int SIGNED_TEXT_EMPTY = 301; + public static final int PROFILE_NOT_DEFINED = 302; + public static final int SERIAL_NUMBER_INVALID = 303; + public static final int SIG_CERTIFICATE_CANNOT_BE_READ = 304; + + public static final int COULDNT_VERIFY = 310; + + public static final int NOT_SEMANTICALLY_EQUAL = 314; + + public static final int WEB_EXCEPTION = 330; + + + public static final int NORMALIZER_EXCEPTION = 400; + + public static final int SESSION_EXPIRED = 600; + + public static final int PLACEHOLDER_EXCEPTION = 700; + + +} diff --git a/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCodeHelper.java b/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCodeHelper.java new file mode 100644 index 0000000..4144a10 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCodeHelper.java @@ -0,0 +1,43 @@ +/** + * + */ +package at.gv.egiz.pdfas.exceptions; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; +import at.knowcenter.wag.egov.egiz.exceptions.SettingNotFoundException; +import at.knowcenter.wag.egov.egiz.exceptions.SettingsException; + +/** + * @author wprinz + * + */ +public class ErrorCodeHelper +{ + /** + * The log. + */ + private static final Log log = LogFactory.getLog(ErrorCodeHelper.class); + + public static String getMessageForErrorCode(int errorCode) + { + try + { + SettingsReader settings = SettingsReader.getInstance(); + String message = settings.getSetting("error.code." + errorCode); + return message; + } + catch (SettingsException e) + { + log.warn(e); + } + catch (SettingNotFoundException e) + { + log.warn(e); + } + return null; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/exceptions/external/ExternalErrorException.java b/src/main/java/at/gv/egiz/pdfas/exceptions/external/ExternalErrorException.java new file mode 100644 index 0000000..4e12ced --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/exceptions/external/ExternalErrorException.java @@ -0,0 +1,43 @@ +package at.gv.egiz.pdfas.exceptions.external; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.knowcenter.wag.egov.egiz.exceptions.ConnectorException; + +public class ExternalErrorException extends ConnectorException +{ + + /** + * SVUID. + */ + private static final long serialVersionUID = 2108427722915583885L; + + protected String externalErrorCode; + + protected String externalErrorMessage; + + public ExternalErrorException(String externalErrorCode, String externalErrorMessage) + { + super(ErrorCode.EXTERNAL_ERROR, "External Error " + externalErrorCode + ": " + externalErrorMessage); + + this.externalErrorCode = externalErrorCode; + this.externalErrorMessage = externalErrorMessage; + } + + /** + * @return the externalErrorCode + */ + public String getExternalErrorCode() + { + return externalErrorCode; + } + + /** + * @return the externalErrorMessage + */ + public String getExternalErrorMessage() + { + return externalErrorMessage; + } + + +} diff --git a/src/main/java/at/gv/egiz/pdfas/exceptions/framework/SignatorException.java b/src/main/java/at/gv/egiz/pdfas/exceptions/framework/SignatorException.java new file mode 100644 index 0000000..84868d8 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/exceptions/framework/SignatorException.java @@ -0,0 +1,40 @@ +/** + * + */ +package at.gv.egiz.pdfas.exceptions.framework; + +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; + + +/** + * Exception thrown by the Signators. + * @author wprinz + */ +public class SignatorException extends PresentableException +{ + + /** + * SVUID. + */ + private static final long serialVersionUID = 5051232904560832089L; + + public SignatorException(int error_code, String message, Throwable cause) + { + super(error_code, message, cause); + } + + public SignatorException(int error_code, String message) + { + super(error_code, message); + } + + public SignatorException(int error_code, Throwable cause) + { + super(error_code, cause); + } + + public SignatorException(PresentableException pe) + { + super(pe.getErrorCode(), pe); + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/exceptions/framework/VerificationFilterException.java b/src/main/java/at/gv/egiz/pdfas/exceptions/framework/VerificationFilterException.java new file mode 100644 index 0000000..5569e5d --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/exceptions/framework/VerificationFilterException.java @@ -0,0 +1,40 @@ +/** + * + */ +package at.gv.egiz.pdfas.exceptions.framework; + +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; + +/** + * Wrapper exception for the VerificationFilter. + * + * @author wprinz + */ +public class VerificationFilterException extends PresentableException +{ + /** + * SVUID. + */ + private static final long serialVersionUID = -3863253910537746742L; + + public VerificationFilterException(int errorCode, String message, Throwable cause) + { + super(errorCode, message, cause); + } + + public VerificationFilterException(int errorCode, String message) + { + super(errorCode, message); + } + + public VerificationFilterException(int errorCode, Throwable cause) + { + super(errorCode, cause); + } + + public VerificationFilterException(PresentableException cause) + { + super(cause.getErrorCode(), cause); + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/exceptions/framework/VerificatorFactoryException.java b/src/main/java/at/gv/egiz/pdfas/exceptions/framework/VerificatorFactoryException.java new file mode 100644 index 0000000..4721cdb --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/exceptions/framework/VerificatorFactoryException.java @@ -0,0 +1,49 @@ +/** + * + */ +package at.gv.egiz.pdfas.exceptions.framework; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; + +/** + * @author wprinz + * + */ +public class VerificatorFactoryException extends PresentableException +{ + + /** + * + */ + private static final long serialVersionUID = 8765156531863056335L; + + /** + * @param errorCode + * @param message + */ + public VerificatorFactoryException(String message) + { + super(ErrorCode.COULDNT_VERIFY, message); + } + + /** + * @param errorCode + * @param message + * @param cause + */ + public VerificatorFactoryException(String message, Throwable cause) + { + super(ErrorCode.COULDNT_VERIFY, message, cause); + } + + /** + * @param errorCode + * @param cause + */ + public VerificatorFactoryException(Throwable cause) + { + super(ErrorCode.COULDNT_VERIFY, cause); + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/exceptions/pdf/KZSettingNotFoundException.java b/src/main/java/at/gv/egiz/pdfas/exceptions/pdf/KZSettingNotFoundException.java new file mode 100644 index 0000000..816a2c1 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/exceptions/pdf/KZSettingNotFoundException.java @@ -0,0 +1,27 @@ +/** + * + */ +package at.gv.egiz.pdfas.exceptions.pdf; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.knowcenter.wag.egov.egiz.exceptions.SettingNotFoundException; + +/** + * @author wprinz + * + */ +public class KZSettingNotFoundException extends SettingNotFoundException +{ + + /** + * SVUID. + */ + private static final long serialVersionUID = 2516636821733440462L; + + public KZSettingNotFoundException(String message) + { + super(ErrorCode.KZ_SETTING_NOT_FOUND, message); + } + + +} diff --git a/src/main/java/at/gv/egiz/pdfas/exceptions/pdf/TextExtractionException.java b/src/main/java/at/gv/egiz/pdfas/exceptions/pdf/TextExtractionException.java new file mode 100644 index 0000000..1f54bb5 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/exceptions/pdf/TextExtractionException.java @@ -0,0 +1,18 @@ +package at.gv.egiz.pdfas.exceptions.pdf; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; + +public class TextExtractionException extends PresentableException +{ + /** + * SVUID. + */ + private static final long serialVersionUID = 2798763345488999563L; + + public TextExtractionException(Throwable cause) + { + super(ErrorCode.TEXT_EXTRACTION_EXCEPTION, cause); + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/exceptions/web/SessionExpiredException.java b/src/main/java/at/gv/egiz/pdfas/exceptions/web/SessionExpiredException.java new file mode 100644 index 0000000..bb55293 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/exceptions/web/SessionExpiredException.java @@ -0,0 +1,48 @@ +/** + * + */ +package at.gv.egiz.pdfas.exceptions.web; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; + +/** + * @author wprinz + * + */ +public class SessionExpiredException extends PresentableException +{ + /** + * SVUID. + */ + private static final long serialVersionUID = -1877790545371341233L; + + /** + * @param errorCode + * @param message + */ + public SessionExpiredException(String message) + { + super(ErrorCode.SESSION_EXPIRED, message); + } + + /** + * @param errorCode + * @param message + * @param cause + */ + public SessionExpiredException(String message, Throwable cause) + { + super(ErrorCode.SESSION_EXPIRED, message, cause); + } + + /** + * @param errorCode + * @param cause + */ + public SessionExpiredException(Throwable cause) + { + super(ErrorCode.SESSION_EXPIRED, cause); + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/ConnectorFactory.java b/src/main/java/at/gv/egiz/pdfas/framework/ConnectorFactory.java new file mode 100644 index 0000000..65c5af7 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/ConnectorFactory.java @@ -0,0 +1,83 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework; + +import at.knowcenter.wag.egov.egiz.exceptions.ConnectorException; +import at.knowcenter.wag.egov.egiz.exceptions.ConnectorFactoryException; +import at.knowcenter.wag.egov.egiz.sig.connectors.Connector; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.EnvelopedBase64BKUConnector; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.MultipartDetachedBKUConnector; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.OldEnvelopingBase64BKUConnector; +import at.knowcenter.wag.egov.egiz.sig.connectors.moa.DetachedLocRefMOAConnector; +import at.knowcenter.wag.egov.egiz.sig.connectors.moa.EnvelopingBase64MOAConnector; + +/** + * @author wprinz + */ +public class ConnectorFactory +{ + // TODO the functionality of the connector should be split into template + // handling and the actualy hardware access + + public static String DETACHED_MULTIPART_BKU_CONNECTOR = "DetachedMultipartBKUConnector"; + + public static String ENVELOPING_BASE64_BKU_CONNECTOR = "EnvelopingBase64BKUConnector"; + + public static String OLD_ENVELOPING_BASE64_BKU_CONNECTOR = "OldEnvelopingBase64BKUConnector"; + + public static String DETACHED_LOCREF_MOA_CONNECTOR = "DetachedLocRefMOAConnector"; + + public static String ENVELOPING_BASE64_MOA_CONNECTOR = "EnvelopingBase64MOAConnector"; + + + + public static Connector createConnector (String connectorId, String profile, String locRef) throws ConnectorFactoryException, ConnectorException + { + if (connectorId.equals(DETACHED_MULTIPART_BKU_CONNECTOR)) + { + return new MultipartDetachedBKUConnector(profile); + } + + if (connectorId.equals(ENVELOPING_BASE64_BKU_CONNECTOR)) + { + return new EnvelopedBase64BKUConnector(profile); + } + + if (connectorId.equals(OLD_ENVELOPING_BASE64_BKU_CONNECTOR)) + { + return new OldEnvelopingBase64BKUConnector(profile); + } + + if (connectorId.equals(DETACHED_LOCREF_MOA_CONNECTOR)) + { + return new DetachedLocRefMOAConnector(profile, locRef); + } + + if (connectorId.equals(ENVELOPING_BASE64_MOA_CONNECTOR)) + { + return new EnvelopingBase64MOAConnector(profile); + } + + throw new ConnectorFactoryException("The connector Id " + connectorId + " couldn't be found by the ConnectorFactory."); + } + + public static boolean isMOA (String connectorId) + { + if (connectorId.equals(DETACHED_LOCREF_MOA_CONNECTOR) || connectorId.equals(ENVELOPING_BASE64_MOA_CONNECTOR)) + { + return true; + } + + return false; + } + +// public static Connector createConnectorForCommandline (String connectorId) throws ConnectorFactoryException +// { +// } +// +// public static Connector createConnectorForCommandline (String connectorId) throws ConnectorFactoryException +// { +// } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/DataManager.java b/src/main/java/at/gv/egiz/pdfas/framework/DataManager.java new file mode 100644 index 0000000..1640b07 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/DataManager.java @@ -0,0 +1,35 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework; + +/** + * The DataManager is a mediator for all components that need to allocate large + * data elements. + * + *

+ * The DataManager uses a certain DataStrategy to perform the actual tasks. The + * strategy may be different in different environments. E.g. The commandline may + * implement a strategy to keep all data in memory, whereas the web might + * implement one that puts as many data onto the disk as possible to save + * memory. + *

+ * + * @author wprinz + * + */ +public class DataManager +{ + protected static DataStrategy dataStrategy = null; + + public static void initialize(DataStrategy ds) + { + dataStrategy = ds; + } + + public static DataStrategy getDataStrategy() + { + return dataStrategy; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/DataStrategy.java b/src/main/java/at/gv/egiz/pdfas/framework/DataStrategy.java new file mode 100644 index 0000000..22f9676 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/DataStrategy.java @@ -0,0 +1,37 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework; + +import java.io.InputStream; + +import at.gv.egiz.pdfas.framework.input.DataSource; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.framework.input.TextDataSource; +import at.gv.egiz.pdfas.framework.output.DataSink; + +/** + * Factory for creating DataSources. + * + * @author wprinz + */ +public interface DataStrategy +{ + + public TextDataSource createTextDataSource (String text); + + public PdfDataSource createPdfDataSource (InputStream is); + + public PdfDataSource createPdfDataSource (DataSource other, int length); + + /** + * @deprecated - use streaming. + * @param pdf + * @return + */ + public PdfDataSource createPdfDataSource (byte [] pdf); + + public void destroyDataSource (DataSource dataSource); + + public DataSink createDataSink (); +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/SignatorFactory.java b/src/main/java/at/gv/egiz/pdfas/framework/SignatorFactory.java new file mode 100644 index 0000000..d4ecc26 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/SignatorFactory.java @@ -0,0 +1,100 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework; + +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.exceptions.SignatorFactoryException; +import at.gv.egiz.pdfas.impl.signator.binary.BinarySignator_1_0_0; +import at.gv.egiz.pdfas.impl.signator.binary.BinarySignator_1_1_0; +import at.gv.egiz.pdfas.impl.signator.detached.DetachedTextualSignator_1_0_0; +import at.gv.egiz.pdfas.impl.signator.textual.TextualSignator_1_0_0; +import at.gv.egiz.pdfas.impl.signator.textual.TextualSignator_1_1_0; +import at.gv.egiz.pdfas.framework.signator.Signator; + +/** + * @author wprinz + * + */ +public class SignatorFactory +{ + /** + * The Vendor. + */ + public static final String VENDOR = "bka.gv.at"; //$NON-NLS-1$ + + /** + * The binary Signator algorithm. + */ + public static final String TYPE_BINARY = "binaer"; //$NON-NLS-1$ + + /** + * The textual Signator algorithm. + */ + public static final String TYPE_TEXTUAL = "text"; //$NON-NLS-1$ + + /** + * Detached Signator. + */ + public static final String TYPE_DETACHED_TEXTUAL = "detachedtext"; //$NON-NLS-1$ + + /** + * This application's current algorithm versions. + */ + public static final String VERSION_1_0_0 = "v1.0.0"; //$NON-NLS-1$ + + /** + * This application's current algorithm versions. + */ + public static final String VERSION_1_1_0 = "v1.1.0"; //$NON-NLS-1$ + + + public static Signator createSignator (PdfASID id) throws SignatorFactoryException + { + if (!id.getVendor().equals(VENDOR)) + { + throw new SignatorFactoryException("The vendor '" + id.getVendor() + "' is unrecognized by this SignatorFactory. (id='" + id + "')"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + if (id.getType().equals(TYPE_BINARY)) + { + if (id.getVersion().equals(VERSION_1_0_0)) + { + return new BinarySignator_1_0_0(); + } + if (id.getVersion().equals(VERSION_1_1_0)) + { + return new BinarySignator_1_1_0(); + } + + throw new SignatorFactoryException("The version '" + id.getVersion() + "' of type '" + id.getType() + "' is not supported by this SignatorFactory. (id='" + id + "')"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + if (id.getType().equals(TYPE_TEXTUAL)) + { + if (id.getVersion().equals(VERSION_1_0_0)) + { + return new TextualSignator_1_0_0(); + } + if (id.getVersion().equals(VERSION_1_1_0)) + { + return new TextualSignator_1_1_0(); + } + + throw new SignatorFactoryException("The version '" + id.getVersion() + "' of type '" + id.getType() + "' is not supported by this SignatorFactory. (id='" + id + "')"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + if (id.getType().equals(TYPE_DETACHED_TEXTUAL)) + { + if (id.getVersion().equals(VERSION_1_0_0)) + { + return new DetachedTextualSignator_1_0_0(); + } + + throw new SignatorFactoryException("The version '" + id.getVersion() + "' of type '" + id.getType() + "' is not supported by this SignatorFactory. (id='" + id + "')"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + throw new SignatorFactoryException("The type '" + id.getType() + "' is not supported by this SignatorFactory. (id='" + id + "')"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/SignatureHolderHelper.java b/src/main/java/at/gv/egiz/pdfas/framework/SignatureHolderHelper.java new file mode 100644 index 0000000..ab77092 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/SignatureHolderHelper.java @@ -0,0 +1,34 @@ +package at.gv.egiz.pdfas.framework; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import at.knowcenter.wag.egov.egiz.pdf.EGIZDate; +import at.knowcenter.wag.egov.egiz.pdf.SignatureHolder; + +public final class SignatureHolderHelper +{ + + /** + * Sorts the List of SignatureHolders by date. + * + * @param signatureHolders + * The List of SignatureHolders. + */ + public static void sortByDate(List signatureHolders) + { + Collections.sort(signatureHolders, new Comparator() { + public int compare(Object o1, Object o2) + { + SignatureHolder sh1 = (SignatureHolder) o1; + SignatureHolder sh2 = (SignatureHolder) o2; + + EGIZDate date1 = EGIZDate.parseFromString(sh1.getSignatureObject().getSignationDate()); + EGIZDate date2 = EGIZDate.parseFromString(sh2.getSignatureObject().getSignationDate()); + + return date1.compareTo(date2); + } + }); + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/VerificatorFactory.java b/src/main/java/at/gv/egiz/pdfas/framework/VerificatorFactory.java new file mode 100644 index 0000000..f77ca23 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/VerificatorFactory.java @@ -0,0 +1,45 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework; + +import at.gv.egiz.pdfas.exceptions.framework.VerificatorFactoryException; +import at.gv.egiz.pdfas.framework.verificator.Verificator; +import at.gv.egiz.pdfas.impl.verificator.binary.BinaryVerificator_1_0_0; +import at.gv.egiz.pdfas.impl.verificator.binary.BinaryVerificator_1_1_0; +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.framework.SignatorFactory; + +/** + * @author wprinz + * + */ +public class VerificatorFactory +{ + + public static Verificator createVerificator(PdfASID kz) throws VerificatorFactoryException + { + if (kz.getType().equals(SignatorFactory.TYPE_BINARY)) + { + return createBinaryVerificator(kz); + } + + return null; + } + + public static Verificator createBinaryVerificator(PdfASID kz) throws VerificatorFactoryException + { + assert kz.getType().equals(SignatorFactory.TYPE_BINARY); + + if (kz.equals(BinaryVerificator_1_0_0.MY_ID)) + { + return new BinaryVerificator_1_0_0(); + } + if (kz.equals(BinaryVerificator_1_1_0.MY_ID)) + { + return new BinaryVerificator_1_1_0(); + } + + throw new VerificatorFactoryException("kz is not a known binary signator " + kz); + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/config/SettingsHelper.java b/src/main/java/at/gv/egiz/pdfas/framework/config/SettingsHelper.java new file mode 100644 index 0000000..6f67d1d --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/config/SettingsHelper.java @@ -0,0 +1,37 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework.config; + +import at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters; +import at.gv.egiz.pdfas.impl.vfilter.VerificationFilterParametersImpl; +import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; +import at.knowcenter.wag.egov.egiz.exceptions.SettingsException; + +/** + * Contains helpful Settings functions. + * @author wprinz + */ +public final class SettingsHelper +{ + public static VerificationFilterParameters readVerificationFilterParametersFromSettings() throws SettingsException + { + boolean binaryOnly = getFlag("binary_only"); + boolean assumeOnlySB = getFlag("assume_only_signauture_blocks"); + boolean checkOld = getFlag("check_old_textual_sigs"); + + VerificationFilterParameters vfp = new VerificationFilterParametersImpl(binaryOnly, assumeOnlySB, checkOld); + return vfp; + } + + protected static boolean getFlag (String settingsKey) throws SettingsException + { + String flag = SettingsReader.getInstance().getSetting(settingsKey, "false"); + boolean b = true; + if (flag.equals("false")) + { + b = false; + } + return b; + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/input/DataSource.java b/src/main/java/at/gv/egiz/pdfas/framework/input/DataSource.java new file mode 100644 index 0000000..265cb0c --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/input/DataSource.java @@ -0,0 +1,35 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework.input; + +import java.io.InputStream; + +/** + * The input document data source. + * + *

+ * Usually this is a PdfDataSource, but it may be a TextDataSource as well. + *

+ * + * @author wprinz + * + */ +public interface DataSource +{ + /** + * Creates a new InputStream that allows to read out the document's binary + * data from the beginning. + * + * @return Returns the InputStream with the binary data. + */ + public InputStream createInputStream(); + + /** + * Returns the length (number of bytes) of the stream. + * + * @return Returns the length (number of bytes) of the stream. + */ + public int getLength(); + +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/input/ExtractionStage.java b/src/main/java/at/gv/egiz/pdfas/framework/input/ExtractionStage.java new file mode 100644 index 0000000..36d9bd8 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/input/ExtractionStage.java @@ -0,0 +1,66 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework.input; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.gv.egiz.pdfas.exceptions.framework.VerificationFilterException; +import at.gv.egiz.pdfas.framework.vfilter.VerificationFilter; +import at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters; +import at.gv.egiz.pdfas.impl.input.IncrementalUpdateParser; +import at.gv.egiz.pdfas.impl.vfilter.VerificationFilterImpl; +import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; + +/** + * Extracts all signatures from a given input DataSource. + * + * @author wprinz + */ +public class ExtractionStage +{ + /** + * The log. + */ + private static final Log log = LogFactory.getLog(ExtractionStage.class); + + public List extractSignatureHolders(final DataSource dataSource, VerificationFilterParameters parameters) throws PresentableException + { + if (dataSource instanceof PdfDataSource) + { + PdfDataSource pdfDataSource = (PdfDataSource) dataSource; + + List blocks = parsePdfIntoBlocks(pdfDataSource); + + VerificationFilter vf = new VerificationFilterImpl(); + List signatures = vf.extractSignatureHolders(pdfDataSource, blocks, parameters); + + return signatures; + } + + if (dataSource instanceof TextDataSource) + { + TextDataSource textDataSource = (TextDataSource) dataSource; + + VerificationFilter vf = new VerificationFilterImpl(); + List signatures = vf.extractSignaturHolders(textDataSource, parameters); + + return signatures; + } + + String msg = "The input DataSource is neither pdf nor text. class.name = " + dataSource.getClass().getName(); + log.error(msg); + throw new VerificationFilterException(ErrorCode.DOCUMENT_CANNOT_BE_READ, msg); + } + + protected List parsePdfIntoBlocks(PdfDataSource pdfDataSource) throws PDFDocumentException + { + List blocks = IncrementalUpdateParser.parsePdfIntoIUBlocks(pdfDataSource); + return blocks; + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/input/PdfDataSource.java b/src/main/java/at/gv/egiz/pdfas/framework/input/PdfDataSource.java new file mode 100644 index 0000000..b03a67e --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/input/PdfDataSource.java @@ -0,0 +1,21 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework.input; + + +/** + * Represents the binary data of a PDF document. + * + *

+ * This interface allows Pdf data to be handled in an abstract way so that the + * storage (byta array, disk etc.) of pdf documents can be separated from the + * algorithms. + *

+ * + * @author wprinz + */ +public interface PdfDataSource extends DataSource +{ + // jsut a marker interface +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/input/TextDataSource.java b/src/main/java/at/gv/egiz/pdfas/framework/input/TextDataSource.java new file mode 100644 index 0000000..c5fd4b1 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/input/TextDataSource.java @@ -0,0 +1,19 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework.input; + +/** + * Represents a free-text input text to be processed. + * + * @author wprinz + */ +public interface TextDataSource extends DataSource +{ + /** + * Returns the text to be processed. + * @return Returns the text to be processed. + */ + public String getText(); + +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/output/DataSink.java b/src/main/java/at/gv/egiz/pdfas/framework/output/DataSink.java new file mode 100644 index 0000000..d7d0cc4 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/output/DataSink.java @@ -0,0 +1,15 @@ +package at.gv.egiz.pdfas.framework.output; + +import java.io.OutputStream; + +/** + * Output document data sink. + * + * @author wprinz + */ +public interface DataSink +{ + public OutputStream createOutputStream(String mimeType); + + public OutputStream createOutputStream(String mimeType, String characterEncoding); +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/sigdevice/SequentialSignatureDevice.java b/src/main/java/at/gv/egiz/pdfas/framework/sigdevice/SequentialSignatureDevice.java new file mode 100644 index 0000000..70a28db --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/sigdevice/SequentialSignatureDevice.java @@ -0,0 +1,25 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework.sigdevice; + +/** + * A SignatureDevice that can be accessed in a sequential manner. + * + *

+ * A sequential device handles all necessary steps in sequence. E.g. all the + * data is transformed into a http request and sent to a server. The server + * processes the request and answers. The response from the server is then + * analyzed and returned. + *

+ * + * @author wprinz + */ +public interface SequentialSignatureDevice extends SignatureDevice +{ + // This is just a concept how it could be realized in future. + public void sign(); + + public void verify(); + +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/sigdevice/SignatureDevice.java b/src/main/java/at/gv/egiz/pdfas/framework/sigdevice/SignatureDevice.java new file mode 100644 index 0000000..735dd3c --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/sigdevice/SignatureDevice.java @@ -0,0 +1,16 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework.sigdevice; + +/** + * Performs the task of passing the signature XML and data from the application + * to an external signature device and return the response in an form that the + * application can use. + * + * @author wprinz + */ +public interface SignatureDevice +{ + // Marker interface +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/signator/Signator.java b/src/main/java/at/gv/egiz/pdfas/framework/signator/Signator.java new file mode 100644 index 0000000..e77ed08 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/signator/Signator.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2006 by Know-Center, Graz, Austria + * + * This software is the confidential and proprietary information of Know-Center, + * Graz, Austria. You shall not disclose such Confidential Information and shall + * use it only in accordance with the terms of the license agreement you entered + * into with Know-Center. + * + * KNOW-CENTER MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF + * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY + * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS + * DERIVATIVES. + * + * $Id: Signator.java,v 1.1 2006/08/25 17:07:21 wprinz Exp $ + */ +package at.gv.egiz.pdfas.framework.signator; + +import at.gv.egiz.pdfas.exceptions.framework.SignatorException; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.framework.output.DataSink; +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.framework.SignResult; +import at.knowcenter.wag.egov.egiz.pdf.TablePos; + +/** + * The basic interface for signator algorithms. + * + * @author wprinz + */ +public interface Signator +{ + /** + * Returns the PdfASID of this Connector. + * + *

+ * This should always return the MY_ID static field of the connector. Dont't + * forget to override this. + *

+ *

+ * Within connector code always use this method so that code reuse through + * derivation can take place correctly. + *

+ * + * @return Returns the PdfASID of this Connector. + */ + public PdfASID getMyId(); + + + public SignatorInformation prepareSign(PdfDataSource pdfDataSource, + String profile, TablePos pos, boolean has_SIG_ID) throws SignatorException; + + + public void finishSign(SignatorInformation signatorInformation, DataSink dataSink) throws SignatorException; +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/signator/SignatorInformation.java b/src/main/java/at/gv/egiz/pdfas/framework/signator/SignatorInformation.java new file mode 100644 index 0000000..da81e87 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/signator/SignatorInformation.java @@ -0,0 +1,43 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework.signator; + +import at.knowcenter.wag.egov.egiz.sig.SignatureData; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject; + +/** + * Encapsulates the Signator dependant information that needs to be passed from + * prepareSign to finishSign. + * + *

+ * This supersedes the IncrementalUpdateInformation. + *

+ * + * @author wprinz + */ +public interface SignatorInformation +{ + /** + * Returns the SignatureData to be signed. + *

+ * This is passed on to the Connector and signature device for signing. + *

+ * + * @return Returns the SignatureData to be signed. + */ + public SignatureData getSignatureData(); + + /** + * Sets the SignSignatureObject with the signature data. + * + *

+ * This is called by the framework to provide the finishSign with the + * signature data. + *

+ * + * @param signSignatureObject + * The SignSignatureObject. + */ + public void setSignSignatureObject(SignSignatureObject signSignatureObject); +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/verificator/Verificator.java b/src/main/java/at/gv/egiz/pdfas/framework/verificator/Verificator.java new file mode 100644 index 0000000..b134d64 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/verificator/Verificator.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2006 by Know-Center, Graz, Austria + * + * This software is the confidential and proprietary information of Know-Center, + * Graz, Austria. You shall not disclose such Confidential Information and shall + * use it only in accordance with the terms of the license agreement you entered + * into with Know-Center. + * + * KNOW-CENTER MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF + * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY + * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS + * DERIVATIVES. + * + * $Id: Verificator.java,v 1.1 2006/08/25 17:07:21 wprinz Exp $ + */ +package at.gv.egiz.pdfas.framework.verificator; + +import java.util.List; + +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; +import at.knowcenter.wag.exactparser.parsing.results.FooterParseResult; + + +/** + * Given an Incremental Update Block and the corresponding PDF, a verificator + * extracts all Signatures of its type and returns them as valitatable + * SignatureHolders. + * + * @author wprinz + */ +public interface Verificator +{ + /** + * Parses the given document/Block for signatures of this type. + * + * @param pdf + * The whole pdf document. A Verificator must only access the + * document up to its given block (block.next_index) and must not + * modify any byte in the pdf array. + * @param block + * The incremental update block. + * @param start_of_whole_block + * The start of the incremental update block (the end of the previous + * block) - If 0, this is the first block (the original Document). + * @return Returns the List of SignatureHolder objects found for this block. + */ + public List parseBlock(PdfDataSource pdfDataSource, byte [] pdf, final FooterParseResult block, + int start_of_whole_block) throws PresentableException; + +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/vfilter/VerificationFilter.java b/src/main/java/at/gv/egiz/pdfas/framework/vfilter/VerificationFilter.java new file mode 100644 index 0000000..1633b09 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/vfilter/VerificationFilter.java @@ -0,0 +1,52 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework.vfilter; + +import java.util.List; + +import at.gv.egiz.pdfas.exceptions.framework.VerificationFilterException; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.framework.input.TextDataSource; + +/** + * Extracts all signatures from a given PDF document or text. + * + * @see VerificationFilterParameters + * + * @author wprinz + */ +public interface VerificationFilter +{ + + /** + * Extracts the signatures from the given PDF. + * + * @param pdf + * The PDF. + * @param blocks + * The List of Incremental Update blocks. Usually this comes from a + * preprocessing step. + * @param parameters + * The algorithm parameters. + * @return Returns a List of SignatureHolders containing the signatures. May + * be empty in case no signatures have been found. + * @throws VerificationFilterException + * Thrown if something goes wrong. + */ + public List extractSignatureHolders(PdfDataSource pdf, List blocks, VerificationFilterParameters parameters) throws VerificationFilterException; + + /** + * Extracts the text signatures from the given free-text. + * + * @param text + * The free-text. + * @param parameters + * The algorithm parameters. + * @return Returns a List of SignatureHolders containing the signatures. May + * be empty in case no signatures have been found. + * @throws VerificationFilterException + * Thrown if something goes wrong. + */ + public List extractSignaturHolders(TextDataSource text, VerificationFilterParameters parameters) throws VerificationFilterException; +} diff --git a/src/main/java/at/gv/egiz/pdfas/framework/vfilter/VerificationFilterParameters.java b/src/main/java/at/gv/egiz/pdfas/framework/vfilter/VerificationFilterParameters.java new file mode 100644 index 0000000..c518fef --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/framework/vfilter/VerificationFilterParameters.java @@ -0,0 +1,76 @@ +/** + * + */ +package at.gv.egiz.pdfas.framework.vfilter; + +/** + * The parameters of the VerificationFilter algorithm. + * + * @author wprinz + */ +public interface VerificationFilterParameters +{ + + /** + * Tells the VerificationFilter to extract binary signatures only. + * + *

+ * Not scanning for textual signatures allows the algorithm to skip text + * extraction and signature extraction, which are both time and memory + * intensive processes. + *

+ * + * @return Returns true if the VerificationFilter should extract binary + * signatures only. + */ + public boolean extractBinarySignaturesOnly(); + + /** + * Tells the VerificationFilter to assume that there are only singatures (and + * their Incremental Update blocks) younger than the original document. + * + *

+ * This is equivalent to saying that the document was not updated using an + * Incremental update block other than a signature after being singed. The + * incremental update blocks after the original document contain only + * signatures (either text or binary). + *

+ *

+ * This is equivalent to saying that there exists no Incremental Update block + * that would render a text signature before it invalid. + *

+ *

+ * Under this assumption, the process of finding all text signatures + * simplifies to one text extraction of the whole document and one signature + * extraction. This is of course a massive performance gain. + *

+ *

+ * Actually the algorithm performs a text extraction of the whole document not + * including trailing binary signature Incremental Update blocks. This means + * that if a the last n Incremental Update blocks of a document are binary, + * there is no use extract text from them. + *

+ *

+ * Note that if there are Incremental Update blocks with text after a + * signature thus this assumption does not hold the signatures older than this + * block will break. + *

+ * + * @return Returns true if the Verification filter should assume that there + * are only signature blocks after the original document. + */ + public boolean assumeOnlySignatureUpdateBlocks(); + + /** + * Tells the VerificationFilter so scan for old signatures in the rest text. + * + *

+ * The rest text is the text of the oldest text signature or the original + * document text if there is no text signature. + *

+ * + * @return Returns true if the VerificationFilter should scan for old text + * signatures in the rest text. + */ + public boolean scanForOldSignatures(); +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/input/ByteArrayPdfDataSourceImpl.java b/src/main/java/at/gv/egiz/pdfas/impl/input/ByteArrayPdfDataSourceImpl.java new file mode 100644 index 0000000..0d27781 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/input/ByteArrayPdfDataSourceImpl.java @@ -0,0 +1,56 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.input; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import at.gv.egiz.pdfas.performance.PerformanceCounters; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; + +/** + * Implements a PdfDataSource that holds the whole PDF document in a byte array. + * + *

+ * Note that holding the data in a byte array is very memory consuming for large + * documents. + *

+ * + * @author wprinz + */ +public class ByteArrayPdfDataSourceImpl implements PdfDataSource +{ + protected byte[] pdf = null; + + protected int length = -1; + + public ByteArrayPdfDataSourceImpl(byte[] pdf) + { + PerformanceCounters.byteArrays.increment(); + + this.pdf = pdf; + this.length = pdf.length; + } + + public ByteArrayPdfDataSourceImpl(byte[] pdf, int length) + { + PerformanceCounters.byteArrays.increment(); + + this.pdf = pdf; + this.length = length; + } + + + public InputStream createInputStream() + { + ByteArrayInputStream bais = new ByteArrayInputStream(this.pdf, 0, this.length); + return bais; + } + + public int getLength() + { + return this.length; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/input/CompoundPdfDataSourceImpl.java b/src/main/java/at/gv/egiz/pdfas/impl/input/CompoundPdfDataSourceImpl.java new file mode 100644 index 0000000..f77d6be --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/input/CompoundPdfDataSourceImpl.java @@ -0,0 +1,47 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.input; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.SequenceInputStream; + +import at.gv.egiz.pdfas.framework.input.DataSource; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; + +/** + * @author wprinz + * + */ +public class CompoundPdfDataSourceImpl implements PdfDataSource +{ + protected DataSource originalDataSource = null; + + protected byte[] appendix = null; + + public CompoundPdfDataSourceImpl (PdfDataSource original, byte [] appendix) + { + this.originalDataSource = original; + this.appendix = appendix; + } + + /** + * @see at.gv.egiz.pdfas.framework.input.DataSource#createInputStream() + */ + public InputStream createInputStream() + { + ByteArrayInputStream bais = new ByteArrayInputStream(this.appendix); + SequenceInputStream sis = new SequenceInputStream(this.originalDataSource.createInputStream(), bais); + return sis; + } + + /** + * @see at.gv.egiz.pdfas.framework.input.DataSource#getLength() + */ + public int getLength() + { + return this.originalDataSource.getLength() + this.appendix.length; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/input/DelimitedInputStream.java b/src/main/java/at/gv/egiz/pdfas/impl/input/DelimitedInputStream.java new file mode 100644 index 0000000..4be9ec5 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/input/DelimitedInputStream.java @@ -0,0 +1,105 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.input; + +import java.io.IOException; +import java.io.InputStream; + +/** + * An input stream that has a delimited length. + * + * @author wprinz + */ +public class DelimitedInputStream extends InputStream +{ + /** + * The underlying InputStream. + */ + protected InputStream is = null; + + /** + * The number of bytes that can be read from the stream. + */ + protected int bytes_to_read = -1; + + /** + * Constructs the DelimitedInputStream from which a maximum of length bytes + * can be read. + */ + public DelimitedInputStream(InputStream is, int length) + { + this.is = is; + this.bytes_to_read = length; + } + + /** + * @see java.io.InputStream#read() + */ + public int read() throws IOException + { + if (this.bytes_to_read <= 0) + { + return -1; + } + int read = this.is.read(); + if (read > 0) + { + this.bytes_to_read--; + } + return read; + } + + /** + * @see java.io.InputStream#read(byte[], int, int) + */ + public int read(byte[] b, int off, int len) throws IOException + { + int btr = Math.min(len, this.bytes_to_read); + int read = this.is.read(b, off, btr); + if (read > 0) + { + this.bytes_to_read -= read; + } + return read; + } + + /** + * @see java.io.InputStream#read(byte[]) + */ + public int read(byte[] b) throws IOException + { + return read(b, 0, b.length); + } + + /** + * @see java.io.InputStream#skip(long) + */ + public long skip(long n) throws IOException + { + long bts = Math.min(n, this.bytes_to_read); + long skipped = this.is.skip(bts); + if (skipped > 0) + { + this.bytes_to_read -= skipped; + } + return skipped; + } + + /** + * @see java.io.InputStream#close() + */ + public void close() throws IOException + { + this.is.close(); + } + + /** + * @see java.io.InputStream#available() + */ + public int available() throws IOException + { + int avail = this.is.available(); + return Math.min(this.bytes_to_read, avail); + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/input/DelimitedPdfDataSource.java b/src/main/java/at/gv/egiz/pdfas/impl/input/DelimitedPdfDataSource.java new file mode 100644 index 0000000..6c67be2 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/input/DelimitedPdfDataSource.java @@ -0,0 +1,44 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.input; + +import java.io.InputStream; + +import at.gv.egiz.pdfas.framework.input.PdfDataSource; + +/** + * @author wprinz + * + */ +public class DelimitedPdfDataSource implements PdfDataSource +{ + + protected PdfDataSource dataSource = null; + protected int len = -1; + + public DelimitedPdfDataSource (PdfDataSource original, int length) + { + this.dataSource = original; + this.len = length; + } + + /** + * @see at.gv.egiz.pdfas.framework.input.DataSource#createInputStream() + */ + public InputStream createInputStream() + { + InputStream originalIS = this.dataSource.createInputStream(); + DelimitedInputStream dis = new DelimitedInputStream(originalIS, this.len); + return dis; + } + + /** + * @see at.gv.egiz.pdfas.framework.input.DataSource#getLength() + */ + public int getLength() + { + return this.len; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/input/FileBased.java b/src/main/java/at/gv/egiz/pdfas/impl/input/FileBased.java new file mode 100644 index 0000000..54f8842 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/input/FileBased.java @@ -0,0 +1,20 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.input; + +import java.io.File; + +/** + * Interface that reveals the underlying data file. + * + * @author wprinz + */ +public interface FileBased +{ + /** + * Returns the underlying data file. + * @return Returns the underlying data file. + */ + public File getFile(); +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/input/FileBasedPdfDataSourceImpl.java b/src/main/java/at/gv/egiz/pdfas/impl/input/FileBasedPdfDataSourceImpl.java new file mode 100644 index 0000000..8453192 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/input/FileBasedPdfDataSourceImpl.java @@ -0,0 +1,103 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.input; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.pdfas.framework.input.PdfDataSource; + +/** + * @author wprinz + * + */ +public class FileBasedPdfDataSourceImpl implements PdfDataSource, FileBased +{ + /** + * The log. + */ + private static final Log log = LogFactory.getLog(FileBasedPdfDataSourceImpl.class); + + /** + * The underlying file. + */ + protected File inputFile = null; + + protected int length = -1; + + /** + * Constructor that creates this PdfDataSource backed by a file in the file + * system. + * + * @param file + * The input File. + * @param length + * The length of the InputStream. The is the maximum number of bytes + * that can be read from the stream. + * @throws IOException + * Thrown if the file cannot be read properly. + */ + public FileBasedPdfDataSourceImpl(File file, int length) throws IOException + { + + if (!file.exists()) + { + throw new FileNotFoundException("The file '" + file + "' does not exist."); + } + // for some reason the isFile is not always correct... + // if (file.isFile()) + // { + // throw new IOException("The file '" + file + "' is not a normal file."); + // } + if (!file.canRead()) + { + throw new IOException("The file '" + file + "' cannot be read."); + } + + this.inputFile = file; + this.length = length; + } + + /** + * @see at.gv.egiz.pdfas.impl.input.FileBased#getFile() + */ + public File getFile() + { + return this.inputFile; + } + + /** + * @see at.gv.egiz.pdfas.framework.input.PdfDataSource#createInputStream() + */ + public InputStream createInputStream() + { + try + { + FileInputStream fis = new FileInputStream(getFile()); + DelimitedInputStream dis = new DelimitedInputStream(fis, getLength()); + return dis; + } + catch (IOException e) + { + log.error("Couldn't create InputStream for file " + getFile() + ". Returning null."); + log.error(e); + + return null; + } + } + + /** + * @see at.gv.egiz.pdfas.framework.input.PdfDataSource#getLength() + */ + public int getLength() + { + return this.length; + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/input/FileBasedTextDataSourceImpl.java b/src/main/java/at/gv/egiz/pdfas/impl/input/FileBasedTextDataSourceImpl.java new file mode 100644 index 0000000..6f6c7b4 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/input/FileBasedTextDataSourceImpl.java @@ -0,0 +1,124 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.input; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.pdfas.framework.input.TextDataSource; + +/** + * @author wprinz + * + */ +public class FileBasedTextDataSourceImpl implements TextDataSource, FileBased +{ + /** + * The log. + */ + private static final Log log = LogFactory.getLog(FileBasedTextDataSourceImpl.class); + + protected File file = null; + + protected String characterEncoding = null; + + public FileBasedTextDataSourceImpl(File file, String characterEncoding) throws IOException + { + if (!file.exists()) + { + throw new FileNotFoundException("The file '" + file + "' does not exist."); + } + if (!file.canRead()) + { + throw new IOException("The file '" + file + "' cannot be read."); + } + + this.file = file; + this.characterEncoding = characterEncoding; + } + + /** + * @see at.gv.egiz.pdfas.impl.input.FileBased#getFile() + */ + public File getFile() + { + return this.file; + } + + /** + * Returns the character encoding. + * + * @return Returns the character encoding. + */ + public String getCharacterEncoding() + { + return this.characterEncoding; + } + + /** + * @see at.gv.egiz.pdfas.framework.input.TextDataSource#getText() + */ + public String getText() + { + try + { + InputStream is = createInputStream(); + byte[] data = new byte[getLength()]; + int read = 0; + int n = 0; + while ((n = is.read(data, read, data.length - read)) > 0) + { + read += n; + } + is.close(); + + String text = new String(data, getCharacterEncoding()); + + data = null; + + return text; + } + catch (IOException e) + { + log.error("Couldn't read text for file " + getFile() + ". Returning null."); + log.error(e); + + return null; + } + } + + /** + * @see at.gv.egiz.pdfas.framework.input.DataSource#createInputStream() + */ + public InputStream createInputStream() + { + try + { + FileInputStream fis = new FileInputStream(getFile()); + return fis; + } + catch (IOException e) + { + log.error("Couldn't create InputStream for file " + getFile() + ". Returning null."); + log.error(e); + + return null; + } + } + + /** + * @see at.gv.egiz.pdfas.framework.input.DataSource#getLength() + */ + public int getLength() + { + return (int) getFile().length(); + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/input/IncrementalUpdateParser.java b/src/main/java/at/gv/egiz/pdfas/impl/input/IncrementalUpdateParser.java new file mode 100644 index 0000000..b4c2bef --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/input/IncrementalUpdateParser.java @@ -0,0 +1,49 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.input; + +import java.util.List; + +import at.gv.egiz.pdfas.impl.input.helper.DataSourceHelper; +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; +import at.knowcenter.wag.exactparser.ParseDocument; + +/** + * Parses the given PDF document into a list of Incremental Update blocks. + * @author wprinz + */ +public class IncrementalUpdateParser +{ + /** + * The log. + */ + private static final Log log = LogFactory.getLog(IncrementalUpdateParser.class); + + public static List parsePdfIntoIUBlocks (PdfDataSource pdfDataSource) throws PDFDocumentException + { + log.trace("parsePdfIntoIUBlocks:"); + + List blocks = null; + try + { + byte [] pdf = DataSourceHelper.convertDataSourceToByteArray(pdfDataSource); + blocks = ParseDocument.parseDocument(pdf); + } + catch (Exception e) + { + log.error("Error while parsing Document into IU blocks.", e); + throw new PDFDocumentException(ErrorCode.DOCUMENT_CANNOT_BE_READ, e); + } + + log.trace("parsePdfIntoIUBlocks finished."); + return blocks; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/input/TextDataSourceImpl.java b/src/main/java/at/gv/egiz/pdfas/impl/input/TextDataSourceImpl.java new file mode 100644 index 0000000..b259a3e --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/input/TextDataSourceImpl.java @@ -0,0 +1,82 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.input; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +import at.gv.egiz.pdfas.framework.input.TextDataSource; + +/** + * A TextDataSource that keeps the text in memory. + * + *

+ * Keeping the text in memory is fast as long as the text is short, but may + * result in bad memory performance when the text is longer. Use a FileBased + * TextDataSource instead if memory is an issue. + *

+ * + * @author wprinz + */ +public class TextDataSourceImpl implements TextDataSource +{ + /** + * The text. + */ + protected String text = null; + + /** + * Constructor that sets the text. + * + * @param text + * The text. + */ + public TextDataSourceImpl(String text) + { + this.text = text; + } + + /** + * @see at.gv.egiz.pdfas.framework.input.TextDataSource#getText() + */ + public String getText() + { + return this.text; + } + + /** + * @see at.gv.egiz.pdfas.framework.input.DataSource#createInputStream() + */ + public InputStream createInputStream() + { + try + { + byte[] data = getText().getBytes("UTF-8"); + // PERF: if memory is an issue (e.g. in web), use a FileBased TextDataSource instead. + return new ByteArrayInputStream(data); + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException(e); + } + } + + /** + * @see at.gv.egiz.pdfas.framework.input.DataSource#getLength() + */ + public int getLength() + { + try + { + byte[] data = getText().getBytes("UTF-8"); + return data.length; + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/input/helper/DataSourceHelper.java b/src/main/java/at/gv/egiz/pdfas/impl/input/helper/DataSourceHelper.java new file mode 100644 index 0000000..1e2ffdc --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/input/helper/DataSourceHelper.java @@ -0,0 +1,92 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.input.helper; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import at.gv.egiz.pdfas.performance.PerformanceCounters; +import at.gv.egiz.pdfas.framework.input.DataSource; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author wprinz + * + */ +public class DataSourceHelper +{ + /** + * The log. + */ + private static final Log log = LogFactory.getLog(DataSourceHelper.class); + + /** + * Converts a PdfDataSource to a byte array. + * + *

+ * Note that this function is very memory intensive. Use the Streams whereever + * possible. + *

+ * + * @deprecated + * + * @param pdfDataSource + * @return + * @throws IOException + */ + public static byte[] convertDataSourceToByteArray(DataSource pdfDataSource) + { + try + { + PerformanceCounters.byteArrays.increment(); + + byte[] data = new byte[pdfDataSource.getLength()]; + + int bytes_written = 0; + + InputStream is = pdfDataSource.createInputStream(); + int n = 0; + while ((n = is.read(data, bytes_written, data.length - bytes_written)) > 0) + { + bytes_written += n; + } + is.close(); + + assert bytes_written == data.length; + + return data; + } + catch (IOException e) + { + log.error(e); + throw new RuntimeException(e); + } + } + + public static void debugDataSourceToFile(DataSource dataSource, File file) + { + try + { + InputStream is = dataSource.createInputStream(); + FileOutputStream fos = new FileOutputStream(file); + byte[] data = new byte[2048]; + int n = -1; + while ((n = is.read(data)) > 0) + { + fos.write(data, 0, n); + } + is.close(); + fos.close(); + } + catch (IOException e) + { + log.error(e); + } + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/output/ByteArrayDataSink.java b/src/main/java/at/gv/egiz/pdfas/impl/output/ByteArrayDataSink.java new file mode 100644 index 0000000..f3d1283 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/output/ByteArrayDataSink.java @@ -0,0 +1,83 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.output; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.pdfas.framework.output.DataSink; +import at.gv.egiz.pdfas.performance.PerformanceCounters; + +/** + * @author wprinz + * + */ +public class ByteArrayDataSink implements DataSink +{ + /** + * The log. + */ + private static final Log log = LogFactory.getLog(ByteArrayDataSink.class); + + protected String mimeType = null; + protected String characterEncoding = null; + + protected ByteArrayOutputStream baos = null; + + + public ByteArrayDataSink() + { + PerformanceCounters.byteArrays.increment(); + } + + /** + * @see at.gv.egiz.pdfas.framework.output.DataSink#createOutputStream(java.lang.String) + */ + public OutputStream createOutputStream(String mimeType) + { + return createOutputStream(mimeType, null); + } + + /** + * @see at.gv.egiz.pdfas.framework.output.DataSink#createOutputStream(java.lang.String, java.lang.String) + */ + public OutputStream createOutputStream(String mimeType, String characterEncoding) + { + if (this.baos != null) + { + log.warn("An output stream is created twice. The old one will be rendered useless."); + } + this.baos = new ByteArrayOutputStream(4096); + return this.baos; + } + + /** + * Returns the byte array. + * @return Returns the byte array. + */ + public byte [] getByteArray () + { + return this.baos.toByteArray(); + } + + /** + * @return the mimeType + */ + public String getMimeType() + { + return this.mimeType; + } + + /** + * @return the characterEncoding + */ + public String getCharacterEncoding() + { + return this.characterEncoding; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/output/FileBasedDataSink.java b/src/main/java/at/gv/egiz/pdfas/impl/output/FileBasedDataSink.java new file mode 100644 index 0000000..4e1e3b7 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/output/FileBasedDataSink.java @@ -0,0 +1,126 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.output; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import at.gv.egiz.pdfas.framework.output.DataSink; +import at.gv.egiz.pdfas.impl.input.FileBased; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author wprinz + * + */ +public class FileBasedDataSink implements DataSink, FileBased +{ + /** + * The log. + */ + private static final Log log = LogFactory.getLog(FileBasedDataSink.class); + + protected String mimeType = null; + protected String characterEncoding = null; + + /** + * The output file. + */ + protected File outputFile = null; + + /** + * Creates a file based PdfDataSink. + * + * @param file + * The file. + * @throws IOException + * F.e. + */ + public FileBasedDataSink(File file) throws IOException + { + if (!file.exists()) + { + file.createNewFile(); + } + if (!file.isFile()) + { + throw new IOException("The file '" + file + "' is not a normal file."); + } + if (!file.canWrite()) + { + throw new IOException("The file '" + file + "' cannot be written."); + } + + this.outputFile = file; + } + + /** + * @see at.gv.egiz.pdfas.impl.input.FileBased#getFile() + */ + public File getFile() + { + return this.outputFile; + } + + + + protected OutputStream createOutputStream() + { + try + { + FileOutputStream fos = new FileOutputStream(getFile()); + return fos; + } + catch (IOException e) + { + log.error("Couldn't create OutputStream for file " + getFile() + ". Returning null."); + log.error(e); + + return null; + + } + } + + /** + * @see at.gv.egiz.pdfas.framework.output.DataSink#createOutputStream(java.lang.String) + */ + public OutputStream createOutputStream(String mimeType) + { + return createOutputStream(mimeType, null); + } + + /** + * @see at.gv.egiz.pdfas.framework.output.DataSink#createOutputStream(java.lang.String, java.lang.String) + */ + public OutputStream createOutputStream(String mimeType, String characterEncoding) + { + this.mimeType = mimeType; + this.characterEncoding = characterEncoding; + + return createOutputStream(); + } + + /** + * @return the mimeType + */ + public String getMimeType() + { + return mimeType; + } + + /** + * @return the characterEncoding + */ + public String getCharacterEncoding() + { + return characterEncoding; + } + + + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/signator/IncrementalUpdateHelper.java b/src/main/java/at/gv/egiz/pdfas/impl/signator/IncrementalUpdateHelper.java new file mode 100644 index 0000000..a95cdc6 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/signator/IncrementalUpdateHelper.java @@ -0,0 +1,45 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.signator; + +import java.util.List; + +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.framework.output.DataSink; +import at.gv.egiz.pdfas.impl.output.ByteArrayDataSink; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; +import at.knowcenter.wag.egov.egiz.pdf.BinarySignature; +import at.knowcenter.wag.egov.egiz.pdf.IncrementalUpdateInformation; +import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; + +import com.lowagie.text.pdf.PdfPTable; + +/** + * @author wprinz + */ +public final class IncrementalUpdateHelper +{ + public static IncrementalUpdateInformation writeIncrementalUpdate(PdfDataSource pdfDataSource, PdfPTable pdf_table, PositioningInstruction pi, List variable_field_definitions, + List all_field_definitions) throws PresentableException + { + // PERF: binary sig needs the signed_pdf as byte array + ByteArrayDataSink bads = new ByteArrayDataSink(); + IncrementalUpdateInformation iui = BinarySignature.writeIncrementalUpdate(pdfDataSource, bads, pdf_table, pi, variable_field_definitions, all_field_definitions); + iui.signed_pdf = bads.getByteArray(); + bads = null; + + return iui; + } + + public static void writeIncrementalUpdateToDataSink(PdfDataSource pdfDataSource, DataSink dataSink, PdfPTable pdf_table, PositioningInstruction pi) throws PresentableException + { + writeIncrementalUpdateToDataSink(pdfDataSource, dataSink, pdf_table, pi, null, null); + } + + public static void writeIncrementalUpdateToDataSink(PdfDataSource pdfDataSource, DataSink dataSink, PdfPTable pdf_table, PositioningInstruction pi, List variable_field_definitions, + List all_field_definitions) throws PresentableException + { + BinarySignature.writeIncrementalUpdate(pdfDataSource, dataSink, pdf_table, pi, variable_field_definitions, all_field_definitions); + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/signator/SignatorInformationImpl.java b/src/main/java/at/gv/egiz/pdfas/impl/signator/SignatorInformationImpl.java new file mode 100644 index 0000000..d07dc6a --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/signator/SignatorInformationImpl.java @@ -0,0 +1,20 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.signator; + +import at.knowcenter.wag.egov.egiz.sig.SignatureData; +import at.gv.egiz.pdfas.framework.signator.SignatorInformation; + +/** + * @author wprinz + */ +public abstract class SignatorInformationImpl implements SignatorInformation +{ + + /** + * @see at.gv.egiz.pdfas.framework.signator.SignatorInformation#getSignatureData() + */ + public abstract SignatureData getSignatureData(); + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignatorInformation.java b/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignatorInformation.java new file mode 100644 index 0000000..916abf4 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignatorInformation.java @@ -0,0 +1,51 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.signator.binary; + +import java.util.List; + +import at.knowcenter.wag.egov.egiz.sig.SignatureData; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.framework.signator.SignatorInformation; + +/** + * @author wprinz + * + */ +public class BinarySignatorInformation implements SignatorInformation +{ + protected PdfDataSource originalDocument = null; + + protected byte [] incrementalUpdateBlock = null; + + protected SignatureData signatureData = null; + + protected List replaces = null; + + protected int cert_start = -1; + protected int cert_length = -1; + + protected int enc_start = -1; + protected int enc_length = -1; + + protected SignSignatureObject signSignatureObject = null; + + /** + * @see at.gv.egiz.pdfas.framework.signator.SignatorInformation#getSignatureData() + */ + public SignatureData getSignatureData() + { + return this.signatureData; + } + + /** + * @see at.gv.egiz.pdfas.framework.signator.SignatorInformation#setSignSignatureObject(at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject) + */ + public void setSignSignatureObject(SignSignatureObject signSignatureObject) + { + this.signSignatureObject = signSignatureObject; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java b/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java new file mode 100644 index 0000000..6c6ba29 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java @@ -0,0 +1,326 @@ +/** + * Copyright (c) 2006 by Know-Center, Graz, Austria + * + * This software is the confidential and proprietary information of Know-Center, + * Graz, Austria. You shall not disclose such Confidential Information and shall + * use it only in accordance with the terms of the license agreement you entered + * into with Know-Center. + * + * KNOW-CENTER MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF + * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY + * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS + * DERIVATIVES. + * + * $Id: BinarySignator_1_0_0.java,v 1.1 2006/08/25 17:07:35 wprinz Exp $ + */ +package at.gv.egiz.pdfas.impl.signator.binary; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.gv.egiz.pdfas.exceptions.framework.SignatorException; +import at.gv.egiz.pdfas.framework.input.DataSource; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.framework.output.DataSink; +import at.gv.egiz.pdfas.framework.signator.Signator; +import at.gv.egiz.pdfas.framework.signator.SignatorInformation; +import at.gv.egiz.pdfas.impl.input.CompoundPdfDataSourceImpl; +import at.gv.egiz.pdfas.impl.signator.IncrementalUpdateHelper; +import at.knowcenter.wag.egov.egiz.PdfAS; +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; +import at.knowcenter.wag.egov.egiz.framework.SignatorFactory; +import at.knowcenter.wag.egov.egiz.pdf.BinarySignature; +import at.knowcenter.wag.egov.egiz.pdf.IncrementalUpdateInformation; +import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; +import at.knowcenter.wag.egov.egiz.pdf.ReplaceInfo; +import at.knowcenter.wag.egov.egiz.pdf.StringInfo; +import at.knowcenter.wag.egov.egiz.pdf.TablePos; +import at.knowcenter.wag.egov.egiz.sig.SignatureData; +import at.knowcenter.wag.egov.egiz.sig.SignatureDataImpl; +import at.knowcenter.wag.egov.egiz.sig.SignatureFieldDefinition; +import at.knowcenter.wag.egov.egiz.sig.SignatureObject; +import at.knowcenter.wag.egov.egiz.sig.SignatureTypes; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObjectHelper; +import at.knowcenter.wag.exactparser.ByteArrayUtils; + +import com.lowagie.text.pdf.PdfPTable; + +/** + * Signs the document binary. + * + *

+ * In prepareSign, an Incremental Update is created that contains the Signature + * block and the egiz dictionary. For formatting the layout, variable values are + * filled with placeholders. After the layout has been fixed, all variable + * fields (all holes in the byte ranges) are replaced with 0. This document is + * then base64 encoded and signed. + *

+ *

+ * In finishSign, the variable fields (values, /Cert) are replaced with the + * values according to the encoding. + *

+ * + * @author wprinz + */ +public class BinarySignator_1_0_0 implements Signator +{ + /** + * The Pdf-AS ID of this Signator. + */ + public static final PdfASID MY_ID = new PdfASID(SignatorFactory.VENDOR, SignatorFactory.TYPE_BINARY, SignatorFactory.VERSION_1_0_0); + + /** + * @see at.knowcenter.wag.egov.egiz.framework.Signator#getMyId() + */ + public PdfASID getMyId() + { + return MY_ID; + } + + /** + * Default constructor. + */ + public BinarySignator_1_0_0() + { + // Default constructor. + } + + /** + * @see at.gv.egiz.pdfas.framework.signator.Signator#prepareSign(at.gv.egiz.pdfas.framework.input.PdfDataSource, + * java.lang.String, at.knowcenter.wag.egov.egiz.pdf.TablePos, boolean) + */ + public SignatorInformation prepareSign(PdfDataSource pdfDataSource, String profile, TablePos pos, boolean has_SIG_ID) throws SignatorException + { + try + { + SignatureObject signature_object = PdfAS.createSignatureObjectFromType(profile); + signature_object.fillValues((char) BinarySignature.LAYOUT_PLACEHOLDER, has_SIG_ID); + + signature_object.setKZ(getMyId()); + + PdfPTable pdf_table = PdfAS.createPdfPTableFromSignatureObject(signature_object); + + PositioningInstruction pi = PdfAS.determineTablePositioning(pos, profile, pdfDataSource, pdf_table); + + List all_field_definitions = signature_object.getSignatureTypeDefinition().getFieldDefinitions(); + List variable_field_definitions = new ArrayList(); + for (int i = 0; i < all_field_definitions.size(); i++) + { + SignatureFieldDefinition sfd = (SignatureFieldDefinition) all_field_definitions.get(i); + if (sfd.placeholder_length > 0) + { + if (sfd.field_name.equals(SignatureTypes.SIG_ID) && has_SIG_ID == false) + { + continue; + } + variable_field_definitions.add(sfd); + } + } + + IncrementalUpdateInformation iui = IncrementalUpdateHelper.writeIncrementalUpdate(pdfDataSource, pdf_table, pi, variable_field_definitions, all_field_definitions); + + String temp_string = iui.temp_ir_number + " " + iui.temp_ir_generation + " obj"; //$NON-NLS-1$//$NON-NLS-2$ + byte[] temp_bytes = temp_string.getBytes("US-ASCII"); //$NON-NLS-1$ + int temp_start = ByteArrayUtils.lastIndexOf(iui.signed_pdf, temp_bytes); + byte[] stream_bytes = new byte[] { '>', '>', 's', 't', 'r', 'e', 'a', 'm', 0x0A }; + int stream_start = ByteArrayUtils.indexOf(iui.signed_pdf, temp_start, stream_bytes); + iui.content_stream_start = stream_start + stream_bytes.length; + + // update the stream indices + Iterator it = iui.replaces.iterator(); + while (it.hasNext()) + { + ReplaceInfo ri = (ReplaceInfo) it.next(); + + Iterator sit = ri.replaces.iterator(); + while (sit.hasNext()) + { + StringInfo si = (StringInfo) sit.next(); + si.string_start += iui.content_stream_start; + } + } + // update KZ list indices: + it = iui.kz_list.iterator(); + while (it.hasNext()) + { + StringInfo si = (StringInfo) it.next(); + si.string_start += iui.content_stream_start; + } + + BinarySignature.markByteRanges(iui); + + // byte [] old_signed_pdf = iui.signed_pdf; + iui.signed_pdf = BinarySignature.prepareDataToSign(iui.signed_pdf, iui.byte_ranges); + + BinarySignatorInformation bsi = compressIUI(iui); + return bsi; + + } + catch (UnsupportedEncodingException e) + { + throw new SignatorException(201, e); + } + catch (PDFDocumentException e) + { + throw new SignatorException(e.getErrorCode(), e); + } + catch (PresentableException e) + { + throw new SignatorException(201, e); + } + } + + /** + * @see at.gv.egiz.pdfas.framework.signator.Signator#finishSign(at.gv.egiz.pdfas.framework.signator.SignatorInformation, + * at.gv.egiz.pdfas.framework.output.DataSink) + */ + public void finishSign(SignatorInformation signatorInformation, DataSink dataSink) throws SignatorException + { + try + { + IncrementalUpdateInformation iui = uncompressIUI((BinarySignatorInformation) signatorInformation); + + // PERF: need to keep the whole pdf in mem for processing + + // PdfAS.prefixID(iui.signed_signature_object, PdfAS.BINARY_ID); + fillReplacesWithValues(iui); + + BinarySignature.replaceCertificate(iui); + BinarySignature.replacePlaceholders(iui); + + OutputStream os = dataSink.createOutputStream(PdfAS.PDF_MIME_TYPE); + os.write(iui.signed_pdf); + os.close(); + + //SignResult sign_result = new SignResult(PdfAS.PDF_MIME_TYPE, iui.signed_pdf); + //return sign_result; + } + catch (PDFDocumentException e) + { + throw new SignatorException(e.getErrorCode(), e); + } + catch (IOException e) + { + throw new SignatorException(ErrorCode.DOCUMENT_CANNOT_BE_READ, e); + } + } + + /** + * Reads the signature values from the signed signature object and fills the + * corresponding value in the Replaces array. + * + * @param iui + * The IncrementalUpdateInformation. + */ + protected void fillReplacesWithValues(IncrementalUpdateInformation iui) + { + Iterator it = iui.replaces.iterator(); + while (it.hasNext()) + { + ReplaceInfo ri = (ReplaceInfo) it.next(); + + ri.value = SignSignatureObjectHelper.retrieveStringValueFromSignatureObject(iui.signed_signature_object, ri.sfd.field_name); + } + } + + /** + * Forms the SignatureData to be used for signing. + * + * @param iui + * The IncrementalUpdateInformation. + * @return Returns the SignatureData to be used for signing. + */ + protected SignatureData formSignatureData(IncrementalUpdateInformation iui) + { + // String document_text = + // BinarySignature.retrieveSignableTextFromData(iui.signed_pdf, + // iui.signed_pdf.length); // signed_pdf.length); + // + // byte[] data; + // try + // { + // data = document_text.getBytes("UTF-8"); //$NON-NLS-1$ + // } + // catch (UnsupportedEncodingException e) + // { + // throw new RuntimeException("Very strange: UTF-8 character encoding not + // supported.", e); //$NON-NLS-1$ + // } + DataSource ds = new CompoundPdfDataSourceImpl(iui.original_document, iui.sign_iui_block); + SignatureData signature_data = new SignatureDataImpl(ds, PdfAS.PDF_MIME_TYPE); + + return signature_data; + } + + protected BinarySignatorInformation compressIUI(IncrementalUpdateInformation iui) + { + iui.sign_iui_block = new byte[iui.signed_pdf.length - iui.original_document.getLength()]; + System.arraycopy(iui.signed_pdf, iui.original_document.getLength(), iui.sign_iui_block, 0, iui.sign_iui_block.length); + + iui.signature_data = formSignatureData(iui); + + // remove the signed pdf from memory + iui.signed_pdf = null; + + BinarySignatorInformation bsi = new BinarySignatorInformation(); + bsi.originalDocument = iui.original_document; + bsi.incrementalUpdateBlock = iui.sign_iui_block; + bsi.signatureData = iui.signature_data; + bsi.replaces = iui.replaces; + bsi.cert_start = iui.cert_start; + bsi.cert_length = iui.cert_length; + bsi.enc_start = iui.enc_start; + bsi.enc_length = iui.enc_length; + + return bsi; + } + + protected IncrementalUpdateInformation uncompressIUI(BinarySignatorInformation bsi) + { + IncrementalUpdateInformation iui = new IncrementalUpdateInformation(); + + iui.original_document = bsi.originalDocument; + iui.sign_iui_block = bsi.incrementalUpdateBlock; + iui.signature_data = bsi.signatureData; + iui.replaces = bsi.replaces; + iui.cert_start = bsi.cert_start; + iui.cert_length = bsi.cert_length; + iui.enc_start = bsi.enc_start; + iui.enc_length = bsi.enc_length; + + iui.signed_signature_object = bsi.signSignatureObject; + + restoreSignedPdf(iui); + + return iui; + } + + protected void restoreSignedPdf(IncrementalUpdateInformation iui) + { + iui.signed_pdf = new byte[iui.original_document.getLength() + iui.sign_iui_block.length]; + + try + { + InputStream is = iui.original_document.createInputStream(); + is.read(iui.signed_pdf, 0, iui.original_document.getLength()); + is.close(); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + + System.arraycopy(iui.sign_iui_block, 0, iui.signed_pdf, iui.original_document.getLength(), iui.sign_iui_block.length); + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_1_0.java b/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_1_0.java new file mode 100644 index 0000000..7ab62fc --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_1_0.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2006 by Know-Center, Graz, Austria + * + * This software is the confidential and proprietary information of Know-Center, + * Graz, Austria. You shall not disclose such Confidential Information and shall + * use it only in accordance with the terms of the license agreement you entered + * into with Know-Center. + * + * KNOW-CENTER MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF + * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY + * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS + * DERIVATIVES. + * + * $Id: BinarySignator_1_0_0.java,v 1.1 2006/08/25 17:07:35 wprinz Exp $ + */ +package at.gv.egiz.pdfas.impl.signator.binary; + +import at.gv.egiz.pdfas.impl.input.CompoundPdfDataSourceImpl; +import at.gv.egiz.pdfas.framework.input.DataSource; +import at.knowcenter.wag.egov.egiz.PdfAS; +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.framework.SignatorFactory; +import at.knowcenter.wag.egov.egiz.pdf.IncrementalUpdateInformation; +import at.knowcenter.wag.egov.egiz.sig.SignatureData; +import at.knowcenter.wag.egov.egiz.sig.SignatureDataImpl; + +/** + * Signs the document binary. + * + *

+ * This just differs from version 1.0.0 in the fact that the signature data is + * the actual binary PDF instead of a Base64 encoding. + *

+ * + * @see BinarySignator_1_0_0 + * + * @author wprinz + */ +public class BinarySignator_1_1_0 extends BinarySignator_1_0_0 +{ + /** + * The Pdf-AS ID of this Signator. + */ + public static final PdfASID MY_ID = new PdfASID(SignatorFactory.VENDOR, SignatorFactory.TYPE_BINARY, SignatorFactory.VERSION_1_1_0); + + /** + * @see at.knowcenter.wag.egov.egiz.framework.Signator#getMyId() + */ + public PdfASID getMyId() + { + return MY_ID; + } + + /** + * Overrides the SignatureData generation of the BinarySignator 1.0.0 so that + * the SignatureData is the actual binary PDF instead of a Base64 encoding. + * + * @see at.knowcenter.wag.egov.egiz.framework.signators.BinarySignator_1_0_0#formSignatureData(at.knowcenter.wag.egov.egiz.pdf.IncrementalUpdateInformation) + */ + protected SignatureData formSignatureData(IncrementalUpdateInformation iui) + { + DataSource ds = new CompoundPdfDataSourceImpl(iui.original_document, iui.sign_iui_block); + SignatureData signature_data = new SignatureDataImpl(ds, PdfAS.PDF_MIME_TYPE); + + return signature_data; + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/signator/detached/DetachedTextualSignator_1_0_0.java b/src/main/java/at/gv/egiz/pdfas/impl/signator/detached/DetachedTextualSignator_1_0_0.java new file mode 100644 index 0000000..6a242b6 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/signator/detached/DetachedTextualSignator_1_0_0.java @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2006 by Know-Center, Graz, Austria + * + * This software is the confidential and proprietary information of Know-Center, + * Graz, Austria. You shall not disclose such Confidential Information and shall + * use it only in accordance with the terms of the license agreement you entered + * into with Know-Center. + * + * KNOW-CENTER MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF + * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY + * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS + * DERIVATIVES. + * + * $Id: TextualSignator_1_0_0.java,v 1.3 2006/10/31 08:07:50 wprinz Exp $ + */ +package at.gv.egiz.pdfas.impl.signator.detached; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.gv.egiz.pdfas.exceptions.framework.SignatorException; +import at.gv.egiz.pdfas.framework.SignatorFactory; +import at.gv.egiz.pdfas.framework.output.DataSink; +import at.gv.egiz.pdfas.framework.signator.SignatorInformation; +import at.gv.egiz.pdfas.impl.signator.textual.TextualSignatorInformation; +import at.gv.egiz.pdfas.impl.signator.textual.TextualSignator_1_0_0; +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.BKUPostConnection; + +/** + * Signs a document textually. + * + *

+ * In prepareSign, the document text is extracted and normalized. + *

+ *

+ * In finishSign, the signed SignatureObject is transformed into a Signature + * block, which is then written as an Incremental Update. + *

+ * + * @author wprinz + */ +public class DetachedTextualSignator_1_0_0 extends TextualSignator_1_0_0 +{ + /** + * The Mime Type. + */ + public static final String MIME_TYPE = "text/xml"; //$NON-NLS-1$ + + /** + * The Pdf-AS ID of this Signator. + */ + public static final PdfASID MY_ID = new PdfASID(SignatorFactory.VENDOR, SignatorFactory.TYPE_DETACHED_TEXTUAL, SignatorFactory.VERSION_1_0_0); + + /** + * @see at.knowcenter.wag.egov.egiz.framework.Signator#getMyId() + */ + public PdfASID getMyId() + { + return MY_ID; + } + + // /** + // *

+ // * The parameter has_SIG_ID is not used by this Signator because it doesn't + // * pre-format the signature block. + // *

+ // * + // * @see at.knowcenter.wag.egov.egiz.framework.Signator#prepareSign(byte[], + // * String, TablePos, boolean) + // */ + // public IncrementalUpdateInformation prepareSign(PdfDataSource pdf, + // String signature_type, TablePos pos, boolean has_SIG_ID) throws + // PresentableException + // { + // IncrementalUpdateInformation iui = new IncrementalUpdateInformation(); + // iui.original_document = pdf; + // iui.signature_type = signature_type; + // iui.pos = pos; + // + // String document_text = + // PdfAS.extractNormalizedTextTextual(pdf.createInputStream()); + // // logger_.debug("signed_text = " + document_text); + // + // DataSource ds = new TextDataSourceImpl(document_text); + // iui.signature_data = new SignatureDataImpl(ds, MIME_TYPE, "UTF-8"); + // //$NON-NLS-1$ //$NON-NLS-2$ + // + // return iui; + // } + // + // /** + // * @see + // at.knowcenter.wag.egov.egiz.framework.Signator#finishSign(at.knowcenter.wag.egov.egiz.pdf.IncrementalUpdateInformation) + // */ + // public SignResult finishSign(IncrementalUpdateInformation iui) throws + // PresentableException + // { + // try + // { + // String response = + // iui.signed_signature_object.response_properties.getProperty(BKUPostConnection.RESPONSE_STRING_KEY); + // byte[] response_bytes = response.getBytes("UTF-8"); //$NON-NLS-1$ + // + // SignResult sign_result = new SignResult(MIME_TYPE, response_bytes); + // return sign_result; + // } + // catch (UnsupportedEncodingException e) + // { + // e.printStackTrace(); + // throw new PDFDocumentException(300, e); + // } + // } + + /** + * @see at.gv.egiz.pdfas.impl.signator.textual.TextualSignator_1_0_0#finishSign(at.gv.egiz.pdfas.framework.signator.SignatorInformation, + * at.gv.egiz.pdfas.framework.output.DataSink) + */ + public void finishSign(SignatorInformation signatorInformation, DataSink dataSink) throws SignatorException + { + try + { + TextualSignatorInformation tsi = (TextualSignatorInformation) signatorInformation; + + String response = tsi.signSignatureObject.response_properties.getProperty(BKUPostConnection.RESPONSE_STRING_KEY); + + OutputStream os = dataSink.createOutputStream(MIME_TYPE, "UTF-8"); + OutputStreamWriter osw = new OutputStreamWriter(os); + osw.write(response); + osw.close(); + } + catch (IOException e) + { + throw new SignatorException(ErrorCode.SIGNATURE_COULDNT_BE_CREATED, e); + } + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/signator/textual/TextualSignatorInformation.java b/src/main/java/at/gv/egiz/pdfas/impl/signator/textual/TextualSignatorInformation.java new file mode 100644 index 0000000..c5b18ff --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/signator/textual/TextualSignatorInformation.java @@ -0,0 +1,45 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.signator.textual; + +import at.knowcenter.wag.egov.egiz.pdf.TablePos; +import at.knowcenter.wag.egov.egiz.sig.SignatureData; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.framework.signator.SignatorInformation; + +/** + * @author wprinz + * + */ +public class TextualSignatorInformation implements SignatorInformation +{ + protected PdfDataSource originalDocument = null; + + protected SignatureData signatureData = null; + + protected String profile = null; + + protected TablePos pos = null; + + public SignSignatureObject signSignatureObject = null; + + /** + * @see at.gv.egiz.pdfas.framework.signator.SignatorInformation#getSignatureData() + */ + public SignatureData getSignatureData() + { + return this.signatureData; + } + + /** + * + * @see at.gv.egiz.pdfas.framework.signator.SignatorInformation#setSignSignatureObject(at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject) + */ + public void setSignSignatureObject(SignSignatureObject signSignatureObject) + { + this.signSignatureObject = signSignatureObject; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/signator/textual/TextualSignator_1_0_0.java b/src/main/java/at/gv/egiz/pdfas/impl/signator/textual/TextualSignator_1_0_0.java new file mode 100644 index 0000000..b0494c8 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/signator/textual/TextualSignator_1_0_0.java @@ -0,0 +1,149 @@ +/** + * Copyright (c) 2006 by Know-Center, Graz, Austria + * + * This software is the confidential and proprietary information of Know-Center, + * Graz, Austria. You shall not disclose such Confidential Information and shall + * use it only in accordance with the terms of the license agreement you entered + * into with Know-Center. + * + * KNOW-CENTER MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF + * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY + * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS + * DERIVATIVES. + * + * $Id: TextualSignator_1_0_0.java,v 1.3 2006/10/31 08:07:50 wprinz Exp $ + */ +package at.gv.egiz.pdfas.impl.signator.textual; + +import at.gv.egiz.pdfas.exceptions.framework.SignatorException; +import at.gv.egiz.pdfas.framework.input.DataSource; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.framework.output.DataSink; +import at.gv.egiz.pdfas.framework.signator.Signator; +import at.gv.egiz.pdfas.framework.signator.SignatorInformation; +import at.gv.egiz.pdfas.impl.input.TextDataSourceImpl; +import at.gv.egiz.pdfas.impl.signator.IncrementalUpdateHelper; +import at.knowcenter.wag.egov.egiz.PdfAS; +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; +import at.knowcenter.wag.egov.egiz.framework.SignatorFactory; +import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; +import at.knowcenter.wag.egov.egiz.pdf.TablePos; +import at.knowcenter.wag.egov.egiz.sig.SignatureDataImpl; +import at.knowcenter.wag.egov.egiz.sig.SignatureObject; +import at.knowcenter.wag.egov.egiz.sig.signatureobject.SignatureObjectHelper; + +import com.lowagie.text.pdf.PdfPTable; + +/** + * Signs a document textually. + * + *

+ * In prepareSign, the document text is extracted and normalized. + *

+ *

+ * In finishSign, the signed SignatureObject is transformed into a Signature + * block, which is then written as an Incremental Update. + *

+ * + * @author wprinz + */ +public class TextualSignator_1_0_0 implements Signator +{ + /** + * The Pdf-AS ID of this Signator. + */ + public static final PdfASID MY_ID = new PdfASID(SignatorFactory.VENDOR, SignatorFactory.TYPE_TEXTUAL, SignatorFactory.VERSION_1_0_0); + + /** + * @see at.knowcenter.wag.egov.egiz.framework.Signator#getMyId() + */ + public PdfASID getMyId() + { + return MY_ID; + } + + /** + * Default constructor. + */ + public TextualSignator_1_0_0() + { + // Default constructor. + } + + /** + *

+ * The parameter has_SIG_ID is not used by this Signator because it doesn't + * pre-format the signature block. + *

+ * + * @see at.gv.egiz.pdfas.framework.signator.Signator#prepareSign(at.gv.egiz.pdfas.framework.input.PdfDataSource, + * java.lang.String, at.knowcenter.wag.egov.egiz.pdf.TablePos, boolean) + */ + public SignatorInformation prepareSign(PdfDataSource pdfDataSource, String profile, TablePos pos, boolean has_SIG_ID) throws SignatorException + { + try + { + TextualSignatorInformation tsi = new TextualSignatorInformation(); + tsi.originalDocument = pdfDataSource; + tsi.profile = profile; + tsi.pos = pos; + + String document_text = PdfAS.extractNormalizedTextTextual(pdfDataSource.createInputStream()); + // logger_.debug("signed_text = " + document_text); + + DataSource ds = new TextDataSourceImpl(document_text); + tsi.signatureData = new SignatureDataImpl(ds, "text/plain", "UTF-8"); + + return tsi; + } + catch (PresentableException pe) + { + throw new SignatorException(pe); + } + } + + /** + * @see at.gv.egiz.pdfas.framework.signator.Signator#finishSign(at.gv.egiz.pdfas.framework.signator.SignatorInformation, + * at.gv.egiz.pdfas.framework.output.DataSink) + */ + public void finishSign(SignatorInformation signatorInformation, DataSink dataSink) throws SignatorException + { + try + { + TextualSignatorInformation tsi = (TextualSignatorInformation) signatorInformation; + + // PdfAS.prefixID(iui.signed_signature_object, PdfAS.TEXT_ID); + + // iui.signed_signature_object.kz = getMyId().toString(); + tsi.signSignatureObject.kz = getMyId().toString(); + // TODO what is this for? + + SignatureObject so = SignatureObjectHelper.convertSignSignatureObjectToSignatureObject(tsi.signSignatureObject, tsi.profile); + + PdfPTable pdf_table = PdfAS.createPdfPTableFromSignatureObject(so); + + PositioningInstruction pi = PdfAS.determineTablePositioning(tsi.pos, tsi.profile, tsi.originalDocument, pdf_table); + + IncrementalUpdateHelper.writeIncrementalUpdateToDataSink(tsi.originalDocument, dataSink, pdf_table, pi); + +// OutputStream os = dataSink.createOutputStream(PdfAS.PDF_MIME_TYPE); +// os.write(signed_iui.signed_pdf); +// os.close(); + +// SignResult sign_result = new SignResult(PdfAS.PDF_MIME_TYPE, signed_iui.signed_pdf); +// return sign_result; + } + catch (PresentableException pe) + { + throw new SignatorException(pe); + } +// catch (IOException e) +// { +// throw new SignatorException(ErrorCode.DOCUMENT_CANNOT_BE_READ, e); +// } + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/signator/textual/TextualSignator_1_1_0.java b/src/main/java/at/gv/egiz/pdfas/impl/signator/textual/TextualSignator_1_1_0.java new file mode 100644 index 0000000..68cd3a1 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/signator/textual/TextualSignator_1_1_0.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2006 by Know-Center, Graz, Austria + * + * This software is the confidential and proprietary information of Know-Center, + * Graz, Austria. You shall not disclose such Confidential Information and shall + * use it only in accordance with the terms of the license agreement you entered + * into with Know-Center. + * + * KNOW-CENTER MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF + * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY + * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS + * DERIVATIVES. + * + * $Id: TextualSignator_1_0_0.java,v 1.3 2006/10/31 08:07:50 wprinz Exp $ + */ +package at.gv.egiz.pdfas.impl.signator.textual; + +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.framework.SignatorFactory; + +/** + * Signs a document textually. + * + * @see TextualSignator_1_0_0 + * + * @author wprinz + */ +public class TextualSignator_1_1_0 extends TextualSignator_1_0_0 +{ + /** + * The Pdf-AS ID of this Signator. + */ + public static final PdfASID MY_ID = new PdfASID(SignatorFactory.VENDOR, SignatorFactory.TYPE_TEXTUAL, SignatorFactory.VERSION_1_1_0); + + /** + * @see at.knowcenter.wag.egov.egiz.framework.Signator#getMyId() + */ + public PdfASID getMyId() + { + return MY_ID; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/verificator/binary/BinaryVerificator_1_0_0.java b/src/main/java/at/gv/egiz/pdfas/impl/verificator/binary/BinaryVerificator_1_0_0.java new file mode 100644 index 0000000..b52e97f --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/verificator/binary/BinaryVerificator_1_0_0.java @@ -0,0 +1,399 @@ +/** + * Copyright (c) 2006 by Know-Center, Graz, Austria + * + * This software is the confidential and proprietary information of Know-Center, + * Graz, Austria. You shall not disclose such Confidential Information and shall + * use it only in accordance with the terms of the license agreement you entered + * into with Know-Center. + * + * KNOW-CENTER MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF + * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY + * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS + * DERIVATIVES. + * + * $Id: BinaryVerificator_1_0_0.java,v 1.3 2006/10/11 08:03:22 wprinz Exp $ + */ +package at.gv.egiz.pdfas.impl.verificator.binary; + +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.framework.verificator.Verificator; +import at.gv.egiz.pdfas.impl.input.ByteArrayPdfDataSourceImpl; +import at.gv.egiz.pdfas.impl.input.CompoundPdfDataSourceImpl; +import at.gv.egiz.pdfas.impl.input.DelimitedPdfDataSource; +import at.gv.egiz.pdfas.impl.input.helper.DataSourceHelper; + +import org.apache.log4j.Logger; + +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.cfg.ConfigLogger; +import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; +import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; +import at.knowcenter.wag.egov.egiz.framework.SignatorFactory; +import at.knowcenter.wag.egov.egiz.framework.VerificationFilter; +import at.knowcenter.wag.egov.egiz.pdf.BinaryBlockInfo; +import at.knowcenter.wag.egov.egiz.pdf.BinarySignature; +import at.knowcenter.wag.egov.egiz.pdf.BinarySignatureHolder; +import at.knowcenter.wag.egov.egiz.pdf.Placeholder; +import at.knowcenter.wag.egov.egiz.pdf.ReplaceInfo; +import at.knowcenter.wag.egov.egiz.pdf.StringInfo; +import at.knowcenter.wag.egov.egiz.sig.SignatureObject; +import at.knowcenter.wag.egov.egiz.sig.SignatureTypes; +import at.knowcenter.wag.exactparser.parsing.IndirectObjectReference; +import at.knowcenter.wag.exactparser.parsing.PDFUtils; +import at.knowcenter.wag.exactparser.parsing.results.ArrayParseResult; +import at.knowcenter.wag.exactparser.parsing.results.DictionaryParseResult; +import at.knowcenter.wag.exactparser.parsing.results.FooterParseResult; +import at.knowcenter.wag.exactparser.parsing.results.IndirectObjectReferenceParseResult; +import at.knowcenter.wag.exactparser.parsing.results.LiteralStringParseResult; +import at.knowcenter.wag.exactparser.parsing.results.NameParseResult; +import at.knowcenter.wag.exactparser.parsing.results.NumberParseResult; +import at.knowcenter.wag.exactparser.parsing.results.ObjectParseResult; +import at.knowcenter.wag.exactparser.parsing.results.ParseResult; + +/** + * The BinaryVerificator parses the EGIT Dictionary and extracts the signature + * holder from it. + * + * @author wprinz + */ +public class BinaryVerificator_1_0_0 implements Verificator +{ + /** + * The Pdf-AS ID of this Verificator. + */ + public static final PdfASID MY_ID = new PdfASID(SignatorFactory.VENDOR, SignatorFactory.TYPE_BINARY, SignatorFactory.VERSION_1_0_0); + + /** + * Use this to override the MY_ID field. + * + * @return Returns the Id of this Verificator. + */ + protected PdfASID getMyId() + { + return MY_ID; + } + + /** + * The /ODS key in the EGIZ Dict. + */ + public static final byte[] EGIZ_ODS_NAME = new byte[] { 'O', 'D', 'S' }; + + /** + * The /ID key in the EGIZ Dict. + */ + public static final byte[] EGIZ_KZ_NAME = VerificationFilter.EGIZ_KZ_NAME; + + /** + * The /ByteRange key in the EGIZ Dict. + */ + public static final byte[] EGIZ_BYTE_RANGE_NAME = new byte[] { 'B', 'y', 't', 'e', 'R', 'a', 'n', 'g', 'e' }; + + /** + * The /replaces key in the EGIZ Dict. + */ + public static final byte[] EGIZ_REPLACES_NAME = new byte[] { 'r', 'e', 'p', 'l', 'a', 'c', 'e', 's' }; + + /** + * The /encodings key in the EGIZ Dict. + */ + public static final byte[] EGIZ_ENCODINGS_NAME = new byte[] { 'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g', 's' }; + + /** + * The /Cert key in the EGIZ Dict. + */ + public static final byte[] EGIZ_CERT_NAME = new byte[] { 'C', 'e', 'r', 't' }; + + /** + * The logger definition. + */ + private static final Logger logger_ = ConfigLogger.getLogger(BinaryVerificator_1_0_0.class); + + /** + * Default constructor. + */ + public BinaryVerificator_1_0_0() + { + // Default constructor. + } + + /** + * @see at.gv.egiz.pdfas.framework.verificator.Verificator#parseBlock(at.gv.egiz.pdfas.framework.input.PdfDataSource, + * byte[], + * at.knowcenter.wag.exactparser.parsing.results.FooterParseResult, int) + */ + public List parseBlock(PdfDataSource pdfDataSource, byte [] pdf, FooterParseResult block, int start_of_whole_block) throws PresentableException + { + // PERF: BinaryVerificator needs byte array. + + int egiz_index = PDFUtils.indexOfName(pdf, block.tpr.dpr.names, VerificationFilter.EGIZ_DICT_NAME); + if (egiz_index < 0) + { + throw new PDFDocumentException(ErrorCode.COULDNT_VERIFY, "egiz_index = " + egiz_index); + } + + IndirectObjectReferenceParseResult egiz_dict_iorpr = (IndirectObjectReferenceParseResult) block.tpr.dpr.values.get(egiz_index); + + IndirectObjectReference ior = egiz_dict_iorpr.ior; + + final int egiz_dict_offset = PDFUtils.getObjectOffsetFromXRefByIndirectObjectReference(block.xpr, ior); + + ObjectParseResult obj = PDFUtils.parseObject(pdf, egiz_dict_offset); + DictionaryParseResult egiz_dict = (DictionaryParseResult) obj.object; + + NumberParseResult ods_npr = (NumberParseResult) getRequiredValueOfKey(pdf, egiz_dict, EGIZ_ODS_NAME); + + ArrayParseResult kz_apr = (ArrayParseResult) getRequiredValueOfKey(pdf, egiz_dict, EGIZ_KZ_NAME); + PdfASID kz = null; + String kz_string = VerificationFilter.restoreKZ(pdf, kz_apr); + kz = new PdfASID(kz_string); + if (!kz_string.equals(getMyId().toString())) + { + logger_.warn("Warning: Kennzeichnung not recognized:" + kz_string); + } + + ArrayParseResult byte_ranges_apr = (ArrayParseResult) getRequiredValueOfKey(pdf, egiz_dict, EGIZ_BYTE_RANGE_NAME); + + ArrayParseResult replaces_apr = (ArrayParseResult) getRequiredValueOfKey(pdf, egiz_dict, EGIZ_REPLACES_NAME); + + ArrayParseResult encodings_apr = (ArrayParseResult) getRequiredValueOfKey(pdf, egiz_dict, EGIZ_ENCODINGS_NAME); + + ArrayParseResult cert_apr = (ArrayParseResult) getValueOfKey(pdf, egiz_dict, EGIZ_CERT_NAME); + byte[] cert = null; + if (cert_apr != null && !cert_apr.elements.isEmpty()) + { + LiteralStringParseResult lspr = (LiteralStringParseResult) cert_apr.elements.get(0); + int str_length = lspr.content_end_index - lspr.content_start_index; + byte[] encoded = new byte[str_length]; + System.arraycopy(pdf, lspr.content_start_index, encoded, 0, encoded.length); + + cert = Placeholder.unescapePDFString(encoded); + } + + int num_byte_ranges = byte_ranges_apr.elements.size() / 2; + List byte_ranges = new ArrayList(); + for (int i = 0; i < num_byte_ranges; i++) + { + NumberParseResult start_npr = (NumberParseResult) byte_ranges_apr.elements.get(2 * i); + NumberParseResult length_npr = (NumberParseResult) byte_ranges_apr.elements.get(2 * i + 1); + + StringInfo si = new StringInfo(); + si.string_start = start_npr.number; + si.string_length = length_npr.number; + byte_ranges.add(si); + } + + StringInfo sis[] = new StringInfo[num_byte_ranges - 1]; + for (int i = 0; i < num_byte_ranges - 1; i++) + { + StringInfo prev = (StringInfo) byte_ranges.get(i); + StringInfo next = (StringInfo) byte_ranges.get(i + 1); + + StringInfo hole = new StringInfo(); + hole.string_start = prev.string_start + prev.string_length; + hole.string_length = next.string_start - hole.string_start; + + sis[i] = hole; + } + + int n = replaces_apr.elements.size(); + byte[][] brevs = new byte[n][]; + for (int i = 0; i < n; i++) + { + NameParseResult lspr = (NameParseResult) replaces_apr.elements.get(i); + + byte[] brev = new byte[3]; + System.arraycopy(pdf, lspr.name_start_index, brev, 0, brev.length); + + brevs[i] = brev; // SignatureTypes.convertBrevToType(brev); + } + + n = encodings_apr.elements.size(); + byte[][] encodings = new byte[n][]; + for (int i = 0; i < n; i++) + { + NameParseResult lspr = (NameParseResult) encodings_apr.elements.get(i); + + byte[] enc = new byte[3]; + System.arraycopy(pdf, lspr.name_start_index, enc, 0, enc.length); + encodings[i] = enc; + } + + BinaryBlockInfo bbi = new BinaryBlockInfo(); + bbi.replaces = BinarySignature.reconstructReplaces(pdf, brevs, sis, encodings); + bbi.signed_size = ods_npr.number; + + // BinaryBlockInfo bbi = BinarySignature.retrieveEgizDictInformation(pdf, + // ior.object_number, ior.generation_number, egiz_dict_offset); + + // byte[] original_pdf = BinarySignature.restoreEgizDictInformation(pdf, + // bbi); + + byte[] signed_pdf = BinarySignature.prepareDataToSign(pdf, byte_ranges); + // String signed_text = + // BinarySignature.retrieveSignableTextFromData(signed_pdf, + // signed_pdf.length); // has been moved into the BinarySignatureHolder + + SignatureObject signature_object = new SignatureObject(); + String default_type = SettingsReader.getInstance().getValueFromKey(SignatureTypes.DEFAULT_TYPE); + signature_object.setSigType(default_type); + signature_object.initByType(); + + signature_object.setKZ(kz); + + if (cert != null) + { + try + { + // ByteArrayInputStream bais = new ByteArrayInputStream(cert); + // CertificateFactory cf = CertificateFactory.getInstance("X.509"); + // X509Certificate certificate = (X509Certificate) + // cf.generateCertificate(bais); + + // trim zero bytes. - the base 64 cert must not have zero bytes. + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (int i = 0; i < cert.length; i++) + { + if (cert[i] != 0) + { + baos.write(cert[i]); + } + } + byte[] b64 = baos.toByteArray(); + + signature_object.storeNewCertificateInLocalStore(b64); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + Iterator rit = bbi.replaces.iterator(); + while (rit.hasNext()) + { + ReplaceInfo ri = (ReplaceInfo) rit.next(); + + String type = SignatureTypes.convertBrevToType(ri.brev); + + // signature_object.setSigValue(ri.type, ri.value); + if (type.equals(SignatureTypes.SIG_DATE)) + { + signature_object.setSignationDate(ri.value); + continue; + } + + if (type.equals(SignatureTypes.SIG_ISSUER)) + { + signature_object.setSignationIssuer(ri.value); + continue; + } + + if (type.equals(SignatureTypes.SIG_VALUE)) + { + signature_object.setSignationValue(ri.value); + continue; + } + + if (type.equals(SignatureTypes.SIG_NUMBER)) + { + signature_object.setSignationSerialNumber(ri.value); + continue; + } + + if (type.equals(SignatureTypes.SIG_ID)) + { + signature_object.setSignationIDs(ri.value); + continue; + } + } + + int iu_length = signed_pdf.length - start_of_whole_block; + byte [] iu_block = new byte [iu_length]; + System.arraycopy(signed_pdf, start_of_whole_block, iu_block, 0, iu_length); + + DelimitedPdfDataSource dpds = new DelimitedPdfDataSource(pdfDataSource, start_of_whole_block); + PdfDataSource ds = new CompoundPdfDataSourceImpl(dpds, iu_block); + + //PdfDataSource dsByteArray = new ByteArrayPdfDataSourceImpl(signed_pdf, signed_pdf.length); + + BinarySignatureHolder signature_holder = new BinarySignatureHolder(ds, signature_object); + + List holders = new ArrayList(); + holders.add(signature_holder); + return holders; + } + + /** + * Retrieves the value of the key from the dictionary. + * + * @param pdf + * The PDF. + * @param egiz_dict + * The dictionary. + * @param name + * The name of the key. + * @return Returns the value of the key. An exception is thrown if the key + * doesn't exist. + * @throws PDFDocumentException + * Thrown, if the key doesn't exist in the dictionary. + */ + protected ParseResult getRequiredValueOfKey(byte[] pdf, DictionaryParseResult egiz_dict, byte[] name) throws PDFDocumentException + { + final int index = PDFUtils.indexOfName(pdf, egiz_dict.names, name); + checkIndex(index); + ParseResult value = (ParseResult) egiz_dict.values.get(index); + return value; + } + + /** + * Throws an excaption, if the index is lower than 0. + * + * @param name_index + * The index. + * @throws PDFDocumentException + * Thrown, if the index is lower than 0. + */ + protected void checkIndex(int name_index) throws PDFDocumentException + { + if (name_index < 0) + { + throw new PDFDocumentException(ErrorCode.COULDNT_VERIFY, "The name wasn't found in the egiz dict."); + } + } + + /** + * Retrieves the value of the key from the dictionary. + * + * @param pdf + * The PDF. + * @param egiz_dict + * The dictionary. + * @param name + * The name of the key. + * @return Returns the key's value, or null if the dictionary didn't contain + * that key. + */ + protected ParseResult getValueOfKey(byte[] pdf, DictionaryParseResult egiz_dict, byte[] name) + { + final int index = PDFUtils.indexOfName(pdf, egiz_dict.names, name); + if (index < 0) + { + return null; + } + ParseResult value = (ParseResult) egiz_dict.values.get(index); + return value; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/verificator/binary/BinaryVerificator_1_1_0.java b/src/main/java/at/gv/egiz/pdfas/impl/verificator/binary/BinaryVerificator_1_1_0.java new file mode 100644 index 0000000..a78c4a8 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/verificator/binary/BinaryVerificator_1_1_0.java @@ -0,0 +1,24 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.verificator.binary; + +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.framework.SignatorFactory; + +/** + * @author wprinz + * + */ +public class BinaryVerificator_1_1_0 extends BinaryVerificator_1_0_0 +{ + public static final PdfASID MY_ID = new PdfASID(SignatorFactory.VENDOR, SignatorFactory.TYPE_BINARY, SignatorFactory.VERSION_1_1_0); + + /** + * @see at.knowcenter.wag.egov.egiz.framework.verificators.BinaryVerificator_1_0_0#getMyId() + */ + protected PdfASID getMyId() + { + return MY_ID; + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/vfilter/Partition.java b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/Partition.java new file mode 100644 index 0000000..6fe90e5 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/Partition.java @@ -0,0 +1,9 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.vfilter; + +public interface Partition +{ + public boolean isTextPartition(); +} \ No newline at end of file diff --git a/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterImpl.java b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterImpl.java new file mode 100644 index 0000000..981b868 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterImpl.java @@ -0,0 +1,575 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.vfilter; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.pdfas.exceptions.framework.VerificationFilterException; +import at.gv.egiz.pdfas.framework.SignatureHolderHelper; +import at.gv.egiz.pdfas.framework.VerificatorFactory; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.framework.input.TextDataSource; +import at.gv.egiz.pdfas.framework.verificator.Verificator; +import at.gv.egiz.pdfas.framework.vfilter.VerificationFilter; +import at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters; +import at.gv.egiz.pdfas.impl.input.DelimitedInputStream; +import at.gv.egiz.pdfas.impl.input.helper.DataSourceHelper; +import at.gv.egiz.pdfas.impl.vfilter.helper.VerificationFilterBinaryHelper; +import at.gv.egiz.pdfas.impl.vfilter.helper.VerificationFilterHelper; +import at.gv.egiz.pdfas.impl.vfilter.partition.BinaryPartition; +import at.gv.egiz.pdfas.impl.vfilter.partition.TextPartition; +import at.knowcenter.wag.egov.egiz.PdfAS; +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.exceptions.NormalizeException; +import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; +import at.knowcenter.wag.egov.egiz.exceptions.SignatureException; +import at.knowcenter.wag.egov.egiz.exceptions.SignatureTypesException; +import at.knowcenter.wag.egov.egiz.pdf.AbsoluteTextSignature; +import at.knowcenter.wag.egov.egiz.pdf.EGIZDate; +import at.knowcenter.wag.egov.egiz.pdf.SignatureHolder; +import at.knowcenter.wag.egov.egiz.pdf.TextualSignatureHolder; +import at.knowcenter.wag.exactparser.parsing.results.FooterParseResult; + +/** + * @author wprinz + */ +public class VerificationFilterImpl implements VerificationFilter +{ + + /** + * The log. + */ + private static final Log log = LogFactory.getLog(VerificationFilterImpl.class); + + /** + * @see at.gv.egiz.pdfas.framework.vfilter.VerificationFilter#extractSignatureHolders(at.gv.egiz.pdfas.framework.input.PdfDataSource, + * java.util.List, + * at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters) + */ + public List extractSignatureHolders(final PdfDataSource pdf, List blocks, final VerificationFilterParameters parameters) throws VerificationFilterException + { + log.trace("extractSignaturHolders:"); + + if (log.isDebugEnabled()) + { + log.debug("Original IU blocks: " + blocks.size()); + debugIUBlocks(blocks); + } + + unrollLinearization(blocks); + + if (log.isDebugEnabled()) + { + log.debug("IU blocks without linearization: " + blocks.size()); + debugIUBlocks(blocks); + } + + List signatureHolderChain = null; + + if (parameters.extractBinarySignaturesOnly()) + { + log.debug("Extracting only binary signatures. Binary-only mode."); + + signatureHolderChain = performBinaryOnly(pdf, blocks); + } + else + { + List partitions = VerificationFilterHelper.partition(pdf, blocks); + if (log.isDebugEnabled()) + { + debugPartitions(partitions); + } + + if (parameters.assumeOnlySignatureUpdateBlocks()) + { + log.debug("Assuming that there are only signature Incremental Update blocks. Semi-conservative mode."); + + signatureHolderChain = performSemiConservative(pdf, parameters.scanForOldSignatures(), blocks, partitions); + } + else + { + log.debug("Scanning complete document. Conservative mode."); + + signatureHolderChain = performFullConservative(pdf, parameters.scanForOldSignatures(), blocks, partitions); + } + + } + + log.trace("extractSignaturHolders finished."); + return signatureHolderChain; + } + + /** + * @see at.gv.egiz.pdfas.framework.vfilter.VerificationFilter#extractSignaturHolders(at.gv.egiz.pdfas.framework.input.TextDataSource, + * at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters) + */ + public List extractSignaturHolders(TextDataSource text, VerificationFilterParameters parameters) throws VerificationFilterException + { + if (parameters.extractBinarySignaturesOnly()) + { + log + .warn("A free text signature extraction was issued although the VerificationFilter was configured to detect only binary signatures (binary-only mode). The result is of course that no signatures can be found."); + + return new ArrayList(); + } + + String freetext = text.getText(); + String normalizedText = normalizeText(freetext); + + List foundSignatures = null; + if (parameters.scanForOldSignatures()) + { + log.debug("Extracting old and new signatures from text."); + + foundSignatures = extractNewAndOldSignaturesFromText(normalizedText); + } + else + { + log.debug("Extracting new signatures from text (not extracting old ones)."); + + foundSignatures = extractNewSignaturesFromText(normalizedText); + } + + List textOnlySignatures = filterOutBinarySignatures(foundSignatures); + + return textOnlySignatures; + } + + protected String normalizeText(String freetext) throws VerificationFilterException + { + try + { + return PdfAS.normalizeText(freetext); + } + catch (NormalizeException e) + { + throw new VerificationFilterException(e); + } + } + + /** + * Removes the linearization footer from the list of update blocks. + * + * @param blocks + * The list of FooterParseResult objects in \prev order. + */ + protected void unrollLinearization(List blocks) + { + int linearization_index = -1; + for (int i = 0; i < blocks.size(); i++) + { + FooterParseResult bpr = (FooterParseResult) blocks.get(i); + + if (bpr.sxpr.xref_index == 0) + { + if (linearization_index >= 0) + { + throw new RuntimeException("There is more than one linearization block! index = " + i); + } + linearization_index = i; + } + } + + if (linearization_index >= 0) + { + // logger_.debug("The document is linearized - unrolling + // linearization block " + linearization_index); + blocks.remove(linearization_index); + } + } + + protected List performBinaryOnly(PdfDataSource pdf, List blocks) throws VerificationFilterException + { + return extractBinarySignaturesOnly(pdf, blocks); + } + + protected List performSemiConservative(PdfDataSource pdf, boolean scanForOldSignatures, List blocks, List partitions) throws VerificationFilterException + { + List binarySignatures = extractBinarySignaturesOnly(pdf, blocks); + + TextPartition lastTextPartition = VerificationFilterHelper.findLastTextPartition(partitions); + List extractedSignatures = null; + if (scanForOldSignatures) + { + SignaturesAndOld sao = extractSignaturesFromPartitionAndOld(pdf, lastTextPartition); + extractedSignatures = sao.newSignatures; + if (sao.oldSignature != null) + { + extractedSignatures.add(0, sao.oldSignature); + } + } + else + { + extractedSignatures = extractSignaturesFromPartition(pdf, lastTextPartition); + } + + List signatureHolderChain = intermingleSignatures(binarySignatures, extractedSignatures); + + return signatureHolderChain; + } + + protected List performFullConservative(PdfDataSource pdf, boolean scanForOldSignatures, List blocks, List partitions) throws VerificationFilterException + { + List binarySignatures = extractBinarySignaturesOnly(pdf, blocks); + + SignatureHolder oldSignature = null; + + List partitionResults = new ArrayList(partitions.size()); + for (int i = 0; i < partitions.size(); i++) + { + Partition p = (Partition) partitions.get(i); + + if (p instanceof TextPartition) + { + TextPartition tp = (TextPartition) p; + + List partitionResult = null; + + boolean scanThisPartitionForOldSignature = (i == 0) && scanForOldSignatures; + if (scanThisPartitionForOldSignature) + { + SignaturesAndOld sao = extractSignaturesFromPartitionAndOld(pdf, tp); + partitionResult = sao.newSignatures; + oldSignature = sao.oldSignature; + } + else + { + partitionResult = extractSignaturesFromPartition(pdf, tp); + } + + partitionResults.add(partitionResult); + } + } + + List extractedSignatures = new ArrayList(); + Iterator it = partitionResults.iterator(); + List prevPartitionResult = null; + while (it.hasNext()) + { + List partitionResult = (List) it.next(); + + if (prevPartitionResult == null) + { + extractedSignatures.addAll(partitionResult); + } + else + { + assert partitionResult.size() >= prevPartitionResult.size(); + + for (int i = prevPartitionResult.size(); i < partitionResult.size(); i++) + { + SignatureHolder sh = (SignatureHolder) partitionResult.get(i); + extractedSignatures.add(sh); + } + } + + prevPartitionResult = partitionResult; + } + + List signatureHolderChain = intermingleSignatures(binarySignatures, extractedSignatures); + + if (oldSignature != null) + { + signatureHolderChain.add(0, oldSignature); + } + + return signatureHolderChain; + } + + protected String extractText(PdfDataSource pdf, int endOfDocument) throws PresentableException + { + DelimitedInputStream dis = new DelimitedInputStream(pdf.createInputStream(), endOfDocument); + return PdfAS.extractNormalizedTextTextual(dis); + } + + protected List extractNewSignaturesFromText(String text) throws VerificationFilterException + { + try + { + return AbsoluteTextSignature.extractSignatureHoldersFromText(text); + } + catch (PresentableException e) + { + throw new VerificationFilterException(e); + } + } + + protected List extractNewAndOldSignaturesFromText(String text) throws VerificationFilterException + { + SignaturesAndOld sao = extractSignaturesAndOld(text); + if (sao.oldSignature != null) + { + sao.newSignatures.add(0, sao.oldSignature); + } + + return sao.newSignatures; + } + + protected List extractOldSignaturesFromText(String text) throws PresentableException + { + return PdfAS.extractSignatureHoldersTextual(text, true); + } + + protected List intermingleSignatures(List binarySignatures, List extractedSignatures) + { + List textualSignatures = filterOutBinarySignatures(extractedSignatures); + + List intermingled = new ArrayList(binarySignatures.size() + textualSignatures.size()); + intermingled.addAll(binarySignatures); + intermingled.addAll(textualSignatures); + + sortSignatures(intermingled); + + return intermingled; + } + + protected List filterOutBinarySignatures(List signatures) + { + List textOnly = new ArrayList(signatures.size()); + + Iterator it = signatures.iterator(); + while (it.hasNext()) + { + SignatureHolder sh = (SignatureHolder) it.next(); + if (sh.getSignatureObject().isTextual()) + { + textOnly.add(sh); + } + } + + return textOnly; + } + + protected void sortSignatures(List signatures) + { + SignatureHolderHelper.sortByDate(signatures); + } + + protected void debugIUBlocks(List blocks) + { + Iterator it = blocks.iterator(); + while (it.hasNext()) + { + FooterParseResult fpr = (FooterParseResult) it.next(); + log.debug("footer: " + fpr.start_index + " to " + fpr.next_index + ", has predecessor = " + fpr.tpr.has_predecessor); + } + } + + protected void debugPartitions(List partitions) + { + Iterator it = partitions.iterator(); + while (it.hasNext()) + { + Object o = it.next(); + assert o instanceof Partition; + + List blocks = null; + if (o instanceof TextPartition) + { + TextPartition tp = (TextPartition) o; + + blocks = tp.blocks; + + log.debug("text partition with " + tp.blocks.size() + " blocks:"); + } + else + { + BinaryPartition bp = (BinaryPartition) o; + + blocks = bp.blocks; + + log.debug("binary partition: with " + bp.blocks.size() + " blocks:"); + + } + debugIUBlocks(blocks); + log.debug("partition finished."); + } + } + + /** + * Extracts the binary singatures from the given PDF. + * + *

+ * IU blocks without an egiz dict are not considered. + *

+ * + * @param pdf + * @param blocks + * @return Returns the List of signature holders. + * @throws PresentableException + */ + protected List extractBinarySignaturesOnly(PdfDataSource pdf, List blocks) throws VerificationFilterException + { + try + { + // PERF: extract binary signatures needs byte array + byte[] data = DataSourceHelper.convertDataSourceToByteArray(pdf); + + List binarySignatures = new ArrayList(blocks.size()); + + Iterator it = blocks.iterator(); + int prev_end = 0; + while (it.hasNext()) + { + FooterParseResult fpr = (FooterParseResult) it.next(); + assert fpr.next_index > prev_end; + + if (VerificationFilterBinaryHelper.containsEGIZDict(data, fpr)) + { + PdfASID kz = VerificationFilterBinaryHelper.extractKZFromEGIZBlock(data, fpr); + + Verificator verificator = VerificatorFactory.createBinaryVerificator(kz); + List binary_holders = verificator.parseBlock(pdf, data, fpr, prev_end); + + binarySignatures.addAll(binary_holders); + } + + prev_end = fpr.next_index; + } + + return binarySignatures; + } + catch (PresentableException e) + { + throw new VerificationFilterException(e); + } + } + + protected List extractSignatures(PdfDataSource pdf, int endOfDocument) throws VerificationFilterException + { + try + { + log.debug("Extracting text from 0 to " + endOfDocument + " (total document size = " + pdf.getLength() + "):"); + String extractedText = extractText(pdf, endOfDocument); + log.debug("Extracting text finished."); + + log.debug("Extracting signatures:"); + List extractedSignatures = extractNewSignaturesFromText(extractedText); + log.debug("Extracting signatures finished."); + + return extractedSignatures; + } + catch (PresentableException e) + { + throw new VerificationFilterException(e); + } + } + + protected String determineRestText(List newSignatures, String extractedText) + { + if (newSignatures.isEmpty()) + { + return extractedText; + } + + // note that even if the oldest signature is a binary signature, + // the rest text is the text of this binary signature, which was extracted + // like a text signature. + TextualSignatureHolder oldestSignature = (TextualSignatureHolder) newSignatures.get(0); + return oldestSignature.getSignedText(); + } + + protected List extractSignaturesFromPartition(PdfDataSource pdf, Partition partition) throws VerificationFilterException + { + assert partition.isTextPartition(); + + int endOfDocument = VerificationFilterHelper.getEndOfPartition(partition); + return extractSignatures(pdf, endOfDocument); + } + + protected SignaturesAndOld extractSignaturesFromPartitionAndOld(PdfDataSource pdf, Partition partition) throws VerificationFilterException + { + assert partition.isTextPartition(); + + try + { + int endOfDocument = VerificationFilterHelper.getEndOfPartition(partition); + + log.debug("Extracting text from 0 to " + endOfDocument + " (total document size = " + pdf.getLength() + "):"); + String extractedText = extractText(pdf, endOfDocument); + log.debug("Extracting text finished."); + + SignaturesAndOld sao = extractSignaturesAndOld(extractedText); + + return sao; + } + catch (PresentableException e) + { + throw new VerificationFilterException(e); + } + } + + protected static class SignaturesAndOld + { + public List newSignatures = null; + + public SignatureHolder oldSignature = null; + } + + protected SignaturesAndOld extractSignaturesAndOld(String text) throws VerificationFilterException + { + try + { + log.debug("Extracting signatures:"); + List extractedSignatures = extractNewSignaturesFromText(text); + log.debug("Extracting signatures finished."); + + log.debug("Extracting old signatures:"); + SignatureHolder oldSignature = extractOldSignature(text, extractedSignatures); + log.debug("Extracting old signatures finished."); + + SignaturesAndOld sao = new SignaturesAndOld(); + sao.newSignatures = extractedSignatures; + sao.oldSignature = oldSignature; + + return sao; + } + catch (PresentableException e) + { + throw new VerificationFilterException(e); + } + } + + /** + * Extracts the old signature from the text, but only if it is older than the + * oldest signature of the new signatueres. + * + * @param extractedText + * @param newSignatures + * @return + * @throws PDFDocumentException + * @throws SignatureException + * @throws NormalizeException + * @throws SignatureTypesException + */ + protected SignatureHolder extractOldSignature(String extractedText, List newSignatures) throws PDFDocumentException, SignatureException, NormalizeException, SignatureTypesException + { + SignatureHolder oldSignature = null; + + String restText = determineRestText(newSignatures, extractedText); + + List oldSignatures = PdfAS.extractSignatureHoldersTextual(restText, true); + if (!oldSignatures.isEmpty()) + { + oldSignature = (SignatureHolder) oldSignatures.get(0); + if (!newSignatures.isEmpty()) + { + SignatureHolder oldestNewSignature = (SignatureHolder) newSignatures.get(0); + EGIZDate oldDate = EGIZDate.parseFromString(oldSignature.getSignatureObject().getSignationDate()); + EGIZDate newDate = EGIZDate.parseFromString(oldestNewSignature.getSignatureObject().getSignationDate()); + if (newDate.compareTo(oldDate) <= 0) + { + oldSignature = null; + } + } + } + return oldSignature; + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterParametersImpl.java b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterParametersImpl.java new file mode 100644 index 0000000..292e816 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterParametersImpl.java @@ -0,0 +1,67 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.vfilter; + +import java.io.Serializable; + +import at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters; + +/** + * @author wprinz + * + */ +public class VerificationFilterParametersImpl implements VerificationFilterParameters, Serializable +{ + /** + * SVUID. + */ + private static final long serialVersionUID = -7118403150485416046L; + + protected boolean extractBinarySignaturesOnly = false; + + protected boolean assumeOnlySignatureUpdateBlocks = false; + + protected boolean scanForOldSignatures = true; + + public VerificationFilterParametersImpl(boolean extractBinarySignaturesOnly, boolean assumeOnlySignatureUpdateBlocks, boolean scanForOldSignatures) + { + this.extractBinarySignaturesOnly = extractBinarySignaturesOnly; + this.assumeOnlySignatureUpdateBlocks = assumeOnlySignatureUpdateBlocks; + this.scanForOldSignatures = scanForOldSignatures; + } + + /** + * @see at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters#extractBinarySignaturesOnly() + */ + public boolean extractBinarySignaturesOnly() + { + return this.extractBinarySignaturesOnly; + } + + /** + * @see at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters#assumeOnlySignatureUpdateBlocks() + */ + public boolean assumeOnlySignatureUpdateBlocks() + { + return this.assumeOnlySignatureUpdateBlocks; + } + + + /** + * @see at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters#scanForOldSignatures() + */ + public boolean scanForOldSignatures() + { + return this.scanForOldSignatures; + } + + /** + * @see java.lang.Object#toString() + */ + // @override + public String toString() + { + return "{VerificationFilterParametersImpl: extractBinarySignaturesOnly = " + extractBinarySignaturesOnly() + ", assumeOnlySignatureUpdateBlocks = " + assumeOnlySignatureUpdateBlocks() + "}"; + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterBinaryHelper.java b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterBinaryHelper.java new file mode 100644 index 0000000..b7f36d1 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterBinaryHelper.java @@ -0,0 +1,152 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.vfilter.helper; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; + +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.exceptions.InvalidIDException; +import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; +import at.knowcenter.wag.egov.egiz.pdf.BinarySignature; +import at.knowcenter.wag.egov.egiz.pdf.Placeholder; +import at.knowcenter.wag.egov.egiz.pdf.StringInfo; +import at.knowcenter.wag.exactparser.parsing.IndirectObjectReference; +import at.knowcenter.wag.exactparser.parsing.PDFUtils; +import at.knowcenter.wag.exactparser.parsing.results.ArrayParseResult; +import at.knowcenter.wag.exactparser.parsing.results.DictionaryParseResult; +import at.knowcenter.wag.exactparser.parsing.results.FooterParseResult; +import at.knowcenter.wag.exactparser.parsing.results.IndirectObjectReferenceParseResult; +import at.knowcenter.wag.exactparser.parsing.results.NumberParseResult; +import at.knowcenter.wag.exactparser.parsing.results.ObjectParseResult; + +/** + * Contains helpful methods used by the VerificationFilter to analyze the PDF for binary signatures. + * + * @author wprinz + */ +public final class VerificationFilterBinaryHelper +{ + /** + * The name of the egiz dict key. + */ + public static final byte[] EGIZ_DICT_NAME = { 'E', 'G', 'I', 'Z', 'S', 'i', 'g', 'D', 'i', 'c', 't' }; + + /** + * The name of the ID (SIG_KZ) property in the egiz dict. + */ + public static final byte[] EGIZ_KZ_NAME = { 'I', 'D' }; + + /** + * Tells, if the given incremental update block contains a binary signature. + * + *

+ * According to definition, if a block is a binary block, it must/cannot + * contain other signatures than this one. + *

+ * + * @param block + * The incremental update block. + * @return Returns true, if this block is a binary signature block, false + * otherwise. + */ + public static boolean containsEGIZDict(final byte[] pdf, final FooterParseResult block) + { + int dict_index = PDFUtils.indexOfName(pdf, block.tpr.dpr.names, EGIZ_DICT_NAME); + if (dict_index <= 0) + { + return false; + } + + return true; + } + + /** + * Extracts the PDF AS ID of the egiz block. + * + * @param pdf + * The pdf. + * @param block + * The IU block. + * @return Returns the extracted PDF AS ID. + * @throws PDFDocumentException + * Forwarded exception. + * @throws InvalidIDException + * Forwarded exception. + */ + public static PdfASID extractKZFromEGIZBlock(final byte[] pdf, final FooterParseResult block) throws PDFDocumentException, InvalidIDException + { + int egiz_index = PDFUtils.indexOfName(pdf, block.tpr.dpr.names, EGIZ_DICT_NAME); + if (egiz_index < 0) + { + throw new PDFDocumentException(301, "egiz_index = " + egiz_index); + } + + IndirectObjectReferenceParseResult egiz_dict_iorpr = (IndirectObjectReferenceParseResult) block.tpr.dpr.values.get(egiz_index); + // logger_.debug("egiz_dict_ir = " + egiz_dict_iorpr.ior.object_number + // + " " + egiz_dict_iorpr.ior.generation_number); + + IndirectObjectReference ior = egiz_dict_iorpr.ior; + + final int egiz_dict_offset = PDFUtils.getObjectOffsetFromXRefByIndirectObjectReference(block.xpr, ior); + // logger_.debug("egiz_dict_offset = " + egiz_dict_offset); + + ObjectParseResult obj = PDFUtils.parseObject(pdf, egiz_dict_offset); + DictionaryParseResult egiz_dict = (DictionaryParseResult) obj.object; + + int kz_index = PDFUtils.indexOfName(pdf, egiz_dict.names, EGIZ_KZ_NAME); + if (kz_index < 0) + { + throw new PDFDocumentException(301, "kz_index = " + kz_index); + } + ArrayParseResult kz_apr = (ArrayParseResult) egiz_dict.values.get(kz_index); + + String kz_string = restoreKZ(pdf, kz_apr); + PdfASID kz = new PdfASID(kz_string); + + return kz; + } + + /** + * Restores the Kennzeichnung String from an Array. + * + * @param pdf + * The PDF. + * @param kz_apr + * The Array, as parsed from the EGIZ Dict. + * @return Returns the restored KZ. + * @throws PDFDocumentException + * Forwarded exception. + */ + public static String restoreKZ(byte[] pdf, ArrayParseResult kz_apr) throws PDFDocumentException + { + try + { + List partition = new ArrayList(); + + for (int i = 0; i < kz_apr.elements.size() / 2; i++) + { + NumberParseResult start_npr = (NumberParseResult) kz_apr.elements.get(i * 2); + NumberParseResult length_npr = (NumberParseResult) kz_apr.elements.get(i * 2 + 1); + + StringInfo si = new StringInfo(); + si.string_start = start_npr.number; + si.string_length = length_npr.number; + + partition.add(si); + } + + String KZ = Placeholder.reconstructStringFromPartition(pdf, partition, BinarySignature.ENCODING_WIN); + return KZ; + } + catch (IOException e1) + { + throw new PDFDocumentException(ErrorCode.DOCUMENT_CANNOT_BE_READ, e1); + } + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterHelper.java b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterHelper.java new file mode 100644 index 0000000..67af129 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterHelper.java @@ -0,0 +1,142 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.vfilter.helper; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import at.gv.egiz.pdfas.exceptions.framework.VerificationFilterException; +import at.gv.egiz.pdfas.impl.input.helper.DataSourceHelper; +import at.gv.egiz.pdfas.impl.vfilter.Partition; +import at.gv.egiz.pdfas.impl.vfilter.partition.BinaryPartition; +import at.gv.egiz.pdfas.impl.vfilter.partition.TextPartition; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.knowcenter.wag.exactparser.parsing.results.FooterParseResult; + +/** + * Contains helpful methods used by the VerificationFilter. + * + * @author wprinz + */ +public final class VerificationFilterHelper +{ + /** + * Partitions the list of Incremental Update blocks into text and binary + * partitions. + * + *

+ * A partition is a sequence of Incremental Update blocks of the same type. + *

+ *

+ * An Incremental Update block is considered to have the type "binary" if it + * contains an egiz dictionary. A block not containing an egiz dictionary is + * considert to have the type "text". + *

+ * + * @param pdf + * The PDF. + * @param blocks + * The Incremental Update blocks. + * @return Returns the partitioning of the blocks. + * @throws VerificationFilterException + * Thrown if something goes wrong. + */ + public static List partition(PdfDataSource pdf, List blocks) throws VerificationFilterException + { + List partitions = new ArrayList(blocks.size()); + + Iterator it = blocks.iterator(); + while (it.hasNext()) + { + FooterParseResult fpr = (FooterParseResult) it.next(); + + byte[] data = DataSourceHelper.convertDataSourceToByteArray(pdf); + if (VerificationFilterBinaryHelper.containsEGIZDict(data, fpr)) + { + BinaryPartition bp = null; + if (partitions.isEmpty() || ((Partition) partitions.get(partitions.size() - 1)).isTextPartition()) + { + bp = new BinaryPartition(); + bp.blocks = new ArrayList(blocks.size()); + partitions.add(bp); + } + else + { + bp = (BinaryPartition) partitions.get(partitions.size() - 1); + } + assert bp != null; + + bp.blocks.add(fpr); + } + else + { + TextPartition tp = null; + if (partitions.isEmpty() || !((Partition) partitions.get(partitions.size() - 1)).isTextPartition()) + { + tp = new TextPartition(); + tp.blocks = new ArrayList(blocks.size()); + partitions.add(tp); + } + else + { + tp = (TextPartition) partitions.get(partitions.size() - 1); + } + assert tp != null; + + tp.blocks.add(fpr); + } + } + + assert partitions.size() >= 1 : "There must be at least one partition"; + + return partitions; + } + + /** + * Determines the end of the given partiton. + * + * @param partition + * The partition. + * @return Returns the end index of the given partition. + */ + public static int getEndOfPartition(Partition partition) + { + List blocks = null; + if (partition instanceof TextPartition) + { + blocks = ((TextPartition) partition).blocks; + } + else + { + blocks = ((BinaryPartition) partition).blocks; + } + + return ((FooterParseResult) blocks.get(blocks.size() - 1)).next_index; + } + + /** + * Finds the last text partition in the given list of partitions. + * + * @param partitions + * The partitions. + * @return Returns the last TextPartition. + */ + public static TextPartition findLastTextPartition(List partitions) + { + Partition lastTextPartition = (Partition) partitions.get(partitions.size() - 1); + + if (!lastTextPartition.isTextPartition()) + { + assert partitions.size() > 1 : "The only one partition cannot be a binary partition - where is the original document?"; + Partition previousToLastPartition = (Partition) partitions.get(partitions.size() - 2); + assert previousToLastPartition.isTextPartition() : "The previous to last partition must be a text partition or something is wrong with the partitioning algorithm."; + + lastTextPartition = previousToLastPartition; + } + + return (TextPartition) lastTextPartition; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterTextHelper.java b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterTextHelper.java new file mode 100644 index 0000000..f9a79b0 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterTextHelper.java @@ -0,0 +1,15 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.vfilter.helper; + +/** + * Contains helpful methods used by the VerificationFilter to analyze text and + * find text signatures. + * + * @author wprinz + */ +public final class VerificationFilterTextHelper +{ + +} diff --git a/src/main/java/at/gv/egiz/pdfas/impl/vfilter/partition/BinaryPartition.java b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/partition/BinaryPartition.java new file mode 100644 index 0000000..520f3b1 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/partition/BinaryPartition.java @@ -0,0 +1,19 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.vfilter.partition; + +import java.util.List; + +import at.gv.egiz.pdfas.impl.vfilter.Partition; + + +public class BinaryPartition implements Partition +{ + public List blocks = null; + + public boolean isTextPartition() + { + return false; + } +} \ No newline at end of file diff --git a/src/main/java/at/gv/egiz/pdfas/impl/vfilter/partition/TextPartition.java b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/partition/TextPartition.java new file mode 100644 index 0000000..bf17633 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/partition/TextPartition.java @@ -0,0 +1,20 @@ +/** + * + */ +package at.gv.egiz.pdfas.impl.vfilter.partition; + +import java.util.List; + +import at.gv.egiz.pdfas.impl.vfilter.Partition; + + +public class TextPartition implements Partition +{ + public List blocks = null; + + public boolean isTextPartition() + { + return true; + } + +} \ No newline at end of file diff --git a/src/main/java/at/gv/egiz/pdfas/performance/PerformanceCounter.java b/src/main/java/at/gv/egiz/pdfas/performance/PerformanceCounter.java new file mode 100644 index 0000000..2d9b461 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/performance/PerformanceCounter.java @@ -0,0 +1,62 @@ +/** + * + */ +package at.gv.egiz.pdfas.performance; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author wprinz + */ +public class PerformanceCounter +{ + /** + * The log. + */ + private static Log log = LogFactory.getLog(PerformanceCounter.class); + + protected String name = null; + + protected long counter = 0; + + public PerformanceCounter(String name) + { + this.name = name; + reset(); + } + + public PerformanceCounter(Class clazz) + { + this(clazz.getName()); + } + + public void increment() + { + this.counter++; + log.trace(this.name + ": incremented to " + this.counter); + } + + public void reset() + { + this.counter = 0; + log.trace(this.name + ": reset to 0"); + } + + /** + * @return the name + */ + public String getName() + { + return this.name; + } + + /** + * @return the counter + */ + public long getCounter() + { + return this.counter; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/performance/PerformanceCounters.java b/src/main/java/at/gv/egiz/pdfas/performance/PerformanceCounters.java new file mode 100644 index 0000000..6251c55 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/performance/PerformanceCounters.java @@ -0,0 +1,22 @@ +/** + * + */ +package at.gv.egiz.pdfas.performance; + +/** + * Contains various global PerformanceCounters that provide information about the system. + * + * @author wprinz + */ +public final class PerformanceCounters +{ + /** + * Keeps track of the number of text extractions done so far. + */ + public static PerformanceCounter textExtractions = new PerformanceCounter("TextExtractions"); + + /** + * Keeps track of the number of large byte array allocations. + */ + public static PerformanceCounter byteArrays = new PerformanceCounter("ByteArrays"); +} diff --git a/src/main/java/at/gv/egiz/pdfas/performance/PerformanceTimer.java b/src/main/java/at/gv/egiz/pdfas/performance/PerformanceTimer.java new file mode 100644 index 0000000..6ebb9be --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/performance/PerformanceTimer.java @@ -0,0 +1,48 @@ +/** + * + */ +package at.gv.egiz.pdfas.performance; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author wprinz + * + */ +public class PerformanceTimer +{ + /** + * The log. + */ + private static Log log = LogFactory.getLog(PerformanceTimer.class); + + protected String name = null; + + protected long startTime = -1; + + protected long stopTime = -1; + + public PerformanceTimer(String name) + { + this.name = name; + } + + public void start() + { + this.startTime = System.currentTimeMillis(); + log.trace(this.name + ": started at " + this.startTime); + } + + public void stop() + { + this.stopTime = System.currentTimeMillis(); + log.trace(this.name + ": stopped at " + this.stopTime); + log.trace(this.name + ": time elapsed = " + getTimeElapsed()); + } + + public long getTimeElapsed() + { + return this.stopTime - this.startTime; + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/utils/DataHashUtils.java b/src/main/java/at/gv/egiz/pdfas/utils/DataHashUtils.java new file mode 100644 index 0000000..19d7b33 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/utils/DataHashUtils.java @@ -0,0 +1,136 @@ +/** + * + */ +package at.gv.egiz.pdfas.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.apache.commons.codec.binary.Base64; + +import at.knowcenter.wag.egov.egiz.pdf.BinarySignatureHolder; +import at.knowcenter.wag.egov.egiz.pdf.SignatureHolder; +import at.knowcenter.wag.egov.egiz.pdf.TextualSignatureHolder; + +/** + * Contains helpful methods for building data hashed. + * + *

+ * Data hashes are useful for summarizing the signed data of signatures for + * debugging and testing purposes. Do not use these hashes for signatures. + *

+ *

+ * A data hash is always a Base64 encoded String. + *

+ * + * @author wprinz + * + */ +public final class DataHashUtils +{ + + /** + * + * @param text + * @return + */ + public static String buildDataHash(String text) + { + try + { + MessageDigest md = getMessageDigest(); + // probable performance leak for very large texts + md.update(text.getBytes("UTF-8")); + byte[] rawDigest = md.digest(); + + return encodeDigest(rawDigest); + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException(e); + } + } + + public static String buildDataHash(byte[] data) + { + MessageDigest md = getMessageDigest(); + md.update(data); + byte[] rawDigest = md.digest(); + + return encodeDigest(rawDigest); + } + + public static String buildDataHash(InputStream is) + { + try + { + MessageDigest md = getMessageDigest(); + + DigestInputStream dis = new DigestInputStream(is, md); + + byte[] temp = new byte[1024]; + int i = 0; + while (dis.read(temp) >= 0) + { + // this just keeps the compiler from optimizing this loop away + i++; + } + dis.close(); + + byte[] rawDigest = md.digest(); + + return encodeDigest(rawDigest); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + public static String buildDataHash(SignatureHolder sh) + { + if (sh instanceof TextualSignatureHolder) + { + TextualSignatureHolder tsh = (TextualSignatureHolder) sh; + String signedText = tsh.getSignedText(); + return buildDataHash(signedText); + } + + { + BinarySignatureHolder bsh = (BinarySignatureHolder) sh; + InputStream is = bsh.getSignedPdf().createInputStream(); + return buildDataHash(is); + } + } + + protected static MessageDigest getMessageDigest() + { + try + { + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + return sha1; + } + catch (NoSuchAlgorithmException e) + { + throw new RuntimeException(e); + } + } + + protected static String encodeDigest(byte[] rawDigest) + { + try + { + byte[] encoded = Base64.encodeBase64(rawDigest); + String str = new String(encoded, "US-ASCII"); + return str; + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/utils/StreamUtils.java b/src/main/java/at/gv/egiz/pdfas/utils/StreamUtils.java new file mode 100644 index 0000000..acf7e75 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/utils/StreamUtils.java @@ -0,0 +1,42 @@ +package at.gv.egiz.pdfas.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class StreamUtils +{ + + public static void writeInputStreamToOutputStream (InputStream is, OutputStream os) throws IOException + { + byte [] buffer = new byte [2048]; + + int read = -1; + while ((read = is.read(buffer)) > 0) + { + os.write(buffer, 0, read); + } + is.close(); + os.close(); + } + + // The InputStream should be self- delimited. +// public static void writeInputStreamToOutputStream(InputStream is, OutputStream os, int length) throws IOException +// { +// byte [] buffer = new byte [2048]; +// +// int bytes_to_write = length; +// +// int read = -1; +// while ((read = is.read(buffer, 0, (bytes_to_write >= buffer.length) ? buffer.length : bytes_to_write)) > 0) +// { +// os.write(buffer, 0, read); +// bytes_to_write -= read; +// } +// is.close(); +// os.close(); +// +// assert bytes_to_write == 0; +// } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/web/CurrentLocalOperation.java b/src/main/java/at/gv/egiz/pdfas/web/CurrentLocalOperation.java new file mode 100644 index 0000000..83d214c --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/web/CurrentLocalOperation.java @@ -0,0 +1,87 @@ +/** + * + */ +package at.gv.egiz.pdfas.web; + +import java.util.List; +import java.util.Properties; + +import at.knowcenter.wag.egov.egiz.pdf.SignatureHolder; +import at.knowcenter.wag.egov.egiz.web.LocalRequest; + +/** + * Encapsulates a local operation. + * + *

+ * A local operation is a sequence of successive local verifications. + *

+ *

+ * This is held on the VerifySessionInformation so that the DataURLServlet and RetrieveSignatureDataServlet + * can provide their data to the local service easily. + *

+ * + * @author wprinz + */ +public class CurrentLocalOperation +{ + + /** + * The signature holders to be verified in the current local operation. + */ + public List holders_to_be_verified; + + + /** + * An array of local requests to be processed. + */ + public LocalRequest[] requests = null; + + /** + * An array of response strings of the local requests. + */ + public Properties[] response_properties = null; + + /** + * The index of the holder/request to be processed. + */ + public int current_operation = 0; + +// /** +// * Tells, if the current local request has been finished. +// */ +// public boolean finished = false; + + /** + * @see at.gv.egiz.pdfas.web.LocalOperation#isFinished() + */ + public boolean isFinished() + { + return this.current_operation >= this.requests.length; + } + + /** + * @see at.gv.egiz.pdfas.web.LocalOperation#getCurrentLocalRequest() + */ + public LocalRequest getCurrentLocalRequest() + { + return this.requests[this.current_operation]; + } + + /** + * @see at.gv.egiz.pdfas.web.LocalOperation#getCurrentSignatureHolder() + */ + public SignatureHolder getCurrentSignatureHolder() + { + return (SignatureHolder) this.holders_to_be_verified.get(this.current_operation); + } + + /** + * @see at.gv.egiz.pdfas.web.LocalOperation#finishCurrentOperation(java.util.Properties) + */ + public void finishCurrentOperation(Properties response_properties) + { + this.response_properties[this.current_operation] = response_properties; + + this.current_operation++; + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/web/SignSessionInformation.java b/src/main/java/at/gv/egiz/pdfas/web/SignSessionInformation.java new file mode 100644 index 0000000..459a104 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/web/SignSessionInformation.java @@ -0,0 +1,140 @@ +/** + * + */ +package at.gv.egiz.pdfas.web; + +import java.io.Serializable; +import java.util.Properties; + +import javax.servlet.http.HttpSessionBindingEvent; +import javax.servlet.http.HttpSessionBindingListener; + +import at.gv.egiz.pdfas.impl.input.FileBasedPdfDataSourceImpl; +import at.gv.egiz.pdfas.impl.output.FileBasedDataSink; +import at.gv.egiz.pdfas.web.helper.TempDirHelper; +import at.gv.egiz.pdfas.framework.signator.SignatorInformation; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.knowcenter.wag.egov.egiz.pdf.TablePos; +import at.knowcenter.wag.egov.egiz.web.ExternAppInformation; +import at.knowcenter.wag.egov.egiz.web.LocalRequest; + +/** + * @author wprinz + * + */ +public class SignSessionInformation implements HttpSessionBindingListener, Serializable +{ + /** + * SVUID. + */ + private static final long serialVersionUID = 2739944460007369626L; + + /** + * The log. + */ + private static Log log = LogFactory.getLog(SignSessionInformation.class); + + /** + * The connector. + */ + public String connector = null; + + /** + * For local requests, tells the application (sign, verify). + */ + public String application = null; + + /** + * Tells the operation mode (binary, textual). + */ + public String mode = null; + + /** + * The original, uploaded pdf. + */ + public FileBasedPdfDataSourceImpl pdfDataSource = null; + + /** + * The type/profile of the signature. + */ + public String type = null; + + /** + * The suggested filename. + */ + public String filename; + + /** + * Tells, if the file download should be done inline or as attachment. + */ + public boolean download_inline; + + /** + * Object containing information about the calling webapplication. + * + * @author: Thomas Zefferer + */ + public ExternAppInformation exappinf; + + /** + * Information about the signature position + * + * @author: Thomas Zefferer + */ + public TablePos pos; + + /** + * The SignatorInformation. + */ + public SignatorInformation si = null; + + /** + * The DataSink to write the output data to. + */ + public FileBasedDataSink output = null; + + /** + * The local request to be sent to the device. + */ + public LocalRequest localRequest = null; + + /** + * The response properties of the local request. + */ + public Properties response_properties = null; + + /** + * Tells if the sign request has been processed and the signed document is + * available in the DataSink. + */ + public boolean outputAvailable = false; + + + /** + * @see javax.servlet.http.HttpSessionBindingListener#valueBound(javax.servlet.http.HttpSessionBindingEvent) + */ + public void valueBound(HttpSessionBindingEvent event) + { + log.debug("Bound SignSessionInformation to session."); + } + + /** + * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(javax.servlet.http.HttpSessionBindingEvent) + */ + public void valueUnbound(HttpSessionBindingEvent event) + { + log.debug("Unbound SignSessionInformation from session."); + + if (this.pdfDataSource != null) + { + TempDirHelper.deleteDataSourceIfFileBased(this.pdfDataSource); + } + if (this.output != null) + { + TempDirHelper.deleteDataSinkIfFileBased(this.output); + } + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/web/VerifySessionInformation.java b/src/main/java/at/gv/egiz/pdfas/web/VerifySessionInformation.java new file mode 100644 index 0000000..e998ded --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/web/VerifySessionInformation.java @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2006 by Know-Center, Graz, Austria + * + * This software is the confidential and proprietary information of Know-Center, + * Graz, Austria. You shall not disclose such Confidential Information and shall + * use it only in accordance with the terms of the license agreement you entered + * into with Know-Center. + * + * KNOW-CENTER MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF + * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY + * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS + * DERIVATIVES. + * + * $Id: SessionInformation.java,v 1.2 2006/08/25 17:06:11 wprinz Exp $ + */ +package at.gv.egiz.pdfas.web; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.http.HttpSessionBindingEvent; +import javax.servlet.http.HttpSessionBindingListener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.pdfas.framework.input.DataSource; +import at.gv.egiz.pdfas.web.helper.TempDirHelper; +import at.knowcenter.wag.egov.egiz.pdf.SignatureHolder; +import at.knowcenter.wag.egov.egiz.pdf.TablePos; +import at.knowcenter.wag.egov.egiz.web.ExternAppInformation; + +/** + * This class is a collection of various session parameters that are passed + * between the servlets and jsps. + * + *

+ * The SessionInformation class contains type safe references to the objects. + *

+ * + * @author wprinz + */ +public class VerifySessionInformation implements HttpSessionBindingListener, Serializable +{ + + /** + * SVUID. + */ + private static final long serialVersionUID = -7413884936584659150L; + + /** + * The log. + */ + private static Log log = LogFactory.getLog(VerifySessionInformation.class); + + /** + * The connector. + */ + public String connector = null; + + /** + * For local requests, tells the application (sign, verify). + */ + public String application = null; + + /** + * Tells the operation mode (binary, textual). + */ + public String mode = null; + + /** + * The original, uploaded pdf. + */ + //public FileBasedPdfDataSourceImpl pdfDataSource = null; + public DataSource inputDataSource = null; + + /** + * The type/profile of the signature. + */ + public String type = null; + +// /** +// * The user name. +// */ +// public String user_name = null; +// +// /** +// * The password. +// */ +// public String user_password = null; + + /** + * All SignatureHolders extracted from the document. + */ + public List signature_holders; + + /** + * Keeps track of the currently running local operation. + * + *

+ * Only valid during local verify. + *

+ */ + public CurrentLocalOperation currentLocalOperation = null; + + /** + * This is used only for MOA loc-ref web verify. + */ + public SignatureHolder moa_holder; + + +// /** +// * The incremental update information that has been extracted from the given +// * PDF document. +// */ +// public IncrementalUpdateInformation iui; + +// public SignatorInformation si = null; + +// public FileBasedDataSink output = null; + + +// /** +// * Copy of signature holders. It's needed by BKU when we try to verify single by single +// * signature. +// */ +// public List copy_of_signature_holders; + +// /** +// * The suggested filename. +// */ +// public String filename; +// +// /** +// * Tells, if the file download should be done inline or as attachment. +// */ +// public boolean download_inline; + +//// /** +//// * The sign result to be passed back to the user. +//// */ +//// public SignResult sign_result; +// +// public boolean isSignFinished = false; + + + + /** + * Object containing information about the calling webapplication. + * @author: Thomas Zefferer + */ + public ExternAppInformation exappinf; + + /** + * Information about the signature position + * @author: Thomas Zefferer + */ + public TablePos pos ; + + + + /** + * @see javax.servlet.http.HttpSessionBindingListener#valueBound(javax.servlet.http.HttpSessionBindingEvent) + */ + public void valueBound(HttpSessionBindingEvent event) + { + log.debug("Bound SignSessionInformation to session."); + } + + /** + * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(javax.servlet.http.HttpSessionBindingEvent) + */ + public void valueUnbound(HttpSessionBindingEvent event) + { + log.debug("Unbound SignSessionInformation from session."); + + if (this.inputDataSource != null) + { + TempDirHelper.deleteDataSourceIfFileBased(this.inputDataSource); + } + if (this.signature_holders != null) + { + Iterator it = this.signature_holders.iterator(); + while (it.hasNext()) + { + SignatureHolder sh = (SignatureHolder) it.next(); + TempDirHelper.deleteDataSourceIfFileBased(sh.getDataSource()); + } + } + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/web/helper/SessionHelper.java b/src/main/java/at/gv/egiz/pdfas/web/helper/SessionHelper.java new file mode 100644 index 0000000..5752838 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/web/helper/SessionHelper.java @@ -0,0 +1,48 @@ +/** + * + */ +package at.gv.egiz.pdfas.web.helper; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.pdfas.exceptions.web.SessionExpiredException; +import at.knowcenter.wag.egov.egiz.web.SessionAttributes; + +/** + * @author wprinz + * + */ +public class SessionHelper +{ + /** + * The log. + */ + private static Log log = LogFactory.getLog(SessionHelper.class); + + public static Object getSession(HttpServletRequest request) throws SessionExpiredException + { + + HttpSession session = request.getSession(false); + if (session == null) + { + String msg = "There is no session associated with this request."; //$NON-NLS-1$ + log.error(msg); + throw new SessionExpiredException(msg); + } + + Object sessionObject = session.getAttribute(SessionAttributes.ATTRIBUTE_SESSION_INFORMATION); + if (sessionObject == null) + { + String msg = "The session is not found or no longer valid."; //$NON-NLS-1$ + log.error(msg); + throw new SessionExpiredException(msg); + } + + return sessionObject; + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/web/helper/SignServletHelper.java b/src/main/java/at/gv/egiz/pdfas/web/helper/SignServletHelper.java new file mode 100644 index 0000000..55e0051 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/web/helper/SignServletHelper.java @@ -0,0 +1,229 @@ +/** + * + */ +package at.gv.egiz.pdfas.web.helper; + +import java.io.IOException; +import java.net.URL; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.pdfas.framework.SignatorFactory; +import at.gv.egiz.pdfas.framework.signator.Signator; +import at.gv.egiz.pdfas.web.SignSessionInformation; +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; +import at.knowcenter.wag.egov.egiz.framework.signators.DetachedSignator_1_0_0; +import at.knowcenter.wag.egov.egiz.sig.ConnectorFactory; +import at.knowcenter.wag.egov.egiz.sig.connectors.Connector; +import at.knowcenter.wag.egov.egiz.sig.connectors.ConnectorChooser; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject; +import at.knowcenter.wag.egov.egiz.web.FormFields; +import at.knowcenter.wag.egov.egiz.web.LocalRequestHelper; + +/** + * @author wprinz + * + */ +public class SignServletHelper +{ + /** + * The log. + */ + private static Log log = LogFactory.getLog(SignServletHelper.class); + + 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); + } + + + /** + * Prepares the sign. + * + *

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

+ * + * @param si + * The SessionInformation to be prepared. + * @throws PresentableException + * f.e. + */ + public static void prepareSign(SignSessionInformation si) throws PresentableException + { + log.debug("prepareSign:"); //$NON-NLS-1$ + + 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, ConnectorFactory.needsSIG_ID(si.connector)); + // end modify + log.debug("prepareSign finished."); //$NON-NLS-1$ + } + + /** + * 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 PresentableException + * f.e. + * @throws IOException + * f. e. + * @throws ServletException + * f. e. + */ + public static void finishSign(SignSessionInformation si, HttpServletRequest request, HttpServletResponse response, ServletContext context) throws PresentableException, IOException, ServletException + { + log.debug("finishSign:"); //$NON-NLS-1$ + + log.debug("connector = " + si.connector); //$NON-NLS-1$ + if (ConnectorFactory.isConnectorLocal(si.connector)) + { + log.debug("Connector is local -> dispatching to local processing."); //$NON-NLS-1$ + + String dispatch_to = LocalRequestHelper.processLocalSign(si, request, response); + dispatch(request, response, dispatch_to, context); + return; + } + log.debug("Connector is not local -> finishing the sign."); //$NON-NLS-1$ + + 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(); + URL signature_data_URL = new URL(request.getScheme(), host, request.getServerPort(), request.getContextPath() + "/RetrieveSignatureData"); + 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, response); + + log.debug("finishSign finished."); //$NON-NLS-1$ + } + + /** + * Returns the data in the SignResult with proper content disposition. + * + * @param si + * SessionInformation. + * @param response + * The servlet response. + * @throws IOException + * The IO Exception. + */ + public static void returnSignResponse(SignSessionInformation si, 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 + "\""); + } + + TempDirHelper.writeDataSinkToHttpResponse(si.output, response); + //response.getOutputStream().write(sign_result.getData()); + + // tzefferer: added else-block + } + else + { + // TODO @tzefferer: what is this code? + throw new RuntimeException("This has to be refactored."); +// SignResult sr = si.sign_result; +// byte[] signed_pdf = sr.getData(); +// PDFContainer entry = new PDFContainer(signed_pdf, si.exappinf.pdf_id); +// 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; +// +// // build URL +// int ind = invoke_url.indexOf("?"); +// String query = invoke_url.substring(0, ind) + ";jsessionid=" + session_id + invoke_url.substring(ind) + "&" + FormFields.FIELD_PDF_URL + "=" + providePDFServlet + "&" + FormFields.FIELD_PDF_ID +// + "=" + pdf_id + "&" + FormFields.FIELD_FILE_LENGTH + "=" + signed_pdf.length; +// +// response.sendRedirect(query); + + } + + } + + /** + * 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(DetachedSignator_1_0_0.MIME_TYPE)) + { + output += ".xml"; + } + else + { + output += ".pdf"; + } + + return output; + } +} diff --git a/src/main/java/at/gv/egiz/pdfas/web/helper/TempDirHelper.java b/src/main/java/at/gv/egiz/pdfas/web/helper/TempDirHelper.java new file mode 100644 index 0000000..9f2b6fb --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/web/helper/TempDirHelper.java @@ -0,0 +1,242 @@ +/** + * + */ +package at.gv.egiz.pdfas.web.helper; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.pdfas.framework.input.DataSource; +import at.gv.egiz.pdfas.framework.input.TextDataSource; +import at.gv.egiz.pdfas.framework.output.DataSink; +import at.gv.egiz.pdfas.impl.input.FileBased; +import at.gv.egiz.pdfas.impl.input.FileBasedPdfDataSourceImpl; +import at.gv.egiz.pdfas.impl.input.FileBasedTextDataSourceImpl; +import at.gv.egiz.pdfas.impl.input.TextDataSourceImpl; +import at.gv.egiz.pdfas.impl.output.FileBasedDataSink; +import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; +import at.knowcenter.wag.egov.egiz.pdf.SignatureHolder; +import at.knowcenter.wag.egov.egiz.pdf.TextualSignatureHolder; + +/** + * @author wprinz + * + */ +public class TempDirHelper +{ + /** + * The log. + */ + private static Log log = LogFactory.getLog(TempDirHelper.class); + + protected static long runningIndex = 0; + + public static void storeTextSignatureHoldersIfApplicable(List shs, String fileNameSuffix) throws IOException + { + Iterator it = shs.iterator(); + while (it.hasNext()) + { + SignatureHolder sh = (SignatureHolder) it.next(); + if (sh instanceof TextualSignatureHolder) + { + TextualSignatureHolder tsh = (TextualSignatureHolder) sh; + if (!(tsh.getDataSource() instanceof FileBased)) + { + TextDataSource tds = (TextDataSource) tsh.getDataSource(); + if (isReasonableToStore(tds.getText().length())) + { + TextDataSource fbtds = placeTextIntoTempDir(tds.getText(), fileNameSuffix); + tsh.exchangeDataSource(fbtds); + } + } + } + } + } + + /** + * Places the text into the temp dir if reasonable. + * + *

+ * Reasonable means that the text is longer than a certain threshold. + * Otherwise a short text is simply held in memory. + *

+ * + * @param text + * The text to be stored. + * @param fileNameSuffix + * A file name suffix so that the temp file gets a more "readable" + * name. + * @return Returns the TextDataSource. + * @throws IOException + * F.e. + */ + public static TextDataSource placeTextIntoTempDir(String text, String fileNameSuffix) throws IOException + { + if (isReasonableToStore(text.length())) + { + String fileName = formatFileName(fileNameSuffix); + + File tmpFile = createTempFileInDir(fileName); + + FileOutputStream fos = new FileOutputStream(tmpFile); + OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8"); + osw.write(text); + osw.close(); + + FileBasedTextDataSourceImpl textDataSource = new FileBasedTextDataSourceImpl(tmpFile, "UTF-8"); + return textDataSource; + } + else + { + return new TextDataSourceImpl(text); + } + } + + /** + * Tells, if it is reasonable to store the text of the given length onto the + * disk. + * + * @param textLength + * The length of the text under question. + * @return Returns true if the text should be stored on the disk. + */ + public static boolean isReasonableToStore(int textLength) + { + return textLength >= 10000; + } + + public static FileBasedPdfDataSourceImpl placePdfIntoTempDir(InputStream pdfInput, String fileNameSuffix) throws IOException + { + File pdfFile = placeInputIntoTempDirFile(pdfInput, fileNameSuffix); + + FileBasedPdfDataSourceImpl pdfDataSource = new FileBasedPdfDataSourceImpl(pdfFile, (int) pdfFile.length()); + return pdfDataSource; + } + + protected static File placeInputIntoTempDirFile(InputStream input, String fileNameSuffix) throws IOException + { + String fileName = formatFileName(fileNameSuffix); + + File tmpFile = createTempFileInDir(fileName); + + FileOutputStream fos = new FileOutputStream(tmpFile); + + byte[] buffer = new byte[2048]; + int read = -1; + while ((read = input.read(buffer)) > 0) + { + fos.write(buffer, 0, read); + } + fos.close(); + input.close(); + + return tmpFile; + } + + protected static String formatFileName(String fileNameSuffix) + { + String fileName = "tmp" + formatIndex(runningIndex) + "_" + fileNameSuffix; + runningIndex++; + + return fileName; + } + + protected static String formatIndex(long index) + { + NumberFormat nf = new DecimalFormat("00000000"); + + return nf.format(index); + } + + protected static File createTempFileInDir(String fileName) throws IOException + { + File tempDir = new File(new File(SettingsReader.RESOURCES_PATH), "pdfastmp"); + + File tmpFile = new File(tempDir, fileName); + + tmpFile.createNewFile(); + + tmpFile.deleteOnExit(); + + return tmpFile; + } + + public static FileBasedDataSink createTempDataSink(String fileNameSuffix) throws IOException + { + String fileName = formatFileName(fileNameSuffix); + + File tmpFile = createTempFileInDir(fileName); + + FileBasedDataSink fbds = new FileBasedDataSink(tmpFile); + + return fbds; + } + + public static void writeDataSinkToHttpResponse(FileBasedDataSink fbds, HttpServletResponse response) throws IOException + { + + response.setContentType(fbds.getMimeType()); + response.setCharacterEncoding(fbds.getCharacterEncoding()); + + OutputStream os = response.getOutputStream(); + + byte[] buffer = new byte[2048]; + FileInputStream fis = new FileInputStream(fbds.getFile()); + int n = -1; + while ((n = fis.read(buffer)) > 0) + { + os.write(buffer, 0, n); + } + fis.close(); + os.close(); + } + + /** + * Deletes the underlying file of the FileBased DataSource. + * + *

+ * If the DataSource is not FileBased, nothing is done. + *

+ *

+ * This is usually used by the application to delete temporary files. + *

+ * + * @param dataSource + */ + public static void deleteDataSourceIfFileBased(DataSource dataSource) + { + if (dataSource instanceof FileBased) + { + FileBased fb = (FileBased) dataSource; + log.debug("Deleting temp file " + fb.getFile()); + boolean deleted = fb.getFile().delete(); + log.debug("deleted = " + deleted); + } + } + + public static void deleteDataSinkIfFileBased(DataSink dataSink) + { + if (dataSink instanceof FileBased) + { + FileBased fb = (FileBased) dataSink; + log.debug("Deleting temp file " + fb.getFile()); + boolean deleted = fb.getFile().delete(); + log.debug("deleted = " + deleted); + } + } + +} -- cgit v1.2.3