// Source file: main.cpp
// XML DSig Test Application
// Antonio Perdices Gonzalez
// antonio@dilmun.ls.fi.upm.es

#include <iostream>
#include <stdio.h>

// XERCES INCLUDES
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/dom/DOMException.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/framework/MemBufFormatTarget.hpp>

// XSEC INCLUDES
#include <xsec/framework/XSECProvider.hpp>
#include <xsec/framework/XSECException.hpp>
#include <xsec/enc/XSECCryptoException.hpp>
#include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
#include <xsec/dsig/DSIGReference.hpp>

// OPENSSL INCLUDES
#include <openssl/pem.h>
#include <openssl/bio.h>

// Set default namespace
using namespace std;

XERCES_CPP_NAMESPACE_USE

// MAIN FUNCTION

int main(int argc, char **argv)
{
  char *input_file = "signed.xml";
  char *cert_file = "cert.pem";
  
  // Initialize XML Subsystems
  try {
	XMLPlatformUtils::Initialize();
  }
  catch (const XMLException &e) {
    cout << "Error during initialisation of XML Subsystem: " << e.getMessage() << endl;
    exit(-1);
  }

  XercesDOMParser *parser = new XercesDOMParser;
  parser->setValidationScheme(XercesDOMParser::Val_Auto);
  parser->setDoNamespaces(true);
  parser->setDoSchema(true);
  parser->setCreateEntityReferenceNodes(true);

  // Parsing...
  try {
    parser->parse(input_file);
  }
  catch (const XMLException &toCatch) {
    char *message = XMLString::transcode(toCatch.getMessage());
    cout << "Xerces XML Exception: " << message << endl;
    XMLString::release(&message);
    exit (-1);
  }
  catch (const DOMException &toCatch) {
    char *message = XMLString::transcode(toCatch.msg);
    cout << "Xerces DOM Exception: " << message << endl;
    XMLString::release(&message);
    exit (-1);
  }
  catch (...) {
    cout << "XML unspected exception." << endl;
    exit (-1);
  }

  cout << "XML Input file parsed without problems." << endl;

  DOMDocument *doc = parser->getDocument();


  // #### XML VERIFY STUFF ####
  
  // Loading certificate
  X509 *certificate = NULL;

  try {
    // Create bio for certificate
    BIO *bioCert = BIO_new (BIO_s_file());
    BIO_read_filename(bioCert, cert_file);
    
    // Create private key with bio  
    certificate = PEM_read_bio_X509(bioCert, NULL, NULL, NULL);
  }
  catch (...) {
    cout << "OPENSSL unspected exception." << endl;
  }

  if (certificate == NULL) exit (-1);

  cout << "OpenSSL PEM encoded X509 certificate loaded." << endl;

  try {
    XSECPlatformUtils::Initialise();
  }
  catch (const XMLException &e) {
    cout << "Error during initialisation of XSEC Subsystem: " << e.getMessage() << endl;
    exit (-1);
  }

  // Signature Elements
  XSECProvider provider;
  DSIGSignature *signature = NULL;

  try {
    // Get signature element
    signature = provider.newSignatureFromDOM(doc);
    cout << "Signature succesfully retrieved from document." << endl;

    // Load certificate
    OpenSSLCryptoX509 * x509 = new OpenSSLCryptoX509(certificate);
    signature->load();
    signature->setSigningKey(x509->clonePublicKey());
    
    // Verify
    if(!signature->verify()) {
      char *error = XMLString::transcode(signature->getErrMsgs());
      cout << "Signature check failed. Error: " << error << "\n";
      XMLString::release(&error);
      exit (-1);
    } else cout << "XML Signature SUCCESFULLY VERIFIED!!!" << endl;
  }
  catch (XSECException &toCatch) {
    cout << "XSECException: Error verifying signature: " << toCatch.getMsg() << endl;
    exit (-1);
  }
  catch (XSECCryptoException &toCatch) {
    cout << "XSECCryptoException: Error verifying signature: " << toCatch.getMsg() << endl;
    exit (-1);
  }
  catch (const XMLException &toCatch) {
    char *message = XMLString::transcode(toCatch.getMessage());
    cout << "XMLException: Error verifying signature: " << message << endl;
    XMLString::release(&message);
    exit (-1);
  }
  catch (...) {
    cout << "XML unspected exception." << endl;
    exit (-1);
  }

  exit (0);
}

