// SNFMultiDLL.cpp
// Copyright (C) ARM Research Labs, LLC
//
// Provides a full SNFMulti engine with dynamic scanner allocation.

#ifdef WIN32

// Required because threading.hpp includes windows.h.
#include <winsock2.h>

#endif

#include <windows.h>
#include <stdexcept>
#include <cmath>
#include "../SNFMulti/snfmulti.hpp"

using namespace std;

const char* SNF_DLL_VERSION = "SNFMulti DLL Version 3.0 Build: " __DATE__ " " __TIME__;

const int snf_ERROR_NO_HANDLE = -1;
const int snf_ERROR_SCAN_FAILED = -2;
const int snf_ERROR_EXCEPTION = -3;

const double snf_ReputationMehResult = 0.0;                                     /* Don't know or don't care */
const double snf_ReputationBadResult = 1.0;                                     /* IP is pure evil */
const double snf_ReputationGoodResult = -1.0;                                   /* IP is pure good */

////////////////////////////////////////////////////////////////////////////////
// Here we define the interface provided by this DLL.

#define EXP __declspec(dllexport)
extern "C" {

  BOOL WINAPI DllMain ( HINSTANCE hInst, DWORD wDataSeg, LPVOID lpvReserved );  // Main DLL setup function.

  EXP int setThrottle(int Threads);                                             // Set scan thread limit.
  EXP int startupSNF(char* Path);                                               // Start SNF with configuration.
  EXP int startupSNFAuthenticated(char* Path, char* Lic, char* Auth);           // Start SNF with conf & auth.
  EXP int shutdownSNF();                                                        // Shutdown SNF.
  EXP int testIP(unsigned long int IPToCheck);                                  // Test the IP for a GBUdb range.
  EXP double getIPReputation(unsigned long int IPToCheck);                      // Get reputation figure for IP.
  EXP int scanBuffer(unsigned char* Bfr, int Length, char* Name, int Setup);    // Scan msgBuffer, name, setup time.
  EXP int scanFile(char* FilePath, int Setup);                                  // Scan msgFile, setup time.
  EXP int getScanXHeaders(int ScanHandle, char** Bfr, int* Length);             // Get result & XHeaders.
  EXP int getScanXMLLog(int ScanHandle, char** Bfr, int* Length);               // Get result & XML Log.
  EXP int getScanClassicLog(int ScanHandle, char** Bfr, int* Length);           // Get result & Classic Log.
  EXP int getScanResult(int ScanHandle);                                        // Get just the scan result.
  EXP int closeScan(int ScanHandle);                                            // Close the scan result.

}

////////////////////////////////////////////////////////////////////////////////
// Here are the guts of the engine.
//

// A ScannerInstance encapsulates an snf_EngineHandler and buffers for the
// scan results. The buffers can safely be passed back to the calling app.

class ScannerInstance : public snf_EngineHandler {                              // Scanner Instance.
  private:
    int Ordinal;                                                                // Instance ordinal.
    int ScanResult;                                                             // Current scan result.

    string ScanXHeaders;                                                        // Buffered X-Headers.
    char* ScanXHeadersPtr;                                                      // Buffered X-Headers pointer.
    int ScanXHeadersLength;                                                     // Buffered X-Headers length.

    string ScanXMLLog;                                                          // Buffered XML Log.
    char* ScanXMLLogPtr;                                                        // Buffered XML Log pointer.
    int ScanXMLLogLength;                                                       // Buffered XML Log length.

    string ScanClassicLog;                                                      // Buffered Classic Log.
    char* ScanClassicLogPtr;                                                    // Buffered Classic Log pointer.
    int ScanClassicLogLength;                                                   // Buffered Classic Log length.

    void clearBuffers();                                                        // Clears the buffers & result.

  public:
    ScannerInstance(snf_RulebaseHandler* RBH, int N);                           // Initialize w/ Rulebase & Ordinal
    ~ScannerInstance();                                                         // Cleanup before going away.
    int scanBuffer(unsigned char* Bfr, int Length, char* Name, int Setup);      // Scan message buffer, give handle.
    int scanFile(char* FilePath, int Setup);                                    // Scan message file, give handle.
    int getScanXHeaders(char** Bfr, int* Length);                               // Return scan result & X headers.
    int getScanXMLLog(char** Bfr, int* Length);                                 // Return scan result & XML log.
    int getScanClassicLog(char** Bfr, int* Length);                             // Return scan result & Classic log.
    int getScanResult();                                                        // Return scan result.
};

