/* $Cambridge: hermes/src/prayer/cmd/cmd_compose.c,v 1.3 2008/05/23 12:18:33 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"

static void cmd_compose_generate_form(struct session *session)
{
    struct template_vals *tvals = session->template_vals;
    struct request *request = session->request;
    struct draft *draft = session->draft;
    struct prefs *prefs = session->options->prefs;
    struct buffer *b = request->write_buffer;

    template_vals_string(tvals, "hdr_to",  draft->to);
    template_vals_string(tvals, "hdr_cc",  draft->cc);
    template_vals_string(tvals, "hdr_bcc", draft->bcc);
    template_vals_string(tvals, "hdr_fcc", draft->fcc);
    template_vals_string(tvals, "hdr_reply_to", draft->reply_to);
    template_vals_string(tvals, "hdr_subject", draft->subject);
    template_vals_ulong(tvals, "att_count", draft_att_count(draft));
    template_vals_string(tvals, "body", draft->body);

    if (draft->rich_headers)
        template_vals_ulong(tvals, "rich_headers", 1);

    if (prefs->line_wrap_advanced)
        template_vals_ulong(tvals, "line_wrap_advanced", 1);

    if (draft->line_wrap)
        template_vals_ulong(tvals, "line_wrap", 1);

    if (draft->save_copy)
        template_vals_ulong(tvals, "copy_outgoing", 1);

    if (session->compose_large)
        template_vals_ulong(tvals, "large", 1);

    template_vals_ulong(tvals, "large_cols", prefs->large_cols);
    template_vals_ulong(tvals, "large_rows", prefs->large_rows);
    template_vals_ulong(tvals, "small_cols", prefs->small_cols);
    template_vals_ulong(tvals, "small_rows", prefs->small_rows);

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

    /* Send out HTML */
    response_html(request, 200);
}

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

static BOOL cmd_compose_generate_postponed_list(struct session *session)
{
    struct template_vals *tvals = session->template_vals;
    struct config *config = session->config;
    struct prefs *prefs = session->options->prefs;
    struct request *request = session->request;
    struct buffer *b = request->write_buffer;
    MAILSTREAM *stream = session->draft_stream;
    unsigned long msgno;
    unsigned long count = 0;

    for (msgno = 1; msgno <= stream->nmsgs; msgno++) {
        MESSAGECACHE *elt;
        ENVELOPE *env;
        ADDRESS *addr;
        char *string;
        
        template_vals_foreach_init(tvals, "@list", count);

        
        if (!((elt = ml_elt(session, stream, msgno)) &&
              (env = ml_fetch_structure(session, stream, msgno, NIL, 0))))
            return (NIL);
        
        /* Msg No */
        template_vals_foreach_ulong(tvals, "@list", count, "msgno", msgno);
        
        /* Message Date */
        if (prefs->use_full_date)
            string = string_date_to_string_long(elt->month, elt->day,
                                                elt->year + BASEYEAR);
        else
            string = string_date_to_string_short(elt->month, elt->day);

        template_vals_foreach_string(tvals, "@list", count, "date", string);

        /* To/Cc/Bcc address (use first non-empty address) */
        if (!((addr = env->to) && addr->mailbox && addr->host))
            if (!((addr = env->cc) && addr->mailbox && addr->host))
                addr = env->bcc;

        if (addr && addr->personal) {
            string = addr->personal;
        } else if (addr && addr->mailbox && addr->host) {
            string =
                pool_printf(request->pool, "%s@%s", addr->mailbox,
                            addr->host);
        } else {
            string = "(unknown)";
        }
        
        string = (char *)
            rfc1522_decode(pool_alloc(request->pool, strlen(string)),
                           strlen(string), string, NIL);
        
        string =
            utf8_prune(request->pool, string, config->list_addr_maxlen);
        
        template_vals_foreach_string(tvals, "@list", count, "name", string);

        /* Message size */
        if (elt->rfc822_size > 1024)
            string = pool_printf(tvals->pool, "%luK", elt->rfc822_size / 1024);
        else
            string = "<1K";

        template_vals_foreach_string(tvals, "@list", count, "size", string);

        /* Message subject */
        if (env->subject && env->subject[0]) {
            string = env->subject;
            string = (char *)
                rfc1522_decode(pool_alloc(request->pool, strlen(string)),
                               strlen(string), string, NIL);

            string = utf8_prune(request->pool, string,
                                  config->list_subject_maxlen);
        } else
            string = "(No subject)";

        template_vals_foreach_string(tvals, "@list", count, "subject", string);
        count++;
    }
    session_seed_template(session, tvals);
    template_expand("compose_postponed", tvals, b);
    response_html(request, 200);

    return (T);
}

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

