/*
 *Copyright(c)2004,Cisco URP imburses and Network Information Center in Beijing University of Posts and Telecommunications researches.
 *
 *All right reserved
 *
 *File Name: expValueTable.c
 *File Description: expValueTable MIB operation.
 *
 *Current Version:1.0
 *Author:JianShun Tong
 *Date:2004.8.20
 */


/*
 * This file was generated by mib2c and is intended for use as
 * a mib module for the ucd-snmp snmpd agent. 
 */


/*
 * This should always be included first before anything else 
 */
#include <net-snmp/net-snmp-config.h>
#include <ctype.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif

#include <stdio.h>

#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif


/*
 * minimal include directives 
 */
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "utilities/iquery.h"
#include "header_complex.h"
#include "expExpressionTable.h"
#include "expValueTable.h"
#include "expObjectTable.h"


/*
 * expValueTable_variables_oid:
 *   this is the top level oid that we want to register under.  This
 *   is essentially a prefix, with the suffix appearing in the
 *   variable below.
 */

static const oid expValueTable_variables_oid[] = {
    1, 3, 6, 1, 2, 1, 90, 1, 3, 1
};

struct s_node {
    unsigned        data;
    struct s_node  *next;
};
typedef struct s_node nodelink;
static FindVarMethod var_expValueTable;

/*
 * variable2 expObjectTable_variables:
 */

static const struct variable2 expValueTable_variables[] = {
    /*
     * magic number        , variable type , ro/rw , callback fn  , L, oidsuffix 
     */
#define	EXPVALUECOUNTER32VAL 2
    {EXPVALUECOUNTER32VAL,  ASN_COUNTER,  NETSNMP_OLDAPI_RONLY,
     var_expValueTable, 2, {1, 2}},
#define	EXPVALUEUNSIGNED32VAL 3
    {EXPVALUEUNSIGNED32VAL, ASN_UNSIGNED, NETSNMP_OLDAPI_RONLY,
     var_expValueTable, 2, {1, 3}},
#define	EXPVALUETIMETICKSVAL 4
    {EXPVALUETIMETICKSVAL,  ASN_UNSIGNED, NETSNMP_OLDAPI_RONLY,
     var_expValueTable, 2, {1, 4}},
#define	EXPVALUEINTEGER32VAL 5
    {EXPVALUEINTEGER32VAL,  ASN_INTEGER,  NETSNMP_OLDAPI_RONLY,
     var_expValueTable, 2, {1, 5}},
#define	EXPVALUEIPADDRESSVAL 6
    {EXPVALUEIPADDRESSVAL, ASN_IPADDRESS, NETSNMP_OLDAPI_RONLY,
     var_expValueTable, 2, {1, 6}},
#define	EXPVALUEOCTETSTRINGVAL 7
    {EXPVALUEOCTETSTRINGVAL, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
     var_expValueTable, 2, {1, 7}},
#define	EXPVALUEOIDVAL  8
    {EXPVALUEOIDVAL,       ASN_OBJECT_ID, NETSNMP_OLDAPI_RONLY,
     var_expValueTable, 2, {1, 8}},
#define	EXPVALUECOUNTER64VAL  9
    {EXPVALUECOUNTER64VAL, ASN_INTEGER,   NETSNMP_OLDAPI_RONLY,
     var_expValueTable, 2, {1, 9}}
};


static struct header_complex_index *expValueTableStorage = NULL;

/*
 * init_expValueTable():
 *   Initialization routine.  This is called when the agent starts up.
 *   At a minimum, registration of your variables should take place here.
 */
void
init_expValueTable(void)
{
    DEBUGMSGTL(("expValueTable", "initializing...  "));


    /*
     * register ourselves with the agent to handle our mib tree 
     */
    REGISTER_MIB("expValueTable",
                 expValueTable_variables, variable2,
                 expValueTable_variables_oid);

    DEBUGMSGTL(("expValueTable", "done.\n"));
}

