/*s
 * $Id: www-mib-protocol.c 8676 2008-01-17 23:11:17Z ispringer $
 */
/* Apache includes */
#include <httpd.h>
#include <http_log.h>
#include <http_config.h>
#include <scoreboard.h>
#include <apr_strings.h>
#include <apr_optional.h>

/* SNMP includes */
#include "ucd-snmp-config.h"
#include "asn1.h"
#include "snmp.h"
#include "snmp_api.h"
#include "snmp_impl.h"
#include "snmp_debug.h"
#include "snmp_vars.h"
#include "var_struct.h"
#include <mibgroup/mibII/sysORTable.h>

/* includes */
#include "covalent-snmp-config.h"
#include "snmpcommon/snmpcommon.h"
#include "snmpcommon/snmpv2-tc.h"
#include "www-mib/www-mib.h"
#include "www-mib/www-mib-protocol.h"

#ifndef SNMP_MIN
#define SNMP_MIN(a,b)   (((a)<(b)) ? (a) : (b))
#endif

#define NEXT	FALSE
#define EXACT	TRUE
#define INDEX	2

#ifdef WWW_MIB
/* Declaration of functions */
FindVarMethod read_wwwSummaryEntry;
FindVarMethod read_wwwRequestInEntry;
FindVarMethod read_wwwRequestOutEntry;
FindVarMethod read_wwwResponseInEntry;
FindVarMethod read_wwwResponseOutEntry;
wwwProtocolStatistics_t *get_www_mib_protocol_statistics_record(unsigned int index);
wwwProtocolStatistics_t * get_protocol_wwwService_row(struct variable *vp,
        oid *name, size_t *namelength,
        oid *newname, size_t *newname_length,
        int exact, unsigned int *index);
int get_wwwRequestIn_row(oid *name, size_t *name_length,
        oid *newname, size_t *newname_length, int exact, www_protocol_t *protocol);
int get_wwwResponseOut_row(oid *name, size_t *name_length,
        oid *newname, size_t *newname_length, int exact, www_protocol_t *protocol);


static wwwProtocolStatistics_t *www_protocol_array;

wwwProtocolStatistics_t *
get_www_mib_protocol_statistics_record(unsigned int index)
{
    if ((index < 0) || (get_www_service_total() <= index)) {
	return(NULL);
    }
    return(&(www_protocol_array[ index]));
}

wwwProtocolStatistics_t *
get_protocol_wwwService_row(struct variable *vp,
	oid *name, size_t *namelength,
	oid *newname, size_t *newname_length,
	int exact, unsigned int *index)
{
oid *ptr;

    *newname_length = vp->namelen;
    memcpy((char *)newname, (char *)vp->name, *newname_length * sizeof(oid));
    ptr = &(newname[ (*newname_length)++ ]);
    while (*index < get_www_service_total()) {
        *ptr = *index + 1;
        switch (exact) {
            case NEXT:
                if ( 0 > snmp_oid_compare(name, *namelength, newname, *newname_length)) {
                    return(&(www_protocol_array[ *index ]));
                }
                break;
            case EXACT:
                if ( 0 == snmp_oid_compare(name, *namelength, newname, *newname_length)) {
                    return(&(www_protocol_array[ *index ]));
                }
                break;
            case INDEX:
                if ( 0 >= snmp_oid_compare(name,
				SNMP_MIN(*namelength,*newname_length),
				newname,
				SNMP_MIN(*namelength, *newname_length))) {
                    return(&(www_protocol_array[ *index ]));
                }
                break;
        }
	(*index)++;
    }
    return(NULL);
}