static void cmd_compose_generate_role_list(struct session *session)
{
    struct template_vals *tvals = session->template_vals;
    struct request *request = session->request;
    struct buffer *b = request->write_buffer;
    struct options *options = session->options;
    struct list_item *li;
    unsigned long count = 0;

    template_vals_foreach_init(tvals, "@roles", count);
    template_vals_foreach_string(tvals, "@roles", count, "name", "default");
    count++;

    for (li = options->role_list->head; li; li = li->next) {
        struct role *role = (struct role *) li;

        template_vals_foreach_init(tvals, "@roles", count);
        template_vals_foreach_string(tvals, "@roles", count,
                                     "name", role->name);
        count++;
    }

    template_vals_string(tvals, "next", "compose1");

    session_seed_template(session, tvals);
    template_expand("roles_select", tvals, b);
    response_html(request, 200);
}

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

/* Check draft message
 * Returns: T   if draft exists or created by end of function
 *          NIL if redirect or page issued in order to generate draft 
 */

static BOOL cmd_compose_check_draft(struct session *session)
{
    struct request *request = session->request;
    struct draft *draft = session->draft;
    struct options *options = session->options;
    struct assoc *h = NIL;
    char *s;
    unsigned long value;

    if (draft->have_draft)
        return (T);

    if (request->method == GET) {
        request_decode_form(request);
        h = request->form;

        if (assoc_lookup(h, "postponed_cancel")) {
            session_redirect(session, request,
                             session->compose_parent_cmd);
            return (NIL);
        }

        if (assoc_lookup(h, "postponed_list")) {
            struct pool *pool = request->pool;
            char *name;

            name = string_url_encode(pool, session->draft_foldername);
            session_redirect(session, request,
                             pool_strcat(pool, "change/", name));
            return (NIL);
        }
    }

    if (!h && draft_check(session)) {
        if (!cmd_compose_generate_postponed_list(session))
            session_redirect(session, request, "restart");
        return (NIL);
    }

    if (h && !assoc_lookup(h, "postponed_fresh") &&
        (s = assoc_lookup(h, "postponed")) && ((value = atoi(s)) > 0)) {

        /* Conceivable that session has gone to sleep on postponed list page */
        if (!draft_check(session)) {
            session_redirect(session, request, "restart");
            return (NIL);
        }

        draft_role_set(draft, NIL);     /* Set up default role */
        draft_init(draft);

        /* Reload draft message */
        if (!draft_restore_postponed(draft, request->pool,
                                     session->draft_stream, value)) {
            session_redirect(session, request, "restart");
            return (NIL);
        }

        /* Remove from draft folder */
        if (!draft_delete(session, value)) {
            session_redirect(session, request, "restart");
            return (NIL);
        }

        if (draft_att_count(draft) > 1)
            session_message(session,
                            "Restored postponed draft, including %lu attachments",
                            draft_att_count(draft));
        else if (draft_att_count(draft) == 1)
            session_message(session,
                            "Restored postponed draft, including 1 attachment");
        else
            session_message(session,
                            "Restored postponed draft, no attachments");
    } else if (list_length(options->role_list) > 0L) {
        /* Fresh new message and need to select role */
        session_message(session,
                        "Composing fresh message: please select a role");
        cmd_compose_generate_role_list(session);
        return (NIL);
    } else {
        /* Set up default role */
        draft_role_set(draft, NIL);
        draft_init(draft);
        session_message(session, "Composing fresh message");
    }
    session->compose_large = NIL;
    draft_init_rich_headers(draft);
    return (T);
}

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