static int
expValueTable_set(struct expExpressionTable_data *expression_data,
                  const char *owner, size_t owner_len, const char *name,
                  size_t name_len, oid *index, size_t index_len)
{
    netsnmp_variable_list *vars = NULL;
    struct expValueTable_data *thedata;
    struct header_complex_index *hcindex;
    int             found = 0;

    for (hcindex = expValueTableStorage; hcindex; hcindex = hcindex->next) {
        thedata = hcindex->data;
        if (strcmp(thedata->expExpressionOwner, owner) == 0 &&
            thedata->expExpressionOwnerLen == owner_len &&
            strcmp(thedata->expExpressionName, name) == 0 &&
            thedata->expExpressionNameLen == name_len) {
            found = 1;
            break;
        }

    }

    if (found) {
        if (snmp_oid_compare(thedata->expValueInstance,
                             thedata->expValueInstanceLen, index,
                             index_len) != 0) {
            SNMP_FREE(thedata->expValueInstance);
            thedata->expValueInstance = netsnmp_memdup(index, index_len);
            thedata->expValueInstanceLen = index_len;
        } else {
            SNMP_FREE(index);
        }
    } else if ((thedata = calloc(1, sizeof(*thedata)))) {
        thedata->expExpressionOwner = owner;
        thedata->expExpressionOwnerLen = owner_len;
        thedata->expExpressionName = name;
        thedata->expExpressionNameLen = name_len;
        thedata->expValueInstance = index;
        thedata->expValueInstanceLen = index_len;
        thedata->expression_data = expression_data;

        snmp_varlist_add_variable(&vars, NULL, 0, ASN_OCTET_STR,
                                  (const char *) thedata->expExpressionOwner,
                                  thedata->expExpressionOwnerLen);
        snmp_varlist_add_variable(&vars, NULL, 0, ASN_OCTET_STR,
                                  (const char *) thedata->expExpressionName,
                                  thedata->expExpressionNameLen);
        snmp_varlist_add_variable(&vars, NULL, 0, ASN_PRIV_IMPLIED_OBJECT_ID,
                                  (u_char *) thedata->expValueInstance,
                                  thedata->expValueInstanceLen * sizeof(oid));

        header_complex_add_data(&expValueTableStorage, vars, thedata);
    } else {
        return SNMPERR_GENERR;
    }

    thedata->set = 1;

    return SNMPERR_SUCCESS;
}

static void push(nodelink ** stack, unsigned long value)
{
    nodelink           *newnode;
    newnode = (nodelink *) malloc(sizeof(nodelink));
    if (!newnode) {
        printf("\nMemory allocation failure!");
        return;
    }
    newnode->data = value;
    newnode->next = *stack;
    *stack = newnode;
}

static unsigned long pop(nodelink **stack)
{
    unsigned long   value;
    nodelink       *top;

    if (!*stack)
        return 0;

    top = *stack;
    *stack = (*stack)->next;
    value = top->data;
    free(top);
    return value;
}

static int priority(char operator)
{
    switch (operator) {
    case '*':
    case '/':
        return 4;
    case '+':
    case '-':
        return 3;
    case ')':
        return 2;
    case '(':
        return 1;
    default:
        return 0;
    }
}

static unsigned long calculate(int operator, unsigned long a, unsigned long b)
{
    switch (operator) {
    case '+':
        return (a + b);
    case '-':
        return (a - b);
    case '*':
        return (a * b);
    case '/':
        if (operator == '/' && b == 0) {
            snmp_log(LOG_ERR, "Division by zero attempted\n");
            return 0;
        } else
            return (a / b);
    }
    return 0;
}

static unsigned long get_operand(const char *p, int *length)
{
    char            c[13];
    int             i = 0, k = 1;
    unsigned long   result = 0;

    while (isdigit((unsigned char) *p))
        c[i++] = *(p++);
    *length += --i;
    for (; i >= 0; i--) {
        result += (c[i] - 48) * k;
        k *= 10;
    }
    return result;
}

enum operator_class {
    c_other	= 0,
    c_digit	= 1,
    c_binop	= 2,
    c_rpar	= 3,
    c_lpar	= 4,
};

static int operator_class(char c)
{
    if (isdigit((unsigned char) c))
        return c_digit;
    else if (c == '*' || c == '+' || c == '-' || c == '/')
        return c_binop;
    else if (c == ')')
        return c_rpar;
    else if (c == '(')
        return c_lpar;
    else
        return c_other;
}