unsigned char *
read_wwwSummaryEntry(
    struct variable *vp,
    oid     *name,
    size_t  *length,
    int     exact,
    size_t  *var_len,
    WriteMethod **write_method)
{
wwwProtocolStatistics_t	*wwwStats;
oid newname[ MAX_OID_LEN ];
size_t newname_length;
int wwwServiceIndex = 0;

    wwwStats = get_protocol_wwwService_row(vp, name, length, newname, &newname_length, exact, &wwwServiceIndex);
    if (!wwwStats) {
	return(NULL);
    }

    *length = newname_length;
    memcpy( (char *)name,(char *)newname, *length * sizeof(oid));
    *write_method = 0;
    *var_len = sizeof(long);

    switch (vp->magic) {
	case WWWSUMMARYINREQUESTS:
	    return (unsigned char *) &(wwwStats->wwwSummaryInRequests);
	case WWWSUMMARYOUTREQUESTS:
	    return (unsigned char *) NULL;
	case WWWSUMMARYINRESPONSES:
	    return (unsigned char *) NULL;
	case WWWSUMMARYOUTRESPONSES:
	    return (unsigned char *) &(wwwStats->wwwSummaryOutResponses);
	case WWWSUMMARYINBYTES:
	    return (unsigned char *) NULL;
	case WWWSUMMARYINLOWBYTES:
	    return (unsigned char *) &(wwwStats->wwwSummaryInLowBytes);
	case WWWSUMMARYOUTBYTES:
	    return (unsigned char *) NULL;
	case WWWSUMMARYOUTLOWBYTES:
	    return (unsigned char *) &(wwwStats->wwwSummaryOutLowBytes);
        default:
            return NULL;
    }
}


int
get_wwwRequestIn_row(oid *name, size_t *name_length, oid *newname, size_t *newname_length, int exact, www_protocol_t *protocol)
{
int wwwRequestIn_ArrayIndex;
unsigned int i;
int result;
size_t len = *newname_length;
const char *request_type;
const int max_requests = protocol->nr_of_request_types;

    for (wwwRequestIn_ArrayIndex = 0 ;
                wwwRequestIn_ArrayIndex < max_requests ;
                wwwRequestIn_ArrayIndex++) {
        request_type =  get_request_type(protocol, wwwRequestIn_ArrayIndex);
        newname[ len ] = strlen(request_type);
        /* build the OID string from the chars as in the status_codes.h
         * file; which is automatically made from httpd.h; NOTE that things
         * barf horribly if the httpd.h file and this status_codes.h file
         * are out of sync! Also note that the sort order is of importance.
         */
        for( i = 0 ; i < strlen(request_type) ; i++ )
                newname[ len + i + 1 ] = request_type[ i ];
	*newname_length =  len + i + 1;
        result = snmp_oid_compare(name, *name_length, newname, *newname_length);
        if (((exact == EXACT) && (result == 0)) ||
			((exact == NEXT) && (result < 0))) {
            return(wwwRequestIn_ArrayIndex);
        } /* if */

     } /* for loop */
return(-1);
}

#ifdef WWW_REQUEST_IN_GROUP
unsigned char *
read_wwwRequestInEntry(
    struct variable *vp,
    oid     *name,
    size_t  *length,
    int     exact,
    size_t  *var_len,
    WriteMethod **write_method)
{
wwwProtocolStatistics_t *wwwStats;
oid newname[ MAX_OID_LEN ];
size_t newname_length;
int reqIndex;
int wwwServiceIndex = 0;

    do {
	wwwStats = get_protocol_wwwService_row(vp, name, length, newname, &newname_length, INDEX, &wwwServiceIndex);
	if (wwwStats) {
            reqIndex = get_wwwRequestIn_row(name, length,
                                        newname, &newname_length,
                                        exact, wwwStats->protocol);
	} else {
	    return(NULL);
	}
        wwwServiceIndex++;
    } while (reqIndex < 0 );

    *length = newname_length;
    memcpy( (char *)name,(char *)newname, *length * sizeof(oid));
    *write_method = 0;
    *var_len = sizeof(long);    /* default length */
    switch (vp->magic) {
	case WWWREQUESTINREQUESTS:
	    return (unsigned char *) &(wwwStats->wwwRequestIn[reqIndex].requests);
	case WWWREQUESTINBYTES:
	    return (unsigned char *) &(wwwStats->wwwRequestIn[reqIndex].bytes);
	case WWWREQUESTINLASTIME:
	    *var_len = snmp_time2DateAndTime(wwwStats->wwwRequestIn[reqIndex].lastTime, return_buf);
	    return (unsigned char *) return_buf;
        default:
            return NULL;
    }
}
#endif

