/* $Date: 2008-01-18 07:11:17 +0800 (Fri, 18 Jan 2008) $ $Id: www-mib.c 8676 2008-01-17 23:11:17Z ispringer $ harrie@covalent.net
 */
/* Apache includes */
#include "httpd.h"
#include "http_log.h"
#include "http_config.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 "covalent-snmp-config.h"
#include "ietf-mibs/snmp-generic.h"
#include "covalent-snmp-logging.h"
#include "covalent-snmp.h"
#include "covalent-snmp-sconfig.h"
#include "snmpv2-tc.h"
#include "www-mib.h"
#include "www-mib-utils.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
extern server_rec *www_services; /* www service records (covalent_snmp-snmp.c) */
extern int nr_www_services; /* total amount of www services (covalent_snmp-snmp.c) */
static wwwStatistics_t *www_statistics_array;

wwwStatistics_t *
get_www_statistics_record(int index)
{
    if ((index < 0) || (nr_www_services <= index)) {
	return(NULL);
    }
    return(&(www_statistics_array[ index]));
}

server_rec *
get_admin_wwwService_row(struct variable *vp,
	oid *name, size_t *namelength,
	oid *newname, size_t *newname_length,
	int exact, server_rec *server)
{
oid *ptr;

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

unsigned char *
read_wwwServiceEntry(
    struct variable *vp,
    oid     *name,
    size_t  *length,
    int     exact,
    size_t  *var_len,
    WriteMethod **write_method)
{
static oid protocol_oid[] = { 1, 3, 6, 1, 2, 1, 6, 80 };
oid newname[ MAX_OID_LEN ];
size_t newname_length;
server_rec *service;

    service = get_admin_wwwService_row(vp, name, length, newname, &newname_length, exact, www_services);
    if (!service) {
	return NULL; 
    }

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

    switch (vp->magic) {
	case WWWSERVICEDESCRIPTION:
	    *var_len = strlen(ap_get_server_version());
	    return (unsigned char *) ap_get_server_version();
	case WWWSERVICECONTACT:
	    *var_len = strlen(service->server_admin);
	    return (unsigned char *) service->server_admin;
	case WWWSERVICEPROTOCOL:
            protocol_oid[ 7 ] = service->port;
	    *var_len = sizeof(protocol_oid);
	    return (unsigned char *) protocol_oid;
	case WWWSERVICENAME:
	    *var_len = strlen(service->server_hostname);
	    return (unsigned char *) service->server_hostname;
	case WWWSERVICETYPE:
	    long_return = 2; /* server only */
	    return (unsigned char *) &long_return;
	case WWWSERVICESTARTTIME:
	    *var_len = snmp_time2DateAndTime(ap_restart_time, return_buf);
	    return (unsigned char *) return_buf;
	case WWWSERVICEOPERSTATUS:
	    long_return = OPERSTATUS_RUNNING;
	    return (unsigned char *) &long_return;
	case WWWSERVICELASTCHANGE:
	    *var_len = snmp_time2DateAndTime(ap_restart_time, return_buf);
	    return (unsigned char *) return_buf;
        default:
            return NULL;
    }
}

wwwStatistics_t *
get_statistics_wwwService_row(struct variable *vp,
	oid *name, size_t *namelength,
	oid *newname, size_t *newname_length,
	int exact, int *index)
{
oid *ptr;

    *newname_length = vp->namelen;
    memcpy((char *)newname, (char *)vp->name, *newname_length * sizeof(oid));
    ptr = &(newname[ (*newname_length)++ ]);
    while (*index < nr_www_services) {
        *ptr = *index + 1;
        switch (exact) {
            case NEXT:
                if ( 0 > snmp_oid_compare(name, *namelength, newname, *newname_length)) {
                    return(&(www_statistics_array[ *index ]));
                }
                break;
            case EXACT:
                if ( 0 == snmp_oid_compare(name, *namelength, newname, *newname_length)) {
                    return(&(www_statistics_array[ *index ]));
                }
                break;
            case INDEX:
                if ( 0 >= snmp_oid_compare(name,
				SNMP_MIN(*namelength,*newname_length),
				newname,
				SNMP_MIN(*namelength, *newname_length))) {
                    return(&(www_statistics_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)
{
wwwStatistics_t	*wwwStats;
oid newname[ MAX_OID_LEN ];
size_t newname_length;
int wwwServiceIndex = 0;

    wwwStats = get_statistics_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)
{
int     wwwRequestIn_ArrayIndex;
int     i, result;
size_t	len = *newname_length;

    for (wwwRequestIn_ArrayIndex = 0 ; requestTypes[wwwRequestIn_ArrayIndex] != NULL ; wwwRequestIn_ArrayIndex++) {
        newname[ len ] = strlen(requestTypes[wwwRequestIn_ArrayIndex]);
        /* 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(requestTypes[ wwwRequestIn_ArrayIndex ]) ; i++ )
                newname[ len + i + 1 ] = requestTypes[ wwwRequestIn_ArrayIndex ][ 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)
{
wwwStatistics_t *wwwStats;
oid newname[ MAX_OID_LEN ];
size_t newname_length;
int reqIndex;
int wwwServiceIndex = 0;

    do {
	wwwStats = get_statistics_wwwService_row(vp, name, length, newname, &newname_length, INDEX, &wwwServiceIndex);
	if (wwwStats) {
            reqIndex = get_wwwRequestIn_row(name, length, newname, &newname_length, exact);
	} 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)
{
int	responseArrayIndex;
int	result;
int	newnameLen = (*newname_length)++;

    responseArrayIndex = 0;
    while (responseTypes[ responseArrayIndex ] > 0) {
	newname[ newnameLen ] = responseTypes[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)
{
wwwStatistics_t *wwwStats;
oid newname[ MAX_OID_LEN ];
size_t newname_length;
int responseIndex;
int wwwServiceIndex = 0;

    do {
	wwwStats = get_statistics_wwwService_row(vp, name, length, newname, &newname_length, INDEX, &wwwServiceIndex);
	if (wwwStats) {
	    responseIndex = get_wwwResponseOut_row(name, length, newname, &newname_length, exact);
        } 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 */

#ifdef WWW_DOCUMENT_GROUP
/* An entry used to configure the wwwDocLastNTable,
 * the wwwDocBucketTable, the wwwDocAccessTopNTable,
 * and the wwwDocBytesTopNTable.
 */
int
write_wwwDocCtrlLastNSize(int action,
	unsigned char *var_val, unsigned char var_val_type, size_t var_val_len,
	unsigned char *statP, oid *name, size_t name_length)
{
int wwwServiceIndex = name[ name_length - 1];
wwwStatistics_t *wwwServiceSnmpData = get_www_statistics_record(wwwServiceIndex);

    if (wwwServiceSnmpData == NULL) {
        return(SNMP_ERR_NOTWRITABLE);
    }

    if (var_val_type != ASN_UNSIGNED) {
	return SNMP_ERR_WRONGTYPE;
    }

    if (var_val_len > sizeof(wwwServiceSnmpData->temp_wwwDocCtrlLastNSize)) {
	return(SNMP_ERR_WRONGLENGTH);
    }

    wwwServiceSnmpData->temp_wwwDocCtrlLastNSize = *((long *) var_val);
    if ((wwwServiceSnmpData->temp_wwwDocCtrlLastNSize < 0)
		|| (wwwServiceSnmpData->temp_wwwDocCtrlLastNSize > MAX_WWWDOCLASTNSIZE)) {
	return SNMP_ERR_WRONGVALUE;
    }

    if (action == COMMIT) {
	wwwServiceSnmpData->wwwDocCtrlLastNSize = wwwServiceSnmpData->temp_wwwDocCtrlLastNSize;
    }
    return SNMP_ERR_NOERROR;
}

int
write_wwwDocCtrlBuckets(int action,
	unsigned char *var_val, unsigned char var_val_type, size_t var_val_len,
	unsigned char *statP, oid *name, size_t name_length)
{
int wwwServiceIndex = name[ name_length - 1];
wwwStatistics_t *wwwServiceSnmpData = get_www_statistics_record(wwwServiceIndex);

    if (wwwServiceSnmpData == NULL) {
        return(SNMP_ERR_NOTWRITABLE);
    }

    if (var_val_type != ASN_UNSIGNED) {
	return SNMP_ERR_WRONGTYPE;
    }

    if (var_val_len > sizeof(wwwServiceSnmpData->temp_wwwDocCtrlBuckets)) {
	return(SNMP_ERR_WRONGLENGTH);
    }

    wwwServiceSnmpData->temp_wwwDocCtrlBuckets = *((unsigned long *) var_val);
    if ((wwwServiceSnmpData->temp_wwwDocCtrlBuckets < 0)
			|| (wwwServiceSnmpData->temp_wwwDocCtrlBuckets > MAX_WWWDOCBUCKETS)) {
	return SNMP_ERR_WRONGVALUE;
    }

    if (action == COMMIT) {
	wwwServiceSnmpData->wwwDocCtrlBuckets = wwwServiceSnmpData->temp_wwwDocCtrlBuckets;
    }
    return SNMP_ERR_NOERROR;
}

int
write_wwwDocCtrlBucketTimeInterval(int action,
	unsigned char *var_val, unsigned char var_val_type, size_t var_val_len,
	unsigned char *statP, oid *name, size_t name_length)
{
int wwwServiceIndex = name[ name_length - 1];
wwwStatistics_t *wwwServiceSnmpData = get_www_statistics_record(wwwServiceIndex);

    if (wwwServiceSnmpData == NULL) {
        return(SNMP_ERR_NOTWRITABLE);
    }

    if (var_val_type != ASN_UNSIGNED) {
	return SNMP_ERR_WRONGTYPE;
    }

    if (var_val_len > sizeof(wwwServiceSnmpData->temp_wwwDocCtrlTopNSize)) {
	return(SNMP_ERR_WRONGLENGTH);
    }
    wwwServiceSnmpData->temp_wwwDocCtrlTopNSize = *((unsigned long *) var_val);

    /* We only accept seconds */
    if ((wwwServiceSnmpData->temp_wwwDocCtrlBucketTimeInterval % 100) != 0) {
	return SNMP_ERR_WRONGVALUE;
    }

    if (action == COMMIT) {
	wwwServiceSnmpData->wwwDocCtrlBucketTimeInterval = wwwServiceSnmpData->temp_wwwDocCtrlBucketTimeInterval;
    }
    return SNMP_ERR_NOERROR;
}

int
write_wwwDocCtrlTopNSize(int action,
	unsigned char *var_val, unsigned char var_val_type, size_t var_val_len,
	unsigned char *statP, oid *name, size_t name_length)
{
int wwwServiceIndex = name[ name_length - 1];
wwwStatistics_t *wwwServiceSnmpData = get_www_statistics_record(wwwServiceIndex);

    if (wwwServiceSnmpData == NULL) {
        return(SNMP_ERR_NOTWRITABLE);
    }

    if (var_val_type != ASN_UNSIGNED) {
	return SNMP_ERR_WRONGTYPE;
    }

    if (var_val_len > sizeof(wwwServiceSnmpData->temp_wwwDocCtrlTopNSize)) {
	return(SNMP_ERR_WRONGLENGTH);
    }
    wwwServiceSnmpData->temp_wwwDocCtrlTopNSize = *((unsigned long *) var_val);
    if ((wwwServiceSnmpData->temp_wwwDocCtrlTopNSize < 0)
			|| (wwwServiceSnmpData->temp_wwwDocCtrlTopNSize > MAX_WWWDOCTOPNSIZE)) {
        return SNMP_ERR_WRONGVALUE;
    }

    if (action == COMMIT) {
	wwwServiceSnmpData->wwwDocCtrlTopNSize = wwwServiceSnmpData->temp_wwwDocCtrlTopNSize;
    }
    return SNMP_ERR_NOERROR;
}

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

    wwwStats = get_statistics_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);    /* default length */

    switch (vp->magic) {
	case WWWDOCCTRLNSIZE:
	    *write_method = write_wwwDocCtrlLastNSize;
	    return (unsigned char *) &(wwwStats->wwwDocCtrlLastNSize);
	case WWWDOCCTRLNLOCK:
	  /*	    *write_method = write_wwwDocCtrlLastNLock;*/
	    *write_method = 0;
	    return (unsigned char *) &(wwwStats->wwwDocCtrlLastNLock);
	case WWWDOCCTRLBUCKETS:
	    *write_method = write_wwwDocCtrlBuckets;
	    return (unsigned char *) &(wwwStats->wwwDocCtrlBuckets);
	case WWWDOCCTRLBUCKETSTIMEINTERVAL:
	    *write_method = write_wwwDocCtrlBucketTimeInterval;
	    long_return = wwwStats->wwwDocCtrlBucketTimeInterval * 100; /* from sec to msec */
	    return (unsigned char *) &long_return;
	case WWWDOCCTRLTOPNSIZE:
	    *write_method = write_wwwDocCtrlTopNSize;
	    return (unsigned char *) &(wwwStats->wwwDocCtrlTopNSize);
        default:
            return NULL;
    }
}


wwwDocLastN_t *
get_wwwDocLastN_row(oid* name, size_t *name_length, oid *newname, size_t *newname_length, int exact, wwwStatistics_t *wwwStats)
{
int	result;

    if (!wwwStats) return(NULL);
    if (!wwwStats->wwwDocLastNTable) return(NULL);
    *newname_length = 14;
    if (0 < (int)(wwwStats->wwwDocLastNIndex - wwwStats->wwwDocCtrlLastNSize)) {
	newname[13] = (wwwStats->wwwDocLastNIndex - wwwStats->wwwDocCtrlLastNSize);
    } else {
	newname[13] = 0;
    }
    while (++newname[13] <= wwwStats->wwwDocLastNIndex) {
        result = snmp_oid_compare(name, *name_length, newname, *newname_length);
	if (((exact == EXACT) && (result == 0)) || ((exact == NEXT) && (result < 0))) {
	    return(&(wwwStats->wwwDocLastNTable[ newname[13] % MAX_WWWDOCLASTNSIZE ]));
	}
    }
    return(NULL);
}


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

    do {
	wwwStats = get_statistics_wwwService_row(vp, name, length, newname, &newname_length, INDEX, &wwwServiceIndex);
	if (wwwStats) {
	    currentRow = get_wwwDocLastN_row(name, length, newname, &newname_length, exact, wwwStats);
	} else {
 	    return(NULL);
        }
	wwwServiceIndex++;
    } while (!currentRow);

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

    switch (vp->magic) {
	case WWWDOCLASTNNAME:
	    *var_len = strlen(currentRow->name);
	    return (unsigned char *) currentRow->name;
	case WWWDOCLASTNTIMESTAMP:
	    *var_len = snmp_time2DateAndTime(currentRow->timestamp,
								return_buf);
	    return (unsigned char *) return_buf;
	case WWWDOCLASTNREQUESTTYPE:
	    if (currentRow->requestType == -1) {
		*var_len = 0;
		return (unsigned char *) return_buf;
	    }
	    *var_len = strlen(requestTypes[currentRow->requestType]);
	    return (unsigned char *) requestTypes[currentRow->requestType];
	case WWWDOCLASTNRESPONSETYPE:
	    if (currentRow->responseType) {
		*var_len = sizeof(unsigned long);
		return (unsigned char *) &responseTypes[currentRow->responseType];
	    }
	    return(NULL);
	case WWWDOCLASTNSTATUSMSG:
	    return(NULL);
	    *var_len = strlen(currentRow->statusMsg);
	    return (unsigned char *) currentRow->statusMsg;
	case WWWDOCLASTNBYTES:
	    *var_len = sizeof(currentRow->bytes);
	    return (unsigned char *) &currentRow->bytes;
        default:
            return NULL;
    }
}

wwwDocBucket_t *
get_wwwDocBucket_row(oid *name, size_t *name_length, oid *newname, size_t *newname_length, int exact, wwwStatistics_t *wwwStats)
{
int	result;

    if (!wwwStats) return(NULL);
    *newname_length = 14;
    if (0 < (int)(wwwStats->wwwDocBucketIndex - wwwStats->wwwDocCtrlBuckets)) {
	newname[13] = wwwStats->wwwDocBucketIndex - wwwStats->wwwDocCtrlBuckets;
    } else {
	newname[13] = 0;
    }
    while ((++newname[13]) <= wwwStats->wwwDocBucketIndex) {
	result = snmp_oid_compare(name, *name_length,  newname, *newname_length);
	if (((exact == EXACT) && (result == 0)) ||
				((exact == NEXT) && (0 > result))) {
	    return(&(wwwStats->wwwDocBucketTable[ (int)newname[13] % MAX_WWWDOCBUCKETS ]));
	}
    }
    return(NULL);
}


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

    do {
	wwwStats = get_statistics_wwwService_row(vp, name, length, newname, &newname_length, INDEX, &wwwServiceIndex);
	if (wwwStats) {
	    bucket = get_wwwDocBucket_row(name, length, newname, &newname_length, exact, wwwStats);
	} else {
            return(NULL);
	}
        wwwServiceIndex++;
    } while (!bucket);

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

    switch (vp->magic) {
	case WWWDOCBUCKETTIMESTAMP:
	    *var_len = snmp_time2DateAndTime(bucket->timestamp,
								return_buf);
	    return (unsigned char *) return_buf;
	case WWWDOCBUCKETACCESSES:
	    return (unsigned char *) &(bucket->accesses);
	case WWWDOCBUCKETDOCUMENTS:
	    return (unsigned char *) &(bucket->documents);
	case WWWDOCBUCKETBYTES:
	    return (unsigned char *) &(bucket->bytes);
        default:
            return NULL;
    }
}

wwwDocTopN_t *
get_wwwDocTopN_row(oid *name, size_t *name_length, oid *newname, size_t *newname_length,
	int exact, wwwStatistics_t *wwwStats, int whichTopN)
{
int		result;
wwwDocBucket_t	*bucket;
int		topNSize;

    if (!wwwStats) return(NULL);
    if (0 < (int)(wwwStats->wwwDocBucketIndex - wwwStats->wwwDocCtrlBuckets)) {
	newname[13] = wwwStats->wwwDocBucketIndex - wwwStats->wwwDocCtrlBuckets;
    } else {
	newname[13] = 0;
    }
    *newname_length = 15;
    while ((++newname[13]) <= wwwStats->wwwDocBucketIndex) {
	bucket = &(wwwStats->wwwDocBucketTable[ newname[13] % MAX_WWWDOCBUCKETS ]);
	topNSize = SNMP_MIN(bucket->documents, wwwStats->wwwDocCtrlTopNSize);
	newname[14] = 0;
	while ((++newname[14]) <= topNSize) {
	     result = snmp_oid_compare(name, *name_length, newname, *newname_length);
	     if (((exact == EXACT) && (result == 0)) ||
				((exact == NEXT) && (result < 0))) {
		if (whichTopN == 0) {
		     return(&(bucket->accessTopNTable[(newname[14] - 1)]));
		} else {
		     return(&(bucket->bytesTopNTable[(newname[14] - 1)]));
		}
	     }
	}
    }
    return(NULL);
}

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

    do {
	wwwStats = get_statistics_wwwService_row(vp, name, length, newname, &newname_length, INDEX, &wwwServiceIndex);
	if (wwwStats) {
	    currentRow = get_wwwDocTopN_row(name, length, newname, &newname_length, exact, wwwStats, 0);
	} else {
	    return(NULL);
	}
	wwwServiceIndex++;
    } while (!currentRow);

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

    switch (vp->magic) {
	case WWWDOCACCESSTOPNNAME:
	    *var_len = strlen(currentRow->name);
	    return (unsigned char *) currentRow->name;
	case WWWDOCACCESSTOPNACCESSES:
	    *var_len = sizeof(currentRow->accesses);
	    return (unsigned char *) &(currentRow->accesses);
	case WWWDOCACCESSTOPNBYTES:
	    *var_len = sizeof(currentRow->bytes);
	    return (unsigned char *) &(currentRow->bytes);
	case WWWDOCACCESSTOPNLASTRESPONSETYPE:
	    *var_len = sizeof(currentRow->lastResponseType);
	    return (unsigned char *) &responseTypes[currentRow->lastResponseType];
        default:
            return NULL;
    }
}

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

    do {
	wwwStats = get_statistics_wwwService_row(vp, name, length, newname, &newname_length, INDEX, &wwwServiceIndex);
	if (wwwStats) {
	    currentRow = get_wwwDocTopN_row(name, length, newname, &newname_length, exact, wwwStats, 1);
	} else {
	    return(NULL);
	}
	wwwServiceIndex++;
    } while (!currentRow);

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

    switch (vp->magic) {
	case WWWDOCBYTESTOPNNAME:
	    *var_len = strlen(currentRow->name);
	    return (unsigned char *) currentRow->name;
	case WWWDOCBYTESTOPNACCESSES:
	    *var_len = sizeof(currentRow->accesses);
	    return (unsigned char *) &(currentRow->accesses);
	case WWWDOCBYTESTOPNBYTES:
	    *var_len = sizeof(currentRow->bytes);
	    return (unsigned char *) &(currentRow->bytes);
	case WWWDOCBYTESTOPNLASTNRESPONSETYPE:
	    return (unsigned char *) &responseTypes[currentRow->lastResponseType];
        default:
            return NULL;
    }
}
#endif /* WWW_DOCUMENT_GROUP */

oid wwwMIB_oid[] = { 1, 3, 6, 1, 2, 1, 65 };
oid wwwServiceEntry_oid[] = { 1, 3, 6, 1, 2, 1, 65, 1, 1, 1, 1 };
struct variable2 wwwServiceEntry_variables[] = {
    { WWWSERVICEDESCRIPTION, ASN_OCTET_STR, RONLY, read_wwwServiceEntry, 1, { 2 }},
    { WWWSERVICECONTACT, ASN_OCTET_STR, RONLY, read_wwwServiceEntry, 1, { 3 }},
    { WWWSERVICEPROTOCOL, ASN_OBJECT_ID, RONLY, read_wwwServiceEntry, 1, { 4 }},
    { WWWSERVICENAME, ASN_OCTET_STR, RONLY, read_wwwServiceEntry, 1, { 5 }},
    { WWWSERVICETYPE, ASN_INTEGER, RONLY, read_wwwServiceEntry, 1, { 6 }},
    { WWWSERVICESTARTTIME, ASN_OCTET_STR, RONLY, read_wwwServiceEntry, 1, { 7 }},
    { WWWSERVICEOPERSTATUS, ASN_INTEGER, RONLY, read_wwwServiceEntry, 1, { 8 }},
    { WWWSERVICELASTCHANGE, ASN_OCTET_STR, RONLY, read_wwwServiceEntry, 1, { 9 }}
    };

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 */

#ifdef WWW_DOCUMENT_GROUP
oid wwwDocCtrlEntry_oid[] = { 1, 3, 6, 1, 2, 1, 65, 1, 3, 1, 1 };
struct variable2 wwwDocCtrlEntry_variables[] = {
    { WWWDOCCTRLNSIZE, ASN_UNSIGNED, RWRITE, read_wwwDocCtrlEntry, 1, { 1 }},
    { WWWDOCCTRLNLOCK, ASN_TIMETICKS, RWRITE, read_wwwDocCtrlEntry, 1, { 2 }},
    { WWWDOCCTRLBUCKETS, ASN_UNSIGNED, RWRITE, read_wwwDocCtrlEntry, 1, { 3 }},
    { WWWDOCCTRLBUCKETSTIMEINTERVAL, ASN_INTEGER, RWRITE, read_wwwDocCtrlEntry, 1, { 4 }},
    { WWWDOCCTRLTOPNSIZE, ASN_UNSIGNED, RWRITE, read_wwwDocCtrlEntry, 1, { 5 }}
    };

oid wwwDocLastNEntry_oid[] = { 1, 3, 6, 1, 2, 1, 65, 1, 3, 2, 1 };
struct variable2 wwwDocLastNEntry_variables[] = {
    { WWWDOCLASTNNAME, ASN_OCTET_STR, RONLY, read_wwwDocLastNEntry, 1, { 2 }},
    { WWWDOCLASTNTIMESTAMP, ASN_OCTET_STR, RONLY, read_wwwDocLastNEntry, 1, { 3 }},
    { WWWDOCLASTNREQUESTTYPE, ASN_OCTET_STR, RONLY, read_wwwDocLastNEntry, 1, { 4 }},
    { WWWDOCLASTNRESPONSETYPE, ASN_INTEGER, RONLY, read_wwwDocLastNEntry, 1, { 5 }},
    { WWWDOCLASTNSTATUSMSG, ASN_OCTET_STR, RONLY, read_wwwDocLastNEntry, 1, { 6 }},
    { WWWDOCLASTNBYTES, ASN_UNSIGNED, RONLY, read_wwwDocLastNEntry, 1, { 7 }}
    };

oid wwwDocBucketEntry_oid[] = { 1, 3, 6, 1, 2, 1, 65, 1, 3, 3, 1 };
struct variable2 wwwDocBucketEntry_variables[] = {
    { WWWDOCBUCKETTIMESTAMP, ASN_OCTET_STR, RONLY, read_wwwDocBucketEntry, 1, { 2 }},
    { WWWDOCBUCKETACCESSES, ASN_UNSIGNED, RONLY, read_wwwDocBucketEntry, 1, { 3 }},
    { WWWDOCBUCKETDOCUMENTS, ASN_UNSIGNED, RONLY, read_wwwDocBucketEntry, 1, { 4 }},
    { WWWDOCBUCKETBYTES, ASN_UNSIGNED, RONLY, read_wwwDocBucketEntry, 1, { 5 }}
    };

oid wwwDocAccessTopNEntry_oid[] = { 1, 3, 6, 1, 2, 1, 65, 1, 3, 4, 1 };
struct variable2 wwwDocAccessTopNEntry_variables[] = {
    { WWWDOCACCESSTOPNNAME, ASN_OCTET_STR, RONLY, read_wwwDocAccessTopNEntry, 1, { 2 }},
    { WWWDOCACCESSTOPNACCESSES, ASN_UNSIGNED, RONLY, read_wwwDocAccessTopNEntry, 1, { 3 }},
    { WWWDOCACCESSTOPNBYTES, ASN_UNSIGNED, RONLY, read_wwwDocAccessTopNEntry, 1, { 4 }},
    { WWWDOCACCESSTOPNLASTRESPONSETYPE, ASN_INTEGER, RONLY, read_wwwDocAccessTopNEntry, 1, { 5 }}
    };

oid wwwDocBytesTopNEntry_oid[] = { 1, 3, 6, 1, 2, 1, 65, 1, 3, 5, 1 };
struct variable2 wwwDocBytesTopNEntry_variables[] = {
    { WWWDOCBYTESTOPNNAME, ASN_OCTET_STR, RONLY, read_wwwDocBytesTopNEntry, 1, { 2 }},
    { WWWDOCBYTESTOPNACCESSES, ASN_UNSIGNED, RONLY, read_wwwDocBytesTopNEntry, 1, { 3 }},
    { WWWDOCBYTESTOPNBYTES, ASN_UNSIGNED, RONLY, read_wwwDocBytesTopNEntry, 1, { 4 }},
    { WWWDOCBYTESTOPNLASTNRESPONSETYPE, ASN_INTEGER, RONLY, read_wwwDocBytesTopNEntry, 1, { 5 }}
    };
#endif /* WWW_DOCUMENT_GROUP */

void init_www_mib(server_rec *s, pool *p, char *bucket_directory)
{
int i;
server_rec *service;

    www_statistics_array = (wwwStatistics_t *)
                        ap_pcalloc (p, (sizeof(wwwStatistics_t) * nr_www_services));
    if (www_statistics_array == NULL) {
	ap_log_error(APLOG_MARK, (APLOG_NOERRNO|APLOG_NOTICE|APLOG_NOERRNO), s,
                        "SNMP:www-mib initialization: could not create service statistics array");
	return;
    }
    memset(www_statistics_array, 0, (sizeof(wwwStatistics_t) * nr_www_services));
#ifdef WWW_DOCUMENT_GROUP
    for (i = 0, service = s; ((service != NULL) && (i < nr_www_services)) ; i++, service = service->next) {
	www_statistics_array[ i ].wwwDocCtrlLastNSize = DEFAULT_WWWDOCLASTNSIZE;
        www_statistics_array[ i ].wwwDocCtrlBuckets = DEFAULT_WWWDOCBUCKETS;
        www_statistics_array[ i ].wwwDocCtrlBucketTimeInterval = DEFAULT_WWWDOCBUCKETTIMEINTERVAL;  
        www_statistics_array[ i ].wwwDocCtrlTopNSize = DEFAULT_WWWDOCTOPNSIZE;
        www_statistics_array[ i ].wwwDocBucketName = ap_pstrcat(p, ap_server_root_relative(p,
					bucket_directory), "/wwwbucket.", service->server_hostname, NULL);
        www_statistics_array[ i ].wwwDocBucket = covalent_snmp_wwwDocBucket_open(www_statistics_array[ i ].wwwDocBucketName);
        if (!www_statistics_array[ i ].wwwDocBucket) {
                ap_log_error(APLOG_MARK, (APLOG_NOERRNO|APLOG_NOTICE|APLOG_NOERRNO), s,
                        "SNMP:covalent_snmp_wwwDocBucket_open: %s '%s'", covalent_snmp_wwwDocBucket_strerror(),
                        www_statistics_array[ i ].wwwDocBucketName);
         }
    }
#endif /* WWW_DOCUMENT_GROUP */

    REGISTER_MIB("www-mib/wwwServiceTable", wwwServiceEntry_variables, variable2, wwwServiceEntry_oid);
    REGISTER_MIB("www-mib/wwwSummaryTable", wwwSummaryEntry_variables, variable2, wwwSummaryEntry_oid);
#ifdef WWW_REQUEST_IN_GROUP
    REGISTER_MIB("www-mib/wwwRequestInTable", wwwRequestInEntry_variables, variable2, wwwRequestInEntry_oid);
#endif
#ifdef WWW_REQUEST_OUT_GROUP
    REGISTER_MIB("www-mib/wwwRequestOutTable", wwwRequestOutEntry_variables, variable2, wwwRequestOutEntry_oid);
#endif
#ifdef WWW_RESPONSE_IN_GROUP
    REGISTER_MIB("www-mib/wwwResponseInTable", wwwResponseInEntry_variables, variable2, wwwResponseInEntry_oid);
#endif
#ifdef WWW_RESPONSE_OUT_GROUP
    REGISTER_MIB("www-mib/wwwResponseOutTable", wwwResponseOutEntry_variables, variable2, wwwResponseOutEntry_oid);
#endif
#ifdef WWW_DOCUMENT_GROUP
    REGISTER_MIB("www-mib/wwwDocCtrlTable", wwwDocCtrlEntry_variables, variable2, wwwDocCtrlEntry_oid);
    REGISTER_MIB("www-mib/wwwDocLastNTable", wwwDocLastNEntry_variables, variable2, wwwDocLastNEntry_oid);
    REGISTER_MIB("www-mib/wwwDocBucketTable", wwwDocBucketEntry_variables, variable2, wwwDocBucketEntry_oid);
    REGISTER_MIB("www-mib/wwwDocAccessTopNTable", wwwDocAccessTopNEntry_variables, variable2, wwwDocAccessTopNEntry_oid);
    REGISTER_MIB("www-mib/wwwDocBytesTopNTable", wwwDocBytesTopNEntry_variables, variable2, wwwDocBytesTopNEntry_oid);
    /* We also need to do some additional initialaztion for the
     * time based buckets
     */
    init_alarm_bucketsTimeInterval();
    set_alarm_bucketsTimeInterval(0);

#endif
    /* insert the Object Resource in sysORTable */
#ifdef USING_MIBII_SYSORTABLE_MODULE
  register_sysORTable(wwwMIB_oid, 7, "The WWW service MIB module");
#endif

}

#endif /* WWW_MIB */