static void eval(nodelink **operator, nodelink **operand, char new_op)
{
    unsigned long a, b, op, c;

    DEBUGMSG(("expValueTable", "eval: operator %c; new_op %c\n",
              *operator ? (*operator)->data : '?', new_op));

    while (*operator != NULL &&
           priority(new_op) <= priority((*operator)->data)) {
        b = pop(operand);
        op = pop(operator);
        if (op) {
            a = pop(operand);
            c = calculate(op, a, b);
            DEBUGMSG(("expValueTable", "eval: %ld %c %ld -> %ld\n", a, (char)op,
                      b, c));
            push(operand, c);
        } else {
            DEBUGMSG(("expValueTable", "eval: returning %ld\n", b));
            push(operand, b);
            break;
        }
    }

}

static unsigned long get_result(const char *expr)
{
    int             position = 0;
    unsigned long   a, result = 0;
    const char     *expression;
    nodelink       *operator = NULL;
    nodelink       *operand = NULL;

    expression = expr;
    while (*(expression + position) != '\0'
           && *(expression + position) != '\n') {
        switch (operator_class(*(expression + position))) {
        case c_digit:
            push(&operand, get_operand(expression + position, &position));
            break;
        case c_binop:
            eval(&operator, &operand, *(expression + position));
            push(&operator, *(expression + position));
            break;
        case c_rpar:
            eval(&operator, &operand, ')');
            if (operator->data == '(')
                pop(&operator);
            break;
        case c_lpar:
            push(&operator, '(');
            break;
        default:
            printf("\nInvalid character in expression:");
            a = 0;
            while (*(expression + (int) a) != '\n'
                   && *(expression + (int) a) != '\0') {
                if (a != position)
                    printf("%c", *(expression + (int) a));
                else
                    printf("<%c>", *(expression + (int) a));
                a++;
            }
            return 0;
        }                       /* end switch */
        position++;
    }
    eval(&operator, &operand, ')');
    result = pop(&operand);
    DEBUGMSG(("expValueTable", "%s: %s -> %ld\n", __func__, expr, result));
    return result;
}

static int iquery(struct variable_list **vars, char *secName, int snmp_version,
                  const oid *name, int name_len)
{
    struct snmp_session *ss;
    struct snmp_pdu *pdu;
    struct snmp_pdu *response;
    struct variable_list *v;
    int status, rc = SNMP_ERR_GENERR;

    ss = netsnmp_query_get_default_session();
    if (!ss) {
        snmp_log(LOG_ERR, "%s: default SNMP session not available\n", __func__);
        goto out;
    }

    ss->retries = 0;

    pdu = snmp_pdu_create(SNMP_MSG_GET);
    if (!pdu) {
        snmp_log(LOG_ERR, "%s: failed to create an SNMP PDU\n", __func__);
        goto out;
    }

    if (snmp_add_null_var(pdu, name, name_len) == NULL) {
        snmp_log(LOG_ERR, "%s: appending a variable to a PDU failed\n",
                 __func__);
        goto free_pdu;
    }

    DEBUGMSGTL(("expValueTable", "%s: querying OID ", __func__));
    DEBUGMSGOID(("expValueTable", name, name_len));
    DEBUGMSG(("expValueTable", "\n"));

    status = snmp_synch_response(ss, pdu, &response);

    DEBUGMSGTL(("expValueTable", "%s: SNMP response status %d; rc %ld\n",
                __func__, status, response ? response->errstat : -1));

    if (status != STAT_SUCCESS)
        goto free_pdu;

    rc = response->errstat;
    *vars = snmp_clone_varbind(response->variables);
    if (*vars == NULL)
        goto free_response;

    for (v = *vars; v; v = v->next_variable) {
        DEBUGMSGTL(("expValueTable", "%s: response variable type %d; oid ",
                    __func__, v->type));
        DEBUGMSGOID(("expValueTable", v->name, v->name_length));
        if (v->type == ASN_INTEGER)
            DEBUGMSG(("expValueTable", "; value %ld\n", *v->val.integer));
        DEBUGMSG(("expValueTable", "\n"));
    }

    rc = SNMPERR_SUCCESS;

free_response:
    if (response)
        snmp_free_pdu(response);

free_pdu:
    /* if (pdu) snmp_free_pdu(pdu); -- triggers a use-after-free */

out:
    return rc;
}