const int XHeaderReserve = 4096;                                                // Space to reserve for X headers.
const int XMLLogReserve = 4096;                                                 // Space to reserve for XML log data.
const int ClassicReserve = 4096;                                                // Space to reserve for Classic log data.

ScannerInstance::ScannerInstance(snf_RulebaseHandler* RBH, int N) :             // Initialize.
  Ordinal(N),                                                                  // Ordinal as assigned.
  ScanResult(snf_ERROR_UNKNOWN),                                                // No good result yet.
  ScanXHeaders(XHeaderReserve, '\0'),                                           // Pre-allocate some space for our
  ScanXMLLog(XMLLogReserve, '\0'),                                              // result caching buffers (strings)
  ScanClassicLog(ClassicReserve, '\0') {                                        // so we might not have to later.
    clearBuffers();                                                             // Clear the buffers.
    open(RBH);                                                                  // Open the engine handler.
}

ScannerInstance::~ScannerInstance() {                                           // When shutting down
    close();                                                                    // close the engine handler
    clearBuffers();                                                             // and clear the buffers.
    Ordinal = -1;                                                               // Invalidate our handle.
}

// In the C world, 0 cannot be a handle, so when we return a "handle" from
// our scanXX() methods we must convert the scanner's ordinal into a "handle"
// by adding 1. Later on when we get a scanner by it's handle, that function
// reverses the conversion by subtracting 1 from the handle to get the ordinal.

int ScannerInstance::
  scanBuffer(unsigned char* Bfr, int Length, char* Name, int Setup) {           // Scan a message buffer.
    try {                                                                       // Prepare to capture exceptions.
        clearBuffers();                                                         // Clear the buffers first.
        ScanResult = scanMessage(Bfr, Length, Name, Setup);                     // Scan and capture the result.
    }                                                                           // Translate exceptions into
    catch(snf_EngineHandler::FileError& e) {                                    // result codes and capture them.
        ScanResult = snf_ERROR_MSG_FILE;
    }
    catch(snf_EngineHandler::XHDRError& e) {
        ScanResult = snf_ERROR_MSG_FILE;
    }
    catch(snf_EngineHandler::BadMatrix& e) {
        ScanResult = snf_ERROR_BAD_MATRIX;
    }
    catch(snf_EngineHandler::MaxEvals& e) {
        ScanResult = snf_ERROR_MAX_EVALS;
    }
    catch(snf_EngineHandler::AllocationError& e) {
        ScanResult = snf_ERROR_ALLOCATION;
    }
    catch(...) {
        ScanResult = snf_ERROR_UNKNOWN;
    }
    return (Ordinal + 1);                                                       // Return our handle.
}

int ScannerInstance::
  scanFile(char* FilePath, int Setup) {                                         // Scan a message file.
    try {                                                                       // Prepare to capture exceptions.
        clearBuffers();                                                         // Clear the buffers first.
        ScanResult = scanMessageFile(FilePath, Setup);                          // Scan and capture the result.
    }                                                                           // Translate exceptions into
    catch(snf_EngineHandler::FileError& e) {                                    // result codes and capture them.
        ScanResult = snf_ERROR_MSG_FILE;
    }
    catch(snf_EngineHandler::XHDRError& e) {
        ScanResult = snf_ERROR_MSG_FILE;
    }
    catch(snf_EngineHandler::BadMatrix& e) {
        ScanResult = snf_ERROR_BAD_MATRIX;
    }
    catch(snf_EngineHandler::MaxEvals& e) {
        ScanResult = snf_ERROR_MAX_EVALS;
    }
    catch(snf_EngineHandler::AllocationError& e) {
        ScanResult = snf_ERROR_ALLOCATION;
    }
    catch(...) {
        ScanResult = snf_ERROR_UNKNOWN;
    }
    return (Ordinal + 1);                                                       // Return our handle.
}

