/* 
 * $Id: www-mib-document.c 8676 2008-01-17 23:11:17Z ispringer $
 */
/* General includes */
#ifndef WIN32
#include <unistd.h>
#endif

/* 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_alarm.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"
#include "www-mib/www-mib-document.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_DOCUMENT_GROUP
/* Local function declarations */
FindVarMethod read_wwwDocCtrlEntry;
FindVarMethod read_wwwDocLastNEntry;
FindVarMethod read_wwwDocBucketEntry;
FindVarMethod read_wwwDocAccessTopNEntry;
FindVarMethod read_wwwDocBytesTopNEntry;
WriteMethod write_wwwDocCtrlLastNSize;
WriteMethod write_wwwDocCtrlTopNSize;
WriteMethod write_wwwDocCtrlBuckets;
WriteMethod write_wwwDocCtrlBucketTimeInterval;
wwwDocStatistics_t *get_www_mib_document_statistics_record(unsigned int index);
void insert_in_accessTopNTable(wwwDocTopN_t *doc, wwwDocTopN_t *accessTopNTable, int size);
void insert_in_bytesTopNTable(wwwDocTopN_t *doc, wwwDocTopN_t *bytesTopNTable, int size);
void compute_wwwDocTopNTable(wwwDocStatistics_t *wwwStatistics);
void compute_wwwDocTopNTables(unsigned int registration, void *data);
wwwDocStatistics_t *get_mib_documents_wwwService_row(struct variable *vp,
        oid *name, size_t *namelength,
        oid *newname, size_t *newname_length,
        int exact, unsigned int *index);
wwwDocLastN_t *get_wwwDocLastN_row(oid* name, size_t *name_length,
        oid *newname, size_t *newname_length, int exact, wwwDocStatistics_t *wwwStats);
wwwDocBucket_t *get_wwwDocBucket_row(oid *name, size_t *name_length,
        oid *newname, size_t *newname_length, int exact, wwwDocStatistics_t *wwwStats);
wwwDocTopN_t *get_wwwDocTopN_row(oid *name, size_t *name_length,
        oid *newname, size_t *newname_length, int exact, wwwDocStatistics_t *wwwStats,
        int whichTopN);


static wwwDocStatistics_t *wwwDoc_array;

wwwDocStatistics_t *
get_www_mib_document_statistics_record(unsigned int index)
{
    if ((index < 0) || (get_www_service_total() <= index)) {
        return(NULL);
    }
    return(&(wwwDoc_array[ index]));
}

/* Functions to compute the document statistics. */
void
insert_in_accessTopNTable(wwwDocTopN_t *doc, wwwDocTopN_t *accessTopNTable, int size)
{
int     i;

    DEBUGTRACE;
    DEBUGMSGTL(("www-mib", "doc accesses %d\n", doc->accesses));
    size--;
    for ( i=0 ; i<size ; i++) {
    DEBUGMSGTL(("www-mib", "topN(%d) accesses %d\n", i, accessTopNTable[i].accesses));
        if (accessTopNTable[i].accesses <= doc->accesses) {
            DEBUGMSGTL(("www-mib", "Moving %d records index %d size %d\n", (size - i), i, size));
            DEBUGMSGTL(("www-mib", "record %d to %d\n", i, i + 1));
            memmove(&(accessTopNTable[i+1]), &(accessTopNTable[i]), sizeof(wwwDocTopN_t) * (size - i));
            memcpy(&(accessTopNTable[i]), doc, sizeof(wwwDocTopN_t));
            return;
        }
    }
    if (accessTopNTable[i].accesses <= doc->accesses) {
        DEBUGMSGTL(("www-mib", "Filling record %d\n", i));
        memcpy(&(accessTopNTable[i]), doc, sizeof(wwwDocTopN_t));
    }
}


