From db52e4d66d60184d53a27ba4d6772461daacc03d Mon Sep 17 00:00:00 2001 From: tknall Date: Fri, 22 Mar 2013 08:57:51 +0000 Subject: Maintenance update (bugfixes, new features, cleanup...) Refer to /dok/RELEASE_NOTES-3.3.txt for further information. git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/pdf-as/trunk@931 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../at/gv/egiz/pdfas/api/commons/Constants.java | 33 +- .../api/commons/DynamicSignatureProfileImpl.java | 8 +- .../egiz/pdfas/api/commons/SignatureProfile.java | 11 + .../at/gv/egiz/pdfas/api/sign/SignParameters.java | 78 ++-- .../java/at/gv/egiz/pdfas/commandline/Main.java | 123 +++--- .../at/gv/egiz/pdfas/exceptions/ErrorCode.java | 13 + .../framework/logging/CsvStatisticLogger.java | 105 +++++ .../pdfas/framework/logging/StatisticData.java | 314 +++++++++++++++ .../framework/logging/StatisticLogFactory.java | 63 +++ .../pdfas/framework/logging/StatisticLogger.java | 47 +++ .../at/gv/egiz/pdfas/impl/api/PdfAsObject.java | 441 +++++++++++++++------ .../pdfas/impl/api/commons/DataSinkAdapter.java | 6 +- .../impl/api/commons/SignatureProfileImpl.java | 49 ++- .../impl/input/correction/InternalCorrector.java | 9 +- .../impl/signator/binary/BinarySignator_1_0_0.java | 16 +- .../detached/DetachedTextualSignator_1_0_0.java | 7 +- .../pdfas/impl/vfilter/VerificationFilterImpl.java | 218 +++++----- .../helper/VerificationFilterBinaryHelper.java | 19 +- .../pdfas/io/FileBasedTextBasedDataSource.java | 3 +- .../egiz/pdfas/io/StringTextBasedDataSource.java | 5 +- .../placeholder/SignaturePlaceholderExtractor.java | 37 +- .../main/java/at/gv/egiz/pdfas/utils/CsvUtils.java | 82 ++++ .../java/at/gv/egiz/pdfas/utils/PDFASUtils.java | 268 ++++++++++--- 23 files changed, 1488 insertions(+), 467 deletions(-) create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/CsvStatisticLogger.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/StatisticData.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/StatisticLogFactory.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/StatisticLogger.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/utils/CsvUtils.java (limited to 'pdf-as-lib/src/main/java/at/gv') diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/commons/Constants.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/commons/Constants.java index b351d50..4afec65 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/commons/Constants.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/commons/Constants.java @@ -27,7 +27,7 @@ import at.knowcenter.wag.egov.egiz.pdf.AdobeSignatureHelper; /** * Contains commonly used constants. - * + * * @author wprinz */ public final class Constants @@ -52,7 +52,7 @@ public final class Constants * This value should not be modified due to external dependencies! */ public static final String SIGNATURE_TYPE_TEXTUAL = "textual"; - + /** * The default signature type (one of "textual", "binary", "detachedtextual"). */ @@ -60,7 +60,7 @@ public final class Constants /** * A "detached" textual signature. - * + * *

* The document text is signed, but instead of returning the pdf with the signature block, * the sign result XML of the connector is returned. @@ -98,14 +98,14 @@ public final class Constants * This value should not be modified due to external dependencies! */ public static final String SIGNATURE_DEVICE_MOBILE = "mobile"; - + /** * Added by rpiazzi * The signature device MOBILETEST for the test version of the MOBILE CCS. * This value should not be modified due to external dependencies! */ public static final String SIGNATURE_DEVICE_MOBILETEST = "mobiletest"; - + /** * Only binary signatures are verified. */ @@ -113,7 +113,7 @@ public final class Constants /** * Binary and textual signatures are verified with time optimization. - * + * *

* This mode of operation tries to minimize the numbers of text extractions, * which are very time intensive, at the cost of some rare cases, in which some @@ -141,19 +141,19 @@ public final class Constants * The zip file containing the default configuration. */ public static final String DEFAULT_CONFIGURATION_ZIP_RESOURCE = "DefaultConfiguration.zip"; - + /** * The configuration folder for pdf-as within the user's home folder. */ public static final String USERHOME_CONFIG_FOLDER = "PDF-AS"; - + /** * The name of the directory, where temporary files are stored. */ public static final String TEMP_DIR_NAME = "pdfastmp"; - + public static final String BKU_HEADER_SIGNATURE_LAYOUT = "SignatureLayout"; - + public static final String ADOBE_SIG_FILTER = AdobeSignatureHelper.ADOBE_SIG_FILTER; /** @@ -161,25 +161,30 @@ public final class Constants * If the placeholder with the given id is not found in the document, an exception will be thrown. */ public static final int PLACEHOLDER_MATCH_MODE_STRICT = 0; - + /** * A moderate matching mode for placeholder extraction.
* If the placeholder with the given id is not found in the document, the first placeholder without an id will be taken.
* If there is no such placeholder, the signature will be placed as usual, according to the pos parameter of the signature profile used. */ public static final int PLACEHOLDER_MATCH_MODE_MODERATE = 1; - + /** * A more lenient matching mode for placeholder extraction.
* If the placeholder with the given id is not found in the document, the first found placeholder will be taken, regardless if it has an id set, or not.
* If there is no placeholder at all, the signature will be placed as usual, according to the pos parameter of the signature profile used. */ public static final int PLACEHOLDER_MATCH_MODE_LENIENT = 2; - + /** * Identifier for QR based placeholders. */ public static final String QR_PLACEHOLDER_IDENTIFIER = "PDF-AS-POS"; - + + /** + * The name of a logger used for statistical logging. + */ + public static final String STATISTIC_LOGGER_NAME = "statistic"; + } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/commons/DynamicSignatureProfileImpl.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/commons/DynamicSignatureProfileImpl.java index 7e1672a..e1f31f7 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/commons/DynamicSignatureProfileImpl.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/commons/DynamicSignatureProfileImpl.java @@ -159,8 +159,10 @@ public class DynamicSignatureProfileImpl implements DynamicSignatureProfile { cfg = SettingsReader.getInstance(); - Properties props = cfg.getProperties(); - for (Enumeration e = props.keys(); e.hasMoreElements();) { + Properties props = cfg.getProperties(); + // DTI: props.keys() does not support default properties, therefore we should better use props.propertyNames() +// for (Enumeration e = props.keys(); e.hasMoreElements();) { + for (Enumeration e = props.propertyNames(); e.hasMoreElements();) { String oldKey = (String) e.nextElement(); if (oldKey.startsWith("sig_obj." + parentProfile + ".")) { String newKey = StringUtils.replace(oldKey, parentProfile, name); @@ -168,7 +170,7 @@ public class DynamicSignatureProfileImpl implements DynamicSignatureProfile { this.newProps.put(newKey, val); } } - this.newProps.put("sig_obj.types." + name, "on"); + this.newProps.put("sig_obj.types." + name, "on"); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/commons/SignatureProfile.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/commons/SignatureProfile.java index 8cd3108..1325d04 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/commons/SignatureProfile.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/commons/SignatureProfile.java @@ -25,6 +25,8 @@ package at.gv.egiz.pdfas.api.commons; import java.util.Properties; +import at.knowcenter.wag.egov.egiz.sig.SignatureTypes.State; + /** * Definition of a signature profile. * @@ -73,4 +75,13 @@ public interface SignatureProfile { * @return */ public boolean isDefault(); + + /** + * Returns the state of the signature profile. Signature profiles may be restricted to signature ( + * {@link State#SIGN_ONLY}) or to verification ({@link State#VERIFY_ONLY}). + * + * @return The state of the profile. + */ + public State getState(); + } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/sign/SignParameters.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/sign/SignParameters.java index 8834481..5533b8b 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/sign/SignParameters.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/api/sign/SignParameters.java @@ -34,16 +34,16 @@ import at.knowcenter.wag.egov.egiz.sig.SignatureTypes; /** * Parameter object that holds the sign parameters. - * + * * @author wprinz */ public class SignParameters { // 23.11.2010 changed by exthex - added parameters for placeholder handling - + /** * The document to be signed. - * + * *

* The DataSource implementation encapsulates the actual representaion of the * data. E.g. the DataSource may be File based or byte array based. See @@ -54,7 +54,7 @@ public class SignParameters /** * The type of the signature. - * + * *

* May be {@link Constants#SIGNATURE_TYPE_BINARY} or * {@link Constants#SIGNATURE_TYPE_TEXTUAL}. @@ -64,7 +64,7 @@ public class SignParameters /** * The signature device to perform the actual signature. - * + * *

* May be {@link Constants#SIGNATURE_DEVICE_MOA} or * {@link Constants#SIGNATURE_DEVICE_BKU}. @@ -75,18 +75,18 @@ public class SignParameters /** * The signature profile identifier identifying the profile to be used in the * config file. - * + * *

* Note: In near future it will be possible to provide a full specified * profile here instead of the profile id. *

*/ protected String signatureProfileId = null; - + /** * The signature key identifier specifying which signature key should be used * by the signature device to perform the signature. - * + * *

* Providing a null value (default) means that no explicit signature key * identifier is provided. The selected signature device will then use its @@ -122,7 +122,7 @@ public class SignParameters * The output DataSink that will receive the signed document. */ protected DataSink output = null; - + protected TimeStamper timeStamperImpl; /** @@ -142,12 +142,12 @@ public class SignParameters * Defaults to {@link Constants#PLACEHOLDER_MATCH_MODE_MODERATE}. */ protected int placeholderMatchMode = Constants.PLACEHOLDER_MATCH_MODE_MODERATE; - + protected Properties overrideProps = new Properties(); - - + + /** * {@link #setTimeStamperImpl(TimeStamper)} @@ -158,7 +158,7 @@ public class SignParameters } /** - * Set a {@link TimeStamper} to create a timestamp on the signature value. Will be + * Set a {@link TimeStamper} to create a timestamp on the signature value. Will be * called after sign. For binary signatures only. Timestamp will be embedded in egiz dict /TimeStamp. * @param timeStamperImpl */ @@ -285,21 +285,21 @@ public class SignParameters } /** - * Override user defined values from the used signature profile like "value.SIG_META". - * You cannot override pre defined values like SIG_VALUE, SIG_DATE {@link SignatureTypes#REQUIRED_SIG_KEYS}. + * Override user defined values from the used signature profile like "value.SIG_META". + * You cannot override pre defined values like SIG_VALUE, SIG_DATE {@link SignatureTypes#REQUIRED_SIG_KEYS}. * The override values are bound to the {@link SignParameters} instance. *

* Sample usage: *

       SignParameters sp = new SignParameters();
       . . .
-      
-      sp.setSignatureProfileId("SIGNATURBLOCK_DE");  
-      
+
+      sp.setSignatureProfileId("SIGNATURBLOCK_DE");
+
       // expressions do not work on binary signature fields without phlength setting!!
       sp.setProfileOverrideValue("SIG_META", "It's nice to be important, but it is more important to be nice ${subject.L}");;
-      sp.setProfileOverrideValue("SIG_LABEL", "./images/signatur-logo_en.png");                  
-      
+      sp.setProfileOverrideValue("SIG_LABEL", "./images/signatur-logo_en.png");
+
       // execute sign using the overrides
       pdfAs.sign(sp);
   
@@ -311,51 +311,51 @@ public class SignParameters if (SignatureTypes.isRequredSigTypeKey(key)) { throw new RuntimeException("cannot set value for pre defined signature field names"); } - this.overrideProps.put(key, value); + this.overrideProps.put(key, value); } - + /** * Get override values created via {@link #setProfileOverrideValue(String, String)} * @return */ public Properties getProfileOverrideProperties() { return this.overrideProps; - + } /** * Get the value of the checkForPlaceholder flag. - * + * * @return */ public Boolean isCheckForPlaceholder() { return this.checkForPlaceholder; } - + /** - * Set this to true, if you want a search for placeholder images to be performed and + * Set this to true, if you want a search for placeholder images to be performed and * appropriate images to be replaced. * If this is not set, a search will only be performed if the configuration property "enable_placeholder_search" is set to true. - * + * * @param check */ - public void setCheckForPlaceholder(boolean check) { - this.checkForPlaceholder = Boolean.valueOf(check); + public void setCheckForPlaceholder(Boolean searchForPlaceHolder) { + this.checkForPlaceholder = searchForPlaceHolder; } /** - * Set an explicit placeholder id. + * Set an explicit placeholder id. * Only placeholder images that have a matching ID property embedded will be considered for replacement. - * + * * @param placeholderId */ public void setPlaceholderId(String placeholderId) { - this.placeholderId = placeholderId; + this.placeholderId = placeholderId; } - + /** * The id of the placeholder to replace. - * + * * @return the placeholderId */ public String getPlaceholderId() { @@ -367,13 +367,13 @@ public class SignParameters * Exactly matching meaning:
* - * + * * @see Constants#PLACEHOLDER_MATCH_MODE_LENIENT * @see Constants#PLACEHOLDER_MATCH_MODE_MODERATE * @see Constants#PLACEHOLDER_MATCH_MODE_STRICT - * + * * Defaults to {@link Constants#PLACEHOLDER_MATCH_MODE_MODERATE}. - * + * * @param placeholderMatchMode */ public void setPlaceholderMatchMode(int placeholderMatchMode) { @@ -382,12 +382,12 @@ public class SignParameters /** * Get the placeholder matching mode. - * + * * @see SignParameters#getPlaceholderMatchMode() * @return the placeholderMatchMode */ public int getPlaceholderMatchMode() { return this.placeholderMatchMode; } - + } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/commandline/Main.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/commandline/Main.java index 52355f8..d5f7e2f 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/commandline/Main.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/commandline/Main.java @@ -36,6 +36,7 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import org.apache.commons.lang.BooleanUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -78,11 +79,11 @@ import at.knowcenter.wag.egov.egiz.sig.SignatureTypes; /** * The main program entry point of the commandline tool. - * + * *

* The commandline uses the PDF-AS API. *

- * + * * @author wprinz */ public abstract class Main @@ -127,12 +128,12 @@ public abstract class Main * Command line parameter signaling to search the source document for a placeholder for the signature */ protected static final String PARAMETER_PLACEHOLDER_SEARCH = "-searchplaceholder"; - + /** * Command line parameter selecting the id of the placeholder to use */ protected static final String PARAMETER_PLACEHOLDER_ID = "-placeholder_id"; - + /** * Command line parameter selecting the match mode for the placeholder */ @@ -143,7 +144,7 @@ public abstract class Main * verified. */ protected static final String PARAMETER_VERIFY_WHICH = "-verify_which"; - + /** * Command line parameter that starts the deployment of the default configuration to the current * user's home folder. @@ -195,7 +196,7 @@ public abstract class Main * The placeholder match mode STRICT */ public static final String VALUE_PLACEHOLDER_MATCH_MODE_LENIENT = "lenient"; - + /** * The log. */ @@ -203,7 +204,7 @@ public abstract class Main /** * Main program entry point. - * + * * @param args * The commandline arguments. * @throws IOException @@ -216,7 +217,7 @@ public abstract class Main for (int i = 0; i < args.length; i++) { if (args[i].trim().equals(PARAMETER_DEPLOY_DEFAULT_CONFIGURATION)) { try { - String defaultConfigurationDeployedTo = ConfigUtils.deployDefaultConfiguration(); + String defaultConfigurationDeployedTo = ConfigUtils.deployDefaultConfiguration(); if (defaultConfigurationDeployedTo != null) { System.out.println("Default configuration successfully deployed to \"" + defaultConfigurationDeployedTo + "\"."); } else { @@ -229,7 +230,7 @@ public abstract class Main } catch (ConfigUtilsException e) { System.err.println("Deployment of default configuration failed: " + e.getMessage()); System.exit(1); - } + } } } @@ -247,7 +248,8 @@ public abstract class Main String user_password = null; String pos_string = null; - boolean search_placeholder = true; + // DTI: fixed searchplaceholder parameter handling preventing consideration of respective configuration parameter + Boolean searchPlaceHolder = null; String placeholderId = null; int placeholderMatchMode = Constants.PLACEHOLDER_MATCH_MODE_MODERATE; @@ -274,7 +276,7 @@ public abstract class Main // already applied continue; } - + if (cur_arg.equals(PARAMETER_MODE)) { i++; @@ -351,7 +353,8 @@ public abstract class Main printNoValue(PARAMETER_PLACEHOLDER_SEARCH); return; } - search_placeholder = Boolean.valueOf(args[i]).booleanValue(); +// search_placeholder = Boolean.valueOf(args[i]).booleanValue(); + searchPlaceHolder = BooleanUtils.toBooleanObject(args[i]); continue; } @@ -522,7 +525,7 @@ public abstract class Main output = generateOutputFileNameFromInput(input, signature_mode); } - carryOutCommand(mode, signature_mode, connector, signature_type, user_name, user_password, verify_which, input, output, pos_string, search_placeholder, placeholderId, placeholderMatchMode); + carryOutCommand(mode, signature_mode, connector, signature_type, user_name, user_password, verify_which, input, output, pos_string, searchPlaceHolder, placeholderId, placeholderMatchMode); } catch (PdfAsException e) @@ -549,7 +552,7 @@ public abstract class Main } protected static void carryOutCommand(final String mode, final String signature_mode, final String connector, final String signature_type, final String user_name, final String user_password, - final int verify_which, final String input, String output, final String pos_string, boolean search_placeholder, String placeholderId, int placeholderMatchMode) throws PdfAsException + final int verify_which, final String input, String output, final String pos_string, Boolean searchPlaceHolder, String placeholderId, int placeholderMatchMode) throws PdfAsException { // File file = new File(input); // @@ -570,7 +573,7 @@ public abstract class Main if (mode.equals(VALUE_MODE_SIGN)) { - carryOutSign(input, connector, signature_mode, signature_type, pos_string, user_name, user_password, output, messageOutput, search_placeholder, placeholderId, placeholderMatchMode); + carryOutSign(input, connector, signature_mode, signature_type, pos_string, user_name, user_password, output, messageOutput, searchPlaceHolder, placeholderId, placeholderMatchMode); } else { @@ -580,7 +583,7 @@ public abstract class Main } public static void carryOutSign(String input, String connector, String signature_mode, String signature_type, String pos_string, String user_name, String user_password, String output, - PrintWriter messageOutput, boolean search_placeholder, String placeholderId, int placeholderMatchMode) throws PdfAsException + PrintWriter messageOutput, Boolean searchPlaceHolder, String placeholderId, int placeholderMatchMode) throws PdfAsException { messageOutput.println("Signing " + input + "..."); @@ -621,7 +624,7 @@ public abstract class Main } try { - processSign(dataSource, connector, signature_mode, signature_type, pos_string, search_placeholder, placeholderId, placeholderMatchMode, dataSink); + processSign(dataSource, connector, signature_mode, signature_type, pos_string, searchPlaceHolder, placeholderId, placeholderMatchMode, dataSink); } catch (Exception e) { // Exception caught in order to delete file based datasink if (outputFile != null && outputFile.exists()) @@ -641,14 +644,6 @@ public abstract class Main } } - // for performance measurement - if (logger_.isInfoEnabled()) - { - long endTime = System.currentTimeMillis(); - String toReport = "SIGN;" + signature_mode + ";" + input + ";" + fileSize + ";" + (endTime - startTime); - logger_.info(toReport); - } - messageOutput.println("Signing was successful (" + output + ")."); } @@ -656,22 +651,10 @@ public abstract class Main { messageOutput.println("Verifying " + input + "..."); - // for performance measurement - long startTime = 0; - long fileSize = 0; - if (logger_.isInfoEnabled()) - { - startTime = System.currentTimeMillis(); - } - DataSource dataSource = null; try { File file = new File(input); - if (logger_.isDebugEnabled()) - { - fileSize = file.length(); - } String extension = extractExtension(input); if (extension != null && extension.equals("txt")) { @@ -705,23 +688,15 @@ public abstract class Main messageOutput.println("Verification results:"); formatVerifyResults(results, messageOutput); - // for performance measurement - if (logger_.isInfoEnabled()) - { - long endTime = System.currentTimeMillis(); - String toReport = "VERIFY;" + input + ";" + fileSize + ";" + (endTime - startTime) + ";" + debugVerifyResults(results); - logger_.info(toReport); - } - } - + /** * Extracts the extension from a file name string. - * + * *

* The extension of a file name is whatever text follows the last '.'. *

- * + * * @param file_name * The file name. * @return Returns the extension. If the file name ends with the '.', then an @@ -743,7 +718,7 @@ public abstract class Main return file_name.substring(dot_index + 1); } - public static void processSign(DataSource dataSource, String connector, String signature_mode, String signature_type, String pos_string, boolean search_placeholder, String placeholderId, int placeholderMatchMode, DataSink dataSink) throws PdfAsException + public static void processSign(DataSource dataSource, String connector, String signature_mode, String signature_type, String pos_string, Boolean searchPlaceHolder, String placeholderId, int placeholderMatchMode, DataSink dataSink) throws PdfAsException { TablePos pos = null; if (pos_string != null) @@ -795,7 +770,7 @@ public abstract class Main sp.setSignatureDevice(connector); sp.setSignatureProfileId(signature_type); sp.setSignaturePositioning(posi); - sp.setCheckForPlaceholder(search_placeholder); + sp.setCheckForPlaceholder(searchPlaceHolder); sp.setPlaceholderId(placeholderId); sp.setPlaceholderMatchMode(placeholderMatchMode); pdfAs.sign(sp); @@ -853,7 +828,7 @@ public abstract class Main /** * Prints that the provided option was unrecognized. - * + * * @param option * The unrecognized option. * @throws PresentableException @@ -867,7 +842,7 @@ public abstract class Main /** * Prints that the provided value was unrecognized. - * + * * @param parameter * The parameter, which is missing a value. * @throws PresentableException @@ -881,7 +856,7 @@ public abstract class Main /** * Prints that the provided value was unrecognized. - * + * * @param value * The unrecognized value. * @throws PresentableException @@ -895,7 +870,7 @@ public abstract class Main /** * Prints that the provided additional commandline argument was unrecognized. - * + * * @param argument * The unrecognized argument. * @throws PresentableException @@ -909,7 +884,7 @@ public abstract class Main /** * Prints that a certain parameter was missing. - * + * * @param missing_term * A description of the missing parameter ("e.g. a mode"). * @param parameter @@ -924,7 +899,7 @@ public abstract class Main /** * Prints that something is missing. - * + * * @param missing_term * A descriptive message of the missing thing. * @throws PresentableException @@ -938,7 +913,7 @@ public abstract class Main /** * Prints out the ErrorCodeException in a descriptive form. - * + * * @param ece * The ErrorCodeException to be printed. */ @@ -974,7 +949,7 @@ public abstract class Main /** * Prints the usage text. - * + * * @param writer * The writer to print the text to. * @throws PresentableException @@ -991,7 +966,7 @@ public abstract class Main writer.print(" " + PARAMETER_CONNECTOR + " "); ConnectorInformation[] ci = ConnectorFactory.getConnectorInformationArray(); - + // prepare list of connectors available for commandline ArrayList ciList = new ArrayList(); for (int i = 0; i < ci.length; i++) { @@ -1000,7 +975,7 @@ public abstract class Main ciList.add(ci[i]); } } - + // list available connectors wrapped in <...|...> Iterator ciIt = ciList.iterator(); writer.print("<"); @@ -1012,7 +987,7 @@ public abstract class Main } writer.print(">"); writer.println(); - + // for (int i = 0; i < ci.length; i++) // { // String id = ci[i].getIdentifier(); @@ -1046,7 +1021,7 @@ public abstract class Main writer.println(" " + PARAMETER_DEPLOY_DEFAULT_CONFIGURATION + " ... deploys the default configuration to the current user's home"); - writer.println(" OPTIONS for signation:"); + writer.println(" OPTIONS for signature:"); writer.println(" " + PARAMETER_SIGNATURE_MODE + " <" + VALUE_SIGNATURE_MODE_BINARY + "|" + VALUE_SIGNATURE_MODE_TEXTUAL + "> [optional]"); writer.println(" " + VALUE_SIGNATURE_MODE_BINARY + " ... signs the complete binary document" + (Constants.DEFAULT_SIGNATURE_TYPE.equals(VALUE_SIGNATURE_MODE_BINARY) ? " (default)" : "")); @@ -1056,7 +1031,9 @@ public abstract class Main writer.print(" " + PARAMETER_SIGNATURE_TYPE + " <"); SignatureTypes sig_types = SignatureTypes.getInstance(); SettingsReader settings = SettingsReader.getInstance(); - Set types_array = sig_types.getSignatureTypes(); + // show only signature profiles that can be used for signature +// Set types_array = sig_types.getSignatureTypes(); + Set types_array = sig_types.getSignatureTypesForSignature(); Iterator it = types_array.iterator(); while (it.hasNext()) { @@ -1117,7 +1094,7 @@ public abstract class Main /** * Checks the value for correctness. - * + * * @param mode * The parameter's value. * @return Returns true, if the value is correct, false otherwise. @@ -1129,7 +1106,7 @@ public abstract class Main /** * Checks the value for correctness. - * + * * @param signature_mode * The parameter's value. * @return Returns true, if the value is correct, false otherwise. @@ -1143,7 +1120,7 @@ public abstract class Main /** * Checks the value for correctness. - * + * * @param connector * The parameter's value. * @return Returns true, if the value is correct, false otherwise. @@ -1157,7 +1134,7 @@ public abstract class Main /** * Checks the value for correctness (meaning if it exists) - * + * * @param signature_type * The parameter's value. * @return Returns true, if the value is correct, false otherwise. @@ -1166,10 +1143,10 @@ public abstract class Main { return SignatureTypes.getInstance().getSignatureTypes().contains(signature_type); } - + /** * Translates the commandline argument to a PDF-AS-ID. - * + * * @param signature_mode * The signator mode commandline argument. * @return Returns the corresponding PDFASID. @@ -1197,7 +1174,7 @@ public abstract class Main /** * Formats the verification results. - * + * * @param results * The List of SignatureResponse verification results. * @param writer @@ -1223,9 +1200,9 @@ public abstract class Main /** * Formats the verification results for debugging. Returns 0 if no error * occurs or the sum of all error-codes. - * + * * @param results - * + * * @param writer * The output sink to write the formatted text to. * @throws SettingNotFoundException @@ -1259,7 +1236,7 @@ public abstract class Main String public_property = (String) it.next(); writer.println(" Eigenschaft: " + public_property); } - + writer.println(" Zertifikat-Check:"); writer.println(" " + result.getCertificateCheck().getCode() + " - " + result.getCertificateCheck().getMessage()); writer.println(" Signatur-Check:"); diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java index e0ddbf2..e7d2b47 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java @@ -23,6 +23,8 @@ */ package at.gv.egiz.pdfas.exceptions; +import at.knowcenter.wag.egov.egiz.exceptions.SignatureException; + /** * Contains constants for the error codes. * @@ -44,6 +46,8 @@ public final class ErrorCode public static final int NO_EMBEDABLE_TTF_CONFIGURED_FOR_PDFA = 103; public static final int INVALID_SIGNATURE_LAYOUT_IMPL_CONFIGURED = 104; public static final int MISSING_HEADER_SERVER_USER_AGENT = 105; + public static final int CIRCULAR_INCLUDE_INSTRUCTION_DETECTED = 106; + public static final int UNABLE_TO_LOAD_DEFAULT_CONFIG = 107; public static final int DOCUMENT_CANNOT_BE_READ = 201; public static final int TEXT_EXTRACTION_EXCEPTION = 202; @@ -56,6 +60,15 @@ public final class ErrorCode //23.11.2010 changed by exthex - added error code for failed extraction public static final int SIGNATURE_PLACEHOLDER_EXTRACTION_FAILED = 233; + /** + * Error code for {@code SignatureException}s occurring when trying to sign with a certain signature profile that + * is not allowed to be used for signature, e.g. because ist has been set to + *

+ * {@code sig_obj.types. = verify_only} + * @author Datentechnik Innovation GmbH + */ + public static final int SIGNATURE_PROFILE_IS_NOT_ALLOWED_FOR_SIGNATURE = 234; + public static final int INVALID_SIGNATURE_POSITION = 224; public static final int NO_TEXTUAL_CONTENT = 251; diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/CsvStatisticLogger.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/CsvStatisticLogger.java new file mode 100644 index 0000000..a295a7b --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/CsvStatisticLogger.java @@ -0,0 +1,105 @@ +/** + * Copyright 2006 by Know-Center, Graz, Austria + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egiz.pdfas.framework.logging; + +import org.apache.commons.logging.Log; + +import at.gv.egiz.pdfas.api.exceptions.PdfAsException; +import at.gv.egiz.pdfas.exceptions.external.ExternalErrorException; +import at.gv.egiz.pdfas.utils.CsvUtils; + +/** + * A statistic logger implementation that prodoces csv based logging entries. + * + * @author Datentechnik Innovation GmbH + */ +public class CsvStatisticLogger implements StatisticLogger { + + /** + * The underlying logging implementation. + */ + private Log log; + + /** + * A csv value indication error conditions. + */ + private static final String VALUE_ERROR = "ERROR"; + + /** + * A csv value indication success conditions. + */ + private static final String VALUE_OK = "OK"; + + /** + * Creates a new instance. + * + * @param log + * The underlying logger. + */ + CsvStatisticLogger(Log log) { + this.log = log; + } + + public void log(StatisticData data) { + if (isEnabled()) { + StringBuffer msg = new StringBuffer(); + + // add escapted fields + msg.append(CsvUtils.escapeCsvValue(data.operation)).append(CsvUtils.DEFAULT_DELIMITER); + msg.append(CsvUtils.escapeCsvValue(data.signatureMode)).append(CsvUtils.DEFAULT_DELIMITER); + msg.append(CsvUtils.escapeCsvValue(data.connector)).append(CsvUtils.DEFAULT_DELIMITER); + msg.append(CsvUtils.escapeCsvValue(data.signatureProfileId)).append(CsvUtils.DEFAULT_DELIMITER); + msg.append(CsvUtils.escapeCsvValue(data.fileSize)).append(CsvUtils.DEFAULT_DELIMITER); + msg.append(CsvUtils.escapeCsvValue(data.userAgent)).append(CsvUtils.DEFAULT_DELIMITER); + + // handle error conditions + Integer errorCode = null; + String externalErrorCode = null; + if (data.exception != null) { + msg.append(VALUE_ERROR).append(CsvUtils.DEFAULT_DELIMITER); + if (data.exception instanceof PdfAsException) { + PdfAsException pdfAsException = (PdfAsException) data.exception; + errorCode = pdfAsException.getErrorCode(); + if (pdfAsException instanceof ExternalErrorException) { + externalErrorCode = ((ExternalErrorException) pdfAsException).getExternalErrorCode(); + } + } + msg.append(CsvUtils.escapeCsvValue(data.exception.getClass().getName())).append(CsvUtils.DEFAULT_DELIMITER); + } else { + msg.append(VALUE_OK).append(CsvUtils.DEFAULT_DELIMITER).append(CsvUtils.DEFAULT_DELIMITER); + } + msg.append(CsvUtils.escapeCsvValue(errorCode)).append(CsvUtils.DEFAULT_DELIMITER); + msg.append(CsvUtils.escapeCsvValue(externalErrorCode)).append(CsvUtils.DEFAULT_DELIMITER); + + msg.append(CsvUtils.escapeCsvValue(data.duration)); + + log.info(msg); + } + } + + public boolean isEnabled() { + return log.isInfoEnabled(); + } + +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/StatisticData.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/StatisticData.java new file mode 100644 index 0000000..3cb1c66 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/StatisticData.java @@ -0,0 +1,314 @@ +/** + * Copyright 2006 by Know-Center, Graz, Austria + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egiz.pdfas.framework.logging; + +import at.gv.egiz.pdfas.api.analyze.AnalyzeParameters; +import at.gv.egiz.pdfas.api.commons.Constants; +import at.gv.egiz.pdfas.api.sign.SignParameters; +import at.gv.egiz.pdfas.api.verify.VerifyAfterAnalysisParameters; +import at.gv.egiz.pdfas.api.verify.VerifyAfterReconstructXMLDsigParameters; +import at.gv.egiz.pdfas.api.verify.VerifyParameters; +import at.gv.egiz.pdfas.api.xmldsig.ReconstructXMLDsigAfterAnalysisParameters; +import at.gv.egiz.pdfas.api.xmldsig.ReconstructXMLDsigParameters; + +public class StatisticData { + + String operation; + String connector; + String signatureMode; + String signatureProfileId; + Integer fileSize; + Long duration; + String userAgent; + Exception exception; + + /** + * Creates new data for statistical logging purposes. + */ + public StatisticData() { + } + + /** + * Creates new data for statistical logging purposes. + * + * @param operation + * The operation (one of 'SIGN' and 'VERIFY'). + * @param connector + * The connector being used for the certain operation (one of {@link Constants#SIGNATURE_DEVICE_BKU}, + * {@link Constants#SIGNATURE_DEVICE_MOA}, {@link Constants#SIGNATURE_DEVICE_MOC}, + * {@link Constants#SIGNATURE_DEVICE_MOBILE}...) + * @param fileSize + * The size of the file being processed. + * @param signatureMode + * The signature mode (one of {@link Constants#SIGNATURE_TYPE_BINARY}, + * {@link Constants#SIGNATURE_TYPE_BINARY} or {@code null} in case of verification. + * @param duration + * The duration the certain operation took (in ms). + * @param userAgent + * The user agent String. + * @param errorCode + * The error code resulting from the current operation (0 means no error). + */ + public StatisticData(String operation, String connector, Integer fileSize, String signatureMode, Long duration, + String userAgent, Integer errorCode) { + this(); + this.operation = operation; + this.connector = connector; + this.signatureMode = signatureMode; + this.fileSize = fileSize; + this.duration = duration; + this.userAgent = userAgent; + } + + /** + * Creates new data for statistical logging purposes. + * + * @param operation + * The operation (one of 'SIGN' and 'VERIFY'). + * @param connector + * The connector being used for the certain operation (one of {@link Constants#SIGNATURE_DEVICE_BKU}, + * {@link Constants#SIGNATURE_DEVICE_MOA}, {@link Constants#SIGNATURE_DEVICE_MOC}, + * {@link Constants#SIGNATURE_DEVICE_MOBILE}...) + * @param fileSize + * The size of the file being processed. + */ + public StatisticData(String operation, String connector, Integer fileSize) { + this(); + this.operation = operation; + this.connector = connector; + this.fileSize = fileSize; + } + + /** + * Creates new data for statistical logging purposes. + * + * @param operation + * The operation (one of 'SIGN' and 'VERIFY'). + * @param connector + * The connector being used for the certain operation (one of {@link Constants#SIGNATURE_DEVICE_BKU}, + * {@link Constants#SIGNATURE_DEVICE_MOA}, {@link Constants#SIGNATURE_DEVICE_MOC}, + * {@link Constants#SIGNATURE_DEVICE_MOBILE}...) + * @param fileSize + * The size of the file being processed. + * @param signatureMode + * The signature mode (one of {@link Constants#SIGNATURE_TYPE_BINARY}, + * {@link Constants#SIGNATURE_TYPE_BINARY} or {@code null} in case of verification. + * @param duration + * The duration the certain operation took (in ms). + * @param userAgent + * The user agent String. + */ + public StatisticData(String operation, String connector, Integer fileSize, String signatureMode, Long duration, + String userAgent) { + this(operation, connector, fileSize, signatureMode, duration, userAgent, null); + } + + /** + * Creates statistical data based on given sign parameters. + * + * @param signParameters + * The given sign parameters. + */ + public StatisticData(SignParameters signParameters) { + this(); + operation = "SIGN"; + connector = signParameters.getSignatureDevice(); + signatureMode = signParameters.getSignatureType(); + fileSize = signParameters.getDocument().getLength(); + signatureProfileId = signParameters.getSignatureProfileId(); + } + + /** + * Creates statistical data based on given verify parameters. + * + * @param verifyParameters + * The given verify parameters. + */ + public StatisticData(VerifyParameters verifyParameters) { + this(); + operation = "VERIFY"; + connector = verifyParameters.getSignatureDevice(); + fileSize = verifyParameters.getDocument().getLength(); + } + + /** + * Creates statistical data based on given analyze parameters. + * + * @param varxdp + * The given analyze parameters. + */ + public StatisticData(AnalyzeParameters analyzeParameters) { + this(); + operation = "ANALYZE"; + fileSize = analyzeParameters.getDocument().getLength(); + } + + /** + * Creates statistical data based on given reconstruction parameters. + * + * @param rxdaap + * The given reconstruction parameters. + */ + public StatisticData(ReconstructXMLDsigAfterAnalysisParameters rxdaap) { + this(); + operation = "RECONSTRUCT"; + connector = rxdaap.getSignatureDevice(); + } + + /** + * Creates statistical data based on given reconstruction parameters. + * + * @param reconstructParameters + * The given reconstruction parameters. + */ + public StatisticData(ReconstructXMLDsigParameters reconstructParameters) { + this(); + operation = "RECONSTRUCT"; + fileSize = reconstructParameters.getDocument().getLength(); + connector = reconstructParameters.getSignatureDevice(); + } + + /** + * Creates statistical data based on given parameters. + * + * @param vaaParameters + * The given verify parameters after analysis. + */ + public StatisticData(VerifyAfterAnalysisParameters vaaParameters) { + this(); + operation = "VERIFY"; + connector = vaaParameters.getSignatureDevice(); + } + + /** + * Creates statistical data based on given verify parameters after reconstruction. + * + * @param varxdp + * The given verify parameters after reconstruction. + */ + public StatisticData(VerifyAfterReconstructXMLDsigParameters varxdp) { + this(); + operation = "VERIFY"; + connector = varxdp.getSignatureDevice(); + } + + /** + * Sets the current operation name. + * + * @param operation + * The operation name. + */ + public StatisticData setOperation(String operation) { + this.operation = operation; + return this; + } + + /** + * Sets the connector. + * + * @param connector + * The connector. + */ + public StatisticData setConnector(String connector) { + this.connector = connector; + return this; + } + + /** + * Sets the file size of the processed document. + * + * @param fileSize + * The file size in bytes. + */ + public StatisticData setFileSize(Integer fileSize) { + this.fileSize = fileSize; + return this; + } + + /** + * Sets the duration of the performed operation. + * + * @param duration + * The duration in ms. + */ + public StatisticData setDuration(Long duration) { + this.duration = duration; + return this; + } + + /** + * Sets the user agent identifier. + * + * @param userAgent + * The user agent identifier. + */ + public StatisticData setUserAgent(String userAgent) { + this.userAgent = userAgent; + return this; + } + + /** + * Sets the signature mode. + * + * @param signatureMode + * The signature mode (one of {@link Constants#SIGNATURE_TYPE_TEXTUAL} and + * {@link Constants#SIGNATURE_TYPE_BINARY}). + */ + public StatisticData setSignatureMode(String signatureMode) { + this.signatureMode = signatureMode; + return this; + } + + /** + * Sets the profile id. + * + * @param profileId + * The profile id. + */ + public StatisticData setSignatureProfileId(String signatureProfileId) { + this.signatureProfileId = signatureProfileId; + return this; + } + + /** + * Sets the exception that has been thrown. + * + * @param exception + * The exception. + */ + public StatisticData setException(Exception exception) { + this.exception = exception; + return this; + } + + /** + * Returns {@code true} if an error condition has been set, {@code false} otherwise. + * + * @return {@code true} in case of an error, {@code false} if not. + */ + public boolean isError() { + return exception != null; + } + +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/StatisticLogFactory.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/StatisticLogFactory.java new file mode 100644 index 0000000..2b6671c --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/StatisticLogFactory.java @@ -0,0 +1,63 @@ +/** + * Copyright 2006 by Know-Center, Graz, Austria + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egiz.pdfas.framework.logging; + +import org.apache.commons.logging.LogFactory; + +/** + * This factory creates statistic logger implementations.
+ * Note that it exclusively creates {@link CsvStatisticLogger} implementations at the moment. It should just be regarded + * as template for further factory implementations. Strictly speaking this factory does not fulfil the respective GoF + * pattern. + * + * @see StatisticLogger + * @author Datentechnik Innovation GmbH + */ +public class StatisticLogFactory { + + /** + * Returns a statistic logger implementation. + * + * @param clazz + * The class. + * @return A statistic logger implementation. + */ + public static StatisticLogger getLog(Class clazz) { + return getLog(clazz.getName()); + } + + /** + * Returns a statistic logger implementation. + * + * @param name + * The name of the logger. + * @return A statistic logger implementation. + */ + public static StatisticLogger getLog(String name) { + + // to be changed some day, producing various logger implementations + return new CsvStatisticLogger(LogFactory.getLog(name)); + } + +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/StatisticLogger.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/StatisticLogger.java new file mode 100644 index 0000000..6e2c587 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/framework/logging/StatisticLogger.java @@ -0,0 +1,47 @@ +/** + * Copyright 2006 by Know-Center, Graz, Austria + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egiz.pdfas.framework.logging; + +/** + * + * @author Datentechnik Innovation GmbH + */ +public interface StatisticLogger { + + /** + * Creates a log entry with the given statistical data. + * + * @param statisticData + * The statistical data. + */ + public void log(StatisticData statisticData); + + /** + * Returns {@code true} in case the logger is enabled, {@code false} if not. + * + * @return {@code true} in case the logger is enabled, {@code false} if not. + */ + public boolean isEnabled(); + +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/api/PdfAsObject.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/api/PdfAsObject.java index eda94c0..a2bcd15 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/api/PdfAsObject.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/api/PdfAsObject.java @@ -33,6 +33,7 @@ import java.util.Properties; import java.util.Vector; import org.apache.commons.lang.math.NumberUtils; +import org.apache.commons.lang.time.StopWatch; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -66,6 +67,9 @@ import at.gv.egiz.pdfas.framework.DataSourceHolder; import at.gv.egiz.pdfas.framework.config.SettingsHelper; import at.gv.egiz.pdfas.framework.input.ExtractionStage; import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.framework.logging.StatisticData; +import at.gv.egiz.pdfas.framework.logging.StatisticLogFactory; +import at.gv.egiz.pdfas.framework.logging.StatisticLogger; import at.gv.egiz.pdfas.framework.signator.Signator; import at.gv.egiz.pdfas.framework.signator.SignatorInformation; import at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters; @@ -87,6 +91,7 @@ import at.gv.egiz.pdfas.placeholder.SignaturePlaceholderContext; import at.gv.egiz.pdfas.placeholder.SignaturePlaceholderData; import at.gv.egiz.pdfas.placeholder.SignaturePlaceholderExtractor; import at.gv.egiz.pdfas.utils.ConfigUtils; +import at.gv.egiz.pdfas.utils.PDFASUtils; import at.knowcenter.wag.egov.egiz.PdfAS; import at.knowcenter.wag.egov.egiz.PdfASID; import at.knowcenter.wag.egov.egiz.cfg.OverridePropertyHolder; @@ -111,12 +116,13 @@ import at.knowcenter.wag.egov.egiz.sig.SignatureDataImpl; import at.knowcenter.wag.egov.egiz.sig.SignatureResponse; import at.knowcenter.wag.egov.egiz.sig.SignatureTypeDefinition; import at.knowcenter.wag.egov.egiz.sig.SignatureTypes; +import at.knowcenter.wag.egov.egiz.sig.SignatureTypes.State; import at.knowcenter.wag.egov.egiz.sig.connectors.Connector; import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject; /** * Implementation of the {@link PdfAs} interface. - * + * * @author wprinz */ public class PdfAsObject implements PdfAs @@ -127,25 +133,26 @@ public class PdfAsObject implements PdfAs * The log. */ private static Log log = LogFactory.getLog(PdfAsObject.class); - + private static StatisticLogger statLog = StatisticLogFactory.getLog(Constants.STATISTIC_LOGGER_NAME); + private static final String ENABLE_PLACEHOLDER_SEARCH_KEY = "enable_placeholder_search"; - + /** * Configuration key for minimal signature block width threshold. Any width below this certain value will lead to a warning log entry." */ private static final String SIGNATURE_BLOCK_WIDTH_THRESHOLD_FOR_WARNING_KEY = "signature_block_width_warning_threshold"; - + /** * Minimal signature block width. If a width below that value is defined (by parameter, by placeholder or by configuration) a warning log entry is created. */ public static final float DEFAULT_SIGNATURE_BLOCK_WIDTH_THRESHOLD = 150; - + /** * This constructor is for internal use only - use * {@link at.gv.egiz.pdfas.PdfAsFactory} instead. * Note: IAIK JCE and IAIK ECC security providers are automatically registered. - * + * * @param workDirectory * The work directory. * @throws PdfAsException @@ -155,16 +162,16 @@ public class PdfAsObject implements PdfAs { this(workDirectory, SettingsReader.REGISTER_IAIK_PROVIDERS_ON_DEFAULT); } - + /** * This constructor is for internal use only - use * {@link at.gv.egiz.pdfas.PdfAsFactory} instead. - * + * * @param workDirectory * The work directory. * @param registerProvider true: automatically registers IAIK JCE and ECC Provider; * false: providers will NOT be automatically registered, providers - * needed have to be registered by the API user + * needed have to be registered by the API user * @throws PdfAsException * Thrown, if the configuration cannot be processed. */ @@ -174,12 +181,12 @@ public class PdfAsObject implements PdfAs SettingsReader.initialize(path, path); reloadConfig(registerProvider); } - + /** * This constructor is for internal use only - use * {@link at.gv.egiz.pdfas.PdfAsFactory} instead. * Note: IAIK JCE and IAIK ECC security providers are automatically registered. - * + * * @throws PdfAsException * Thrown, if the configuration cannot be processed. */ @@ -187,7 +194,7 @@ public class PdfAsObject implements PdfAs { this(null); } - + /** * @see at.gv.egiz.pdfas.api.PdfAs#reloadConfig() */ @@ -197,11 +204,11 @@ public class PdfAsObject implements PdfAs SettingsReader.createInstance(); SignatureTypes.createInstance(); } - + /** * @param registerProvider true: automatically registers IAIK JCE and ECC Provider; * false: providers will NOT be automatically registered, providers - * needed have to be registered by the API user + * needed have to be registered by the API user * @see at.gv.egiz.pdfas.api.PdfAs#reloadConfig() */ private void reloadConfig(boolean registerProvider) throws PdfAsException @@ -228,7 +235,7 @@ public class PdfAsObject implements PdfAs List profileInformation = new ArrayList(profiles.size()); String default_type = settings.getValueFromKey(SignatureTypes.DEFAULT_TYPE); - + Iterator it = profiles.iterator(); while (it.hasNext()) { @@ -240,14 +247,17 @@ public class PdfAsObject implements PdfAs final String profileDescription = settings.getSetting("sig_obj." + profileId + "." + SignatureTypes.SIG_DESCR, null); boolean isDefault = (default_type != null && default_type.equals(profileId)); + + State state = State.fromString(settings.getSetting(SignatureTypes.TYPES + "." + profileId)); + // modified by tknall - SignatureProfileImpl signatureProfile = new SignatureProfileImpl(profileId, profileDescription, moaKeyIdentifier, isDefault); + SignatureProfileImpl signatureProfile = new SignatureProfileImpl(profileId, state, profileDescription, moaKeyIdentifier, isDefault); // start - added by tknall - + // signature entries relevant to the search algorithm Properties signatureEntries = new Properties(); - + // search for table entries String parentPropertyKey = "sig_obj." + profileId + ".table"; log.debug("Looking for subkeys of \"" + parentPropertyKey + "\"."); @@ -275,7 +285,7 @@ public class PdfAsObject implements PdfAs } } } - + // search for table entries parentPropertyKey = "sig_obj." + profileId + ".key"; log.debug("Looking for subkeys of \"" + parentPropertyKey + "\"."); @@ -295,46 +305,89 @@ public class PdfAsObject implements PdfAs } } } - + // set properties signatureProfile.setSignatureBlockEntries(signatureEntries); // stop - added by tknall - + profileInformation.add(signatureProfile); } return profileInformation; } - /** - * @see at.gv.egiz.pdfas.api.PdfAs#sign(at.gv.egiz.pdfas.api.sign.SignParameters) - */ - public SignResult sign(SignParameters signParameters) throws PdfAsException + public SignResult sign(SignParameters signParameters) throws PdfAsException { + // prepare log data + StatisticData sd = new StatisticData(signParameters); + StopWatch sw = new StopWatch(); + try { + sw.start(); + return performSign(signParameters); + } catch (PdfAsException e) { + sd.setException(e); + throw e; + } catch (RuntimeException e) { + sd.setException(e); + throw e; + } finally { + sw.stop(); + sd.setDuration(sw.getTime()); + statLog.log(sd); + } + } + + private SignResult performSign(SignParameters signParameters) throws PdfAsException { CheckHelper.checkSignParameters(signParameters, false); - + + // make sure that the selected profile is allowed to be used for signature + SettingsReader settings = SettingsReader.getInstance(); + if (!State.fromString(settings.getSetting(SignatureTypes.TYPES + "." + signParameters.getSignatureProfileId())) + .canSign()) { + throw new SignatureException(ErrorCode.SIGNATURE_PROFILE_IS_NOT_ALLOWED_FOR_SIGNATURE, + "The signature profile '" + signParameters.getSignatureProfileId() + + "' is not allowed to be used for signature."); + } + try { - SignatureDetailInformation signatorInfo = prepareSign(signParameters); - - return sign(signParameters, signatorInfo); - + + SignatureDetailInformation signatorInfo = performPrepareSign(signParameters); + return performSign(signParameters, signatorInfo); + } catch (java.lang.OutOfMemoryError e) { throw new OutOfMemoryException(ErrorCode.OUT_OF_MEMORY_ERROR, "Insufficient memory allocated to virtual machine. Start Java with parameters \"-Xms128m -Xmx786m -XX:MaxPermSize=256m\".", e); } } - /** - * @see at.gv.egiz.pdfas.api.PdfAs#verify(at.gv.egiz.pdfas.api.verify.VerifyParameters) - */ - public VerifyResults verify(VerifyParameters verifyParameters) throws PdfAsException + public VerifyResults verify(VerifyParameters verifyParameters) throws PdfAsException { + // prepare log data + StatisticData sd = new StatisticData(verifyParameters); + StopWatch sw = new StopWatch(); + try { + sw.start(); + return performVerify(verifyParameters); + } catch (PdfAsException e) { + sd.setException(e); + throw e; + } catch (RuntimeException e) { + sd.setException(e); + throw e; + } finally { + sw.stop(); + sd.setDuration(sw.getTime()); + statLog.log(sd); + } + } + + private VerifyResults performVerify(VerifyParameters verifyParameters) throws PdfAsException { CheckHelper.checkVerifyParameters(verifyParameters); AnalyzeParameters ap = new AnalyzeParameters(); fillAnalyzeParametersWithVerifyParameters(ap, verifyParameters); - AnalyzeResult analyzeResult = analyze(ap); + AnalyzeResult analyzeResult = performAnalyze(ap); if (verifyParameters.getSignatureToVerify() != Constants.VERIFY_ALL) { @@ -352,17 +405,17 @@ public class PdfAsObject implements PdfAs VerifyAfterAnalysisParameters vaap = new VerifyAfterAnalysisParameters(); vaap.setAnalyzeResult(analyzeResult); fillVerifyAfterAnalysisParametersWithVerifyParameters(vaap, verifyParameters); - VerifyResults res = verify(vaap); - + VerifyResults res = performVerify(vaap); + return res; - + } - + /** * Copies all adequate parameters from the {@link VerifyParameters} to the * {@link AnalyzeParameters}. - * + * * @param ap * The {@link AnalyzeParameters}. * @param vp @@ -385,7 +438,7 @@ public class PdfAsObject implements PdfAs /** * Copies all adequate parameters from the {@link VerifyParameters} to the * {@link VerifyAfterAnalysisParameters}. - * + * * @param vaap * The {@link VerifyAfterAnalysisParameters}. * @param vp @@ -406,10 +459,30 @@ public class PdfAsObject implements PdfAs reconstructParams.setSignatureDevice(verifyAfterAnalysisParameters.getSignatureDevice()); } - /** - * @see at.gv.egiz.pdfas.api.PdfAs#analyze(at.gv.egiz.pdfas.api.analyze.AnalyzeParameters) - */ - public AnalyzeResult analyze(AnalyzeParameters analyzeParameters) throws PdfAsException + public AnalyzeResult analyze(AnalyzeParameters analyzeParameters) throws PdfAsException { + // prepare log data + StatisticData sd = new StatisticData(analyzeParameters); + StopWatch sw = new StopWatch(); + try { + sw.start(); + return performAnalyze(analyzeParameters); + } catch (PdfAsException e) { + sd.setException(e); + throw e; + } catch (RuntimeException e) { + sd.setException(e); + throw e; + } finally { + sw.stop(); + sd.setDuration(sw.getTime()); + // only log in case of error + if (sd.isError()) { + statLog.log(sd); + } + } + } + + private AnalyzeResult performAnalyze(AnalyzeParameters analyzeParameters) throws PdfAsException { CheckHelper.checkAnalyzeParameters(analyzeParameters); @@ -429,10 +502,12 @@ public class PdfAsObject implements PdfAs assumeOnlySB = false; } VerificationFilterParameters parameters = new VerificationFilterParametersImpl(binaryOnly, assumeOnlySB, parametersConfig.scanForOldSignatures()); - + at.gv.egiz.pdfas.framework.input.DataSource inputDataSource = null; if (analyzeParameters.getDocument().getMimeType().equals("application/pdf")) { + // check document permissions + PDFASUtils.createPdfReaderCheckingPermissions(analyzeParameters.getDocument()); inputDataSource = new PdfDataSourceAdapter(analyzeParameters.getDocument()); } else @@ -447,13 +522,13 @@ public class PdfAsObject implements PdfAs } } assert inputDataSource != null; - + try { ExtractionStage es = new ExtractionStage(); DataSourceHolder dsh = new DataSourceHolder(inputDataSource); List signature_holders = es.extractSignatureHolders(dsh, parameters); - - + + // List sigInfs = new ArrayList(signature_holders.size()); List sigInfs = new ArrayList(); List noSigs = new ArrayList(); @@ -461,24 +536,24 @@ public class PdfAsObject implements PdfAs while (it.hasNext()) { SignatureHolder sh = (SignatureHolder)it.next(); - + if(sh instanceof NoSignatureHolder) { noSigs.add(sh); } else { - + SignatureInformation si = new SignatureInformationAdapter(sh); sigInfs.add(si); - if (analyzeParameters.isReturnNonTextualObjects()) { - si.setNonTextualObjects(doExtractNonTexualObjects(sh, (PdfDataSource) dsh.getDataSource())); - } - + if (analyzeParameters.isReturnNonTextualObjects()) { + si.setNonTextualObjects(doExtractNonTexualObjects(sh, (PdfDataSource) dsh.getDataSource())); + } + } } return new AnalyzeResultImpl(sigInfs, noSigs, parameters.hasBeenCorrected()); } catch (java.lang.OutOfMemoryError e) { throw new OutOfMemoryException(ErrorCode.OUT_OF_MEMORY_ERROR, "Insufficient memory allocated to virtual machine. Start Java with parameters \"-Xms128m -Xmx786m -XX:MaxPermSize=256m\".", e); } - + } private List doExtractNonTexualObjects(SignatureHolder sh, PdfDataSource pdfDataSource) { @@ -499,30 +574,47 @@ public class PdfAsObject implements PdfAs } } -/** - * @see at.gv.egiz.pdfas.api.PdfAs#verify(at.gv.egiz.pdfas.api.verify.VerifyAfterAnalysisParameters) - */ - public VerifyResults verify(VerifyAfterAnalysisParameters verifyAfterAnalysisParameters) throws PdfAsException + public VerifyResults verify(VerifyAfterAnalysisParameters verifyAfterAnalysisParameters) throws PdfAsException { + // prepare log data + StatisticData sd = new StatisticData(verifyAfterAnalysisParameters); + StopWatch sw = new StopWatch(); + try { + sw.start(); + return performVerify(verifyAfterAnalysisParameters); + } catch (PdfAsException e) { + sd.setException(e); + throw e; + } catch (RuntimeException e) { + sd.setException(e); + throw e; + } finally { + sw.stop(); + sd.setDuration(sw.getTime()); + statLog.log(sd); + } + } + + private VerifyResults performVerify(VerifyAfterAnalysisParameters verifyAfterAnalysisParameters) throws PdfAsException { CheckHelper.checkVerifyAfterAnalysisParameters(verifyAfterAnalysisParameters); List signatures = verifyAfterAnalysisParameters.getAnalyzeResult().getSignatures(); - + // added by tknall if (signatures == null || signatures.isEmpty()) { throw new PDFDocumentException(ErrorCode.DOCUMENT_NOT_SIGNED, "PDF document not signed."); //$NON-NLS-1$ } - + ReconstructXMLDsigAfterAnalysisParameters rxaap = new ReconstructXMLDsigAfterAnalysisParameters(); fillReconstructXMLDsigAfterAnalysisParametersWithVerifyAfterAnalysisParameters(rxaap, verifyAfterAnalysisParameters); - ReconstructXMLDsigResult reconstructResult = reconstructXMLDSIG(rxaap); - + ReconstructXMLDsigResult reconstructResult = performReconstructXMLDSIG(rxaap); + VerifyAfterReconstructXMLDsigParameters varxp = new VerifyAfterReconstructXMLDsigParameters(); fillVerifyAfterReconstructXMLDsigParametersWithVerifyAfterAnalysisParameters(varxp, verifyAfterAnalysisParameters); varxp.setReconstructXMLDsigResult(reconstructResult); - - return verify(varxp); - + + return performVerify(varxp); + } protected void fillVerifyAfterReconstructXMLDsigParametersWithVerifyAfterAnalysisParameters( @@ -534,31 +626,75 @@ public class PdfAsObject implements PdfAs varxp.setVerifySignatureIndex(verifyAfterAnalysisParameters.getVerifySignatureIndex()); } - /** - * @see PdfAs#reconstructXMLDSIG(ReconstructXMLDsigParameters) - */ public ReconstructXMLDsigResult reconstructXMLDSIG( + ReconstructXMLDsigParameters reconstructXMLDsigParameters) + throws PdfAsException { + // prepare log data + StatisticData sd = new StatisticData(reconstructXMLDsigParameters); + StopWatch sw = new StopWatch(); + try { + sw.start(); + return performReconstructXMLDSIG(reconstructXMLDsigParameters); + } catch (PdfAsException e) { + sd.setException(e); + throw e; + } catch (RuntimeException e) { + sd.setException(e); + throw e; + } finally { + sw.stop(); + sd.setDuration(sw.getTime()); + // only log in case of error + if (sd.isError()) { + statLog.log(sd); + } + } + } + + private ReconstructXMLDsigResult performReconstructXMLDSIG( ReconstructXMLDsigParameters reconstructXMLDsigParameters) throws PdfAsException { - + AnalyzeParameters analyzeParameters = new AnalyzeParameters(); fillAnalyzeParametersWithReconstructXMLDsigParameters(analyzeParameters, reconstructXMLDsigParameters); - AnalyzeResult ar = analyze(analyzeParameters); - + AnalyzeResult ar = performAnalyze(analyzeParameters); + ReconstructXMLDsigAfterAnalysisParameters rxaap = new ReconstructXMLDsigAfterAnalysisParameters(); rxaap.setSignatureDevice(reconstructXMLDsigParameters.getSignatureDevice()); rxaap.setAnalyzeResult(ar); - - return reconstructXMLDSIG(rxaap); + + return performReconstructXMLDSIG(rxaap); } - /** - * @see PdfAs#reconstructXMLDSIG(ReconstructXMLDsigAfterAnalysisParameters) - */ public ReconstructXMLDsigResult reconstructXMLDSIG( + ReconstructXMLDsigAfterAnalysisParameters reconstructXMLDsigParameters) + throws PdfAsException { + // prepare log data + StatisticData sd = new StatisticData(reconstructXMLDsigParameters); + StopWatch sw = new StopWatch(); + try { + sw.start(); + return performReconstructXMLDSIG(reconstructXMLDsigParameters); + } catch (PdfAsException e) { + sd.setException(e); + throw e; + } catch (RuntimeException e) { + sd.setException(e); + throw e; + } finally { + sw.stop(); + sd.setDuration(sw.getTime()); + // only log in case of error + if (sd.isError()) { + statLog.log(sd); + } + } + } + + private ReconstructXMLDsigResult performReconstructXMLDSIG( ReconstructXMLDsigAfterAnalysisParameters reconstructXMLDsigParameters) throws PdfAsException { - + AnalyzeResult ar = reconstructXMLDsigParameters.getAnalyzeResult(); List extendedSignatureInfos = new Vector(); for (int i = 0; i < ar.getSignatures().size(); i++) @@ -572,31 +708,52 @@ public class PdfAsObject implements PdfAs // don't care for connector exceptions because of mutli signs. they are handled during verify extendedSignatureInfos.add(new ExtendedSignatureInformation(si, null)); } - + } return new ReconstructXMLDsigResult(extendedSignatureInfos, reconstructXMLDsigParameters.getSignatureDevice()); } + public VerifyResults verify(VerifyAfterReconstructXMLDsigParameters verifyAfterReconstructXMLDsigParameters) + throws PdfAsException { + // prepare log data + StatisticData sd = new StatisticData(verifyAfterReconstructXMLDsigParameters); + StopWatch sw = new StopWatch(); + try { + sw.start(); + return performVerify(verifyAfterReconstructXMLDsigParameters); + } catch (PdfAsException e) { + sd.setException(e); + throw e; + } catch (RuntimeException e) { + sd.setException(e); + throw e; + } finally { + sw.stop(); + sd.setDuration(sw.getTime()); + statLog.log(sd); + } + } + /** * @see PdfAs#verify(VerifyAfterReconstructXMLDsigParameters) */ - public VerifyResults verify( + private VerifyResults performVerify( VerifyAfterReconstructXMLDsigParameters verifyAfterReconstructXMLDsigParameters) throws PdfAsException { - + try { List extSignatures = verifyAfterReconstructXMLDsigParameters.getReconstructXMLDsigResult().getExtendedSignatures(); String signatureDevice = verifyAfterReconstructXMLDsigParameters.getSignatureDevice(); if (signatureDevice == null){ signatureDevice = verifyAfterReconstructXMLDsigParameters.getReconstructXMLDsigResult().getDevice(); } - List results = PdfAS.verifyExtendedSignatureHolders(extSignatures, - signatureDevice, - verifyAfterReconstructXMLDsigParameters.isReturnHashInputData(), + List results = PdfAS.verifyExtendedSignatureHolders(extSignatures, + signatureDevice, + verifyAfterReconstructXMLDsigParameters.isReturnHashInputData(), verifyAfterReconstructXMLDsigParameters.getVerificationTime(), verifyAfterReconstructXMLDsigParameters.getVerifySignatureIndex()); - + List vrs = new ArrayList(results.size()); - + int verifySignatureIndex = verifyAfterReconstructXMLDsigParameters.getVerifySignatureIndex(); if (verifySignatureIndex < 0) { @@ -605,23 +762,23 @@ public class PdfAsObject implements PdfAs SignatureResponse response = (SignatureResponse) results.get(i); ExtendedSignatureInformation extSigInfo = (ExtendedSignatureInformation)extSignatures.get(i); SignatureHolder holder = (SignatureHolder) extSigInfo.getSignatureInformation().getInternalSignatureInformation(); - + VerifyResult vr = new VerifyResultAdapter(response, holder, verifyAfterReconstructXMLDsigParameters.getVerificationTime(), extSigInfo.getXmlDsigData()); vr.setNonTextualObjects( extSigInfo.getSignatureInformation().getNonTextualObjects()); - + vrs.add(vr); } }else{ SignatureResponse response = (SignatureResponse) results.get(0); ExtendedSignatureInformation extSigInfo = (ExtendedSignatureInformation)extSignatures.get(verifySignatureIndex); SignatureHolder holder = (SignatureHolder) extSigInfo.getSignatureInformation().getInternalSignatureInformation(); - + VerifyResult vr = new VerifyResultAdapter(response, holder, verifyAfterReconstructXMLDsigParameters.getVerificationTime(), extSigInfo.getXmlDsigData()); vr.setNonTextualObjects( extSigInfo.getSignatureInformation().getNonTextualObjects()); - + vrs.add(vr); } - + VerifyResultsImpl verifyResults = new VerifyResultsImpl(vrs); return verifyResults; } catch (java.lang.OutOfMemoryError e) { @@ -632,7 +789,7 @@ public class PdfAsObject implements PdfAs public DynamicSignatureProfile createDynamicSignatureProfile(String parentProfile, DynamicSignatureLifetimeEnum mode) { return DynamicSignatureProfileImpl.createFromParent(null, parentProfile, mode); } - + public DynamicSignatureProfile createEmptyDynamicSignatureProfile(DynamicSignatureLifetimeEnum mode) { return DynamicSignatureProfileImpl.createEmptyProfile(null, mode); } @@ -648,19 +805,44 @@ public class PdfAsObject implements PdfAs public DynamicSignatureProfile createEmptyDynamicSignatureProfile(String myUniqueName, DynamicSignatureLifetimeEnum mode) { - return DynamicSignatureProfileImpl.createEmptyProfile(myUniqueName, mode); + return DynamicSignatureProfileImpl.createEmptyProfile(myUniqueName, mode); + } + + public SignatureDetailInformation prepareSign(SignParameters signParameters) throws PdfAsException { + // prepare log data + StatisticData sd = new StatisticData(signParameters); + StopWatch sw = new StopWatch(); + try { + sw.start(); + return performPrepareSign(signParameters); + } catch (PdfAsException e) { + sd.setException(e); + throw e; + } catch (RuntimeException e) { + sd.setException(e); + throw e; + } finally { + sw.stop(); + sd.setDuration(sw.getTime()); + // only log in case of error + if (sd.isError()) { + statLog.log(sd); + } + } } /** * @see PdfAs#prepareSign(SignParameters) */ - public SignatureDetailInformation prepareSign(SignParameters signParameters) throws PdfAsException { + private SignatureDetailInformation performPrepareSign(SignParameters signParameters) throws PdfAsException { CheckHelper.checkSignParameters(signParameters, true); - + if (signParameters.getProfileOverrideProperties() != null) { OverridePropertyHolder.setOverrideProps(signParameters.getProfileOverrideProperties()); } + PDFASUtils.createPdfReaderCheckingPermissions(signParameters.getDocument()); + signParameters.setDocument(PdfAS.applyStrictMode(signParameters.getDocument())); SettingsReader settings = SettingsReader.getInstance(); @@ -730,29 +912,29 @@ public class PdfAsObject implements PdfAs } else if (fromConfig) { arguments[0] = "width defined by the profile " + signParameters.getSignatureProfileId(); } - log.warn(MessageFormat.format(msg, arguments)); + log.warn(MessageFormat.format(msg, (Object[]) arguments)); } } - + Signator signator = createSignator(signParameters.getSignatureType()); SignatorInformation signatorInfo = signator.prepareSign( - new PdfDataSourceAdapter(signParameters.getDocument()), - signParameters.getSignatureProfileId(), - pos, + new PdfDataSourceAdapter(signParameters.getDocument()), + signParameters.getSignatureProfileId(), + pos, signParameters.getTimeStamperImpl()); - + SignatureDetailInformationImpl ret = new SignatureDetailInformationImpl(); ret.setSignatorInformation(signatorInfo); return ret; } - + private SignaturePlaceholderData getSignaturePlaceholder(SignParameters signParameters, String defaultProfile) throws SettingsException, PDFDocumentException, PlaceholderExtractionException { SignaturePlaceholderData spd = null; SignaturePlaceholderContext.setSignaturePlaceholderData(null); - + SettingsReader settings = SettingsReader.getInstance(); // check sig_obj.PROFILEID.enable_placeholder_search @@ -766,15 +948,15 @@ public class PdfAsObject implements PdfAs // check global enable_placeholder_search configFileActivedString = settings.getValueFromKey(ENABLE_PLACEHOLDER_SEARCH_KEY); } - + Boolean configFileActived = null; if (configFileActivedString != null) configFileActived = Boolean.valueOf(configFileActivedString); Boolean signParamsActivated = signParameters.isCheckForPlaceholder(); - + boolean enableSearch; - + if (signParamsActivated != null) { enableSearch = signParamsActivated.booleanValue(); @@ -788,8 +970,8 @@ public class PdfAsObject implements PdfAs if (enableSearch) { spd = SignaturePlaceholderExtractor.extract( - signParameters.getDocument().createInputStream(), - signParameters.getPlaceholderId(), + signParameters.getDocument().createInputStream(), + signParameters.getPlaceholderId(), signParameters.getPlaceholderMatchMode()); } return spd; @@ -809,15 +991,36 @@ public class PdfAsObject implements PdfAs { signatorId = SignatorFactory.MOST_RECENT_DETACHEDTEXT_SIGNATOR_ID; } - + return at.gv.egiz.pdfas.framework.SignatorFactory.createSignator(signatorId); } - public SignResult sign(SignParameters signParameters, SignatureDetailInformation signatorInfo) + public SignResult sign(SignParameters signParameters, SignatureDetailInformation signatorInfo) + throws PdfAsException { + // prepare log data + StatisticData sd = new StatisticData(signParameters); + StopWatch sw = new StopWatch(); + try { + sw.start(); + return performSign(signParameters, signatorInfo); + } catch (PdfAsException e) { + sd.setException(e); + throw e; + } catch (RuntimeException e) { + sd.setException(e); + throw e; + } finally { + sw.stop(); + sd.setDuration(sw.getTime()); + statLog.log(sd); + } + } + + private SignResult performSign(SignParameters signParameters, SignatureDetailInformation signatorInfo) throws PdfAsException { CheckHelper.checkSignParametersForSignAfterPrepare(signParameters, false); - + if (signParameters.getProfileOverrideProperties() != null) { OverridePropertyHolder.setOverrideProps(signParameters.getProfileOverrideProperties()); } @@ -832,34 +1035,34 @@ public class PdfAsObject implements PdfAs SignatureData sd = new SignatureDataImpl(new PdfDataSourceAdapter(signatorInfo.getSignatureData()), signatorInfo.getSignatureData().getMimeType()); SignSignatureObject sso = PdfAS.sign(sd, c, signParameters.getTimeStamperImpl()); ((SignatureDetailInformationImpl)signatorInfo).setSignSignatureObject(sso); - + return finishSign(signParameters, signatorInfo); } - public SignResult finishSign(SignParameters signParameters, SignatureDetailInformation signatureDetailInformation) + public SignResult finishSign(SignParameters signParameters, SignatureDetailInformation signatureDetailInformation) throws PdfAsException { try { CheckHelper.checkSignParametersForSignAfterPrepare(signParameters, true); CheckHelper.checkSignatureDetailInformation(signatureDetailInformation); - + if (signParameters.getProfileOverrideProperties() != null) { OverridePropertyHolder.setOverrideProps(signParameters.getProfileOverrideProperties()); } - + Signator signator = createSignator(signParameters.getSignatureType()); SignatorInformation signatorInfo = ((SignatureDetailInformationImpl)signatureDetailInformation).getSignatorInfo(); signator.finishSign(signatorInfo, new DataSinkAdapter(signParameters.getOutput())); return new SignResultImpl( - signParameters.getOutput(), + signParameters.getOutput(), signatorInfo.getSignSignatureObject().getX509Certificate(), - new ActualSignaturePositionAdapter(signatorInfo.getActualTablePos()), + new ActualSignaturePositionAdapter(signatorInfo.getActualTablePos()), signatorInfo.getNonTextualObjects()); } finally { OverridePropertyHolder.removeProperties(); DynamicSignatureProfileImpl.disposeLocalProfile(); } } - + } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/api/commons/DataSinkAdapter.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/api/commons/DataSinkAdapter.java index 2aee44f..b91d5a9 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/api/commons/DataSinkAdapter.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/api/commons/DataSinkAdapter.java @@ -30,7 +30,7 @@ import at.gv.egiz.pdfas.api.io.DataSink; /** * Adapter that converts an API DataSink to a framework DataSink. - * + * * @author wprinz */ public class DataSinkAdapter implements at.gv.egiz.pdfas.framework.output.DataSink @@ -42,7 +42,7 @@ public class DataSinkAdapter implements at.gv.egiz.pdfas.framework.output.DataSi /** * Constructor. - * + * * @param apiDataSink * The API DataSink to be adapted to a framework DataSink. */ @@ -62,7 +62,6 @@ public class DataSinkAdapter implements at.gv.egiz.pdfas.framework.output.DataSi } catch (IOException e) { - e.printStackTrace(); throw new RuntimeException(e); } } @@ -79,7 +78,6 @@ public class DataSinkAdapter implements at.gv.egiz.pdfas.framework.output.DataSi } catch (IOException e) { - e.printStackTrace(); throw new RuntimeException(e); } } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/api/commons/SignatureProfileImpl.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/api/commons/SignatureProfileImpl.java index 35d8c17..1df1a65 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/api/commons/SignatureProfileImpl.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/api/commons/SignatureProfileImpl.java @@ -28,6 +28,7 @@ import java.util.Properties; import org.apache.commons.lang.builder.ToStringBuilder; import at.gv.egiz.pdfas.api.commons.SignatureProfile; +import at.knowcenter.wag.egov.egiz.sig.SignatureTypes.State; /** * Holds the data of a signature profile. @@ -61,6 +62,13 @@ public class SignatureProfileImpl implements SignatureProfile { * true if this is the default profile, false otherwise. */ protected boolean defaultProfile = false; + + /** + * The state of the signature profile. Signature profiles may be disabled (OFF), allowed for signature only + * (SIGN_ONLY), allowed for verification only (VERIFY_ONLY) or just enabled (ON). + * Default value here of ON in order to assure backward compatibility. + */ + private State state = State.ON; /** * Constructor. @@ -88,13 +96,25 @@ public class SignatureProfileImpl implements SignatureProfile { * @param isDefault */ public SignatureProfileImpl(String profileId, String profileDescription, String moaKeyIdentifier, boolean isDefault) { - this.profileId = profileId; - this.moaKeyIdentifier = moaKeyIdentifier; - this.profileDescription = profileDescription; - this.signatureBlockEntries = new Properties(); - this.defaultProfile = isDefault; + this(profileId, moaKeyIdentifier); + this.profileDescription = profileDescription; + this.defaultProfile = isDefault; } + /** + * Creates a new signature profile instance. + * @param profileId The profile identifier. + * @param state The profile's state. + * @param profileDescription The profile description. + * @param moaKeyIdentifier The MOA-SS key identifier. + * @param isDefault {@code true} if this is a default profile, {@code false} if not. + */ + public SignatureProfileImpl(String profileId, State state, String profileDescription, String moaKeyIdentifier, boolean isDefault) { + this(profileId, profileDescription, moaKeyIdentifier, isDefault); + this.state = state; + } + + /** * @see at.gv.egiz.pdfas.api.commons.SignatureProfile#getProfileId() */ @@ -140,19 +160,22 @@ public class SignatureProfileImpl implements SignatureProfile { return this.profileDescription; } - public String toString() { - return new ToStringBuilder(this) - .append("profileId", this.profileId) - .append("profileDescription", this.profileDescription) - .append("moaKeyIdentifier", this.moaKeyIdentifier) - .toString(); - } + @Override + public String toString() { + return "SignatureProfileImpl [profileId=" + profileId + ", state=" + state + ", profileDescription=" + + profileDescription + ", moaKeyIdentifier=" + moaKeyIdentifier + ", defaultProfile=" + defaultProfile + + "]"; + } - /** +/** * */ public boolean isDefault() { return this.defaultProfile; } + public State getState() { + return state; + } + } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/input/correction/InternalCorrector.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/input/correction/InternalCorrector.java index eaa6b7f..b193936 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/input/correction/InternalCorrector.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/input/correction/InternalCorrector.java @@ -26,6 +26,7 @@ package at.gv.egiz.pdfas.impl.input.correction; import java.io.ByteArrayOutputStream; import java.io.IOException; +import at.gv.egiz.pdfas.api.io.DataSource; import at.gv.egiz.pdfas.exceptions.ErrorCode; import at.gv.egiz.pdfas.exceptions.framework.CorrectorException; import at.gv.egiz.pdfas.framework.input.PdfDataSource; @@ -40,7 +41,7 @@ import com.lowagie.text.pdf.PdfStamper; /** * Corrects a document using iText. - * + * * @author wprinz */ public class InternalCorrector implements Corrector @@ -53,11 +54,9 @@ public class InternalCorrector implements Corrector { try { - byte[] pdf = document.getAsByteArray(); - PdfReader reader = new PdfReader(pdf); - PDFASUtils.checkReaderPermissions(reader); + PdfReader reader = PDFASUtils.createPdfReaderCheckingPermissions(document); - ByteArrayOutputStream baos = new ByteArrayOutputStream(pdf.length); + ByteArrayOutputStream baos = new ByteArrayOutputStream(document.getLength()); PdfStamper stamper = new PdfStamper(reader, baos, '\0', false); stamper.close(); diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java index 0af6538..a3ff856 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java @@ -83,7 +83,7 @@ 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 @@ -92,7 +92,7 @@ import com.lowagie.text.pdf.PdfPTable; *

* 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 { @@ -204,13 +204,19 @@ public class BinarySignator_1_0_0 implements Signator { // on a new page, prevent pdf-as to do that, because why should make // a new page just for an invisible block // added by rpiazzi + + // disabled by dti: code fixes certain cases with invisible signatures but prevents usage of minimal + // signature profiles; the actual invisible signature issue has fixed in method + // adjustSignatureTableandCalculatePosition(...) in class at.knowcenter.wag.egov.egiz.PdfAS + /* if (signature_object.getSignatureTypeDefinition().getInvisibleFieldDefinitions().size() == SignatureTypes.REQUIRED_SIG_KEYS.length) { if (pi.isMakeNewPage()) { int pageNumber = pi.getPage(); pi = new PositioningInstruction(false, pageNumber - 1, 0, 0); } } - // end added + */ + // end added (rpiazzi) IncrementalUpdateInformation iui = IncrementalUpdateHelper.writeIncrementalUpdate(pdfDataSource, pdf_table, profile, pi, variable_field_definitions, all_field_definitions, invisible_field_definitions, @@ -318,7 +324,7 @@ public class BinarySignator_1_0_0 implements Signator { /** * Reads the signature values from the signed signature object and fills the corresponding value in the Replaces * array. - * + * * @param iui * The IncrementalUpdateInformation. */ @@ -394,7 +400,7 @@ public class BinarySignator_1_0_0 implements Signator { /** * Forms the SignatureData to be used for signing. - * + * * @param iui * The IncrementalUpdateInformation. * @return Returns the SignatureData to be used for signing. diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/detached/DetachedTextualSignator_1_0_0.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/detached/DetachedTextualSignator_1_0_0.java index 7fcdb2a..16e2718 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/detached/DetachedTextualSignator_1_0_0.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/detached/DetachedTextualSignator_1_0_0.java @@ -41,7 +41,7 @@ import at.knowcenter.wag.egov.egiz.sig.connectors.bku.BKUPostConnection; /** * Signs a document textually. - * + * *

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

@@ -49,7 +49,7 @@ import at.knowcenter.wag.egov.egiz.sig.connectors.bku.BKUPostConnection; * 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 @@ -93,7 +93,7 @@ public class DetachedTextualSignator_1_0_0 extends TextualSignator_1_0_0 // 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$ @@ -119,7 +119,6 @@ public class DetachedTextualSignator_1_0_0 extends TextualSignator_1_0_0 // } // catch (UnsupportedEncodingException e) // { - // e.printStackTrace(); // throw new PDFDocumentException(300, e); // } // } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterImpl.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterImpl.java index 3f0f482..0e0da78 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterImpl.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterImpl.java @@ -80,7 +80,7 @@ public class VerificationFilterImpl implements VerificationFilter public static final String SUPRESS_EXCEPTION_WHEN_LAST_UIBLOCK_IS_NO_SIGNATURE = "supress_exception_when_last_iublock_is_no_signature"; public static final String BINARY_ONLY = "binary_only"; public static final String ASSUME_ONLY_SIGNATURE_BLOCKS = "assume_only_signature_blocks"; - + /** * @see at.gv.egiz.pdfas.framework.vfilter.VerificationFilter#extractSignatureHolders(at.gv.egiz.pdfas.framework.input.PdfDataSource, * java.util.List, @@ -97,7 +97,7 @@ public class VerificationFilterImpl implements VerificationFilter log.debug("Original IU blocks: " + blocks.size()); debugIUBlocks(blocks); } - + unrollLinearization(blocks); if (log.isDebugEnabled()) @@ -106,7 +106,7 @@ public class VerificationFilterImpl implements VerificationFilter debugIUBlocks(blocks); } - + SettingsReader settings; try { settings = SettingsReader.getInstance(); @@ -115,7 +115,7 @@ public class VerificationFilterImpl implements VerificationFilter } String check_doc = settings.getSetting(CHECK_DOCUMENT, "false"); - // check document for textual sigs here here if binary_only is set + // check document for textual sigs here here if binary_only is set if ("true".equalsIgnoreCase(check_doc) && parameters.extractBinarySignaturesOnly()) { @@ -125,7 +125,7 @@ public class VerificationFilterImpl implements VerificationFilter log.debug("Skipping checkDocument for textual sigs."); } // end add - + List signatureHolderChain = null; if (parameters.extractBinarySignaturesOnly()) @@ -156,11 +156,11 @@ public class VerificationFilterImpl implements VerificationFilter } } - + log.trace("extractSignaturHolders finished (" + (signatureHolderChain != null ? signatureHolderChain.size() : 0) + " elements)."); sw.stop(); log.debug("extractSignatureHolders: " + sw.getTime() + "ms."); - + return signatureHolderChain; } @@ -194,12 +194,12 @@ public class VerificationFilterImpl implements VerificationFilter foundSignatures = extractNewSignaturesFromText(normalizedText); } - + List textOnlySignatures = filterOutBinarySignatures(foundSignatures); - + return textOnlySignatures; } - + protected String normalizeText(String freetext) throws VerificationFilterException { try @@ -214,7 +214,7 @@ public class VerificationFilterImpl implements VerificationFilter /** * Removes the linearization footer from the list of update blocks. - * + * * @param blocks * The list of FooterParseResult objects in \prev order. */ @@ -263,15 +263,15 @@ public class VerificationFilterImpl implements VerificationFilter if (sao.oldSignature != null) { extractedSignatures.add(0, sao.oldSignature); - } + } } else { log.debug("extracting signatures from last partition..."); extractedSignatures = extractSignaturesFromPartition(pdf, lastTextPartition); } - - + + List signatureHolderChain = intermingleSignatures(binarySignatures, extractedSignatures); return signatureHolderChain; @@ -287,15 +287,15 @@ public class VerificationFilterImpl implements VerificationFilter Set binarySigValues = new HashSet(); Iterator iterator = binarySignatures.iterator(); while(iterator.hasNext()) { - + SignatureHolder sh = (SignatureHolder)iterator.next(); - + String sigVal = sh.getSignatureObject().getSignationValue(); binarySigValues.add(sigVal); } - + SignatureHolder oldSignature = null; - + //List originalPartitions = partitions; // This gives every IU block an own text partition // This allows text signatures to be found correctly if there are @@ -313,36 +313,36 @@ public class VerificationFilterImpl implements VerificationFilter } String check_doc = settings.getSetting(CHECK_DOCUMENT, "false"); boolean supressException = "true".equalsIgnoreCase(settings.getSetting(SUPRESS_EXCEPTION_WHEN_LAST_UIBLOCK_IS_NO_SIGNATURE, "false")); - + // flag indicating that the last IU-block of the document is a non-signature IU-block boolean lastBlockWasModified = false; - + // counter of all signatures (textual and binary) of this document int signatureCounter = 0; - + // counter of all textual signatures in this document int txtSigsSoFar = 0; - + // counter of all textual signatures in the current partition int txtSigsThisPartition = 0; - + List partitionResults = new ArrayList(partitions.size()); - List nshList = new ArrayList(); - + List nshList = new ArrayList(); + boolean sigFound = false; - + for (int i = 0; i < partitions.size(); i++) { Partition p = (Partition) partitions.get(i); - + // updating flag and counter boolean partitionContainsNewTextSignatures = true; txtSigsSoFar = txtSigsThisPartition; - + if (p instanceof TextPartition) { TextPartition tp = (TextPartition) p; - + List partitionResult = null; boolean scanThisPartitionForOldSignature = (i == 0) && scanForOldSignatures; @@ -356,34 +356,34 @@ public class VerificationFilterImpl implements VerificationFilter { partitionResult = extractSignaturesFromPartition(pdf, tp); } - - // binary signature blocks that have been detected as well are identified by comparing their signature values + + // binary signature blocks that have been detected as well are identified by comparing their signature values // with those stored in our Set above and are not considered for our IU-check List onlyTextSignatures = new ArrayList(); Iterator iter = partitionResult.iterator(); while(iter.hasNext()) { - + SignatureHolder sh = (SignatureHolder)iter.next(); if(!binarySigValues.contains(sh.getSignatureObject().getSignationValue())) { - + onlyTextSignatures.add(sh); } } - + // update signature counters txtSigsThisPartition = onlyTextSignatures.size(); int newTextSignatures = txtSigsThisPartition - txtSigsSoFar; signatureCounter = signatureCounter + newTextSignatures; - + // update sigFound flag if(txtSigsThisPartition > 0) { - + sigFound = true; } - + // TextPartition is only valid, if at least one more text signature has been found than in the previous text partition if(!(newTextSignatures > 0)) { - + partitionContainsNewTextSignatures = false; } @@ -391,39 +391,39 @@ public class VerificationFilterImpl implements VerificationFilter } else { // should be binary partition if(p instanceof BinaryPartition) { - + BinaryPartition binpart = (BinaryPartition)p; // updating counter and flag signatureCounter = signatureCounter + binpart.blocks.size(); sigFound = true; - + } } - + // if document checking is enabled, at least one signature has been found so far, we are dealing with a // non-signature IU-block if ((check_doc.equalsIgnoreCase("true"))&& (sigFound && !partitionContainsNewTextSignatures)) { - - nshList.add(new NoSignatureHolder(signatureCounter)); + + nshList.add(new NoSignatureHolder(signatureCounter)); lastBlockWasModified = true; - + } else { - + lastBlockWasModified = false; } - + } // throw an exception if the last update block does not contain a signature and signatures have been found in this document if (lastBlockWasModified) { if (!supressException) { - throw new VerificationFilterException(ErrorCode.MODIFIED_AFTER_SIGNATION, "The document has been modified after being signed."); + throw new VerificationFilterException(ErrorCode.MODIFIED_AFTER_SIGNATION, "The document has been modified after being signed."); } else { log.debug("The document has been modified after being signed. According to the configuration, no exception is thrown."); } } - + List extractedSignatures = new ArrayList(); Iterator it = partitionResults.iterator(); List prevPartitionResult = null; @@ -451,54 +451,54 @@ public class VerificationFilterImpl implements VerificationFilter } List signatureHolderChain = intermingleSignatures(binarySignatures, extractedSignatures); - + if (oldSignature != null) { signatureHolderChain.add(0, oldSignature); } - + // add the created NoSignatureHolders signatureHolderChain.addAll(nshList); - + return signatureHolderChain; } - + private void mergeSignatures(List oldList, List newList, List result) { - + for(int i=0; i < newList.size(); i++) { - + SignatureHolder currentNewSh = (SignatureHolder)newList.get(i); - + boolean shAlreadyPresentInOldList = false; int pos = -1; - + for(int j=0; j add result.add(currentNewSh); } - + } - - + + return; } - - + + protected List flattenOutTextPartitions (List partitions, List blocks) { - + List blockPartitions = new ArrayList(blocks.size()); Iterator it = partitions.iterator(); while (it.hasNext()) @@ -523,10 +523,10 @@ public class VerificationFilterImpl implements VerificationFilter blockPartitions.add(p); } } - + // note: successive binary blocks are still combined to one binary partition assert blockPartitions.size() <= blocks.size(); - + return blockPartitions; } @@ -536,15 +536,15 @@ public class VerificationFilterImpl implements VerificationFilter protected String extractText(PdfDataSource pdf, int endOfDocument, String encoding) throws PresentableException { - + log.debug("EXTRACTING TEXT (" + encoding + ")... end index = " + endOfDocument); - + DelimitedPdfDataSource dds = new DelimitedPdfDataSource(pdf, endOfDocument); //DelimitedInputStream dis = new DelimitedInputStream(pdf.createInputStream(), endOfDocument); return PdfAS.extractNormalizedTextTextual(dds, encoding); } - - + + protected List extractNewSignaturesFromText(String text) throws VerificationFilterException { try @@ -556,7 +556,7 @@ public class VerificationFilterImpl implements VerificationFilter throw new VerificationFilterException(e); } } - + protected List extractNewAndOldSignaturesFromText(String text) throws VerificationFilterException { SignaturesAndOld sao = extractSignaturesAndOld(text); @@ -651,11 +651,11 @@ public class VerificationFilterImpl implements VerificationFilter /** * 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. @@ -673,7 +673,7 @@ public class VerificationFilterImpl implements VerificationFilter String binary_only = settings.getSetting(BINARY_ONLY, "false"); String assume_sigs_only = settings.getSetting(ASSUME_ONLY_SIGNATURE_BLOCKS, "false"); boolean supressException = "true".equalsIgnoreCase(settings.getSetting(SUPRESS_EXCEPTION_WHEN_LAST_UIBLOCK_IS_NO_SIGNATURE, "false")); - + try { // PERF: extract binary signatures needs byte array @@ -689,15 +689,15 @@ public class VerificationFilterImpl implements VerificationFilter FooterParseResult fpr = (FooterParseResult) it.next(); assert fpr.next_index > prev_end; - if (VerificationFilterBinaryHelper.containsEGIZDict(data, fpr)) + PdfASID kz = null; + if (VerificationFilterBinaryHelper.containsEGIZDict(data, fpr) && + (kz = VerificationFilterBinaryHelper.extractKZFromEGIZBlock(data, fpr)) != null) { - PdfASID kz = VerificationFilterBinaryHelper.extractKZFromEGIZBlock(data, fpr); - // TODO dferbas hack baik test //kz = new PdfASID("urn:pdfsigfilter:bka.gv.at:binaer:v1.1.0"); - + Verificator verificator = VerificatorFactory.createBinaryVerificator(kz); - List binary_holders = verificator.parseBlock(pdf, data, fpr, prev_end); + List binary_holders = verificator.parseBlock(pdf, data, fpr, prev_end); binarySignatures.addAll(binary_holders); if(binary_holders.size() > 0) { @@ -707,21 +707,21 @@ public class VerificationFilterImpl implements VerificationFilter // an Exception is thrown here if: // 1) check_document is activated // 2) assume_only_signature_blocks is false - otherwise we permit updates - // 3) binary_only is true - otherwise updates are handled in method performFullConservative(). - // when binary-only is true, we can be sure that a block that contains no egiz-dict is no textual + // 3) binary_only is true - otherwise updates are handled in method performFullConservative(). + // when binary-only is true, we can be sure that a block that contains no egiz-dict is no textual // signature either but an illegal update, otherwise an Exception (doc contains textual sig) would have been thrown before // 4) a binary signature has been detected in a previous block - if(check_doc.equalsIgnoreCase("true") && + if(check_doc.equalsIgnoreCase("true") && binary_only.equalsIgnoreCase("true") && assume_sigs_only.equalsIgnoreCase("false") && - sig_detected) { - + sig_detected) { + if (!supressException) { - throw new VerificationFilterException(ErrorCode.MODIFIED_AFTER_SIGNATION, "The document has been modified after being signed."); + throw new VerificationFilterException(ErrorCode.MODIFIED_AFTER_SIGNATION, "The document has been modified after being signed."); } else { log.debug("The document has been modified after being signed. According to the configuration, no exception is thrown."); } - + } } @@ -749,7 +749,7 @@ public class VerificationFilterImpl implements VerificationFilter List extractedSignatures = extractNewSignaturesFromText(extractedText); log.debug("Extracting signatures finished."); log.debug("Number of found signatures: " + extractedSignatures.size()); - + if (extractedSignatures.size() > 0) { List cp1252SignaturesPositions = new ArrayList(); //boolean iscp1252Sig = false; @@ -760,19 +760,19 @@ public class VerificationFilterImpl implements VerificationFilter log.debug("found cp1252 signature"); cp1252SignaturesPositions.add(new Integer(i)); //iscp1252Sig = true; - //break; + //break; } - } + } if (cp1252SignaturesPositions.size() > 0) { log.debug("redo text and signature extraction with cp1252 encoding"); extractedText = extractText(pdf, endOfDocument, "cp1252"); log.debug("Extracting text finished."); - + log.debug("Extracting signatures:"); List cp1252ExtractedSignatures = extractNewSignaturesFromText(extractedText); log.debug("Extracting signatures finished."); log.debug("Number of found signatures: " + extractedSignatures.size()); - + if (cp1252ExtractedSignatures.size() != extractedSignatures.size()) { log.error("Invalid cp1252 signatures found. Skipping cp1252 compatibility."); } @@ -780,12 +780,12 @@ public class VerificationFilterImpl implements VerificationFilter for (int i = 0; i < cp1252SignaturesPositions.size(); i++) { int replaceIndex = ((Integer)cp1252SignaturesPositions.get(i)).intValue(); extractedSignatures.remove(replaceIndex); - extractedSignatures.add(replaceIndex, cp1252ExtractedSignatures.get(replaceIndex)); + extractedSignatures.add(replaceIndex, cp1252ExtractedSignatures.get(replaceIndex)); } } - + } - + if (log.isDebugEnabled()) { log.debug("extracted signatures:"); @@ -856,7 +856,7 @@ public class VerificationFilterImpl implements VerificationFilter } protected void checkBinaryOnly(PdfDataSource pdf, boolean considerOldSigs) throws VerificationFilterException { - + DelimitedPdfDataSource dds = new DelimitedPdfDataSource(pdf, pdf.getLength()); String text = null; try { @@ -864,10 +864,10 @@ public class VerificationFilterImpl implements VerificationFilter } catch (PresentableException e) { throw new VerificationFilterException(e); } - + List sigs = new ArrayList(); - - if(considerOldSigs) { + + if(considerOldSigs) { SignaturesAndOld sao = extractSignaturesAndOld(text); if(sao != null) { if(sao.newSignatures != null) { @@ -877,30 +877,30 @@ public class VerificationFilterImpl implements VerificationFilter sigs.add(sao.oldSignature); } } - } else { + } else { List signatures = extractSignatures(pdf, pdf.getLength()); if(signatures != null) { sigs.addAll(signatures); } } - + Iterator it = sigs.iterator(); while(it.hasNext()) { SignatureHolder current = (SignatureHolder)it.next(); if((current != null)&&(!current.getSignatureObject().isBinary())) { throw new VerificationFilterException(ErrorCode.NON_BINARY_SIGNATURES_PRESENT, "The document contains non-binary signatures."); } - } + } } - - + + protected static class SignaturesAndOld { public List newSignatures = null; public SignatureHolder oldSignature = null; } - + protected SignaturesAndOld extractSignaturesAndOld(String text) throws VerificationFilterException { try @@ -908,7 +908,7 @@ public class VerificationFilterImpl implements VerificationFilter 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."); @@ -929,7 +929,7 @@ public class VerificationFilterImpl implements VerificationFilter /** * 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 diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterBinaryHelper.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterBinaryHelper.java index 735b874..3093c36 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterBinaryHelper.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/vfilter/helper/VerificationFilterBinaryHelper.java @@ -48,7 +48,7 @@ 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 @@ -70,12 +70,12 @@ public final class VerificationFilterBinaryHelper /** * 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 @@ -94,7 +94,7 @@ public final class VerificationFilterBinaryHelper /** * Extracts the PDF AS ID of the egiz block. - * + * * @param pdf * The pdf. * @param block @@ -120,6 +120,13 @@ public final class VerificationFilterBinaryHelper IndirectObjectReference ior = egiz_dict_iorpr.ior; final int egiz_dict_offset = PDFUtils.getObjectOffsetFromXRefByIndirectObjectReference(block.xpr, ior); + + // fix dti: + if (egiz_dict_offset < 0) { + log.debug("Current ui block contains a /EGIZSigDict trailer entry but no respective xref table entry. This indicates that this ui block does not contain binary signatures."); + return null; + } + // logger_.debug("egiz_dict_offset = " + egiz_dict_offset); ObjectParseResult obj = PDFUtils.parseObject(pdf, egiz_dict_offset); @@ -140,7 +147,7 @@ public final class VerificationFilterBinaryHelper /** * Restores the Kennzeichnung String from an Array. - * + * * @param pdf * The PDF. * @param kz_apr @@ -174,7 +181,7 @@ public final class VerificationFilterBinaryHelper si.pdf = pdf; log.trace("Adding KZ: " + si.toString()); - + partition.add(si); } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/io/FileBasedTextBasedDataSource.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/io/FileBasedTextBasedDataSource.java index aaf99ea..918b13d 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/io/FileBasedTextBasedDataSource.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/io/FileBasedTextBasedDataSource.java @@ -33,7 +33,7 @@ import at.gv.egiz.pdfas.api.io.TextBased; /** * FileBased DataSource that is TextBased. - * + * * @author wprinz */ public class FileBasedTextBasedDataSource extends FileBasedDataSource implements TextBased @@ -113,7 +113,6 @@ public class FileBasedTextBasedDataSource extends FileBasedDataSource implements } catch (UnsupportedEncodingException e) { - e.printStackTrace(); throw new RuntimeException(e); } } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/io/StringTextBasedDataSource.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/io/StringTextBasedDataSource.java index 8fe0dd6..8234b2a 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/io/StringTextBasedDataSource.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/io/StringTextBasedDataSource.java @@ -32,9 +32,9 @@ import at.gv.egiz.pdfas.api.io.TextBased; /** * A String TextBased DataSource. - * + * * @author wprinz - * + * */ public class StringTextBasedDataSource implements DataSource, TextBased { @@ -74,7 +74,6 @@ public class StringTextBasedDataSource implements DataSource, TextBased } catch (UnsupportedEncodingException e) { - e.printStackTrace(); throw new RuntimeException(e); } } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderExtractor.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderExtractor.java index 8b8c22e..453b620 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderExtractor.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderExtractor.java @@ -71,7 +71,7 @@ import com.google.zxing.common.HybridBinarizer; /** * Extract all relevant information from a placeholder image. - * + * * @author exthex * */ @@ -80,10 +80,10 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine { * The log. */ private static Log log = LogFactory.getLog(SignaturePlaceholderExtractor.class); - + private List placeholders = new Vector(); private int currentPage = 0; - + private SignaturePlaceholderExtractor(String placeholderId, int placeholderMatchMode) throws IOException { super(ResourceLoader.loadProperties("at/gv/egiz/pdfas/placeholder/pdfbox-reader.properties", true)); @@ -93,7 +93,7 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine { * Search the document for placeholder images and possibly included * additional info.
* Searches only for the first placeholder page after page from top. - * + * * @param inputStream * @return all available info from the first found placeholder. * @throws PDFDocumentException if the document could not be read. @@ -103,14 +103,13 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine { throws PDFDocumentException, PlaceholderExtractionException { SignaturePlaceholderContext.setSignaturePlaceholderData(null); PDDocument doc = null; - try + try { try { doc = PDDocument.load(inputStream); } catch (IOException e) { throw new PDFDocumentException(ErrorCode.DOCUMENT_CANNOT_BE_READ, e); } - PDFASUtils.checkDocumentPermissions(doc); SignaturePlaceholderExtractor extractor; try { @@ -135,7 +134,7 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine { } catch (IOException e1) { throw new PDFDocumentException(ErrorCode.DOCUMENT_CANNOT_BE_READ, e1); } - + } if (extractor.placeholders.size() > 0){ SignaturePlaceholderData ret = matchPlaceholderDocument(extractor.placeholders, placeholderId, matchMode); @@ -146,7 +145,7 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine { if (matchMode == Constants.PLACEHOLDER_MATCH_MODE_STRICT) { throw new PlaceholderExtractionException(ErrorCode.SIGNATURE_PLACEHOLDER_EXTRACTION_FAILED, "no suitable placeholder found and STRICT matching mode requested."); } - + return null; } finally { if (doc != null) @@ -158,23 +157,23 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine { } } - + private static SignaturePlaceholderData matchPlaceholderDocument( List placeholders, String placeholderId, int matchMode) throws PlaceholderExtractionException { - + if (matchMode == Constants.PLACEHOLDER_MATCH_MODE_STRICT) throw new PlaceholderExtractionException(ErrorCode.SIGNATURE_PLACEHOLDER_EXTRACTION_FAILED, "no suitable placeholder found and STRICT matching mode requested."); - + if (placeholders.size() == 0) return null; - + for (int i = 0; i < placeholders.size(); i++) { SignaturePlaceholderData spd = (SignaturePlaceholderData)placeholders.get(i); if (spd.getId() == null) return spd; } - + if (matchMode == Constants.PLACEHOLDER_MATCH_MODE_LENIENT) return (SignaturePlaceholderData)placeholders.get(0); @@ -219,7 +218,7 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine { PDPage page = getCurrentPage(); Matrix ctm = getGraphicsState().getCurrentTransformationMatrix(); double rotationInRadians = (page.findRotation() * Math.PI)/180; - + AffineTransform rotation = new AffineTransform(); rotation.setToRotation( rotationInRadians ); AffineTransform rotationInverse = rotation.createInverse(); @@ -227,13 +226,13 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine { rotationInverseMatrix.setFromAffineTransform( rotationInverse ); Matrix rotationMatrix = new Matrix(); rotationMatrix.setFromAffineTransform( rotation ); - + Matrix unrotatedCTM = ctm.multiply( rotationInverseMatrix ); - + float x = unrotatedCTM.getXPosition(); float y = unrotatedCTM.getYPosition() + unrotatedCTM.getYScale(); float w = unrotatedCTM.getXScale(); - + String posString = "p:" + currentPage + ";x:" + x + ";y:" + y + ";w:" + w; try { @@ -259,7 +258,7 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine { /** * Checks an image if it is a placeholder for a signature. - * + * * @param image * @return * @throws IOException @@ -291,7 +290,7 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine { formats.add(BarcodeFormat.QR_CODE); hints.put(DecodeHintType.POSSIBLE_FORMATS, formats); result = new MultiFormatReader().decode(bitmap, hints); - + String text = result.getText(); String profile = null; String type = null; diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/utils/CsvUtils.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/utils/CsvUtils.java new file mode 100644 index 0000000..a78da93 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/utils/CsvUtils.java @@ -0,0 +1,82 @@ +/** + * Copyright 2006 by Know-Center, Graz, Austria + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egiz.pdfas.utils; + +import org.apache.commons.lang.StringUtils; + +/** + * Methods in order to create valid delimiter separated data, where the semicolon as used as delimiter, also known as + * the German version of CSV. + * + * @author Datentechnik Innovation GmbH + * @see RFC 4180 + */ +public class CsvUtils { + + /** + * The delimiter char used instead of COMMA. + */ + public static final char DEFAULT_DELIMITER = ';'; + + private static final char QUOTE_CHAR = '"'; + private static final char LF_CHAR = '\n'; + private static final char CR_CHAR = '\r'; + + private static final char[] SHOULD_BE_QUOTED = { LF_CHAR, CR_CHAR, DEFAULT_DELIMITER, QUOTE_CHAR }; + private static final String CSV_QUOTE = String.valueOf(QUOTE_CHAR); + + /** + * Returns an DSV escapted representation of the given object using the default delimiter character ( + * {@link #DEFAULT_DELIMITER}). + * + * @param obj + * The object. + * @return An escapted representation of the given object. + */ + public static String escapeCsvValue(Object obj) { + // null value should be printed as empty column + if (obj == null) { + return ""; + } + // get String representation of object + String asString = String.valueOf(obj); + // do we need to escape anything? + if (StringUtils.containsNone(asString, SHOULD_BE_QUOTED)) { + return asString; + } + + // RFC 4180 (7): If double-quotes are used to enclose fields, then a double-quote + // appearing inside a field must be escaped by preceding it with + // another double quote. + asString = StringUtils.replace(asString, CSV_QUOTE, CSV_QUOTE + CSV_QUOTE); + + // RFC 4180 (6): Fields containing line breaks (CRLF), double quotes, and commas + // should be enclosed in double-quotes. + if (StringUtils.containsAny(asString, SHOULD_BE_QUOTED)) { + return QUOTE_CHAR + asString + QUOTE_CHAR; + } + return asString; + } + +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/utils/PDFASUtils.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/utils/PDFASUtils.java index 7f09dfc..a2332dd 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/utils/PDFASUtils.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/utils/PDFASUtils.java @@ -30,63 +30,233 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.pdfbox.pdmodel.PDDocument; +import at.gv.egiz.pdfas.api.io.DataSource; import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; +import com.lowagie.text.Document; +import com.lowagie.text.pdf.BadPasswordException; import com.lowagie.text.pdf.PdfReader; /** - * @author tknall + * Collection of useful utilities. */ public class PDFASUtils { -//23.11.2010 changed by exthex - added checkDocumentPermissions(PDDocument doc) - - private PDFASUtils() { - } - - /** - * Verifies that a document could be opened with full permissions. - * @param pdfReader The PdfReader - * @throws PDFDocumentException Thrown if document has not been opened with full permissions. - */ - public static void checkReaderPermissions(PdfReader pdfReader) throws PDFDocumentException { - if (pdfReader.isEncrypted()) { - throw new PDFDocumentException(ErrorCode.DOCUMENT_IS_PROTECTED, "Document is encrypted."); - } - if (!pdfReader.isOpenedWithFullPermissions()) { - throw new PDFDocumentException(ErrorCode.DOCUMENT_IS_PROTECTED, "Document is protected."); - } - } - - public static boolean toFile(byte[] data, File file) throws IOException { - return PDFASUtils.toFile(new ByteArrayInputStream(data), file); - } - - public static boolean toFile(InputStream inputStream, File file) throws IOException { - boolean result = false; - BufferedOutputStream bufferedOutputStream = null; - try { - bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file)); - ConfigUtils.writeInputStreamToOutputStream(inputStream, bufferedOutputStream); - } finally { - if (bufferedOutputStream != null) { - try { - bufferedOutputStream.close(); - result = true; - } catch (IOException e) { - result = false; - } - } - } - return result; - } - - public static void checkDocumentPermissions(PDDocument doc) throws PDFDocumentException { - if (doc.isEncrypted()) { - throw new PDFDocumentException(ErrorCode.DOCUMENT_IS_PROTECTED, "Document is encrypted."); - } - } - + + private static Log log = LogFactory.getLog(PDFASUtils.class); + + /** + * The configuration key for PDF/A support. + */ + public static final String CFG_KEY_PDFA = "SIG_PDFA1B_VALID"; + + private PDFASUtils() { + } + + /** + * Verifies that a document could be opened with full permissions. + * + * @param pdfReader + * The PdfReader. + * @throws PDFDocumentException + * Thrown if document has not been opened with full permissions. + */ + private static void checkReaderPermissions(PdfReader pdfReader) throws PDFDocumentException { + if (pdfReader.isEncrypted()) { + throw new PDFDocumentException(ErrorCode.DOCUMENT_IS_PROTECTED, "Document is encrypted."); + } + if (!pdfReader.isOpenedWithFullPermissions()) { + throw new PDFDocumentException(ErrorCode.DOCUMENT_IS_PROTECTED, "Document is protected."); + } + } + + /** + * Verifies that the document is not encrypted and/or protected. In case no restrictions have been applied to the + * document a pdf reader is returned. + * + * @param dataSource + * The document data source. + * @throws PDFDocumentException + * Thrown if the document could not be opened with full permissions. + */ + public static PdfReader createPdfReaderCheckingPermissions(DataSource dataSource) throws PDFDocumentException { + return createPdfReaderCheckingPermissions(dataSource.createInputStream()); + } + + /** + * Verifies that the document is not encrypted and/or protected. In case no restrictions have been applied to the + * document a pdf reader is returned. + * + * @param dataSource + * The document data source. + * @throws PDFDocumentException + * Thrown if the document could not be opened with full permissions. + */ + public static PdfReader createPdfReaderCheckingPermissions(at.gv.egiz.pdfas.framework.input.DataSource dataSource) + throws PDFDocumentException { + return createPdfReaderCheckingPermissions(dataSource.createInputStream()); + } + + /** + * Verifies that the document is not encrypted and/or protected. In case no restrictions have been applied to the + * document a pdf reader is returned. + * + * @param inputStream + * The document data input stream. + * @throws PDFDocumentException + * Thrown if the document could not be opened with full permissions. + */ + public static PdfReader createPdfReaderCheckingPermissions(InputStream inputStream) throws PDFDocumentException { + PdfReader reader = null; + try { + // try to parse document + // If fully encrypted, PdfReader will fail; + // It should throw a BadPasswordException, but unfortunately does not (throws an IOException instead, + // internally catching BadPAsswordException; see comments below). + reader = new PdfReader(inputStream); + checkReaderPermissions(reader); + return reader; + } catch (BadPasswordException e) { + // will never be reached with itext-2.1.5-rev3628-pdfas:v1.1 + // just added for later versions... (see comments below) + // itext-2.1.5-rev3628-pdfas:v1.2 correctly throws BadPasswordException + throw new PDFDocumentException(ErrorCode.DOCUMENT_IS_PROTECTED, "Document is protected."); + } catch (PDFDocumentException e) { + throw e; + } catch (Exception e) { + final String EX_MSG_FOR_ENCRYPTED_DOCUMENT = "Bad user Password"; + // Inspecting the exception message seems to be the only way when using itext-2.1.5-rev3628-pdfas:v1.1: + // itext neither externally throws a BadPasswordException nor passed the cause..., + // later versions do! + // String "Bad user Password" is set in com.lowagie.text.pdf.BadPasswordException so this approach will + // work as long as the underlying itext library is not beeing updated. + if (StringUtils.containsIgnoreCase(e.getMessage(), EX_MSG_FOR_ENCRYPTED_DOCUMENT)) { + throw new PDFDocumentException(ErrorCode.DOCUMENT_IS_PROTECTED, "Document is protected."); + } + throw new PDFDocumentException(ErrorCode.DOCUMENT_CANNOT_BE_READ, "Unable to parse document."); + } finally { + IOUtils.closeQuietly(inputStream); + closeQuietly(reader); + } + } + + /** + * Quietly closes a pdf reader. + * + * @param reader + * The reader. + */ + public static void closeQuietly(PdfReader reader) { + if (reader != null) { + try { + reader.close(); + } catch (Throwable e) { + // ignore + } + } + } + + /** + * Quietly closes a pdf document. + * + * @param pdDocument + * The pdf document. + */ + public static void closeQuietly(PDDocument pdDocument) { + if (pdDocument != null) { + try { + pdDocument.close(); + } catch (Throwable e) { + // ignore + } + } + } + + /** + * Quietly closes a pdf document. + * + * @param pdDocument + * The pdf document. + */ + public static void closeQuietly(org.pdfbox.pdmodel.PDDocument pdDocument) { + if (pdDocument != null) { + try { + pdDocument.close(); + } catch (Throwable e) { + // ignore + } + } + } + + /** + * Quietly closes a pdf document. + * + * @param pdDocument + * The pdf document. + */ + public static void closeQuietly(Document doc) { + if (doc != null) { + try { + doc.close(); + } catch (Throwable e) { + // ignore + } + } + } + + public static boolean toFile(byte[] data, File file) throws IOException { + return PDFASUtils.toFile(new ByteArrayInputStream(data), file); + } + + public static boolean toFile(InputStream inputStream, File file) throws IOException { + boolean result = false; + BufferedOutputStream bufferedOutputStream = null; + try { + bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file)); + ConfigUtils.writeInputStreamToOutputStream(inputStream, bufferedOutputStream); + } finally { + if (bufferedOutputStream != null) { + try { + bufferedOutputStream.close(); + result = true; + } catch (IOException e) { + result = false; + } + } + } + return result; + } + + /** + * Returns {@code true} if the given {@code profileId} is PDF/A-1b enabled, {@code false} if not. + * + * @param profileId + * The signature profile. + * @return {@code true} if the given {@code profileId} is PDF/A-1b enabled, {@code false} if not. + */ + public static boolean isPdfAEnabled(String profileId) { + if (profileId == null) { + throw new NullPointerException("Profile identifier must not be null."); + } + if (StringUtils.isEmpty(profileId)) { + throw new IllegalArgumentException("Profile identifier must not be empty."); + } + try { + String pdfa = SettingsReader.getInstance().getSetting("sig_obj." + profileId + ".key." + CFG_KEY_PDFA, + "default." + CFG_KEY_PDFA, "false"); + return BooleanUtils.toBoolean(pdfa); + } catch (Exception e) { + log.error("Unable to read settings for PDF/A functionality.", e); + return false; + } + } + } -- cgit v1.2.3