#ifdef WWW_REQUEST_OUT_GROUP
unsigned char *
read_wwwRequestOutEntry(
    struct variable *vp,
    oid     *name,
    size_t  *length,
    int     exact,
    size_t  *var_len,
    WriteMethod **write_method)
{
/* Variables defined */
oid newname[ MAX_OID_LEN ];
int newname_length;
int	result;

/* Check OID */
    result = snmp_oid_compare(name, *length, newname, newname_length);
    if (((exact == EXACT) && (result != 0)) ||
        ((exact == NEXT) && (result >= 0)))
        return NULL;

    *write_method = 0;
    *var_len = sizeof(long);    /* default length */

    switch (vp->magic) {
	case WWWREQUESTOUTREQUESTS:
	    return (unsigned char *) NULL;
	case WWWREQUESTOUTBYTES:
	    return (unsigned char *) NULL;
	case WWWREQUESTOUTLASTTIME:
	    return (unsigned char *) NULL;
        default:
            return NULL;
    }
}
#endif /* WWW_REQUEST_OUT_GROUP */

#ifdef WWW_RESPONSE_IN_GROUP
unsigned char *
read_wwwResponseInEntry(
    struct variable *vp,
    oid     *name,
    size_t  *length,
    int     exact,
    size_t  *var_len,
    WriteMethod **write_method)
{
oid newname[ MAX_OID_LEN ];
int newname_length;
int	result;

    /* Check OID */
    result = snmp_oid_compare(name, *length, newname, newname_length);
    if (((exact == EXACT) && (result != 0)) ||
        ((exact == NEXT) && (result >= 0)))
        return NULL;

    *length = newname_length;
    memcpy( (char *)name,(char *)newname, *length * sizeof(oid));
    *write_method = 0;
    *var_len = sizeof(long);    /* default length */

    switch (vp->magic) {
	case WWWRESPONSEINRESPONSES:
	    return (unsigned char *) NULL;
	case WWWRESPONSEINBYTES:
	    return (unsigned char *) NULL;
	case WWWRESPONSEINLASTTIME:
	    return (unsigned char *) NULL;
        default:
            return NULL;
    }
}
#endif /* WWW_RESPONSE_IN_GROUP */

#ifdef WWW_RESPONSE_OUT_GROUP
int
get_wwwResponseOut_row(oid *name, size_t *name_length, oid *newname, size_t *newname_length, int exact, www_protocol_t *protocol)
{
int	responseArrayIndex;
int	result;
int	newnameLen = (*newname_length)++;

    responseArrayIndex = 0;
    while (get_response_type(protocol, responseArrayIndex) > 0) {
	newname[ newnameLen ] = get_response_type(protocol, responseArrayIndex);
        result = snmp_oid_compare(name, *name_length, newname, *newname_length);
	if (((exact == EXACT) && (result == 0)) || ((exact == NEXT) && (0 > result))) {
	    return(responseArrayIndex);
	} /* if */
	responseArrayIndex++;
    } /* for */
return(-1);
}


unsigned char *
read_wwwResponseOutEntry(
    struct variable *vp,
    oid     *name,
    size_t  *length,
    int     exact,
    size_t  *var_len,
    WriteMethod **write_method)
{
wwwProtocolStatistics_t *wwwStats;
oid newname[ MAX_OID_LEN ];
size_t newname_length;
int responseIndex;
int wwwServiceIndex = 0;

    do {
	wwwStats = get_protocol_wwwService_row(vp, name, length, newname, &newname_length, INDEX, &wwwServiceIndex);
	if (wwwStats) {
	    responseIndex = get_wwwResponseOut_row(name, length, newname, &newname_length, exact, wwwStats->protocol);
        } else {
	    return(NULL);
	}
	wwwServiceIndex++;
    } while ( responseIndex < 0 );

    *length = newname_length;
    memcpy( (char *)name,(char *)newname, *length * sizeof(oid));
    *write_method = 0;
    *var_len = sizeof(long);    /* default length */

    switch (vp->magic) {
	case WWWRESPONSEOUTRESPONSES:
	    return (unsigned char *) &(wwwStats->wwwResponseOut[responseIndex].responses);
	case WWWRESPONSEOUTBYTES:
	    return (unsigned char *) &(wwwStats->wwwResponseOut[responseIndex].bytes);
	case WWWRESPONSEOUTLASTTIME:
	    *var_len = snmp_time2DateAndTime(wwwStats->wwwResponseOut[responseIndex].lastTime, return_buf);
	    return (unsigned char *) return_buf;
        default:
            return NULL;
    }
}
#endif /* WWW_RESPONSE_OUT_GROUP */