void ScannerInstance::clearBuffers() {                                          // Clear the buffers.
    ScanXHeaders.clear();                                                       // Clear the buffer strings
    ScanXHeadersPtr = 0;                                                        // zero their pointers and
    ScanXHeadersLength = 0;                                                     // zero their lengths.

    ScanXMLLog.clear();
    ScanXMLLogPtr = 0;
    ScanXMLLogLength = 0;

    ScanClassicLog.clear();
    ScanClassicLogPtr = 0;
    ScanClassicLogLength = 0;

    ScanResult = snf_ERROR_UNKNOWN;
}

int ScannerInstance::getScanXHeaders(char** Bfr, int* Length) {                 // Return the result & X headers.
    if(0 == ScanXHeadersPtr) {                                                  // If we haven't already then
        ScanXHeaders = getXHDRs();                                              // capture the data.
        ScanXHeadersLength = ScanXHeaders.length();
        ScanXHeadersPtr = const_cast<char*>(ScanXHeaders.c_str());
    }
    *Bfr = ScanXHeadersPtr;                                                     // Pass the pointer and
    *Length = ScanXHeadersLength;                                               // length back to the caller.
    return ScanResult;                                                          // Return the scan result.
}

int ScannerInstance::getScanXMLLog(char** Bfr, int* Length) {                   // Return the result & XML log.
    if(0 == ScanXMLLogPtr) {                                                    // If we haven't already then
        ScanXMLLog = getXMLLog();                                               // capture the data.
        ScanXMLLogPtr = const_cast<char*>(ScanXMLLog.c_str());
        ScanXMLLogLength = ScanXMLLog.length();
    }
    *Bfr = ScanXMLLogPtr;                                                       // Pass the pointer and
    *Length = ScanXMLLogLength;                                                 // length back to the caller.
    return ScanResult;                                                          // Return the scan result.
}

int ScannerInstance::getScanClassicLog(char** Bfr, int* Length) {               // Return the result & Classic log.
    if(0 == ScanClassicLogPtr) {                                                // If we haven't already then
        ScanClassicLog = getClassicLog();                                       // capture the data.
        ScanClassicLogPtr = const_cast<char*>(ScanClassicLog.c_str());
        ScanClassicLogLength = ScanClassicLog.length();
    }
    *Bfr = ScanClassicLogPtr;                                                   // Pass the pointer and
    *Length = ScanClassicLogLength;                                             // length back to the caller.
    return ScanResult;                                                          // Return the scan result.
}

int ScannerInstance::getScanResult() {                                          // Return the scan result only.
    return ScanResult;                                                          // Just like that.
}

// The snfScannerPool starts up and shuts down the rulebase engine and keeps
// track of the ScannerInstances.

class snfScannerPool {                                                          // Dynamically allocated scanner pool.
  private:
    codedweller::Mutex myMutex;                                                 // Protect the state of the pool.
    snf_RulebaseHandler* myRulebaseHandler;                                     // This is our rulebase manager.
    vector<ScannerInstance*> ScannerPool;                                       // This is a collection of scanners.
    codedweller::ProductionQueue<ScannerInstance*> AvailableScanners;           // This is the available scanners queue.
    unsigned int ScanThreadLimit;                                               // Maximum number of concurrent threads.

  public:
    snfScannerPool();                                                           // This is how we are constructed.
    ~snfScannerPool();                                                          // This is how we are destroyed.
    void Throttle(int ThreadLimit);                                             // Set a Scan Thread Limit.
    void startup(char* ConfigurationPath);                                      // Startup the engine w/ config.
    void startupAuthenticated(char* Path, char* Lic, char* Auth);               // Startup engine w/ full auth.
    void shutdown();                                                            // Shutdown the engine.
    int testIP(unsigned long int IPToCheck);                                    // Test an IP against gbudb.
    double getIPReputation(unsigned long int IPToCheck);                        // This is how we get IP reps.
    ScannerInstance* grabScanner();                                             // Get a new (available) scanner.
    ScannerInstance* getScanner(int ScanHandle);                                // What scanner goes with this handle?
    void dropScanner(ScannerInstance* S);                                       // When done with a scanner, drop it.
};

snfScannerPool  Scanners;                                                       // Call our local pool "Scanners".

snfScannerPool::snfScannerPool() :                                              // When created, our
  myRulebaseHandler(0),                                                         // rulebase handler is 0.
  ScanThreadLimit(0) {}                                                         // There is no limit.

snfScannerPool::~snfScannerPool() {                                             // When destroyed we must
    shutdown();                                                                 // shutdown if needed.
}