void cmd_compose(struct session *session)
{
    struct request *request = session->request;
    struct draft *draft = session->draft;
    struct draft *draft0 = session->draft0;
    struct options *options = session->options;
    struct assoc *h = NIL;
    char *hdr;

    if ((request->argc >= 2) && (!strcmp(request->argv[1], "cancel"))) {
        session_log(session, "[cmd_compose] Cancel");
        session->compose_large = NIL;
        session->abook_parent_cmd = "list";
        /* Kill the draft */
        draft_clear_atts(draft);
        draft_free(draft);
        draft_free(draft0);
        session_redirect(session, request, session->compose_parent_cmd);
        return;
    }

    if (!cmd_compose_check_draft(session))
        return;

    /* If we get this far then draft exists with correct role */

    if (request->method != POST) {
        cmd_compose_generate_form(session);
        return;
    }

    request_decode_form(request);
    h = request->form;

    /* Undo is special: rewind to previous version */
    if (assoc_lookup(h, "sub_undo")) {
        struct draft *tmp;

        if (!draft0->have_draft)
            draft_init(draft0);

        /* Update existing draft so that undo can be toggled */
        if (session->compose_large)
            draft_update_body(draft, h);
        else
            draft_update(draft, h);

        /* Then switch draft and draft0 */
        tmp = session->draft;
        session->draft = session->draft0;
        session->draft0 = tmp;
        cmd_compose_generate_form(session);
        return;
    }

    /* Create backup copy of existing draft */
    draft_free(draft0);
    draft_copy(draft0, draft);

    /* Address book lookups on To:/Cc:/Bcc: */
    /* Need to do this before draft structure is updated */
    if ((assoc_lookup(h, "sub_abook_To"))
        && (hdr = assoc_lookup(h, "hdr_To"))) {
        hdr = string_trim_whitespace(hdr);
        if (hdr[0]) {
            if ((hdr =
                 abook_substitute(session, request->pool, options->abook,
                                  hdr)))
                assoc_update(h, "hdr_To", hdr, T);
            session_message(session, "Header To Lookup Complete");
            session_log(session,
                        "[cmd_compose] Header To Lookup Complete");
        } else {
            session_message(session, "Header To Empty");
            session_log(session, "[cmd_compose] Header To Empty");
        }
    }

    if ((assoc_lookup(h, "sub_abook_Cc"))
        && (hdr = assoc_lookup(h, "hdr_Cc"))) {
        hdr = string_trim_whitespace(hdr);
        if (hdr[0]) {
            if ((hdr =
                 abook_substitute(session, request->pool, options->abook,
                                  hdr)))
                assoc_update(h, "hdr_Cc", hdr, T);
            session_message(session, "Header Cc Lookup Complete");
            session_log(session,
                        "[cmd_compose] Header Cc Lookup Complete");
        } else {
            session_message(session, "Header Cc Empty");
            session_log(session, "[cmd_compose] Header Cc Empty");
        }
    }

    if ((assoc_lookup(h, "sub_abook_Bcc"))
        && (hdr = assoc_lookup(h, "hdr_Bcc"))) {
        hdr = string_trim_whitespace(hdr);
        if (hdr[0]) {
            if ((hdr =
                 abook_substitute(session, request->pool, options->abook,
                                  hdr)))
                assoc_update(h, "hdr_Bcc", hdr, T);
            session_message(session, "Header Bcc Lookup Complete");
            session_log(session,
                        "[cmd_compose] Header Bcc Lookup Complete");
        } else {
            session_message(session, "Header Bcc Empty");
            session_log(session, "[cmd_compose] Header Bcc Empty");
        }
    }

    /* Create new draft from updated assoc table (i.e: contents of form) */
    if (session->compose_large)
        draft_update_body(draft, h);
    else
        draft_update(draft, h);

    /* Process buttons which move us to other screens */
    /* Three most common operations first */

    if (assoc_lookup(h, "sub_cancel")) {
        session_log(session, "[cmd_compose] Cancel");
        session->compose_large = NIL;
        /* Kill the draft */
        draft_clear_atts(draft);
        draft_free(draft);
        draft_free(draft0);
        session_message(session, "Draft message cancelled");
        session_redirect(session, request, session->compose_parent_cmd);
        return;
    }

    if (assoc_lookup(h, "sub_postpone")) {
        session_log(session, "[cmd_compose] Postpone");
        /* Save, then kill the draft */
        if (draft_write(draft)) {
            session->abook_parent_cmd = "list";
            draft_clear_atts(draft);
            draft_free(draft);
            draft_free(draft0);
            session_message(session, "Draft message postponed");
            session_redirect(session, request,
                             session->compose_parent_cmd);
            return;
        } else {
            session_message(session, "Failed to write postponed message");
            session_log(session,
                        "[cmd_compose] Failed to write postponed message: %s",
                        ml_errmsg());
        }
    }

    if (assoc_lookup(h, "sub_send")) {
        session_log(session, "[cmd_compose] Sending message");
        session->compose_large = NIL;
        session_redirect(session, request, "send");
        return;
    }

    /* Toolbar actions moving us to other screens */
    if (assoc_lookup(h, "sub_list")) {
        session_redirect(session, request, "list");
        return;
    }

    if (assoc_lookup(h, "sub_display")) {
        session_redirect(session, request, "display");
        return;
    }

    if (assoc_lookup(h, "sub_abook_list")) {
        session->abook_parent_cmd = "compose";
        session_redirect(session, request, "abook_list");
        return;
    }

    if (assoc_lookup(h, "sub_folders")) {
        session_redirect(session, request, "folders");
        return;
    }

    if (assoc_lookup(h, "sub_manage")) {
        session_redirect(session, request, "manage");
        return;
    }

    if (assoc_lookup(h, "sub_folder_dialogue")) {
        char *name = assoc_lookup(request->form, "folder");

        if (name && name[0]) {
            session_redirect(session, request,
                             pool_strcat(request->pool, "change/", name));
            return;
        } else
            session_message(session, "Invalid mail folder");
    }

    if (assoc_lookup(h, "sub_help")) {
        session_log(session, "[cmd_compose] Help");
        session->help_enabled = (session->help_enabled) ? NIL : T;
    }

    if (assoc_lookup(h, "sub_logout")) {
        session_log(session, "[cmd_compose] Logout");
        session_redirect(session, request, "logout/compose");
        return;
    }

    /* Subsiduary screens */
    if (assoc_lookup(h, "sub_import")) {
        session_redirect(session, request, "include");
        return;
    }

    if (assoc_lookup(h, "sub_attachments")) {
        session_redirect(session, request, "attachments");
        return;
    }

    if (assoc_lookup(h, "sub_spell")) {
        session_log(session, "[cmd_compose] Spell Check");
        session_redirect(session, request, "spell");
        return;
    }

    /* Subsiduary actions which leave us on compose screen */

    /* Enable/Disable rich headers */
    if (assoc_lookup(h, "sub_rich_headers")) {
        session_log(session, "[cmd_compose] Rich headers enabled");
        draft_rich_headers(draft, T);
    }

    if (assoc_lookup(h, "sub_no_rich_headers")) {
        session_log(session, "[cmd_compose] Rich headers disabled");
        draft_rich_headers(draft, NIL);
    }

    if (assoc_lookup(h, "sub_large")) {
        session_log(session, "[cmd_compose] Large compose window");
        session->compose_large = T;
    }

    if (assoc_lookup(h, "sub_small")) {
        session_log(session, "[cmd_compose] Small compose window");
        session->compose_large = NIL;
    }

    if (assoc_lookup(h, "sub_clear_headers")) {
        session_log(session, "[cmd_compose] Clear headers");
        draft_clear_hdrs(draft);
    }

    if (assoc_lookup(h, "sub_clear_body")) {
        session_log(session, "[cmd_compose] Clear body");
        draft_clear_body(draft);
    }

    if (assoc_lookup(h, "sub_line_wrap")) {
        session_log(session, "[cmd_compose] Line wrap body");
        draft_line_wrap_body(draft);
    }

    cmd_compose_generate_form(session);
}