void
insert_in_bytesTopNTable(wwwDocTopN_t *doc, wwwDocTopN_t *bytesTopNTable, int size)
{
int     i;

    DEBUGTRACE;
    DEBUGMSGTL(("www-mib", "doc bytes %d\n", doc->bytes));
    size--;
    for ( i=0 ; i<size ; i++) {
    DEBUGMSGTL(("www-mib", "topN(%d) bytes %d\n", i, bytesTopNTable[i].bytes));
        if (bytesTopNTable[i].bytes <= doc->bytes) {
            DEBUGMSGTL(("www-mib", "Moving %d records index %d size %d\n", (size - i), i, size));
            DEBUGMSGTL(("www-mib", "record %d to %d\n", i, i + 1));
            memmove(&(bytesTopNTable[i+1]), &(bytesTopNTable[i]), sizeof(wwwDocTopN_t) * (size - i));
            memcpy(&(bytesTopNTable[i]), doc, sizeof(wwwDocTopN_t));
            return;
        }
    }
    if (bytesTopNTable[i].bytes <= doc->bytes) {
        DEBUGMSGTL(("www-mib", "Filling record %d\n", i));
        memcpy(&(bytesTopNTable[i]), doc, sizeof(wwwDocTopN_t));
    }
}


void
compute_wwwDocTopNTable(wwwDocStatistics_t *wwwStatistics)
{
wwwDocBucket_t  *wwwDocBucket;
wwwDocTopN_t    *documentData;

    DEBUGTRACE;
    wwwDocBucket = &(wwwStatistics->wwwDocBucketTable[++wwwStatistics->wwwDocBucketIndex %
                                                MAX_WWWDOCBUCKETS ]);;
    if (wwwDocBucket) {
        wwwDocBucket->timestamp = apr_time_now();
        wwwDocBucket->accesses = 0;
        wwwDocBucket->documents = 0;
        wwwDocBucket->bytes = 0;
        if (wwwDocBucket->accessTopNTable) {
            memset(wwwDocBucket->accessTopNTable, 0,
                (sizeof(wwwDocTopN_t) * MAX_WWWDOCLASTNSIZE));
        }
        if (wwwDocBucket->bytesTopNTable) {
            memset(wwwDocBucket->bytesTopNTable, 0,
                (sizeof(wwwDocTopN_t) * MAX_WWWDOCLASTNSIZE));
        }
        /* We now go through the complete bucket and sort the documentData */
        documentData = covalent_snmp_wwwDocBucket_seqfetch(wwwStatistics->wwwDocBucket, FIRST_RECORD);
        while (documentData) {
            insert_in_accessTopNTable(documentData, wwwDocBucket->accessTopNTable,
                                        wwwStatistics->wwwDocCtrlTopNSize);
            insert_in_bytesTopNTable(documentData, wwwDocBucket->bytesTopNTable,
                                        wwwStatistics->wwwDocCtrlTopNSize);
            wwwDocBucket->accesses += documentData->accesses;
            wwwDocBucket->documents++;
            wwwDocBucket->bytes += documentData->bytes;
            documentData = covalent_snmp_wwwDocBucket_seqfetch(wwwStatistics->wwwDocBucket, NEXT_RECORD);
        }
        DEBUGMSGTL(("www-mib", "Nr of docs %d\n", wwwDocBucket->documents));
    }
}

/* This function walks through to compute all bucket related tables.
 * It does them always together, but it should be possible to do this
 * independent.
 */
void
compute_wwwDocTopNTables(unsigned int registration, void *data)
{
wwwDocStatistics_t *www_stats = (wwwDocStatistics_t *)data;

    DEBUGTRACE;
    compute_wwwDocTopNTable(www_stats);
    covalent_snmp_wwwDocBucket_close(www_stats->wwwDocBucket);
    www_stats->wwwDocBucket = covalent_snmp_wwwDocBucket_open(www_stats->wwwDocBucketName);
    if (!www_stats->wwwDocBucket) {
        ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL,
              "SNMP: covalent_snmp_wwwDocBucket_open '%s'", covalent_snmp_get_wwwDocBucket_strerror());
    }
}

/* MIB access functions */
wwwDocStatistics_t *
get_mib_documents_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(&(wwwDoc_array[ *index ]));
                }
                break;
            case EXACT:
                if ( 0 == snmp_oid_compare(name, *namelength, newname, *newname_length)) 
{
                    return(&(wwwDoc_array[ *index ]));
                }
                break;
            case INDEX:
                if ( 0 >= snmp_oid_compare(name,
                                SNMP_MIN(*namelength,*newname_length),
                                newname,
                                SNMP_MIN(*namelength, *newname_length))) {
                    return(&(wwwDoc_array[ *index ]));
                }
                break;
        }
        (*index)++;
    }
    return(NULL);
}