void snfScannerPool::Throttle(int ThreadLimit) {                                // Set a scanner thread limit.
    ThreadLimit = (0 > ThreadLimit) ? 0 : ThreadLimit;                          // Throttle will be 0 or above.
    ScanThreadLimit = ThreadLimit;
}

void snfScannerPool::startup(char* ConfigurationPath) {                         // Start the engine with this config.
    codedweller::ScopeMutex StateIsInFlux(myMutex);                             // Protect our state data.
    if(myRulebaseHandler) return;                                               // If we are started, do nothing.
    myRulebaseHandler = new snf_RulebaseHandler();                              // Create a new rulebase handler.
    myRulebaseHandler->PlatformVersion(SNF_DLL_VERSION);                        // Record our version identification.
    myRulebaseHandler->open(ConfigurationPath, "", "");                         // Open the rulebase with the config.
}

void snfScannerPool::startupAuthenticated(char* Path, char* Lic, char* Auth) {  // Startup engine w/ full auth.
    codedweller::ScopeMutex StateIsInFlux(myMutex);                             // Protect our state data.
    if(myRulebaseHandler) return;                                               // If we are started, do nothing.
    myRulebaseHandler = new snf_RulebaseHandler();                              // Create a new rulebase handler.
    myRulebaseHandler->PlatformVersion(SNF_DLL_VERSION);                        // Record our version identification.
    myRulebaseHandler->open(Path, Lic, Auth);                                   // Open rulebase with full auth.
}

void snfScannerPool::shutdown() {                                               // Shutdown the engine.
    codedweller::ScopeMutex StateIsInFlux(myMutex);                             // Protect our state data.
    if(
      0 == ScannerPool.size() &&
      0 == AvailableScanners.size() &&
      0 == myRulebaseHandler
      ) return;                                                                 // If we are down don't bother.

    if(ScannerPool.size() != AvailableScanners.size()) throw false;             // If some scanners are in use throw!

    for(
      vector<ScannerInstance*>::iterator iS = ScannerPool.begin();              // Destroy all of the scanner
      iS != ScannerPool.end();                                                  // instances in the pool.
      iS++) delete (*iS);

    ScannerPool.clear();                                                        // Empty the pool of pointers.

    while(0 < AvailableScanners.size()) AvailableScanners.take();               // Empty the available scanners.
    if(myRulebaseHandler) {                                                     // Safely destroy the rulebase handler.
        myRulebaseHandler->close();                                             // Close it first,
        delete myRulebaseHandler;                                               // then delete it,
        myRulebaseHandler = 0;                                                  // then forget about it.
    }
}

int snfScannerPool::testIP(unsigned long int IPToCheck) {                       // This is how we test IPs.
    codedweller::ScopeMutex StayAsYouAre(myMutex);                              // Hold things steady while we do this.
    if(0 == myRulebaseHandler) return snf_ERROR_NO_HANDLE;                      // If we have no engine, punt!
    IPTestRecord Tester(IPToCheck);                                             // Make up a test record for this IP.
    myRulebaseHandler->performIPTest(Tester);                                   // Run it past the engine.
    return static_cast<int>(Tester.R);                                          // IPRange is an enum, so use as an int.
}

double snfScannerPool::getIPReputation(unsigned long int IPToCheck) {           // This is how we get IP reps.
    codedweller::ScopeMutex StayAsYouAre(myMutex);                              // Hold things steady while we do this.
    if(0 == myRulebaseHandler) return snf_ERROR_NO_HANDLE;                      // If we have no engine, punt!
    IPTestRecord Tester(IPToCheck);                                             // Make up a test record for this IP.
    myRulebaseHandler->performIPTest(Tester);                                   // Run it past the engine.

    // Now that we have a result from the engine let's translate the result.

    double Reputation = snf_ReputationMehResult;                                // Reputation figures are precise.
    switch(Tester.G.Flag()) {                                                   // Flags get priority in our translation.
        case Good: Reputation = snf_ReputationGoodResult; break;                // A good flag means the IP is pure good.
        case Bad: Reputation = snf_ReputationBadResult; break;                  // A bad flag means the IP is pure evil.
        case Ignore: Reputation = snf_ReputationMehResult; break;               // An ignore flag means we don't care.
        default: {                                                              // Ugly means we calculate the reputation
             Reputation =                                                       // figure from the statistics. Start by
               sqrt(fabs(Tester.G.Probability() * Tester.G.Confidence()));      // combining the c & p figures then
             if(0 > Tester.G.Probability()) Reputation *= -1.0;                 // flip the sign if p is negative.
        }
    }
    return Reputation;                                                          // This is what we say about this IP.
}

