/* $Cambridge: hermes/src/prayer/cmd/cmd_sieve.c,v 1.5 2012/02/14 13:55:46 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"

/* Check whether string empty */
static BOOL
cmd_sieve_has_content(char *s)
{
    char c;

    while ((c=*s++)) {
        if ((c != ' ') && (c != '\t') && (c != '\015') && (c != '\012'))
            return(T);
    }
    return(NIL);
}

/* Replace a few daft error messages from Sieve */
static char *
cmd_sieve_substitute(char *s)
{
   if (!strcmp(s, "parse error, unexpected $undefined."))
        s = "syntax error";

   return(s);
}

static void
cmd_sieve_print_error_line(struct session *session, struct buffer *b, char *s)
{
    struct config_theme *theme = session->theme_main;
    unsigned long lineno;

    if ((strncmp(s, "line ", strlen("line ")) != 0) ||
        ((lineno=atoi(s+strlen("line "))) == 0)) {

        while (string_isspace(*s))
            s++;

        bprintf(b, CRLF"%s"CRLF, cmd_sieve_substitute(s));
        return;
    }

    s += strlen("line ");

    while (*s && Uisdigit(*s))
        s++;
        
    if ((s[0] == ':') && (s[1] == ' '))
        s += 2;

    while (string_isspace(*s))
        s++;

    bprintf(b, "<font color=\"%s\"> Line %lu:</font> %s"CRLF,
            theme->fgcolor_quote2, lineno, cmd_sieve_substitute(s));
}

static void
cmd_sieve_merge_text(struct session *session, struct buffer *b,
                     char *script, char *error)
{
    struct config_theme *theme = session->theme_main;
    unsigned long lineno = 0;
    unsigned long tmp_lineno = 0;
    unsigned long err_lineno = 0;
    char *line, *errline, tmp[64];

    tmp_lineno = 0;
    while ((errline = string_get_line(&error))) {
        tmp_lineno++;
        if (!strncmp(errline, "line ", strlen("line ")) &&
            ((err_lineno = atoi(errline + strlen("line "))) > 0))
            break;  /* Got a numbered line */

        if ((tmp_lineno == 1) && !strcmp(errline, "script errors:"))
            continue;

        /* Print unmatched lines at the start */
        cmd_sieve_print_error_line(session, b, errline);
    }

    lineno = 0;
    while ((line=string_get_line(&script))) {
        lineno++;
        sprintf(tmp, "%5lu", lineno);
        bprintf(b, "<font color=\"%s\">%s:</font> %s"CRLF,
                theme->fgcolor_quote1, tmp, line);

        if (errline && (lineno == err_lineno)) {
            bputs(b, ""CRLF);
            cmd_sieve_print_error_line(session, b, errline);

            /* Check for possible continuation lines (no linenumber given)
               or further errors on the same line */
            while ((errline = string_get_line(&error))) {
                if (!strncmp(errline, "line ", strlen("line ")) &&
                    ((err_lineno = atoi(errline + strlen("line "))) > lineno))
                    break;

                cmd_sieve_print_error_line(session, b, errline);
            }
            bputs(b, ""CRLF);
        }
    }
    /* Anything left at the end? */
    while (errline) {
        cmd_sieve_print_error_line(session, b, errline);
        errline = string_get_line(&error);
    }
}

static void
generate_sieve_error(struct session *session, char *script, char *error)
{
    struct template_vals *tvals = session->template_vals;
    struct request *request = session->request;
    struct buffer *b = request->write_buffer;

    session_seed_template(session, tvals);
    template_expand("sieve_error", tvals, b);

    bputs(b, "<pre>"CRLF);
    cmd_sieve_merge_text(session, b, script, error);
    bputs(b, "</pre>"CRLF);

    template_expand("sieve_error_tail", tvals, b);
    response_html(request, 200);        /* Success */
}

static void
generate_connect_error(struct session *session)
{
    struct template_vals *tvals = session->template_vals;
    struct request *request = session->request;
    char *msg = sieve_fetch_message(session->sieve);
    struct buffer *b = request->write_buffer;

    if (!(msg && msg[0]))
        msg = "Unable to check mail processing status with server";

    template_vals_string(tvals, "msg", msg);
    session_seed_template(session, tvals);
    template_expand("sieve_fail", tvals, b);
    response_html(request, 200);
}

