/* $Cambridge: hermes/src/prayer/cmd/cmd_list.c,v 1.2 2008/05/19 15:55:54 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"

/* Add template_vals used to navigate to other pages */

static BOOL
cmd_list_addnav(struct session *session, unsigned long zm_offset)
{
    struct template_vals *tvals = session->template_vals;
    struct prefs *prefs = session->options->prefs;
    struct msgmap *zm = session->zm;
    MAILSTREAM *stream = session->stream;
    unsigned long count = msgmap_size(session->zm);
    unsigned long per_page = prefs->msgs_per_page;
    unsigned long page = 0;
    unsigned long pages = 0;
    unsigned msgno;
    unsigned uid;


    if (count > 0) {
        page = ((zm_offset - 1) / prefs->msgs_per_page) + 1;
        pages = ((count - 1) / prefs->msgs_per_page) + 1;
    }

    template_vals_hash_ulong(tvals, "$nav", "msg_count",    count);
    template_vals_hash_ulong(tvals, "$nav", "page_current", page);
    template_vals_hash_ulong(tvals, "$nav", "page_count",   pages);

    if (page > 1) {
        msgno = msgmap_value(zm, 1);
        uid   = ml_uid(session, stream, msgno);
        template_vals_hash_ulong(tvals, "$nav", "first_msg", msgno);
        template_vals_hash_ulong(tvals, "$nav", "first_uid", uid);

        msgno = msgmap_value(zm, zm_offset - per_page);
        uid   = ml_uid(session, stream, msgno);
        template_vals_hash_ulong(tvals, "$nav", "prev_msg", msgno);
        template_vals_hash_ulong(tvals, "$nav", "prev_uid", uid);
    }
    if (page < pages) {
        if ((msgno = (zm_offset + per_page)) > count)
            msgno = count;

        msgno = msgmap_value(zm, msgno);
        uid   = ml_uid(session, stream, msgno);
        template_vals_hash_ulong(tvals, "$nav", "next_msg", msgno);
        template_vals_hash_ulong(tvals, "$nav", "next_uid", uid);

        msgno = msgmap_value(zm, count);
        uid   = ml_uid(session, stream, msgno);
        template_vals_hash_ulong(tvals, "$nav", "last_msg", msgno);
        template_vals_hash_ulong(tvals, "$nav", "last_uid", uid);
    }
    return(T);
}

/* Add template_vals for a single message */