// In order to balance off speed and safety we control access when starting
// up, shutting down, or getting a new scanner. The grabScanner operation is
// usually very fast.
//
// The the calling application behaves properly it will never attempt a scan
// while attempting to shutdown, and will never attempt to shutdown while it
// is performing a scan. If this conflict does occur then one of two cases
// arise:
//
// The shutdown() thread grabs the mutex before the grabScanner() thread,
// the engine is shutdown, then the grabScanner() thread finds the engine
// is down and cannot return a scanner.
//
// The grabScanner() thread gets the mutex before the shutdown() thread,
// a scanner is allocated, and the shutdown() thread discovers an imabalance
// in the number of Available scanners and the number of scanners in the pool.
// When it discovers that it will throw false and the C interface function will
// return an error.

ScannerInstance* snfScannerPool::grabScanner() {                                // Get the next available scanner.
    codedweller::ScopeMutex FreezeOurState(myMutex);                            // Don't change while I'm doing this.
    if(0 == myRulebaseHandler) return 0;                                        // If we're not alive return nothing.
    if(1 > AvailableScanners.size()) {                                          // No scanners? See about making one.
        if(
          0 == ScanThreadLimit ||                                               // If there is no limit OR
          ScanThreadLimit > ScannerPool.size()                                  // we have room for more scanners
          ) {                                                                   // then we can make another one:
            ScannerInstance* NewScanner =
              new ScannerInstance(myRulebaseHandler, ScannerPool.size());       // Start up a new scanner and
            ScannerPool.push_back(NewScanner);                                  // add it to the pool.
            AvailableScanners.give(NewScanner);                                 // Make the new scanner available.
        }                                                                       // If we have reached the limit on
    }                                                                           // scanners we will wait for one.

    return AvailableScanners.take();                                            // Get a scanner from the pool.
}

// Since handles are one bigger than the ordinal (so 0 isn't a handle)
// we must convert the ScanHandle we're given into a proper ordinal so
// we can match the scanner's position in the pool. The reverse happens
// upon scan requestes.

ScannerInstance* snfScannerPool::getScanner(int ScanHandle) {                   // Get the scanner for this handle.
    ScannerInstance* S = 0;                                                     // We're gonna get a scanner.
    try { S = ScannerPool.at(ScanHandle - 1); }                                 // Safely get the scanner at Ordinal.
    catch(...) { return 0; }                                                    // If something goes wrong, no scanner.
    return S;                                                                   // Return the scanner we got.
}

void snfScannerPool::dropScanner(ScannerInstance* S) {                          // When done with a scanner, drop it.
    if(                                                                         // If :
      0 != S &&                                                                 // - we have a good scanner pointer and
      0 != myRulebaseHandler                                                    // - we are not dead yet
      ) AvailableScanners.give(S);                                              // then make the scanner available.
}


////////////////////////////////////////////////////////////////////////////////
// Interface Functions

BOOL WINAPI DLLMain ( HINSTANCE hInst, DWORD wDataSeg, LPVOID lpvReserved ) {
    try {
        switch(wDataSeg) {                                                      // Handle setup & teardown msgs.

            case DLL_PROCESS_ATTACH:                                            // If the DLL is being loaded
                return true;                                                    // return true. Nothing to do yet.
                break;

            case DLL_PROCESS_DETACH:                                            // If the DLL is being dropped
                Scanners.shutdown();                                            // be sure we are shut down first.
                break;

            default:                                                            // For all other messages (thread stuff)
                return true;                                                    // we'll just return ok because
                break;                                                          // we don't have any of that here.

        }
    }
    catch(...) {};                                                              // Do not throw exceptions.
                                                                                // We won't actually get here but we
   	return false;                                                               // want to make the compiler happy :-)
}

EXP int setThrottle(int Threads) {                                              // Set scan thread limit.
    try {
        Scanners.Throttle(Threads);                                             // Set the throttle limit
    }
    catch(...) { return snf_ERROR_EXCEPTION; }
    return Threads;                                                             // and return the value.
}

