// Copyright (C) 2003-2026, Foxit Software Inc..
// All Rights Reserved.
//
// http://www.foxitsoftware.com
//
// The following code is copyrighted and contains proprietary information and trade secrets of Foxit Software Inc..
// You cannot distribute any part of Foxit PDF SDK to any third party or general public,
// unless there is a separate license agreement with Foxit Software Inc. which explicitly grants you such rights.
//
// This file contains an example to demonstrate how to use Foxit PDF SDK to do LTV verification (using default callback) in PDF document.


const FSDK = require("@foxitsoftware/foxit-pdf-sdk-node");
const fs = require('fs');
const os = require('os');
const path = require('path');
const util = require('util');

var sn = "bPxThFlBAYifO3yiW0C51bKbjPap1zHaf5aG5sX378TjFvB7Q0802A==";
var key = "8f3g1sGNtR8NAgfSNv+BvaJWy2fnrw36NZvzh8GUxYXvs+/YczAk4e34yqlOnmL4asgxGMO7+rlOcSHUSSKagx5g4G7VEXfkYLXnDDx77FOOedZPrFFcOCu1vT4+wOKzy1zkORq9cKtiFf44byH5sfRqf+OvELjep2teT9eF9yPCsSw2vRKljXlPy40DMP9XX1O5OTe+mSG3RvwKUMb0+RpG3YobM87j+/ynTDlJMMXw4vgwuTb1ZuAtlvkTdFz7HDLV81C6TYnn73ddHeGD3qYWBVsD4uJkDwyyH/SWr7QX0HSgVg8HSZqnab1VGdAUYxKY2vGWeOTJdu6qDGb5ZEb9O3kZZg+9LYAmUKzDfCYwfIsYZ8Y9LPC4wMx70kIq7JpNnzPvigrdshV8/ew1F0+qP/P5Z1EB53vDVrp2LVSuL873Wv4g3PzjZlNH+1qgT87hC+Buto4qsSUfP5Y2z0uApu+c/TfttCXbeMLSsw9TY1HQD3mkrZExMaQqRrAuXAcZWdvBnVuG3DCZmm8q0DLxJiCfYfViAU/AkbeKMIGaTJdrvIXBcssfde4nmU6qr0M4x4+Hs2ESddtY2QRtLAUBq/7h7GJlJHkEZr8ueaUy/6D/2vvAWz6wMLgmjlPbDTLqzfnCJylHCPtcJmu1SPPT9v1VreBX3+zNWcIT8HueyHEiUniqzwWkRKaotrnNb/zhnHLPpgaBSwKjyi8rOzzVwu/nY84G9ye4jsVjmkIRItKDrW7z22iS+QQuTwfqLVuMB0OD4jMkPCC2slzPfEeZnuh4+Rewln2qLRJVgI72zJ20OBWAelGSpO1Ii2Y3gOvojdmtUwx6R3Z1qFNWTTB25o9ytcux8BLOtTCkA8T8xaT4N339OTCsu/TmfahIDixKvr8cDa6F2WkBT8SOfv64W9PgiNv5B/BMOD2VWYaAl0N7yD0NkYYg/cDWRC4dfpdeip1jrr0k3mND0oSpIkxC+FicZbaYYwRdtJ9OSVWJ5OE0voLv8HgLze8mw6s4TrN3z+a2NQCveMa2VRN7YcEF7agPrvb/qU+otMwHae/wM7+f+HFAaOW+WTfzfxIVFB8/A1DN0NiEX+em4G7nO5F5ZcvfYPpaaioe8Nn33BbVLtMjXqsOBeM8AXiVfpD6YO75j7BqnBUPnivvD8BxJaaIQZvVSuQmvGm0ynt03IYwE8Rq+jYsunvsZ86jh5BiUR+Nm7sK0Qe9BdZn/7jdqohMv9czfMGUAscX+xBMunFbDpcRbWVpUADcm209L8Ndek2XY7YlCMxPgPVNvfWRF2jxehib9Tpd7zB5ZIDfAEH9zrpLfp29DLvCh2Yx8mH7xxdKG1O9Q0GdHphYHhej6smAcPm68R0GUQaTOLfXqNpueGJC1GAOI3Y4swjyt7jMTWcibnKLzwukWE5rnEDhn5n64JnyUM0ZFkRmtOiV";

const input_path = "../input_files/";
const output_path = "../output_files/";

const FREE_ETSIRFC316TSA_SERVER_NAME = "FreeTSAServer";
const FREE_ETSIRFC316TSA_SERVER_URL = "http://ca.signfiles.com/TSAServer.aspx";

const PKCS7_SIGNATURE_FILTER = "Adobe.PPKLite"
const PKCS7_SIGNATURE_SUBFILTER = "adbe.pkcs7.detached"