oid wwwSummaryEntry_oid[] = { 1, 3, 6, 1, 2, 1, 65, 1, 2, 1, 1 };
struct variable2 wwwSummaryEntry_variables[] = {
    { WWWSUMMARYINREQUESTS, ASN_COUNTER, RONLY, read_wwwSummaryEntry, 1, { 1 }},
    { WWWSUMMARYOUTREQUESTS, ASN_COUNTER, RONLY, read_wwwSummaryEntry, 1, { 2 }},
    { WWWSUMMARYINRESPONSES, ASN_COUNTER, RONLY, read_wwwSummaryEntry, 1, { 3 }},
    { WWWSUMMARYOUTRESPONSES, ASN_COUNTER, RONLY, read_wwwSummaryEntry, 1, { 4 }},
    { WWWSUMMARYINBYTES, ASN_COUNTER64, RONLY, read_wwwSummaryEntry, 1, { 5 }},
    { WWWSUMMARYINLOWBYTES, ASN_COUNTER, RONLY, read_wwwSummaryEntry, 1, { 6 }},
    { WWWSUMMARYOUTBYTES, ASN_COUNTER64, RONLY, read_wwwSummaryEntry, 1, { 7 }},
    { WWWSUMMARYOUTLOWBYTES, ASN_COUNTER, RONLY, read_wwwSummaryEntry, 1, { 8 }}
    };

#ifdef WWW_REQUEST_IN_GROUP
oid wwwRequestInEntry_oid[] = { 1, 3, 6, 1, 2, 1, 65, 1, 2, 2, 1 };
struct variable2 wwwRequestInEntry_variables[] = {
    { WWWREQUESTINREQUESTS, ASN_COUNTER, RONLY, read_wwwRequestInEntry, 1, { 2 }},
    { WWWREQUESTINBYTES, ASN_COUNTER, RONLY, read_wwwRequestInEntry, 1, { 3 }},
    { WWWREQUESTINLASTIME, ASN_OCTET_STR, RONLY, read_wwwRequestInEntry, 1, { 4 }}
    };
#endif /* WWW_REQUEST_IN_GROUP */

#ifdef WWW_REQUEST_OUT_GROUP
oid wwwRequestOutEntry_oid[] = { 1, 3, 6, 1, 2, 1, 65, 1, 2, 3, 1 };
struct variable2 wwwRequestOutEntry_variables[] = {
    { WWWREQUESTOUTREQUESTS, ASN_COUNTER, RONLY, read_wwwRequestOutEntry, 1, { 2 }},
    { WWWREQUESTOUTBYTES, ASN_COUNTER, RONLY, read_wwwRequestOutEntry, 1, { 3 }},
    { WWWREQUESTOUTLASTTIME, ASN_OCTET_STR, RONLY, read_wwwRequestOutEntry, 1, { 4 }}
    };
#endif /* WWW_REQUEST_OUT_GROUP */

#ifdef WWW_RESPONSE_IN_GROUP
oid wwwResponseInEntry_oid[] = { 1, 3, 6, 1, 2, 1, 65, 1, 2, 4, 1 };
struct variable2 wwwResponseInEntry_variables[] = {
    { WWWRESPONSEINRESPONSES, ASN_COUNTER, RONLY, read_wwwResponseInEntry, 1, { 2 }},
    { WWWRESPONSEINBYTES, ASN_COUNTER, RONLY, read_wwwResponseInEntry, 1, { 3 }},
    { WWWRESPONSEINLASTTIME, ASN_OCTET_STR, RONLY, read_wwwResponseInEntry, 1, { 4 }}
    };
