#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/agent/scalar.h>

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

#include "agent/nsDebug.h"
#include "util_funcs.h"



/*
 * OIDs for the debugging control scalar objects
 *
 * Note that these we're registering the full object rather
 *  than the (sole) valid instance in each case, in order
 *  to handle requests for invalid instances properly.
 */
oid nsDebugEnabled_oid[]    = { 1, 3, 6, 1, 4, 1, 8072, 1, 7, 1, 1};
oid nsDebugOutputAll_oid[]  = { 1, 3, 6, 1, 4, 1, 8072, 1, 7, 1, 2};
oid nsDebugDumpPdu_oid[]    = { 1, 3, 6, 1, 4, 1, 8072, 1, 7, 1, 3};

/*
 * ... and for the token table.
 */

#define  DBGTOKEN_PREFIX	2
#define  DBGTOKEN_ENABLED	3
#define  DBGTOKEN_STATUS	4
oid nsDebugTokenTable_oid[] = { 1, 3, 6, 1, 4, 1, 8072, 1, 7, 1, 4};


void
init_nsDebug(void)
{
    netsnmp_table_registration_info *table_info;
    netsnmp_iterator_info           *iinfo;

    /*
     * Register the scalar objects...
     */
    DEBUGMSGTL(("nsDebugScalars", "Initializing\n"));
    netsnmp_register_scalar(
        netsnmp_create_handler_registration(
            "nsDebugEnabled", handle_nsDebugEnabled,
            nsDebugEnabled_oid, OID_LENGTH(nsDebugEnabled_oid),
            HANDLER_CAN_RWRITE)
        );
    netsnmp_register_scalar(
        netsnmp_create_handler_registration(
            "nsDebugOutputAll", handle_nsDebugOutputAll,
            nsDebugOutputAll_oid, OID_LENGTH(nsDebugOutputAll_oid),
            HANDLER_CAN_RWRITE)
        );
    netsnmp_register_scalar(
        netsnmp_create_handler_registration(
            "nsDebugDumpPdu", handle_nsDebugDumpPdu,
            nsDebugDumpPdu_oid, OID_LENGTH(nsDebugDumpPdu_oid),
            HANDLER_CAN_RWRITE)
        );

    /*
     * ... and the table.
     * We need to define the column structure and indexing....
     */

    table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
    if (!table_info) {
        return;
    }
    netsnmp_table_helper_add_indexes(table_info, ASN_PRIV_IMPLIED_OCTET_STR, 0);
    table_info->min_column = DBGTOKEN_STATUS;
    table_info->max_column = DBGTOKEN_STATUS;


    /*
     * .... and the iteration information ....
     */
    iinfo      = SNMP_MALLOC_TYPEDEF(netsnmp_iterator_info);
    if (!iinfo) {
        return;
    }
    iinfo->get_first_data_point = get_first_debug_entry;
    iinfo->get_next_data_point = get_next_debug_entry;
    iinfo->table_reginfo        = table_info;


    /*
     * .... and register the table with the agent.
     */
    netsnmp_register_table_iterator(
        netsnmp_create_handler_registration(
            "tzDebugTable", handle_nsDebugTable,
            nsDebugTokenTable_oid, OID_LENGTH(nsDebugTokenTable_oid),
            HANDLER_CAN_RWRITE),
        iinfo);
}


int
handle_nsDebugEnabled(netsnmp_mib_handler *handler,
                netsnmp_handler_registration *reginfo,
                netsnmp_agent_request_info *reqinfo,
                netsnmp_request_info *requests)
{
    long enabled;
    netsnmp_request_info *request=NULL;

    switch (reqinfo->mode) {

    case MODE_GET:
	enabled = snmp_get_do_debugging();
	if ( enabled==0 )
	    enabled=2;		/* false */
	for (request = requests; request; request=request->next) {
            if (requests->processed != 0)
                continue;
	    snmp_set_var_typed_value(request->requestvb, ASN_INTEGER,
                                     (u_char*)&enabled, sizeof(enabled));
	}
	break;


    case MODE_SET_RESERVE1:
	for (request = requests; request; request=request->next) {
            if (requests->processed != 0)
                continue;
            if ( request->status != 0 ) {
                return SNMP_ERR_NOERROR;	/* Already got an error */
            }
            if ( request->requestvb->type != ASN_INTEGER ) {
                netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGTYPE);
                return SNMP_ERR_WRONGTYPE;
            }
            if (( *request->requestvb->val.integer != 1 ) &&
                ( *request->requestvb->val.integer != 2 )) {
                netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGVALUE);
                return SNMP_ERR_WRONGVALUE;
            }
        }
        break;

    case MODE_SET_COMMIT:
        enabled = *requests->requestvb->val.integer;
	if (enabled == 2 )	/* false */
	    enabled = 0;
	snmp_set_do_debugging( enabled );
        break;
    }

    return SNMP_ERR_NOERROR;
}