static unsigned long Evaluate_Expression(struct expValueTable_data *vtable_data)
{
    struct header_complex_index *hcindex;
    struct expObjectTable_data *objstorage, *objfound;
    struct expValueTable_data *const valstorage = vtable_data;
    const char     *expression;
    char           *result, *resultbak;
    char           *temp, *tempbak;
    int             i = 0, j, l;
    unsigned long   result_u_long = 0;
    static int      level;

    temp = malloc(100);
    result = malloc(100);
    tempbak = temp;
    memset(result, 0, 100);
    *result = '\0';
    resultbak = result;

    level++;

    if (level > 1) {
        snmp_log(LOG_ERR, "%s: detected recursion\n", __func__);
        goto out;
    }

    expression = vtable_data->expression_data->expExpression;

    DEBUGMSGTL(("expValueTable", "%s(%s.%s): evaluating %s\n", __func__,
                valstorage->expExpressionOwner, valstorage->expExpressionName,
                expression));

    while (*expression != '\0') {
        if (*expression == '$') {
            objfound = NULL;
            i++;
            for (j = 1; j < 100; j++) {
                if ((*(expression + j) == '+') ||
                    (*(expression + j) == '-') ||
                    (*(expression + j) == '*') ||
                    (*(expression + j) == '/') ||
                    (*(expression + j) == '(') ||
                    (*(expression + j) == ')') ||
                    *(expression + j) == '\0') {
                    break;
                }
            }
            sprintf(temp, "%.*s", j - 1, expression + 1);
            l = atoi(temp);
            expression = expression + j;
            /*
             *   here use snmpget to get value
             */
            for (hcindex = expObjectTableStorage; hcindex != NULL;
                 hcindex = hcindex->next) {
                objstorage = (struct expObjectTable_data *) hcindex->data;
                if (!strcmp
                    (objstorage->expExpressionOwner,
                     valstorage->expExpressionOwner)
                    && (objstorage->expExpressionOwnerLen ==
                        valstorage->expExpressionOwnerLen)
                    && !strcmp(objstorage->expExpressionName,
                               valstorage->expExpressionName)
                    && (objstorage->expExpressionNameLen ==
                        valstorage->expExpressionNameLen)
                    && (l == objstorage->expObjectIndex)) {
                    objfound = objstorage;
                    break;
                }
            }

            if (!objfound) {
                snmp_log(LOG_ERR, "%s: lookup of expression %s.%s failed\n",
                         __func__, valstorage->expExpressionOwner,
                         valstorage->expExpressionName);
                goto out;
            }

            DEBUGMSGTL(("expValueTable", "%s: Found OID ", __func__));
            DEBUGMSGOID(("expValueTable", objfound->expObjectID,
                         objfound->expObjectIDLen));
            DEBUGMSG(("expValueTable", "%s\n",
                      objfound->expObjectIDWildcard ==
                      EXPOBJCETIDWILDCARD_TRUE ? "(wildcard)" : ""));

            oid             anOID[MAX_OID_LEN];
            size_t          anOID_len;

            memcpy(anOID, objfound->expObjectID,
                   objfound->expObjectIDLen * sizeof(oid));
            anOID_len = objfound->expObjectIDLen;
            if (objfound->expObjectIDWildcard == EXPOBJCETIDWILDCARD_TRUE) {
                anOID_len =
                    anOID_len + valstorage->expValueInstanceLen - 2;
                memcpy(anOID + objfound->expObjectIDLen,
                       valstorage->expValueInstance + 2,
                       (valstorage->expValueInstanceLen -
                        2) * sizeof(oid));
            }

            struct variable_list *vars;
            int             rc;

            rc = iquery(&vars,
                        (char *)vtable_data->expression_data->pdu_community,
                        vtable_data->expression_data->pdu_version,
                        anOID, anOID_len);
            if (rc != SNMP_ERR_NOERROR)
                snmp_log(LOG_ERR, "Error in packet: %s\n", snmp_errstring(rc));
            sprintf(result, "%lu", rc == SNMP_ERR_NOERROR ?
                    *(vars->val.integer) : 0);
            result += strlen(result);
        } else {
            *result++ = *expression++;
        }
    }
    result_u_long = get_result(resultbak);
    DEBUGMSGTL(("expValueTable", "%s(%s.%s): evaluated %s into %ld\n", __func__,
                valstorage->expExpressionOwner, valstorage->expExpressionName,
                resultbak, result_u_long));

out:
    free(tempbak);
    free(resultbak);
    level--;
    return result_u_long;
}