#endif /* WWW_RESPONSE_IN_GROUP */

#ifdef WWW_RESPONSE_OUT_GROUP
oid wwwResponseOutEntry_oid[] = { 1, 3, 6, 1, 2, 1, 65, 1, 2, 5, 1 };
struct variable2 wwwResponseOutEntry_variables[] = {
    { WWWRESPONSEOUTRESPONSES, ASN_COUNTER, RONLY, read_wwwResponseOutEntry, 1, { 2 }},
    { WWWRESPONSEOUTBYTES, ASN_COUNTER, RONLY, read_wwwResponseOutEntry, 1, { 3 }},
    { WWWRESPONSEOUTLASTTIME, ASN_OCTET_STR, RONLY, read_wwwResponseOutEntry, 1, { 4 }}
    };
#endif /* WWW_RESPONSE_OUT_GROUP */

const char *
update_www_mib_protocol_statistics(unsigned int wwwServiceIndex,
                                   unsigned int wwwProtocol,
                                   apr_time_t wwwRequestTime,
                                   unsigned long wwwRequestInType,
                                   unsigned long wwwBytesIn,
                                   unsigned long wwwResponseOutType,
                                   unsigned long wwwBytesOut,
                                   char *wwwDocName,
                                   char *wwwStatusMsg)
{
wwwProtocolStatistics_t *wwwProtocolRecord;
int responseOutTypeArrayIndex;
int requestInTypeArrayIndex;
www_protocol_t *protocol;

    DEBUGTRACE;
    wwwProtocolRecord = get_www_mib_protocol_statistics_record(wwwServiceIndex);
    if (wwwProtocolRecord) {
        /* Fill some short cuts for the statsistics update */
        requestInTypeArrayIndex = wwwRequestInType;
        protocol = get_www_protocol_definition_by_number(wwwProtocol);
        responseOutTypeArrayIndex = get_response_type_index(protocol, wwwResponseOutType);

        wwwProtocolRecord->wwwSummaryInRequests++;
        wwwProtocolRecord->wwwSummaryOutResponses++;
        wwwProtocolRecord->wwwSummaryInLowBytes += wwwBytesIn;
        wwwProtocolRecord->wwwSummaryOutLowBytes += wwwBytesOut;
#ifdef WWW_REQUEST_IN_GROUP
        if ((wwwRequestInType >= 0) && (requestInTypeArrayIndex >= 0)) {
            wwwProtocolRecord->wwwRequestIn[ requestInTypeArrayIndex ].requests++;
            wwwProtocolRecord->wwwRequestIn[ requestInTypeArrayIndex ].bytes += wwwBytesIn;
            wwwProtocolRecord->wwwRequestIn[ requestInTypeArrayIndex ].lastTime = wwwRequestTime;
        }
#endif /* WWW_REQUEST_IN_GROUP */
#ifdef WWW_RESPONSE_OUT_GROUP
        if (responseOutTypeArrayIndex >= 0) {
            wwwProtocolRecord->wwwResponseOut[ responseOutTypeArrayIndex ].responses++;
            wwwProtocolRecord->wwwResponseOut[ responseOutTypeArrayIndex ].bytes += wwwBytesOut;
            wwwProtocolRecord->wwwResponseOut[ responseOutTypeArrayIndex ].lastTime = wwwRequestTime;
        }
#endif /* WWW_RESPONSE_OUT_GROUP */
        return(NULL);
    }
    return "Could not find protocol statistics record";
}