EXP int startupSNF(char* Path) {                                                // Start the engine.
    try {                                                                       // Use a try block to translate
        Scanners.startup(Path);                                                 // exceptions into result codes.
    }
    catch(snf_RulebaseHandler::AuthenticationError &e) {                        // Catch and translage exceptions.
        return snf_ERROR_RULE_AUTH;
    }
    catch(snf_RulebaseHandler::AllocationError& e) {
        return snf_ERROR_ALLOCATION;
    }
    catch(snf_RulebaseHandler::FileError& e) {
        return snf_ERROR_RULE_FILE;
    }
    catch(...) {
        return snf_ERROR_EXCEPTION;
    }
    return snf_SUCCESS;                                                         // No exceptions, All happy :-)
}

EXP int startupSNFAuthenticated(char* Path, char* Lic, char* Auth) {            // Start SNF with conf & auth.
    try {                                                                       // Use a try block to translate
        Scanners.startupAuthenticated(Path, Lic, Auth);                         // exceptions into result codes.
    }
    catch(snf_RulebaseHandler::AuthenticationError &e) {                        // Catch and translage exceptions.
        return snf_ERROR_RULE_AUTH;
    }
    catch(snf_RulebaseHandler::AllocationError& e) {
        return snf_ERROR_ALLOCATION;
    }
    catch(snf_RulebaseHandler::FileError& e) {
        return snf_ERROR_RULE_FILE;
    }
    catch(...) {
        return snf_ERROR_EXCEPTION;
    }
    return snf_SUCCESS;                                                         // No exceptions, All happy :-)
}

EXP int shutdownSNF() {                                                         // Shut down the engine.
    try {                                                                       // Use a try block to translate
        Scanners.shutdown();                                                    // exceptions into result codes.
    }
    catch(snf_RulebaseHandler::AuthenticationError &e) {                        // Catch and translage exceptions.
        return snf_ERROR_RULE_AUTH;
    }
    catch(snf_RulebaseHandler::AllocationError& e) {
        return snf_ERROR_ALLOCATION;
    }
    catch(snf_RulebaseHandler::FileError& e) {
        return snf_ERROR_RULE_FILE;
    }
    catch(...) {
        return snf_ERROR_EXCEPTION;
    }
    return snf_SUCCESS;                                                         // No exceptions, All happy :-)
}

EXP int testIP(unsigned long int IPToCheck) {                                   // Test the IP for a GBUdb range.
    int Result = snf_ERROR_EXCEPTION;                                           // On panics return this.
    try {
        Result = Scanners.testIP(IPToCheck);                                    // Testing an IP is easy.
    }
    catch(...) {}                                                               // Do not throw exceptions.
    return Result;                                                              // Return the result.
}

EXP double getIPReputation(unsigned long int IPToCheck) {                       // Get reputation figure for IP.
    double Result = snf_ReputationMehResult;                                    // On panics return this.
    try {
        Result = Scanners.getIPReputation(IPToCheck);                           // Getting an IP rep is easy.
    }
    catch(...) {}                                                               // Do not throw exceptions.
    return Result;                                                              // Return the result.
}

EXP int scanBuffer(unsigned char* Bfr, int Length, char* Name, int Setup) {     // Scan a message.
    ScannerInstance* myScanner = 0;                                             // We need a scanner.
    try { myScanner = Scanners.grabScanner(); }                                 // Safely get a scanner.
    catch(...) { return snf_ERROR_NO_HANDLE; }                                  // If that fails, we're done.

    int ScannerHandle = snf_ERROR_NO_HANDLE;                                    // We will return a handle.
    try { ScannerHandle = myScanner->scanBuffer(Bfr, Length, Name, Setup); }    // Perform the scan.
    catch(...) {                                                                // If we get an unhandled
        Scanners.dropScanner(myScanner);                                        // exception then drop the
        return snf_ERROR_SCAN_FAILED;                                           // scanner and return the failure.
    }                                                                           // If all goes well then
    return ScannerHandle;                                                       // return our scanner's handle.
}