function TransformSignatureStateToString(sig_state) {
  let state_str = "";
  if (sig_state & FSDK.Signature.e_StateUnknown)
    state_str += "Unknown";
  if (sig_state & FSDK.Signature.e_StateNoSignData) {
    if (state_str != "") state_str += "|";
    state_str += "NoSignData";
  }
  if (sig_state & FSDK.Signature.e_StateUnsigned) {
    if (state_str != "") state_str += "|";
    state_str += "Unsigned";
  }
  if (sig_state & FSDK.Signature.e_StateSigned) {
    if (state_str != "") state_str += "|";
    state_str += "Signed";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyValid) {
    if (state_str != "") state_str += "|";
    state_str += "VerfiyValid";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyInvalid) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyInvalid";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyErrorData) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyErrorData";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyNoSupportWay) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyNoSupportWay";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyErrorByteRange) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyErrorByteRange";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyChange) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyChange";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyIncredible) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyIncredible";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyNoChange) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyNoChange";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyIssueValid) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyIssueValid";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyIssueUnknown) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyIssueUnknown";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyIssueRevoke) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyIssueRevoke";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyIssueExpire) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyIssueExpire";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyIssueUncheck) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyIssueUncheck";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyIssueCurrent) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyIssueCurrent";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyTimestampNone) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyTimestampNone";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyTimestampDoc) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyTimestampDoc";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyTimestampValid) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyTimestampValid";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyTimestampInvalid) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyTimestampInvalid";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyTimestampExpire) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyTimestampExpire";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyTimestampIssueUnknown) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyTimestampIssueUnknown";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyTimestampIssueValid) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyTimestampIssueValid";
  }
  if (sig_state & FSDK.Signature.e_StateVerifyTimestampTimeBefore) {
    if (state_str != "") state_str += "|";
    state_str += "VerifyTimestampTimeBefore";
  }
  if (sig_state & FSDK.Signature.e_StateCertCannotGetVRI) {
    if (state_str != "") state_str += "|";
    state_str += "CertCannotGetVRI";
  }
  return state_str;
}

function PKCS7Signature(input_pdf_path, signed_pdf_path) {
  let pdf_doc = new FSDK.PDFDoc(input_pdf_path);
  pdf_doc.StartLoad("", true, null);
  let pdf_page = pdf_doc.GetPage(0);
  let page_width = pdf_page.GetWidth();
  let page_height = pdf_page.GetHeight();
  let new_sig_rect= new FSDK.RectF((page_width / 2 - 50.0), (page_height / 2 - 50.0), (page_width / 2 + 50.0), (page_height / 2 + 50.0));
  let new_signature = pdf_page.AddSignature(new_sig_rect);
  new_signature.SetFilter(PKCS7_SIGNATURE_FILTER);
  new_signature.SetSubFilter(PKCS7_SIGNATURE_SUBFILTER);

  //String new_value;
  new_signature.SetKeyValue(FSDK.Signature.e_KeyNameSigner, "Foxit PDF SDK");
  new_signature.SetKeyValue(FSDK.Signature.e_KeyNameContactInfo, "support@foxitsoftware.com");
  new_signature.SetKeyValue(FSDK.Signature.e_KeyNameDN, "CN=CN,MAIL=MAIL@MAIL.COM");
  new_signature.SetKeyValue(FSDK.Signature.e_KeyNameLocation, "Fuzhou, China");
  let new_value = `As a sample for subfilter \"${PKCS7_SIGNATURE_SUBFILTER}\" `  
  new_signature.SetKeyValue(FSDK.Signature.e_KeyNameReason, new_value);
  new_signature.SetKeyValue(FSDK.Signature.e_KeyNameText, new_value);
  let sign_time = FSDK.DateTime.GetLocalTime();
  new_signature.SetSignTime(sign_time);
  // Set appearance flags to decide which content would be used in appearance.
  let ap_flags = FSDK.Signature.e_APFlagLabel | FSDK.Signature.e_APFlagSigner |
    FSDK.Signature.e_APFlagReason | FSDK.Signature.e_APFlagDN |
    FSDK.Signature.e_APFlagLocation | FSDK.Signature.e_APFlagText |
    FSDK.Signature.e_APFlagSigningTime;
  new_signature.SetAppearanceFlags(ap_flags);

  let cert_path = input_path + "foxit_all.pfx";
  let cert_password = "123456";  
  let sign_progressive = new_signature.StartSign(cert_path, cert_password, FSDK.Signature.e_DigestSHA256, signed_pdf_path, null, null);
  if (sign_progressive.GetRateOfProgress() != 100) {
    if (FSDK.Progressive.e_Finished != sign_progressive.Continue()) {
      console.log("[Failed] Fail to sign the new CAdES signature.\r\n");
      return;
    }
  }
}

// Here, the implementation of TrustedCertStoreCallback is very simple : 
// trust all input certificate(s) when this callback function is triggered during LTV verification.
// User can improve the implementation of the callback class TrustedCertStoreCallback or choose not to use TrustedCertStoreCallback.
class MyTrustedCertStoreCallback {
    constructor(){

    }
  
    IsCertTrusted(cert) {
      return true;
    }
    
    IsCertTrustedRoot(cert) {
      return true;
    }
  };