void init_www_mib_protocol(apr_pool_t *p)
{
server_rec *s;
unsigned int i;
size_t size;
char *protocol;
www_protocol_t *protocol_definition;
#ifdef WWW_REQUEST_IN_GROUP
wwwRequestIn_t *wwwRequestIn;
#endif
#ifdef WWW_REQUEST_OUT_GROUP
wwwRequestOut_t *wwwRequestOut;
#endif
#ifdef WWW_RESPONSE_IN_GROUP
wwwResponseIn_t *wwwResponseIn;
#endif
#ifdef WWW_RESPONSE_OUT_GROUP
wwwResponseOut_t *wwwResponseOut;
#endif

    size = sizeof(wwwProtocolStatistics_t) * get_www_service_total();
    www_protocol_array = (wwwProtocolStatistics_t *) apr_pcalloc (p, size);
    if (www_protocol_array == NULL) {
	ap_log_error(APLOG_MARK, (APLOG_NOERRNO|APLOG_NOTICE), 0, NULL,
                        "SNMP:www-mib-protocol: could not create service statistics array");
	return;
    }

    memset(www_protocol_array, 0, size);
    /* The base array exist register its MIB subtree */
    REGISTER_MIB("www-mib/wwwSummaryTable",
                    wwwSummaryEntry_variables, variable2, wwwSummaryEntry_oid);
    /* insert the Object Resource in sysORTable */
#ifdef USING_MIBII_SYSORTABLE_MODULE
    register_sysORTable(wwwSummaryEntry_oid, 9,
                    "The WWW protocol group of the WWW-MIB module");
#endif

    /* Now insert the assoicated protocol info of the www services. */
    for (i = 0, s = get_www_service_root();
                         ((i < get_www_service_total()) && (s));
                         i++, s = s->next) {
        protocol = get_www_service_protocol(s);
        protocol_definition = get_www_protocol_definition_by_name(protocol);
        if (protocol_definition) {
            www_protocol_array[i].protocol = protocol_definition;
#ifdef WWW_REQUEST_IN_GROUP
            size = sizeof(wwwRequestIn_t) * protocol_definition->nr_of_request_types;
            wwwRequestIn = (wwwRequestIn_t *)apr_pcalloc(p, size);
            if (wwwRequestIn) {
                memset(wwwRequestIn, 0, size);
                www_protocol_array[i].wwwRequestIn = wwwRequestIn;
                REGISTER_MIB("www-mib/wwwRequestInTable",
                            wwwRequestInEntry_variables, variable2,
                            wwwRequestInEntry_oid);
            }
#endif
#ifdef WWW_REQUEST_OUT_GROUP
            size = sizeof(wwwRequestIn_t) * protocol_definition->nr_of_request_types;
            wwwRequestOut = (wwwRequestOut_t *)apr_pcalloc(p, size);
            if (wwwRequestOut) {
                memset(wwwRequestOut, 0, size);
                www_protocol_array[i].wwwRequestOut = wwwRequestOut;
                REGISTER_MIB("www-mib/wwwRequestOutTable",
                            wwwRequestOutEntry_variables, variable2,
                            wwwRequestOutEntry_oid);
            }
#endif
#ifdef WWW_RESPONSE_IN_GROUP
            size = sizeof(wwwRequestIn_t) * protocol_definition->nr_of_request_types;
            wwwResponseIn = (wwwResponseIn_t *)apr_pcalloc(p, size);
            if (wwwResponseIn) {
                memset(wwwResponseIn, 0, size);
                www_protocol_array[i].wwwResponseIn = wwwResponseIn;
                REGISTER_MIB("www-mib/wwwResponseInTable",
                            wwwResponseInEntry_variables, variable2,
                            wwwResponseInEntry_oid);
            }
#endif
#ifdef WWW_RESPONSE_OUT_GROUP
            size = sizeof(wwwResponseOut_t) * protocol_definition->nr_of_response_types;
            wwwResponseOut = (wwwResponseOut_t *)apr_pcalloc(p, size);
            if (wwwResponseOut) {
                memset(wwwResponseOut, 0, size);
                www_protocol_array[i].wwwResponseOut = wwwResponseOut;
                REGISTER_MIB("www-mib/wwwResponseOutTable",
                            wwwResponseOutEntry_variables, variable2,
                            wwwResponseOutEntry_oid);
            }
#endif
        } else {
	    ap_log_error(APLOG_MARK, (APLOG_NOERRNO|APLOG_NOTICE), 0, NULL,
                        "SNMP:www-mib-protocol: protocol '%s' is not registered",
                        protocol);
        }
    }
}
#endif /* WWW_MIB */