int
handle_nsDebugOutputAll(netsnmp_mib_handler *handler,
                netsnmp_handler_registration *reginfo,
                netsnmp_agent_request_info *reqinfo,
                netsnmp_request_info *requests)
{
    long enabled;
    netsnmp_request_info *request=NULL;

    switch (reqinfo->mode) {

    case MODE_GET:
	enabled = snmp_get_do_debugging();
	if ( enabled==0 )
	    enabled=2;		/* false */
	for (request = requests; request; request=request->next) {
            if (requests->processed != 0)
                continue;
	    snmp_set_var_typed_value(request->requestvb, ASN_INTEGER,
                                     (u_char*)&enabled, sizeof(enabled));
	}
	break;


    case MODE_SET_RESERVE1:
	for (request = requests; request; request=request->next) {
            if (requests->processed != 0)
                continue;
            if ( request->status != 0 ) {
                return SNMP_ERR_NOERROR;	/* Already got an error */
            }
            if ( request->requestvb->type != ASN_INTEGER ) {
                netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGTYPE);
                return SNMP_ERR_WRONGTYPE;
            }
            if (( *request->requestvb->val.integer != 1 ) &&
                ( *request->requestvb->val.integer != 2 )) {
                netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGVALUE);
                return SNMP_ERR_WRONGVALUE;
            }
        }
        break;

    case MODE_SET_COMMIT:
        enabled = *requests->requestvb->val.integer;
	if (enabled == 2 )	/* false */
	    enabled = 0;
	snmp_set_do_debugging( enabled );
        break;
    }

    return SNMP_ERR_NOERROR;
}


int
handle_nsDebugDumpPdu(netsnmp_mib_handler *handler,
                netsnmp_handler_registration *reginfo,
                netsnmp_agent_request_info *reqinfo,
                netsnmp_request_info *requests)
{
    long enabled;
    netsnmp_request_info *request=NULL;

    switch (reqinfo->mode) {

    case MODE_GET:
	enabled = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
	                                 NETSNMP_DS_LIB_DUMP_PACKET);
	if ( enabled==0 )
	    enabled=2;		/* false */
	for (request = requests; request; request=request->next) {
            if (requests->processed != 0)
                continue;
	    snmp_set_var_typed_value(request->requestvb, ASN_INTEGER,
                                     (u_char*)&enabled, sizeof(enabled));
	}
	break;


    case MODE_SET_RESERVE1:
	for (request = requests; request; request=request->next) {
            if (requests->processed != 0)
                continue;
            if ( request->status != 0 ) {
                return SNMP_ERR_NOERROR;	/* Already got an error */
            }
            if ( request->requestvb->type != ASN_INTEGER ) {
                netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGTYPE);
                return SNMP_ERR_WRONGTYPE;
            }
            if (( *request->requestvb->val.integer != 1 ) &&
                ( *request->requestvb->val.integer != 2 )) {
                netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGVALUE);
                return SNMP_ERR_WRONGVALUE;
            }
        }
        break;

    case MODE_SET_COMMIT:
        enabled = *requests->requestvb->val.integer;
	if (enabled == 2 )	/* false */
	    enabled = 0;
	netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
	                       NETSNMP_DS_LIB_DUMP_PACKET, enabled);
        break;
    }

    return SNMP_ERR_NOERROR;
}

/*
 * var_tzIntTableFixed():
 *   Handle the tzIntTable as a fixed table of NUMBER_TZ_ENTRIES rows,
 *    with the timezone offset hardwired to be the same as the index.
 */

netsnmp_variable_list *
get_first_debug_entry(void **loop_context, void **data_context,
                      netsnmp_variable_list *index,
                      netsnmp_iterator_info *data)
{
    int i;

    for (i=0; i<debug_num_tokens; i++) {
        /* skip excluded til mib is updated */
        if (dbg_tokens[i].token_name && (dbg_tokens[i].enabled != 2))
            break;
    }
    if ( i==debug_num_tokens )
        return NULL;

    snmp_set_var_value(index, dbg_tokens[i].token_name,
		       strlen(dbg_tokens[i].token_name));
    *loop_context = (void*)(intptr_t)i;
    *data_context = (void*)&dbg_tokens[i];
    return index;
}