static BOOL
cmd_list_msg(struct session *session,
             MAILSTREAM * stream,
             unsigned long msgno,
             ADDRESS * alt_addr,
             int offset)
{
    struct template_vals *tvals = session->template_vals;
    struct config *config = session->config;
    struct prefs *prefs = session->options->prefs;
    struct request *request = session->request;
    struct pool *pool = request->pool;
    MESSAGECACHE *elt;
    ENVELOPE *env;
    ADDRESS *addr;
    BOOL use_to;
    char *string;

    if (!((elt = ml_elt(session, stream, msgno)) &&
          (env = ml_fetch_structure(session, stream, msgno, NIL, 0))))
        return (NIL);

    template_vals_foreach_init(tvals, "@list_msg", offset);
    template_vals_foreach_ulong(tvals, "@list_msg", offset, "num", msgno);
    template_vals_foreach_ulong(tvals, "@list_msg", offset, "uid",
                                ml_uid(session, stream, msgno));
    if (elt->deleted)
        template_vals_foreach_ulong(tvals, "@list_msg", offset,
                                    "is_deleted", 1);
    if (elt->answered)
        template_vals_foreach_ulong(tvals, "@list_msg", offset,
                                    "is_answered", 1);
    if (elt->seen)
        template_vals_foreach_ulong(tvals, "@list_msg", offset,
                                    "is_seen", 1);

    if (prefs->use_mark_persist && msgmap_has_mark(session->zm, msgno))
        template_vals_foreach_ulong(tvals, "@list_msg", offset,
                                    "is_marked", 1);
    if (offset % 2 == 0)
        template_vals_foreach_ulong(tvals, "@list_msg", offset,
                                    "even_row", 1);

    /* Message Date */
    if (env->date) {
        MESSAGECACHE tmp;
        mail_parse_date(&tmp, env->date);

        if (prefs->use_full_date)
            string = string_date_to_string_long(tmp.month, tmp.day,
                                           tmp.year + BASEYEAR);
        else
            string = string_date_to_string_short(tmp.month, tmp.day);

    } else
        string = "(No date)";

    if (string && string[0])
        template_vals_foreach_string(tvals, "@list_msg", offset,
                                     "date", string);

    /* Try and work out if user sent this message
     * Yes => show "To:" rather than from address in header.
     */
    use_to = NIL;
    if ((addr = env->from)) {
        while (alt_addr) {
            if (!strcasecmp(addr->mailbox, alt_addr->mailbox) &&
                !strcasecmp(addr->host, alt_addr->host)) {
                use_to = T;
                break;
            }
            alt_addr = alt_addr->next;
        }

        if (!use_to &&
            !strcasecmp(addr->mailbox, session->username) &&
            config->local_domain_list) {
            /* Find out if address is local use of CRSid
             *  e.g: dpc22@cam.ac.uk, dpc22@hermes.cam.ac.uk */

            struct list_item *li;
            /* Check for username@[username_domain]. */
            for (li = config->local_domain_list->head; li; li = li->next) {
                if (!strcasecmp(li->name, addr->host)) {
                    use_to = T;
                    break;
                }
            }
        }
    }

    /* From or To address depending on context */
    addr = (use_to) ? env->to : env->from;

    if (addr && addr->personal)
        string = addr->personal;
    else if (addr && addr->mailbox && addr->host)
        string =
            pool_printf(pool, "%s@%s", addr->mailbox, addr->host);
    else
        string = "(unknown)";

    string = (char *) rfc1522_decode(pool_alloc(pool, strlen(string)),
                                     strlen(string), string, NIL);

    string = utf8_prune(pool, string, config->list_addr_maxlen);
    if (use_to)
        string = pool_strcat(pool, "To: ", string);

    
    template_vals_foreach_string(tvals, "@list_msg", offset,
                                 "dname", string);
    /* Message size */
    if (elt->rfc822_size > 1024)
        string = pool_printf(pool, "%luK", elt->rfc822_size / 1024);
    else
        string = pool_printf(pool, "%lu", elt->rfc822_size);

    template_vals_foreach_string(tvals, "@list_msg", offset,
                                 "size", string);

    /* Message subject */
    if (env->subject && env->subject[0]) {
        string = env->subject;
        string = (char *)
            rfc1522_decode(pool_alloc(pool, strlen(string)),
                           strlen(string), string, NIL);
        string = utf8_prune(pool, string, config->list_subject_maxlen);
    } else
        string = "(No subject)";

    template_vals_foreach_string(tvals, "@list_msg", offset,
                                 "subject", string);
    return (T);
}

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

/* Couple of small support routines for alt_addr stuff */

static ADDRESS *cmd_list_alt_addr_generate(struct session *session,
                                           struct pool *pool,
                                           struct prefs *prefs)
{
    ADDRESS *alt_addr = NIL;
    char *text;

    if (prefs->from_address && prefs->from_address[0]) {
        if (prefs->alt_addr && prefs->alt_addr[0])
            text = pool_printf(pool, "%s, %s",
                               prefs->from_address, prefs->alt_addr);
        else
            text = pool_strdup(pool, prefs->from_address);
    } else
        text = pool_strdup(pool, prefs->alt_addr);

    if (text && text[0] && !(alt_addr=addr_parse_destructive(text, ""))) {
        session_message(session, "Alt-address invalid - %s", ml_errmsg());
        return(NIL);
    }

    return (alt_addr);
}

static void cmd_list_alt_addr_free(ADDRESS * alt_addr)
{
    mail_free_address(&alt_addr);
}