EXP int scanFile(char* FilePath, int Setup) {
    ScannerInstance* myScanner = 0;                                             // We need a scanner.
    try { myScanner = Scanners.grabScanner(); }                                 // Safely get a scanner.
    catch(...) { return snf_ERROR_NO_HANDLE; }                                  // If that fails, we're done.

    int ScannerHandle = snf_ERROR_NO_HANDLE;                                    // We will return a handle.
    try { ScannerHandle = myScanner->scanFile(FilePath, Setup); }               // Perform the scan.
    catch(...) {                                                                // If we get an unhandled
        Scanners.dropScanner(myScanner);                                        // exception then drop the
        return snf_ERROR_SCAN_FAILED;                                           // scanner and return the failure.
    }                                                                           // If all goes well then
    return ScannerHandle;                                                       // return our scanner's handle.
}

EXP int getScanXHeaders(int ScanHandle, char** Bfr, int* Length) {              // Return the XHeaders for a scan.
    ScannerInstance* myScanner = 0;                                             // We need the scanner.
    try { myScanner = Scanners.getScanner(ScanHandle); }                        // Try to get the scanner by handle.
    catch(...) { return snf_ERROR_NO_HANDLE; }                                  // If we can't, we're broken.
    if(0 == myScanner) return snf_ERROR_NO_HANDLE;                              // A zero pointer is also broken.
    int Result = snf_ERROR_EXCEPTION;                                           // On panic return this error code.
    try {
        Result = myScanner->getScanXHeaders(Bfr, Length);                       // If we can, get our data.
    }
    catch(...) {}                                                               // Do not throw exceptions.
    return Result;                                                              // Return our result.
}

EXP int getScanXMLLog(int ScanHandle, char** Bfr, int* Length) {                // Return the XML Log for a scan.
    ScannerInstance* myScanner = 0;                                             // We need the scanner.
    try { myScanner = Scanners.getScanner(ScanHandle); }                        // Try to get the scanner by handle.
    catch(...) { return snf_ERROR_NO_HANDLE; }                                  // If we can't, we're broken.
    if(0 == myScanner) return snf_ERROR_NO_HANDLE;                              // A zero pointer is also broken.
    int Result = snf_ERROR_EXCEPTION;                                           // On panic return this error code.
    try {
        Result = myScanner->getScanXMLLog(Bfr, Length);                         // If we can, get our data.
    }
    catch(...) {}                                                               // Do not throw exceptions.
    return Result;                                                              // Return our result.
}

EXP int getScanClassicLog(int ScanHandle, char** Bfr, int* Length) {            // Return the classic log for a scan.
    ScannerInstance* myScanner = 0;                                             // We need the scanner.
    try { myScanner = Scanners.getScanner(ScanHandle); }                        // Try to get the scanner by handle.
    catch(...) { return snf_ERROR_NO_HANDLE; }                                  // If we can't we're broken.
    if(0 == myScanner) return snf_ERROR_NO_HANDLE;                              // A zero pointer is also broken.
    int Result = snf_ERROR_EXCEPTION;                                           // On panic return this error code.
    try {
        Result = myScanner->getScanClassicLog(Bfr, Length);                     // If we can, get our data.
    }
    catch(...) {}                                                               // Do not throw exceptions.
    return Result;                                                              // Return our result.
}

EXP int getScanResult(int ScanHandle) {                                         // Return just the scan result.
    ScannerInstance* myScanner = 0;                                             // We need the scanner.
    try { myScanner = Scanners.getScanner(ScanHandle); }                        // Try to get the scanner by handle.
    catch(...) { return snf_ERROR_NO_HANDLE; }                                  // If we can't we're broken.
    if(0 == myScanner) return snf_ERROR_NO_HANDLE;                              // A zero pointer is also broken.
    return myScanner->getScanResult();                                          // If we can, get our data.
}

EXP int closeScan(int ScanHandle) {                                             // Return the scanner to the pool.
    ScannerInstance* myScanner = 0;                                             // We need the scanner.
    try { myScanner = Scanners.getScanner(ScanHandle); }                        // Try to get the scanner by handle.
    catch(...) { return snf_ERROR_NO_HANDLE; }                                  // If we can't we're broken.
    if(0 == myScanner) return snf_ERROR_NO_HANDLE;                              // A zero pointer is also broken.
    try { Scanners.dropScanner(myScanner); }                                    // If we can then drop the scanner.
    catch(...) { return snf_ERROR_EXCEPTION; }                                  // Convert exceptions to a panic code.
    return snf_SUCCESS;                                                         // If all ok then return success.
}