netsnmp_variable_list *
get_next_debug_entry(void **loop_context, void **data_context,
                      netsnmp_variable_list *index,
                      netsnmp_iterator_info *data)
{
    int i = (int)(intptr_t)*loop_context;

    for (i++; i<debug_num_tokens; i++) {
        /* skip excluded til mib is updated */
        if (dbg_tokens[i].token_name && (dbg_tokens[i].enabled != 2))
            break;
    }
    if ( i==debug_num_tokens )
        return NULL;

    snmp_set_var_value(index, dbg_tokens[i].token_name,
		       strlen(dbg_tokens[i].token_name));
    *loop_context = (void*)(intptr_t)i;
    *data_context = (void*)&dbg_tokens[i];
    return index;
}


int
handle_nsDebugTable(netsnmp_mib_handler *handler,
                netsnmp_handler_registration *reginfo,
                netsnmp_agent_request_info *reqinfo,
                netsnmp_request_info *requests)
{
    long status;
    netsnmp_request_info       *request    =NULL;
    netsnmp_table_request_info *table_info    =NULL;
    netsnmp_token_descr        *debug_entry=NULL;

    switch (reqinfo->mode) {

    case MODE_GET:
        for (request=requests; request; request=request->next) {
            if (requests->processed != 0)
                continue;
            debug_entry = (netsnmp_token_descr*)
                           netsnmp_extract_iterator_context(request);
            if (!debug_entry)
                continue;
	    status = (debug_entry->enabled ? RS_ACTIVE : RS_NOTINSERVICE);
	    snmp_set_var_typed_value(request->requestvb, ASN_INTEGER,
                                     (u_char*)&status, sizeof(status));
	}
	break;


    case MODE_SET_RESERVE1:
	for (request = requests; request; request=request->next) {
            if (requests->processed != 0)
                continue;
            if ( request->status != 0 ) {
                return SNMP_ERR_NOERROR;	/* Already got an error */
            }
            if ( request->requestvb->type != ASN_INTEGER ) {
                netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGTYPE);
                return SNMP_ERR_WRONGTYPE;
            }

            debug_entry = (netsnmp_token_descr*)
                           netsnmp_extract_iterator_context(request);
            switch (*request->requestvb->val.integer) {
            case RS_ACTIVE:
            case RS_NOTINSERVICE:
                /*
		 * These operations require an existing row
		 */
                if (!debug_entry) {
		    netsnmp_set_request_error(reqinfo, request,
                                              SNMP_ERR_INCONSISTENTVALUE);
                    return SNMP_ERR_INCONSISTENTVALUE;
		}
		break;

            case RS_CREATEANDWAIT:
            case RS_CREATEANDGO:
                /*
		 * These operations assume the row doesn't already exist
		 */
                if (debug_entry) {
		    netsnmp_set_request_error(reqinfo, request,
                                              SNMP_ERR_INCONSISTENTVALUE);
                    return SNMP_ERR_INCONSISTENTVALUE;
		}
		break;

            case RS_DESTROY:
                /*
		 * This operation can work regardless
		 */
		break;

            case RS_NOTREADY:
            default:
		netsnmp_set_request_error(reqinfo, request,
                                          SNMP_ERR_WRONGVALUE);
                return SNMP_ERR_WRONGVALUE;
	    }
        }
        break;


    case MODE_SET_COMMIT:
	for (request = requests; request; request=request->next) {
            if (requests->processed != 0)
                continue;
            if ( request->status != 0 ) {
                return SNMP_ERR_NOERROR;	/* Already got an error */
            }

            switch (*request->requestvb->val.integer) {
            case RS_ACTIVE:
            case RS_NOTINSERVICE:
                /*
		 * Update the enabled field appropriately
		 */
                debug_entry = (netsnmp_token_descr*)
                               netsnmp_extract_iterator_context(request);
                debug_entry->enabled =
                    (*request->requestvb->val.integer == RS_ACTIVE);
		break;

            case RS_CREATEANDWAIT:
            case RS_CREATEANDGO:
                /*
		 * Create the entry, and set the enabled field appropriately
		 */
                table_info = netsnmp_extract_table_info(request);
                debug_register_tokens(table_info->indexes->val.string);
#ifdef UMMMMM
                if (*request->requestvb->val.integer == RS_CREATEANDWAIT) {
		    /* XXX - how to locate the entry ??  */
		    debug_entry->enabled = 0;
		}
#endif
		break;

            case RS_DESTROY:
                /*
		 * XXX - there's no "remove" API  :-(
		 */
                debug_entry = (netsnmp_token_descr*)
                               netsnmp_extract_iterator_context(request);
                if (debug_entry) {
		    debug_entry->enabled = 0;
		    free(debug_entry->token_name);
		    debug_entry->token_name = NULL;
		}
		break;
	    }
        }
        break;
    }

    return SNMP_ERR_NOERROR;
}