static void
generate_form(struct session *session)
{
    struct template_vals *tvals = session->template_vals;
    struct prefs *prefs = session->options->prefs;
    struct request *request = session->request;
    struct buffer *b = request->write_buffer;
    struct sieve *sieve = session->sieve;
    char *script = sieve_current(sieve);

    template_vals_ulong(tvals, "rows", prefs->large_rows);
    template_vals_ulong(tvals, "cols", prefs->large_cols);
    template_vals_string(tvals, "script", script);

    session_seed_template(session, tvals);
    template_expand("sieve", tvals, b);
    response_html(request, 200);        /* Success */
}

BOOL
my_sieve_check(struct sieve *sieve, struct session *session)
{
    char *script;

    if (sieve->checked)
        return(T);

    if (!sieve_fetch(sieve, session, "sieve", &script))
        return(NIL);

    sieve_current_record(sieve, script);
    sieve_live_record(sieve, script);

    sieve->checked = T;
    return(T);
}

void cmd_sieve(struct session *session)
{
    struct config *config = session->config;
    struct request *request = session->request;
    struct buffer *b = request->write_buffer;
    struct sieve  *sieve = session->sieve;
    struct account *account = session->account;
    struct assoc *h = NIL;
    char *script;
    char *end;
    unsigned long len;

    if (!my_sieve_check(sieve, session)) {
        generate_connect_error(session);
        return;
    }

    if (request->argc == 2) {
        if (!strcmp(request->argv[1], "download")) {
            bputs(b, sieve_current(session->sieve));
            response_raw(request, "sieve.script", "text/plain", 200);
            return;
        }
        if ((strcmp(request->argv[1], "upload") != 0) ||
            (request->method != POST)) {
            generate_form(session);
            return;
        }
        request_decode_post_multipart(request, NIL, &script, &end);
        *end = '\0';

        if (end > script)
            len = (char *) end - (char *) script;
        else
            len = 0;

        if (len < 1) {
            session_alert(session, "Please choose a file to attach");
            generate_form(session);
            return;
        }

        if ((config->sieve_maxsize > 0) && (len > config->sieve_maxsize)) {
            session_alert(session, "Sieve file too large");
            generate_form(session);
            return;
        }

        script = string_expand_crlf(request->pool, script);
    } else {
        if (request->method != POST) {
            generate_form(session);
            return;
        }

        /* Form post */
        request_decode_form(request);
        h = request->form;

        if (assoc_lookup(h, "sub_sieve")) {
            generate_form(session);
            return;
        }

        if (!assoc_lookup(h, "sub_apply")) {
            session_redirect(session, request, "manage");   /* parent screen */
            return;
        }

        if (!(script = assoc_lookup(h, "script"))) {
            session_alert(session, "Invalid form input");
            session_redirect(session, request, "manage");   /* parent screen */
            return;
        }
    }

    /* Process Sieve file uploaded by one means or another */

    /* Useful subset of ISO-8859-1 and UTF-8 is ASCII */
    string_strip8bit(script);

    if (script && cmd_sieve_has_content(script)) {
        sieve_current_record(sieve, script);

        if (!(sieve_upload(sieve, session, "sieve", script) &&
              sieve_activate(sieve, session, "sieve"))) {
            char *error = sieve_fetch_message(sieve);

            generate_sieve_error(session, script, error);
            return;
        }
        sieve_live_record(sieve, script);
        session_message(session, "Sieve script sucessfully installed");
        account->mail_checked = NIL; /* Blat existing automatic filtering */
    } else {
        sieve_current_record(sieve, NIL);

        if (sieve_live(sieve)) {
            /* Need to deactivate sieve file before deleting with Cyrus 2.4 */
            if (sieve_activate(sieve, session, "") &&
                sieve_delete(sieve, session, "sieve")) {
                session_message(session, "Removed sieve filter");
                sieve_live_record(sieve, NIL);
            } else
                session_alert(session, "Problem removing sieve filter");
        }
    }
    session_redirect(session, request, "manage");
}
