/**
 * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright>
 * 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 demo;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;

import at.gv.egiz.pdfas.PdfAsFactory;
import at.gv.egiz.pdfas.api.PdfAs;
import at.gv.egiz.pdfas.api.analyze.AnalyzeParameters;
import at.gv.egiz.pdfas.api.analyze.AnalyzeResult;
import at.gv.egiz.pdfas.api.analyze.NonTextObjectInfo;
import at.gv.egiz.pdfas.api.commons.Constants;
import at.gv.egiz.pdfas.api.commons.SignatureInformation;
import at.gv.egiz.pdfas.api.exceptions.PdfAsException;
import at.gv.egiz.pdfas.api.io.DataSource;
import at.gv.egiz.pdfas.api.verify.VerifyAfterAnalysisParameters;
import at.gv.egiz.pdfas.api.verify.VerifyAfterReconstructXMLDsigParameters;
import at.gv.egiz.pdfas.api.verify.VerifyResult;
import at.gv.egiz.pdfas.api.verify.VerifyResults;
import at.gv.egiz.pdfas.api.xmldsig.ExtendedSignatureInformation;
import at.gv.egiz.pdfas.api.xmldsig.ReconstructXMLDsigAfterAnalysisParameters;
import at.gv.egiz.pdfas.api.xmldsig.ReconstructXMLDsigResult;
import at.gv.egiz.pdfas.api.xmldsig.XMLDsigData;
import at.gv.egiz.pdfas.commandline.Main;
import at.gv.egiz.pdfas.exceptions.ErrorCode;
import at.gv.egiz.pdfas.exceptions.ErrorCodeHelper;
import at.gv.egiz.pdfas.framework.config.SettingsHelper;
import at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters;
import at.gv.egiz.pdfas.io.FileBasedDataSource;

public class SignatureVerificationDemo {

   /**
    * Starts a demo that verifies a document.
    * 
    * @param args
    *           The parameter(s).
    */
   public static void main(String[] args) {

      if (args == null || args.length == 0) {
         System.err.println("Please provide path of file to be verified.");
         System.exit(1);
      }
      
      File configdir = new File("./work");
      File signedFile = new File(args[0]);
      
      AnalyzeResult analyzeResult = null;

      try {

         // instantiate api
         PdfAs pdfasAPI = PdfAsFactory.createPdfAs(configdir);

         // set source
         DataSource dataSource = new FileBasedDataSource(signedFile, "application/pdf");

         // evaluate settings
         VerificationFilterParameters parameters = SettingsHelper.readVerificationFilterParametersFromSettings();
         String verifyMode = Constants.VERIFY_MODE_FULL_CONSERVATIVE;

         if (parameters.extractBinarySignaturesOnly()) {
            verifyMode = Constants.VERIFY_MODE_BINARY_ONLY;
         } else if (parameters.assumeOnlySignatureUpdateBlocks()) {
            verifyMode = Constants.VERIFY_MODE_SEMI_CONSERVATIVE;
         } else {
            verifyMode = Constants.VERIFY_MODE_FULL_CONSERVATIVE;
         }

         // configure analyze parameters
         AnalyzeParameters analyzeParameters = new AnalyzeParameters();
         analyzeParameters.setDocument(dataSource);
         analyzeParameters.setVerifyMode(verifyMode);
         analyzeParameters.setReturnNonTextualObjects(true);

         // analyze
         System.out.println("Analyzing...");
         analyzeResult = pdfasAPI.analyze(analyzeParameters);
         System.out.println("Successfully analyzed.");
         
         // information on non-textual objects can be used after analysis step and/or after verification
         List signatures = analyzeResult.getSignatures();
         if (signatures != null && !signatures.isEmpty()) {
            int counter = 0;
            Iterator sigIterator = signatures.iterator();
            while (sigIterator.hasNext()) {
               counter++;
               SignatureInformation sigInfo = (SignatureInformation) sigIterator.next();
               System.out.println("\n------------------------ SIGNATURE #" + counter + " ------------------------");
               if (sigInfo.hasNonTextualObjects()) {
                  System.out.println("\nWARNING: " + sigInfo.getNonTextualObjects().size() + " non textual object(s) detected for this signature.");
                  Iterator noit = sigInfo.getNonTextualObjects().iterator();
                  while (noit.hasNext()) {
                     NonTextObjectInfo info = (NonTextObjectInfo) noit.next();
                     System.out.println("  -> " + info.toString());
                  }
                  System.out.println();
                  
               } else {
                  System.out.println("\nNo non-textual objects detected for this signature.");
               }
            }
         } else {
            if (analyzeResult != null && analyzeResult.hasBeenCorrected()) {
               System.err.println("The document could not been processed, maybe due to modification by third party tools. An attempt to correct the document was successful but no verifiable signatures could be found. This/these signature(s) - if any - might got lost.");
            } else {
               System.err.println("No signatures found.");
            }
            System.exit(1);
         }
         
         // retrieve reconstructed signature
         ReconstructXMLDsigAfterAnalysisParameters recstrParams = new ReconstructXMLDsigAfterAnalysisParameters();
         recstrParams.setAnalyzeResult(analyzeResult);
         recstrParams.setSignatureDevice(Constants.SIGNATURE_DEVICE_MOA);
         ReconstructXMLDsigResult recstrResult = pdfasAPI.reconstructXMLDSIG(recstrParams);

         // it is now possible to retrieve the reconstructed XMLDSig and the underlying signed
         // data without invoking signature verification. Just enable the following block
         /*
         List extSigInfoList = recstrResult.getExtendedSignatures();
         if (extSigInfoList != null && !extSigInfoList.isEmpty()) {
            Iterator it = extSigInfoList.iterator();
            while (it.hasNext()) {
               ExtendedSignatureInformation extSigInfo = (ExtendedSignatureInformation) it.next();
               SignatureInformation sigInfo = extSigInfo.getSignatureInformation();
               XMLDsigData xmlDSigData = extSigInfo.getXmlDsigData();
               // output XMLDSIG somewhere: xmlDSigData.getXmlDsig()
               if (xmlDSigData.isDetached()) {
                  DataSource signedDataSource = sigInfo.getSignedData();
                  // output signed data somewhere: signedDataSource.getAsByteArray()
               }
            }
         }
         */

         // setup verification
         
         // verification without intermediate step of reconstruction
         /* 
         VerifyAfterAnalysisParameters vaap = new VerifyAfterAnalysisParameters();
         vaap.setAnalyzeResult(analyzeResult);
         vaap.setReturnHashInputData(true);
         vaap.setSignatureDevice(Constants.SIGNATURE_DEVICE_MOA);
         vaap.setVerificationTime(null);
         // try to validate all signatures (do not throw exceptions while validating single signatures)
         // use result.isVerificationDone() in order to find out if a signatures was successfully verified
         vaap.setSuppressVerifyExceptions(true);
          */
         
         // verification with intermediate step of reconstruction
         VerifyAfterReconstructXMLDsigParameters varp = new VerifyAfterReconstructXMLDsigParameters();
         varp.setReconstructXMLDsigResult(recstrResult);
         varp.setReturnHashInputData(true);
         // not needed since already set in recstrParams (the reconstruction step)
         // varp.setSignatureDevice(Constants.SIGNATURE_DEVICE_MOA);
         varp.setVerificationTime(null);
         // try to validate all signatures (do not throw exceptions while validating single signatures)
         // use result.isVerificationDone() in order to find out if a signatures was successfully verified
         varp.setSuppressVerifyExceptions(true);

         // invoke verification
         System.out.println("Verifying...");
         
         // without intermediate step of reconstruction
         /*
         VerifyResults verifyResults = pdfasAPI.verify(vaap);
         */
         
         // with intermediate step of reconstruction
         VerifyResults verifyResults = pdfasAPI.verify(varp);
         
         // retrieve results
         List verifyResultList = verifyResults.getResults();
         System.out.println("Verification complete.\n");

         // iterate over results
         PrintWriter out = new PrintWriter(System.out);
         Iterator it = verifyResultList.iterator();
         int counter = 0;
         while (it.hasNext()) {
            counter++;
            VerifyResult result = (VerifyResult) it.next();
            out.println("\n------------------------ SIGNATURE #" + counter + " ------------------------\n");
            
            // check if signature verification of the current signature was successfully completed (independent from result)
            if (!result.isVerificationDone()) {
               PdfAsException ex = result.getVerificationException();
               out.println(ErrorCodeHelper.formErrorMessage(ex));
               continue;
            }
            
            Main.formatVerifyResult(result, out);
            XMLDsigData xmlDSigData = result.getReconstructedXMLDsig();
            if (xmlDSigData != null) {
               out.println("\n  --- XMLDSIG start ---");
               out.println("  " + xmlDSigData.getXmlDsig());
               out.println("  --- XMLDSIG end ---");
               // fetch data
               // either use result.getHashInputData() (provided varp.setReturnHashInputData(...) has been set true
               // or use
               byte[] data = result.getSignedData().getAsByteArray();
               out.println("\n  Signed Data:  " + data.length + " bytes (" + result.getSignatureType() + ")");
            }
            
            // check if there are timestamps
            if (result.getTimeStampValue() != null) {
               out.println("\n  TimeStamp value available for this signature");
            }
            
            // check if non textual elements have been detected for this signature
            if (result.hasNonTextualObjects()) {
               out.println("\n  WARNING: " + result.getNonTextualObjects().size() + " non textual object(s) detected for this signature");
               Iterator noit = result.getNonTextualObjects().iterator();
               while (noit.hasNext()) {
                  NonTextObjectInfo info = (NonTextObjectInfo) noit.next();
                  out.println("  -> " + info.toString());
               }
               out.println();
               
            } else {
               out.println("\n  No non-textual objects detected for this signature.");
            }
         }
         out.flush();         
         
      } catch (PdfAsException e) {
         if (ErrorCode.DOCUMENT_NOT_SIGNED == e.getErrorCode() && analyzeResult != null && analyzeResult.hasBeenCorrected()) {
            System.err.println("\nThe document could not been processed, maybe due to modification by third party tools. An attempt to correct the document was successful but no verifiable signatures could be found. This/these signature(s) - if any - might got lost.");
         } else {
            e.printStackTrace();
         }
      } catch (IOException e) {
         e.printStackTrace();
      }

   }

}