/* $Cambridge: hermes/src/prayer/accountd/checksum.c,v 1.2 2003/04/16 09:02:41 dpc22 Exp $ */
/************************************************
 *    Prayer - a Webmail Interface              *
 ************************************************/

/* Copyright (c) University of Cambridge 2000 - 2002 */
/* See the file NOTICE for conditions of use and distribution. */

/* Little snippet of code to calculate file checksum and compare with
 * current value. Rather strange little checksum routine: just happens
 * to be compatible with what we are using at the moment... */

#include "accountd.h"

/* checksum_hash_line() **************************************************
 *
 * Convert single line of input into checksum value
 *   line:   Line to checksum
 *   maxlen: Maximum length to checksum
 ************************************************************************/

static int checksum_hash_line(char *line, unsigned int maxlen)
{
    unsigned int total;
    unsigned int i;
    char *p;

    total = 0;

    for (i = 0, p = line; *p != '\0' && *p != '\n' && i < maxlen; p++, i++) {
        total += (((unsigned int) *p) * (i + 1));
    }

    return (total % 65536);
}

/* checksum_test() ********************************************************
 *
 * Calculate checksum value for a string.
 *     config: Global state
 *   existing: Returns existing checksum found in file
 *    current: Returns current checksum value for file
 *
 * Returns: T => Was able to checksum .forward file
 *************************************************************************/

BOOL
checksum_test(struct config * config,
              unsigned long *existing, unsigned long *current)
{
    FILE *file;
    unsigned long total;
    unsigned long file_checksum, checklen;
    static char buffer[MAXLENGTH], *endbuffer, *p;
    char *checkstring;

    endbuffer = &buffer[MAXLENGTH - 1];

    if ((file = fopen(config->forward_name, "r")) == NULL)
        return (NIL);

    checkstring = "# END FILTER FILE. CHECKSUM=";
    checklen = strlen(checkstring);

    file_checksum = 0;
    total = 0;

    while (fgets(buffer, MAXLENGTH, file)) {
        if (buffer[0] == '#') {
            if (strncmp(buffer, checkstring, checklen) == 0) {
                p = buffer + strlen(checkstring);

                while (*p != '\0' && !Uisdigit(*p))
                    p++;
                if (*p != '\0')
                    sscanf(p, "%lu", &file_checksum);
            }
        } else {
            *endbuffer = '\0';
            total += checksum_hash_line(buffer, MAXLENGTH);
            total %= (1024 * 1024);     /* Cap values to < 10^6 */
        }
    }
    fclose(file);

    *existing = file_checksum;
    *current = total;
    return (T);
}

/* checksum_generate() ****************************************************
 *
 * Generate checksum value for a file
 *   Name of file to checksum
 *
 * Returns: T => Was able to checksum .forward file
 *************************************************************************/

BOOL checksum_generate(char *name)
{
    FILE *file;
    unsigned long total;
    static char buffer[MAXLENGTH], *endbuffer;

    endbuffer = &buffer[MAXLENGTH - 1];

    if ((file = fopen(name, "r")) == NULL)
        return (NIL);

    total = 0;

    while (fgets(buffer, MAXLENGTH, file)) {
        if (buffer[0] == '#')
            continue;

        *endbuffer = '\0';
        total += checksum_hash_line(buffer, MAXLENGTH);
        total %= (1024 * 1024); /* Cap values to < 10^6 */
    }
    fclose(file);

    if ((file = fopen(name, "a")) == NULL)
        return (NIL);

    fprintf(file, "# END FILTER FILE. CHECKSUM=%lu\n", total);
    fclose(file);

    return (T);
}
