/* $Cambridge: hermes/src/prayer/session/account_support.c,v 1.3 2008/09/16 09:59:58 dpc22 Exp $ */
/************************************************
 *    Prayer - a Webmail Interface              *
 ************************************************/

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

#include "prayer_session.h"

/* XXX Better error handling please */

/* Couple of helper routines and data structures for account_process_filter
 * See comments below */

struct component {
    enum {
        CO_UNKNOWN, CO_VACATION, CO_SPAM,
        CO_SENDER, CO_RECIPIENT, CO_BLOCK, CO_SUBJECT,
        CO_REDIRECT
    } type;
    char *local_part;
    char *domain;
    char *subject;
    char *mailbox;
    char *address;
    BOOL copy;
    BOOL purge;
    char *threshold;
    char *days;
};

static void
component_reset(struct component *component)
{
    component->type = CO_UNKNOWN;
    component->local_part = NIL;
    component->domain = NIL;
    component->subject = NIL;
    component->mailbox = NIL;
    component->address = NIL;
    component->copy = NIL;
    component->purge = NIL;
    component->threshold = NIL;
    component->days = NIL;
}

static int
component_isvalid(struct component *component)
{
    switch (component->type) {
    case CO_BLOCK:
        if (!(component->local_part && component->local_part[0] &&
              component->domain && component->domain[0]))
            return (NIL);
        break;
    case CO_SENDER:
    case CO_RECIPIENT:
        if (!(component->local_part && component->local_part[0] &&
              component->domain && component->domain[0]))
            return (NIL);
        if (!(component->mailbox && component->mailbox[0]))
            return (NIL);
        break;
        if (!(component->local_part && component->local_part[0] &&
              component->domain && component->domain[0]))
            return (NIL);
        if (!(component->mailbox && component->mailbox[0]))
            return (NIL);
        break;
    case CO_SUBJECT:
        if (!(component->subject && component->subject[0]))
            return (NIL);
        break;
    case CO_REDIRECT:
        if (!(component->address && component->address[0]))
            return (NIL);
        break;
    case CO_SPAM:
        if (!(component->threshold && component->threshold[0]))
            return (NIL);
        break;
    case CO_VACATION:
        break;
    case CO_UNKNOWN:
        return (NIL);
    default:
        break;
    }
    return (T);
}

static BOOL
component_process(struct account *account, struct component *component)
{
    struct session *session = account->session;
    struct config *config = session->config;
    struct filter *filter;

    if (component->type == CO_UNKNOWN)
        session_fatal(session,
                      "[component_process]: Unknown component type");

    if (component->type == CO_VACATION) {
        if (component->days && component->days[0] &&
            string_isnumber(component->days))
            account->vacation_days = atoi(component->days);
        else
            account->vacation_days = config->vacation_default_days;

        account->vacation_enabled = T;
        return(T);
    }

    if (component->type == CO_SPAM) {
        if (!component->threshold || !string_isnumber(component->threshold)) {
            account_message(account,
                            "Invalid construct in filter support file");
            return(NIL);
        }
        account->spam_threshold = atoi(component->threshold);
        account->spam_enabled = T;
        account->spam_threshold_valid   = T;

        if (component->days && string_isnumber(component->days))
            account->spam_purge_timeout = atoi(component->days);

        account->spam_purge_enabled = component->purge;

        return(T);
    }

    if (component->type == CO_REDIRECT) {
        account->redirect_enabled = T;
        account->redirect_copy = component->copy;

        /* string_strdup() handles NIL input gracefully */
        string_strdup(&account->redirect_address, component->address);
        return(T);
    }

    if (!component_isvalid(component)) {
        account_message(account, "Invalid construct in filter support file");
        return(NIL);
    }

    filter = filter_alloc();

    if (component->type == CO_SENDER)
        filter_set_type(filter, FILTER_SENDER);
    else if (component->type == CO_RECIPIENT)
        filter_set_type(filter, FILTER_RECIPIENT);
    else if (component->type == CO_SUBJECT)
        filter_set_type(filter, FILTER_SUBJECT);
    else if (component->type == CO_BLOCK)
        filter_set_type(filter, FILTER_BLOCK);

    filter_set_local_part(filter, component->local_part);
    filter_set_domain(filter, component->domain);
    filter_set_mailbox(filter, component->mailbox);
    filter_set_subject(filter, component->subject);
    filter_set_copy(filter, component->copy);

    if (component->type == CO_BLOCK)
        list_push(account->block_list, (struct list_item *) filter, NIL);
    else
        list_push(account->filter_list, (struct list_item *) filter, NIL);

    return(T);
}

/* ====================================================================== */