/* 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];
wwwDocStatistics_t *wwwServiceSnmpData = get_www_mib_document_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];
wwwDocStatistics_t *wwwServiceSnmpData = get_www_mib_document_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];
wwwDocStatistics_t *wwwServiceSnmpData = get_www_mib_document_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];
wwwDocStatistics_t *wwwServiceSnmpData = get_www_mib_document_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;
wwwDocStatistics_t *wwwStats;
int wwwServiceIndex = 0;

    wwwStats = get_mib_documents_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, wwwDocStatistics_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;
wwwDocStatistics_t *wwwStats;
wwwDocLastN_t	*currentRow;
int wwwServiceIndex = 0;
const char *request_type;

    do {
	wwwStats = get_mib_documents_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:
            request_type = get_request_type(wwwStats->protocol, currentRow->requestType);
            if (request_type) {
                *var_len = strlen(request_type);
	        return (unsigned char *) request_type;
            }
            *var_len = 0;
            return (unsigned char *) return_buf;
	case WWWDOCLASTNRESPONSETYPE:
	    if (currentRow->responseType) {
                long_return = currentRow->responseType;
		*var_len = sizeof(unsigned long);
		return (unsigned char *) &long_return;
	    }
	    return(NULL);
	case WWWDOCLASTNSTATUSMSG:
	    *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, wwwDocStatistics_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;
wwwDocStatistics_t	*wwwStats;
wwwDocBucket_t	*bucket;
int wwwServiceIndex = 0;

    do {
	wwwStats = get_mib_documents_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, wwwDocStatistics_t *wwwStats, int whichTopN)
{
int	result;
wwwDocBucket_t *bucket;
unsigned 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;
wwwDocStatistics_t	*wwwStats;
wwwDocTopN_t	*currentRow;
int wwwServiceIndex = 0;

    do {
	wwwStats = get_mib_documents_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:
            long_return = get_response_type(wwwStats->protocol, currentRow->lastResponseType);
	    *var_len = sizeof(unsigned long);
	    return (unsigned char *) &long_return;
        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;
wwwDocStatistics_t	*wwwStats;
wwwDocTopN_t	*currentRow;
int wwwServiceIndex = 0;

    do {
	wwwStats = get_mib_documents_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:
            long_return = get_response_type(wwwStats->protocol, currentRow->lastResponseType);
	    *var_len = sizeof(unsigned long);
	    return (unsigned char *) &long_return;
        default:
            return NULL;
    }
}

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 }}
    };



const char *
update_www_mib_document_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)
{
wwwDocStatistics_t *wwwDocs;
int             lastNIndex;
wwwDocTopN_t    documentData;
wwwDocTopN_t    *ptr_documentData;

    DEBUGTRACE;
    wwwDocs = get_www_mib_document_statistics_record(wwwServiceIndex);
    if (wwwDocs) {
        /* Fill some short cuts for the statsistics update */

        if (wwwDocs->wwwDocLastNTable) {
            wwwDocs->wwwDocLastNIndex++;
            lastNIndex = wwwDocs->wwwDocLastNIndex % MAX_WWWDOCLASTNSIZE;
            strncpy(wwwDocs->wwwDocLastNTable[ lastNIndex ].name, wwwDocName, MAX_WWWDOCNAME);
            wwwDocs->wwwDocLastNTable[ lastNIndex ].timestamp = wwwRequestTime;
            wwwDocs->wwwDocLastNTable[ lastNIndex ].requestType = wwwRequestInType;
            wwwDocs->wwwDocLastNTable[ lastNIndex ].responseType = wwwResponseOutType;
            strncpy(wwwDocs->wwwDocLastNTable[ lastNIndex ].statusMsg, wwwStatusMsg, MAX_WWWDOCLASTNSTATUSMSG);
            wwwDocs->wwwDocLastNTable[ lastNIndex ].bytes = wwwBytesOut;
        }
        ptr_documentData = covalent_snmp_wwwDocBucket_fetch(wwwDocs->wwwDocBucket, wwwDocName);
        if (ptr_documentData) {
            DEBUGMSGTL(("www-mib", "Document found: '%s' - '%d'\n",
			wwwDocName, ptr_documentData->accesses));
            /* found in bucket; update document document data */
            ptr_documentData->accesses++;
            ptr_documentData->bytes += wwwBytesOut;
            ptr_documentData->lastResponseType = wwwResponseOutType;
            if (covalent_snmp_wwwDocBucket_store(wwwDocs->wwwDocBucket, wwwDocName,
                                ptr_documentData, RECORD_REPLACE)) {
                return(covalent_snmp_get_wwwDocBucket_strerror());
            }
        } else {
            DEBUGMSGTL(("www-mib", "Document not found: '%s'\n", wwwDocName));
            /* not found in bucket; insert document data */
            strcpy(documentData.name, wwwDocName);
            documentData.accesses = 1;
            documentData.bytes = wwwBytesOut;
            documentData.lastResponseType = wwwResponseOutType;
            if (covalent_snmp_wwwDocBucket_store(wwwDocs->wwwDocBucket, wwwDocName,
                                &documentData, RECORD_INSERT)) {
                return(covalent_snmp_get_wwwDocBucket_strerror());
            }
        }
        return(NULL);
    }
    return "Could not find document statistics record";
}


