// 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("@fuxinsoft/foxit-pdf-sdk-node");
const fs = require('fs');
const os = require('os');
const path = require('path');
const util = require('util');

var sn = "Rew1vB0TqoMIxX444UcpScARWdhgJpsJfzmyMLR32a0+EgrCjp2NaQ==";
var key = "8f3g1sFttW8NAgfSNv+BvaJWy4Pp8flcm7ncNDkdvCCO4JwUFbGLyZdujReMaiplbYJwwxam3lr7uHtQaEf0xTSu8j2ZBdrZ8FS4OQkEUjOI9UEGb3Ic0GqmlP9T9OnSItOYqoZZA/OnYg5EaVP+c/ha32nUquOk+5riEdhYgINAZDdgJkPc+YSWsrfixOIROf6t34j3eRcyFnOY0MS7tSXi1AEPE0VZdyu+5bfmBSGvLI+WR5lQ+oOQR5hRGwqf990WEdBelNO7dxhZELVhEZ+/L60PpDf5y9A8qmrY6PiEU8nhqIyLpks0BxeIsYyZ3Cr82jjfbItPGyEARsIVzluBSgVMD4oGjojz8T6wGDEXUbmZH/pra94XAs8wZ28mzbmIEppWnhBqX0c6Ov8iXCfFfUZr4KNuPxMEgk4VOYTMOd70GEjGnylivw18hbn7ZpkWh2Zql+uU2HejvOgn+WHvT0HF9NhK0Xb5vH8aFkgBP92PJx8obIbxBVeeExczxvsqWufuPdXS2sPNIgMYZ2TtvqVVsiUToeGB2FQo09fzZOSH5Vi18u/siyvC3FmjeMf4+OYWPuDPNCcrb21NgcHam7XI8AAoGLTGtLLWR+fxynbBBcg2d2ZPcZ5CMFIfOBHRWCkcA0sDT3cl9nsBppHNJze/jVxHy+lREeQEA/etSIgohTGAWW3ZlpibRXqMxlmVWEPifCubbWTzQ6U54mRMpW2UghH3gvCNFNgdday3Av7EtlIJOdUuiMq3zmmd5J4Jt4C82CKnVewA0DtZ4EEtwRG9IYuLrfpuZlnjBAtoeXRV1lqUZ6BcCe0E3m9jO28sGkALuIeJQ74XG7X8lMC7lMWWQYiYD7D6G9QT6X3qV4vXJwNKRKpYc/rI+amEawTSnDYAMEaYPwtZ0rb/7BL+atsZZaI+ncebdfVfjDJZbCZAc+JnkuNlUn2xrNRuObw8h/G0lm2pNOEJ10S8R4+yplo5B1QmC2dCDhaTaW67QJPqnJPKbVmjw7i6KGBi+CDP3JnAKlOX/YUu8uk56JfeQUuGcQg/KaBMGctnchH2n1i3+oGbibhurQj22+OO66Q6mijKoCRP/QJm5WTGcO4c0iVX0B8dIlai/uv/pGU8Wps9MxysH5ViVIj56LWKuNfy1FNO9+wx4iHYnUxfW0Iccq3YJYAlOBkMv8VZIJl9aTEEFJ626LJEw/UbEBdR9CdlwnylPf4RuNW/lcGYMgq3xNVpp/bzYKwZivvQOFgMdX1tv8D33SHvAm3zrRCU+v94BJXnBi9k25fnKsc705IMwv+I6bueXA+mDnagj1oTqsWXZ0WnPAYI3WUO7sBf7XUH230wfYXnTSVhujJxiEP/2oWqi2813gtQkKwz9N2p6yT8Vsh3+BaAwwDvtOZMJKUFcabTBSL6NoouzmzwItJ4mIboT6KXaef66rs8jCU=";

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();
