// snf_sync.cpp
// Copyright (C) 2006 - 2009 ARM Research Labs, LLC.
// See www.armresearch.com for the copyright terms.
// See snf_sync.hpp for details.

#include "snf_sync.hpp"

void snf_sync::construct() {                                                    // Encapsulate initial construction.
    ClientGBUAlertInitializer.link(ClientGBUAlertHandler);                      // Link the alert configurators.
    ServerGBUAlertInitializer.link(ServerGBUAlertHandler);
    SNFWasParsed.setup(ReadWasGood);                                            // Link our configurator to that flag.
    SetupReader();                                                              // Configure our reader.
    reset();                                                                    // and initialize our data.
}

snf_sync::snf_sync() :                                                          // Construcing a blank snf_sync.
  Reader("snf"),                                                                // The Reader looks for "snf"
  ReadWasGood(false) {                                                          // There has been no good read yet.
    construct();                                                                // Internal wiring & initialization.
}

snf_sync::snf_sync(const char* bfr, int len) :                                  // Constructing with a full buffer.
  Reader("snf"),                                                                // Start with our blank construction.
  ReadWasGood(false) {
    construct();                                                                // Internal wiring & initialization.
    ConfigurationData Data(bfr, len);                                           // Then build ConfigurationData from
    Reader.interpret(Data);                                                     // the buffer and interpret it.
}

snf_sync::snf_sync(string& input) :                                             // Constructing with a string.
  Reader("snf"),                                                                // Start with our blank construction.
  ReadWasGood(false) {
    construct();                                                                // Internal wiring & initialization.
    ConfigurationData Data(input.c_str(), input.length());                      // Then build ConfigurationData from
    Reader.interpret(Data);                                                     // the string and interpret it.
}

void snf_sync::SetupReader() {                                                  // Configure the reader to recognize
    Reader                                                                      // the snf_sync protocol.
        .atEndCall(SNFWasParsed)                                                // Set flag to true when successful.
        .Element("<!-- SNFWasParsed -->", ReadWasGood, false).End()             // Trick using impossible element name.
        .Element("sync")
            .Element("challenge")
                .Attribute("text", snf_sync_challenge_txt, "")
            .End("challenge")
            .Element("response")
                .Attribute("nodeid", snf_sync_response_nodeid, "")
                .Attribute("text", snf_sync_response_text, "")
            .End("response")
            .Element("error")
                .Attribute("message", snf_sync_error_message, "")
                .Attribute("code", snf_sync_error_code, 0)
            .End("error")
            .Element("rulebase")
                .Attribute("utc", snf_sync_rulebase_utc, "")
            .End("rulebase")
            .Element("client")
                .atStartCall(ClientGBUAlertInitializer)
                .Element("gbu")
                    .atEndCall(ClientGBUAlertHandler)
                    .Attribute("time", ClientGBUAlertHandler.Alert_time, "")
                    .Attribute("ip", ClientGBUAlertHandler.Alert_ip, "")
                    .Attribute("t", ClientGBUAlertHandler.Alert_t, "Ignore")
                    .Attribute("b", ClientGBUAlertHandler.Alert_b, 0)
                    .Attribute("g", ClientGBUAlertHandler.Alert_g, 0)
                .End("gbu")
            .End("client")
            .Element("server")
                .atStartCall(ServerGBUAlertInitializer)
                .Element("gbu")
                    .atEndCall(ServerGBUAlertHandler)
                    .Attribute("time", ServerGBUAlertHandler.Alert_time, "")
                    .Attribute("ip", ServerGBUAlertHandler.Alert_ip, "")
                    .Attribute("t", ServerGBUAlertHandler.Alert_t, "Ignore")
                    .Attribute("b", ServerGBUAlertHandler.Alert_b, 0)
                    .Attribute("g", ServerGBUAlertHandler.Alert_g, 0)
                .End("gbu")
                .Element("resync")
                    .Attribute("secs", snf_sync_server_resync_secs, -1)
                .End("resync")
            .End("server")
        .End("sync")
    .End("snf");
}

void snf_sync::reset() {                                                        // Reset the reader for new data.
    ReadWasGood = false;                                                        // There has been no read yet.
    Reader.initialize();                                                        // Initialize to the defaults.
};

bool snf_sync::read(const char* bfr, int len) {                                 // To read from a buffer we
    ConfigurationData Data(bfr, len);                                           // construct ConfigurationData from
    Reader.interpret(Data);                                                     // the buffer and interpret it.
    return good();                                                              // Return true if it looked good.
}

bool snf_sync::read(string& input) {                                            // To read from a string we
    return read(input.c_str(), input.length());                                 // get the strings buffer and hand off
}                                                                               // to our buffer read()

bool snf_sync::good() {                                                         // True if the Reader finished the
    return (true == ReadWasGood);                                               // snf element successfully.
}

bool snf_sync::bad() {                                                          // False if the Reader finished the
    return (false == ReadWasGood);                                              // snf element successfully.
}

void GBUAlertHandler::operator()(
  ConfigurationElement& E, ConfigurationData& D) {                              // Add an alert.
    GBUdbAlert NewAlert;                                                        // Create an alert object.
    SocketAddress IPAddress;                                                    // Grab one of these for a converter.
    IPAddress.setAddress(const_cast<char*>(Alert_ip.c_str()));                  // Conver the IP address to an int.
    NewAlert.IP = IPAddress.getAddress();                                       // Put the IP into it's place.
    NewAlert.R.Bad(Alert_b);                                                    // Set the bad count on the record.
    NewAlert.R.Good(Alert_g);                                                   // Set the good count on the record.
    strncpy(NewAlert.UTC, Alert_time.c_str(), UTCBufferSize);                   // Copy the timestamp.
    switch(Alert_t.at(0)) {                                                     // Use the first byte to set the flag.
        case 'U': {                                                             // U means Ugly.
            NewAlert.R.Flag(Ugly);
            break;
        }
        case 'I': {                                                             // I means Ignore.
            NewAlert.R.Flag(Ignore);
            break;
        }
        case 'G': {                                                             // G means Good.
            NewAlert.R.Flag(Good);
            break;
        }
        case 'B': {                                                             // B means Bad.
            NewAlert.R.Flag(Bad);
            break;
        }
    }
    AlertList.push_back(NewAlert);                                              // Push back the new alert.
}

void GBUAlertHandler::reset() {                                                 // To reset the handler,
    Alert_time = "";                                                            // clear all of the input strings
    Alert_ip = "";                                                              // to the empty string and all of
    Alert_t = "";                                                               // the input counts to zero.
    Alert_b = 0;
    Alert_g = 0;
    AlertList.clear();                                                          // Clear out the list.
}