void init_www_mib_documents(apr_pool_t *p, const char *bucket_directory)
{
unsigned int i;
char *protocol;
server_rec *service = get_www_service_root();

    wwwDoc_array = (wwwDocStatistics_t *)
                        apr_pcalloc (p, (sizeof(wwwDocStatistics_t) * get_www_service_total()));
    if (wwwDoc_array == NULL) {
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
                        "SNMP:www-mib initialization: could not create service statistics array");
	return;
    }
    memset(wwwDoc_array, 0, (sizeof(wwwDocStatistics_t) * get_www_service_total()));
    for (i = 0; ((service != NULL) && (i < get_www_service_total())) ; i++, service = service->next) {
        protocol = get_www_service_protocol(service);
        wwwDoc_array[ i ].protocol = get_www_protocol_definition_by_name(protocol);
        wwwDoc_array[ i ].wwwDocCtrlLastNSize = DEFAULT_WWWDOCLASTNSIZE;
        wwwDoc_array[ i ].wwwDocCtrlBuckets = DEFAULT_WWWDOCBUCKETS;
        wwwDoc_array[ i ].wwwDocCtrlBucketTimeInterval = DEFAULT_WWWDOCBUCKETTIMEINTERVAL;  
        wwwDoc_array[ i ].wwwDocCtrlTopNSize = DEFAULT_WWWDOCTOPNSIZE;
        wwwDoc_array[ i ].wwwDocBucketName = apr_pstrcat(p, ap_server_root_relative(p,
					bucket_directory), "/wwwbucket.", service->server_hostname, NULL);
        wwwDoc_array[ i ].wwwDocBucket = covalent_snmp_wwwDocBucket_open(wwwDoc_array[ i ].wwwDocBucketName);
        if (!wwwDoc_array[ i ].wwwDocBucket) {
                ap_log_error(APLOG_MARK, APLOG_INFO, covalent_snmp_get_wwwDocBucket_errno(), NULL,
                        "SNMP:covalent_snmp_wwwDocBucket_open: %s '%s'", covalent_snmp_get_wwwDocBucket_strerror(),
                        wwwDoc_array[ i ].wwwDocBucketName);
         }
         snmp_alarm_register(wwwDoc_array[ i ].wwwDocCtrlBucketTimeInterval, SA_REPEAT,
                  compute_wwwDocTopNTables, &(wwwDoc_array[ i ]));
    }

    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);

    /* insert the Object Resource in sysORTable */
#ifdef USING_MIBII_SYSORTABLE_MODULE
  register_sysORTable(wwwDocCtrlEntry_oid, 9, "WWW Documents Group of the WWW-MIB module");
#endif
}
#endif /* WWW_DOCUMENT_GROUP */