function UseLTVVerifier(pdf_doc, is_to_add_dss) {
  // Here use default RevocationCallback, so no need to call LTVVerifier::SetRevocationCallback
  let ltv_verifier = new FSDK.LTVVerifier (pdf_doc, true, true, false, FSDK.LTVVerifier.e_SignatureCreationTime);

  // Use implemented TrustedCertStoreCallback to trust some cerificates during LTV verification.
  // Here, the implementation of TrustedCertStoreCallback is very simple : 
  // trust all input certificate(s) when this callback function is triggered during LTV verification.
  // User can improve the implementation of the callback class TrustedCertStoreCallback or choose not to use TrustedCertStoreCallback.
  let my_callback = new MyTrustedCertStoreCallback();
  let trusted_cert_store_callback = new FSDK.TrustedCertStoreCallback(my_callback);
  ltv_verifier.SetTrustedCertStoreCallback(trusted_cert_store_callback);

  ltv_verifier.SetVerifyMode(FSDK.LTVVerifier.e_VerifyModeAcrobat);

  let sig_verify_result_array = ltv_verifier.Verify();
  for (let i = 0; i < sig_verify_result_array.GetSize(); i++) {
    let sig_verify_result = sig_verify_result_array.GetAt(i);
    let signature_name = sig_verify_result.GetSignatureName();
    let sig_state = sig_verify_result.GetSignatureState();
    let ltv_state = sig_verify_result.GetLTVState();
    let ltv_state_str;
    switch (ltv_state) {
      case FSDK.SignatureVerifyResult.e_LTVStateInactive:
        ltv_state_str = "inactive";
        break;
      case FSDK.SignatureVerifyResult.e_LTVStateEnable:
        ltv_state_str = "enabled";
        break;
      case FSDK.SignatureVerifyResult.e_LTVStateNotEnable:
        ltv_state_str = "not enabled";
        break;
    }
    console.log("Signature name:%s, signature state: %s, LTV state: %s\r\n",
           signature_name, TransformSignatureStateToString(sig_state), ltv_state_str);
  }

  if (is_to_add_dss) {
    for (let i = 0; i < sig_verify_result_array.GetSize(); i++) {
      if (sig_verify_result_array.GetAt(i).GetSignatureState() & FSDK.Signature.e_StateVerifyValid)
        ltv_verifier.AddDSS(sig_verify_result_array.GetAt(i));
    }
  }
}

function DoLTV(signed_pdf_path, saved_ltv_pdf_path) {
  // Use default SignatureCallback for signing a time stamp signature with filter "Adobe.PPKLite" and subfilter "ETSI.RFC3161",
  // so no need to register a custom signature callback.

  FSDK.TimeStampServerMgr.Initialize();
  let timestamp_server = FSDK.TimeStampServerMgr.AddServer(FREE_ETSIRFC316TSA_SERVER_NAME, FREE_ETSIRFC316TSA_SERVER_URL, "", "");
  FSDK.TimeStampServerMgr.SetDefaultServer(timestamp_server);

  let pdf_doc = new FSDK.PDFDoc(signed_pdf_path);
  pdf_doc.StartLoad("", true, null);
  // Add DSS
  console.log("== Before Add DSS ==\r\n");
  UseLTVVerifier(pdf_doc, true);

  // Add DTS
  let pdf_page = pdf_doc.GetPage(0);
  // The new time stamp signature will have default filter name "Adobe.PPKLite" and default subfilter name "ETSI.RFC3161".
  let timestamp_signature = pdf_page.AddSignature(new FSDK.RectF(), "", FSDK.Signature.e_SignatureTypeTimeStamp, true);
  let sign_progressive = timestamp_signature.StartSign("", "", FSDK.Signature.e_DigestSHA256, saved_ltv_pdf_path, null, null);
  if (sign_progressive.GetRateOfProgress() != 100)
    sign_progressive.Continue();

  // Check saved file.
  let check_pdf_doc = new FSDK.PDFDoc(saved_ltv_pdf_path);
  check_pdf_doc.StartLoad("", true, null);
  // Just LTV veify.
  console.log("== After Add DSS ==\r\n");
  UseLTVVerifier(check_pdf_doc, false);

  FSDK.TimeStampServerMgr.Release();
}


let error_code = FSDK.Library.Initialize(sn, key);
if (FSDK.e_ErrSuccess != error_code) {
  if (FSDK.e_ErrInvalidLicense == error_code)
    console.log("[Failed] Current used Foxit PDF SDK key information is invalid.");
  else
    console.log("Library Initialize Error: %d", FSDK.e_ErrSuccess);
  return;
}

let output_directory = output_path + "ltv/";
if (!fs.existsSync(output_directory))  {
  fs.mkdirSync(output_directory);
}

let input_file_name = "AboutFoxit.pdf";
let input_file_path = input_path + input_file_name;
console.log("Input file path: %s\r\n", input_file_path);
try {
  // Add a PKCS7 signature and sign it.
  let signed_pdf_path = output_directory + "AboutFoxit_signed.pdf";
  PKCS7Signature(input_file_path, signed_pdf_path);

  // Do LTV
  let saved_ltv_pdf_path = output_directory + "AboutFoxit_signed_ltv.pdf";
  DoLTV(signed_pdf_path, saved_ltv_pdf_path);

} catch (e) {
  console.log(e);
}
FSDK.Library.Release();