static void expValueTable_clean(void *data)
{
    struct expValueTable_data *cleanme = data;

    SNMP_FREE(cleanme->expValueInstance);
    SNMP_FREE(cleanme);
}

static void build_valuetable(void)
{
    struct expExpressionTable_data *expstorage;
    struct expObjectTable_data *objstorage, *objfound = NULL;
    struct header_complex_index *hcindex, *object_hcindex;
    const char     *expression;
    oid            *index;
    int             i = 0, j, l;

    DEBUGMSGTL(("expValueTable", "building valuetable...  \n"));

    for (hcindex = expExpressionTableStorage; hcindex != NULL;
         hcindex = hcindex->next) {
        expstorage = (struct expExpressionTable_data *) hcindex->data;
        if (expstorage->expExpressionEntryStatus == RS_ACTIVE) {
            expression = expstorage->expExpression;
            while (*expression != '\0') {
                if (*expression == '$') {
                    i++;
                    for (j = 1; j < 100; j++) {
                        if ((*(expression + j) == '+') ||
                            (*(expression + j) == '-') ||
                            (*(expression + j) == '*') ||
                            (*(expression + j) == '/') ||
                            (*(expression + j) == '(') ||
                            (*(expression + j) == ')') ||
                            *(expression + j) == '\0') {
                            break;
                        }
                    }
                    {
                        char temp[100];

                        sprintf(temp, "%.*s", j - 1, expression + 1);
                        l = atoi(temp);
                    }
                    for (object_hcindex = expObjectTableStorage;
                         object_hcindex != NULL;
                         object_hcindex = object_hcindex->next) {
                        objstorage =
                            (struct expObjectTable_data *) object_hcindex->
                            data;
                        if (!strcmp
                            (objstorage->expExpressionOwner,
                             expstorage->expExpressionOwner)
                            && (objstorage->expExpressionOwnerLen ==
                                expstorage->expExpressionOwnerLen)
                            && !strcmp(objstorage->expExpressionName,
                                       expstorage->expExpressionName)
                            && (objstorage->expExpressionNameLen ==
                                expstorage->expExpressionNameLen)
                            && (l == objstorage->expObjectIndex)) {
                            if (objfound == NULL) {
                                objfound = objstorage;
                            }
                            if (objstorage->expObjectIDWildcard ==
                                EXPOBJCETIDWILDCARD_TRUE)
                                objfound = objstorage;
                        }
                    }
                    expression = expression + j;
                } else {
                    expression++;
                }
            };
        }

        if (!objfound) {
            continue;
        }
        if (objfound->expObjectIDWildcard == EXPOBJCETIDWILDCARD_FALSE) {
            index = calloc(1, MAX_OID_LEN);
            *index = 0;
            *(index + 1) = 0;
            *(index + 2) = 0;
            expValueTable_set(expstorage, objfound->expExpressionOwner,
                              objfound->expExpressionOwnerLen,
                              objfound->expExpressionName,
                              objfound->expExpressionNameLen, index, 3);
        } else {
            oid            *targetOID = objfound->expObjectID;
            size_t          taggetOID_len = objfound->expObjectIDLen;
            oid            *next_OID;
            size_t          next_OID_len;
            struct variable_list *vars;
            int             rc;

            next_OID = targetOID;
            next_OID_len = taggetOID_len;
            do {
                index = calloc(1, MAX_OID_LEN);

                rc = iquery(&vars, (char *)expstorage->pdu_community,
                            expstorage->pdu_version, next_OID, next_OID_len);
                if (rc == SNMP_ERR_NOERROR) {
                    if (((vars->type >= SNMP_NOSUCHOBJECT &&
                          vars->type <= SNMP_ENDOFMIBVIEW)
                         || snmp_oid_compare(targetOID, taggetOID_len,
                                             vars->name,
                                             taggetOID_len) != 0)) {
                        break;
                    }
                    /* add to expValueTable */

                    *index = 0;
                    *(index + 1) = 0;
                    memcpy(index + 2, vars->name + taggetOID_len,
                           (vars->name_length - taggetOID_len) * sizeof(oid));
                    expValueTable_set(expstorage,
                                      objfound->expExpressionOwner,
                                      objfound->expExpressionOwnerLen,
                                      objfound->expExpressionName,
                                      objfound->expExpressionNameLen,
                                      index,
                                      vars->name_length -
                                      taggetOID_len + 2);

                    next_OID = vars->name;
                    next_OID_len = vars->name_length;
                } else {
                    snmp_log(LOG_ERR, "Error in packet: %s\n",
                             snmp_errstring(rc));
                }
            } while (TRUE);
        }
    }
}