/* account_support_process_filter() **************************************
 *
 * Convert filter file (downloaded from server) into a list of filter
 * actions which Prayer can manipulate.
 * account:
 *  filter: Filter file as text string
 *    pool: Scratch pool nominated by client routine.
 *
 * Returns: MSforward text from buffer
 ***********************************************************************/

BOOL
account_support_process_filter(struct account *account,
                               char *filter, struct pool *pool)
{
    struct component component;
    char *line;
    char *s;

    component_reset(&component);

    while ((line = string_get_line(&filter))) {
        if (line[0] == '#')     /* Ignore comment lines */
            continue;

        for (s = line; *s; s++) {      /* Ignore blank lines */
            if (!string_isspace(*s))
                break;
        }
        if (*s == '\0')
            continue;

        if ((line[0] != ' ') && (line[0] != '\t')) {
            /* New action */

            if (component.type != CO_UNKNOWN) {
                if (!component_process(account, &component))
                    return(NIL);
            }

            component_reset(&component);

            if (!strcmp(line, "vacation"))
                component.type = CO_VACATION;
            else if (!strcmp(line, "spam"))
                component.type = CO_SPAM;
            else if (!strcmp(line, "block"))
                component.type = CO_BLOCK;
            else if (!strcmp(line, "sender"))
                component.type = CO_SENDER;
            else if (!strcmp(line, "recip"))
                component.type = CO_RECIPIENT;
            else if (!strcmp(line, "subject"))
                component.type = CO_SUBJECT;
            else if (!strcmp(line, "redirect"))
                component.type = CO_REDIRECT;
            else
                return (NIL);   /* Unknown type */
        } else {
            /* Continuation line */
            char *arg0 = string_get_token(&line);
            char *arg1 = string_next_token(&line);

            if (!strcmp(arg0, "local_part"))
                component.local_part = pool_strdup(pool, arg1);
            else if (!strcmp(arg0, "domain"))
                component.domain = pool_strdup(pool, arg1);
            else if (!strcmp(arg0, "subject"))
                component.subject = pool_strdup(pool, arg1);
            else if (!strcmp(arg0, "mailbox"))
                component.mailbox = pool_strdup(pool, arg1);
            else if (!strcmp(arg0, "address"))
                component.address = pool_strdup(pool, arg1);
            else if (!strcmp(arg0, "copy"))
                component.copy = (!strcmp(arg1, "true")) ? T : NIL;
            else if (!strcmp(arg0, "purge"))
                component.purge = (!strcmp(arg1, "true")) ? T : NIL;
            else if (!strcmp(arg0, "threshold"))
                component.threshold = pool_strdup(pool, arg1);
            else if (!strcmp(arg0, "days"))
                component.days = pool_strdup(pool, arg1);
            else {
                account_message(account, "Invalid filter file");
                return (NIL);
            }
        }
    }
    if (component.type != CO_UNKNOWN) {
        if (!component_process(account, &component))
            return(NIL);
    }

    return (T);
}

/* ====================================================================== */

/* account_support_mail_text() ******************************************
 *
 * Convert account filter list to equivaent text which can be uploaded to
 * accountd server and converted into Exim filter file there.
 * account:
 *    pool: Scratch pool nominated by client.
 *   sieve: Generating text for Sieve world (=> no vacation log)
 *
 * Returns: MSforward text from buffer
 ***********************************************************************/

char *
account_support_mail_text(struct account *account, struct pool *pool,
                          BOOL sieve)
{
    struct session *session = account->session;
    struct config  *config  = session->config;
    struct buffer *b = buffer_create(pool, ACCOUNT_BLOCKSIZE);
    struct list_item *item;

    bputs(b, "# MSforward file: change only via Hermes menu system\n");

    for (item = account->block_list->head; item; item = item->next)
        filter_print((struct filter *) item, b);

    if (account->spam_enabled && account->spam_threshold_valid) {
        bprintf(b, "spam\n");
        bprintf(b, "   threshold\t%lu\n", account->spam_threshold);
        if (!account->spam_purge_enabled ||
            (account->spam_purge_timeout != config->spam_purge_timeout)) {
            if (account->spam_purge_enabled)
                bputs(b, "   purge true\n");
            else
                bputs(b, "   purge false\n");
            bprintf(b, "   days\t%lu\n", account->spam_purge_timeout);
        }
    }

    if (account->vacation_enabled) {
        bputs(b, "vacation\n");
        if (sieve)
            bprintf(b, "   days %d\n", account->vacation_days);
    }

    for (item = account->filter_list->head; item; item = item->next)
        filter_print((struct filter *) item, b);

    if (account->redirect_enabled) {
        bputs(b, "redirect\n");
        if (account->redirect_address)
            bprintf(b, "   address\t%s\n", account->redirect_address);
        if (account->redirect_copy)
            bprintf(b, "   copy\ttrue\n");
    }

    return (buffer_fetch(b, 0, buffer_size(b), NIL));
}