/* cmd_list_work() *******************************************************
 *
 * Generate list screen. Parent routine will clean up after error.
 ************************************************************************/

static BOOL cmd_list_work(struct session *session)
{
    struct request *request = session->request;
    struct prefs *prefs = session->options->prefs;
    unsigned long first, current, last;
    MAILSTREAM *stream = session->stream;
    struct buffer *b = request->write_buffer;
    struct msgmap *zm = session->zm;
    unsigned long zm_offset;
    ADDRESS *alt_addr;
    unsigned long count;
    struct template_vals *tvals = session->template_vals;
    int offset = 0;

    if (!msgmap_update(zm))
        return (NIL);

    if (request->argc == 3) {
        unsigned long msgno = atoi(request->argv[1]);
        unsigned long msguid = atoi(request->argv[2]);

        if ((msgno == 0) || (msgno > zm->nmsgs) ||
            (!(msgno = stream_check_uid(session, stream, msgno, msguid))))
            msgno = zm->nmsgs;

        session->current = msgno;
    }

    /* Expunge event => session->current may no longer be valid? */
    if ((session->current == 0) || (session->current > zm->nmsgs))
        session->current = zm->nmsgs;

    zm_offset = msgmap_find(zm, session->current);
    count = msgmap_size(session->zm);

    if (zm_offset > prefs->msgs_per_page)
        first = zm_offset - ((zm_offset - 1) % prefs->msgs_per_page);
    else
        first = 1;

    last = (((first + prefs->msgs_per_page - 1) < count)
            ? (first + prefs->msgs_per_page - 1) : count);

    if (!stream_fill_overview_msgmap(session, request->pool, stream,
                                     session->zm, first, last))
        return (NIL);

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

    if (msgmap_marked_count(session->zm) > 0)
        template_vals_ulong(tvals, "have_marked", 1);

    if (msgmap_have_zoom(session->zm))
        template_vals_ulong(tvals, "have_zoom", 1);

    cmd_list_addnav(session, zm_offset);

    alt_addr = cmd_list_alt_addr_generate(session, request->pool, prefs);

    offset = 0;
    for (current = first; current <= last; current++) {
        if (!cmd_list_msg(session, stream, msgmap_value(zm, current),
                          alt_addr, offset))
            return (NIL);
        offset++;
    }
    cmd_list_alt_addr_free(alt_addr);

    template_vals_string(tvals, "foldername", session->foldername);
    session_seed_template(session, tvals);
    template_expand("list", tvals, b);
    return (T);
}

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

void cmd_list(struct session *session)
{
    struct options *options = session->options;
    struct msgmap *zm = session->zm;
    struct request *request = session->request;
    char *cmd, *folder;

    /* Handle POST requests: folder change requests */
    if (request->method == POST) {
        request_decode_form(request);

        if (assoc_lookup(request->form, "sub_folder_dialogue") &&
            (folder = assoc_lookup(request->form, "folder")) && folder[0])
        {

            if ((cmd = assoc_lookup(request->form, "command")) &&
                !strcmp(cmd, "copy")) {
                session->aggregate = T;
                session_redirect(session, request,
                                 pool_strcat(request->pool, "copy_msg/",
                                             folder));
            } else
                session_redirect(session, request,
                                 pool_strcat(request->pool, "change/",
                                             folder));
            return;
        }
    }

    /* Save options if anything changed */
    if (options->save)
        session_streams_save_options(session);

    /* Record last command for compose/cancel and friends */
    if (!session->draft->have_draft)
        session->compose_parent_cmd = "list";

    /* Record last command for copy/cancel */
    session->copy_parent_cmd = "list";

    if (msgmap_have_zoom(zm) && (msgmap_marked_count(zm) == 0)) {
        msgmap_unmark_all(zm);
        msgmap_disable_zoom(zm);
        session_message(session,
                        "No messages marked: disabling zoom mode");
    }

    if (!cmd_list_work(session)) {
        session_redirect(session, request, "restart");
        return;
    }

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