static unsigned char *var_expValueTable(struct variable *vp, oid * name,
                                        size_t *length, int exact,
                                        size_t *var_len,
                                        WriteMethod ** write_method)
{
    struct expValueTable_data *StorageTmp = NULL;

    DEBUGMSGTL(("expValueTable", "var_expValueTable: Entering...  \n"));

    struct header_complex_index *hciptr, *hciptrn;

    for (hciptr = expValueTableStorage; hciptr; hciptr = hciptr->next) {
        StorageTmp = hciptr->data;
        StorageTmp->set = 0;
    }

    build_valuetable();

    for (hciptr = expValueTableStorage; hciptr; hciptr = hciptrn) {
        hciptrn = hciptr->next;
        StorageTmp = hciptr->data;
        if (!StorageTmp->set)
            header_complex_free_entry(hciptr, expValueTable_clean);
    }

    /*
     * this assumes you have registered all your data properly
     */
    if ((StorageTmp =
         header_complex(expValueTableStorage, vp, name, length, exact,
                        var_len, write_method)) == NULL) {
        DEBUGMSGTL(("expValueTable", "%s: entry not found.\n", __func__));
        return NULL;
    }

    DEBUGMSGTL(("expValueTable", "%s: vp->magic = %d.\n", __func__, vp->magic));


    /*
     * this is where we do the value assignments for the mib results.
     */
    switch (vp->magic) {
        /*
         *   we only support counter32val
         */

    case EXPVALUECOUNTER32VAL:
        StorageTmp->expValueCounter32Val = Evaluate_Expression(StorageTmp);
        *var_len = sizeof(StorageTmp->expValueCounter32Val);
        return (u_char *) & StorageTmp->expValueCounter32Val;

    case EXPVALUEUNSIGNED32VAL:
        /* var_len = sizeof(StorageTmp->expValueUnsigned32Val); */
        /* return (u_char *) & StorageTmp->expValueUnsigned32Val;         */
        return NULL;

    case EXPVALUETIMETICKSVAL:
        /* var_len = sizeof(StorageTmp->expValueTimeTicksVal); */
        /* return (u_char *) & StorageTmp->expValueTimeTicksVal; */
        return NULL;

    case EXPVALUEINTEGER32VAL:
        /* var_len = sizeof(StorageTmp->expValueInteger32Val); */
        /* return (u_char *) & StorageTmp->expValueInteger32Val; */
        return NULL;

    case EXPVALUEIPADDRESSVAL:
        /* var_len = sizeof(StorageTmp->expValueIpAddressVal); */
        /* return (u_char *) & StorageTmp->expValueIpAddressVal; */
        return NULL;

    case EXPVALUEOCTETSTRINGVAL:
        /* var_len = sizeof(StorageTmp->expValueOctetStringVal); */
        /* return (u_char *) & StorageTmp->expValueOctetStringVal;        */
        return NULL;

    case EXPVALUEOIDVAL:
        /* var_len = StorageTmp->expValueOidValLen; */
        /* return (u_char *) & StorageTmp->expValueOidVal; */
        return NULL;

    case EXPVALUECOUNTER64VAL:
        /* var_len = sizeof(StorageTmp->expValueCounter64Val); */
        /* return (u_char *) & StorageTmp->expValueCounter64Val; */
        return NULL;
    default:
        ERROR_MSG("");
	return NULL;
    }
}
