/* $Cambridge: hermes/src/prayer/cmd/cmd_prefs.c,v 1.4 2008/09/26 09:58:05 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"

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

/* General options */

static void
generate_general_form(struct session *session, struct prefs *prefs)
{
    struct template_vals *tvals = session->template_vals;
    struct config *config = session->config;
    struct request *request = session->request;
    struct buffer *b = request->write_buffer;

    template_vals_string(tvals, "template_set", prefs->template_set);

    if (config->template_list) {
        unsigned long offset = 0;
        struct list_item *li;

        for (li = config->template_list->head; li; li = li->next) {
            struct config_template *template = (struct config_template *) li;

            template_vals_foreach_init(tvals, "@templates", offset);
            template_vals_foreach_string(tvals, "@templates", offset,
                                         "name", template->name);
            template_vals_foreach_string(tvals, "@templates", offset,
                                         "fullname", template->description);
            offset++;
        }
    }

    template_vals_string(tvals, "theme_main_name", prefs->theme_main_name);
    template_vals_string(tvals, "theme_help_name", prefs->theme_help_name);
    if (config->theme_list && (list_length(config->theme_list) > 1)) {
        struct config_theme *theme;
        unsigned long offset = 0;

        theme = (struct config_theme *) config->theme_list->head;
        while (theme) {
            template_vals_foreach_init(tvals, "@themes", offset);
            template_vals_foreach_string(tvals, "@themes", offset,
                                         "name", theme->name);
            template_vals_foreach_string(tvals, "@themes", offset,
                                         "fullname", theme->description);
            theme = theme->next;
            offset++;
        }
    }

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

    if (config->raven_enable)
        template_vals_ulong(tvals, "raven_enable", 1);

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

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

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

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

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

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

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

    session_seed_template(session, tvals);
    template_expand("prefs_general", tvals, b);
}

static BOOL
process_general_prefs(struct session *session,
                      struct prefs *prefs, struct assoc *h)
{
    struct config *config = session->config;
    char *s;

    if ((s = assoc_lookup(h, "template_set")) && s[0]) {
        if (config->template_list &&
            list_lookup_byname(config->template_list, s))
            prefs->template_set = pool_strdup(prefs->pool, s);
    }

    if ((s = assoc_lookup(h, "theme_main_name")) && s[0]) {
        prefs->theme_main_name = pool_strdup(prefs->pool, s);
    }

    if ((s = assoc_lookup(h, "theme_help_name")) && s[0]) {
        prefs->theme_help_name = pool_strdup(prefs->pool, s);
    }

    prefs->use_welcome = (assoc_lookup(h, "use_welcome")) ? T : NIL;
    prefs->use_mark_persist =
        (assoc_lookup(h, "use_mark_persist")) ? T : NIL;
    prefs->use_search_zoom =
        (assoc_lookup(h, "use_search_zoom")) ? T : NIL;
    prefs->use_agg_unmark = (assoc_lookup(h, "use_agg_unmark")) ? T : NIL;

    if (config->raven_enable) {
        prefs->allow_raven_login
            = (assoc_lookup(h, "allow_raven_login")) ? T : NIL;
    }

    prefs->confirm_expunge = (assoc_lookup(h, "confirm_expunge")) ? T : NIL;
    prefs->confirm_logout = (assoc_lookup(h, "confirm_logout")) ? T : NIL;
    prefs->expunge_on_exit = (assoc_lookup(h, "expunge_on_exit")) ? T : NIL;

    return (T);
}

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

/* Display Preferences */

static void
generate_display_form(struct session *session, struct prefs *prefs)
{
    struct template_vals *tvals = session->template_vals;
    struct request *request = session->request;
    struct buffer *b = request->write_buffer;
    SORTMODE sort_mode = prefs->sort_mode;
    BOOL sort_reverse = prefs->sort_reverse;
    abook_sort_mode abook_sort_mode = prefs->abook_sort_mode;
    BOOL abook_sort_reverse = prefs->abook_sort_reverse;

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

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

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

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

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

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

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

    switch (sort_mode) {
    case ARRIVAL:
        template_vals_string(tvals, "folder_sort_mode",
                             (sort_reverse) ? "reverse-arrival" : "arrival");
        break;
    case DATE:
        template_vals_string(tvals, "folder_sort_mode",
                             (sort_reverse) ? "reverse-date" : "date");
        break;
    case FROM:
        template_vals_string(tvals, "folder_sort_mode",
                             (sort_reverse) ? "reverse-from" : "from");
        break;
    case TO:
        template_vals_string(tvals, "folder_sort_mode",
                             (sort_reverse) ? "reverse-to" : "to");
        break;
    case CC:
        template_vals_string(tvals, "folder_sort_mode",
                             (sort_reverse) ? "reverse-cc" : "cc");
        break;
    case SIZE:
        template_vals_string(tvals, "folder_sort_mode",
                             (sort_reverse) ? "reverse-size" : "size");
        break;
    case SUBJECT:
        template_vals_string(tvals, "folder_sort_mode",
                             (sort_reverse) ? "reverse-subject" : "subject");
        break;
    case REFERENCES:
        template_vals_string(tvals, "folder_sort_mode",
                             (sort_reverse) ?
                             "reverse-references" : "references");
        break;
    case ORDEREDSUBJECT:
        template_vals_string(tvals, "folder_sort_mode",
                             (sort_reverse) ?
                             "reverse-orderedsubject" : "orderedsubject");
        break;
    }

    template_vals_ulong(tvals, "msgs_per_page", prefs->msgs_per_page);
        
    switch (abook_sort_mode) {
    case ABOOK_SORT_ORDER:
        template_vals_string(tvals, "abook_sort_mode",
                             (abook_sort_reverse) ?
                             "reverse-order" : "order");
        break;
    case ABOOK_SORT_ALIAS:
        template_vals_string(tvals, "abook_sort_mode",
                             (abook_sort_reverse) ?
                             "reverse-alias" : "alias");
        break;
    case ABOOK_SORT_NAME:
        template_vals_string(tvals, "abook_sort_mode",
                             (abook_sort_reverse) ?
                             "reverse-name" : "name");
        break;
    case ABOOK_SORT_COMMENT:
        template_vals_string(tvals, "abook_sort_mode",
                             (abook_sort_reverse) ?
                             "reverse-comment" : "comment");
        break;
    case ABOOK_SORT_EMAIL:
        template_vals_string(tvals, "abook_sort_mode",
                             (abook_sort_reverse) ?
                             "reverse-email" : "email");
        break;
    }

    template_vals_ulong(tvals, "abook_per_page", prefs->abook_per_page);
    template_vals_string(tvals, "alt_addr", prefs->alt_addr);

    session_seed_template(session, tvals);
    template_expand("prefs_display", tvals, b);
}

static BOOL
process_display_prefs(struct session *session,
                       struct prefs *prefs, struct assoc *h)
{
    struct config *config = session->config;
    struct pool *pool = prefs->pool;
    char *s;
    ADDRESS *addr = NIL;
    ADDRESS *a = NIL;
    BOOL rc = T;

    if ((s = assoc_lookup(h, "msgs_per_page"))) {
        int value = atoi(s);

        if (value < config->msgs_per_page_min) {
            session_message(session,
                            ("Messages per page too small"
                             " (minimum allowed: %lu)"),
                            config->msgs_per_page_min);
            return (NIL);
        }

        if (value > config->msgs_per_page_max) {
            session_message(session,
                            ("Messages per page too large"
                             " (maximum allowed: %lu)"),
                            config->msgs_per_page_max);
            return (NIL);
        }

        prefs->msgs_per_page = value;
    }

    if ((s = assoc_lookup(h, "abook_per_page"))) {
        int value = atoi(s);

        if (value < config->abook_per_page_min) {
            session_message(session,
                            ("Addressbook entries per page too small "
                             "(minimum allowed: %lu)"),
                            config->abook_per_page_min);
            return (NIL);
        }
        if (value > config->abook_per_page_max) {
            session_message(session,
                            ("Addressbook entries per page too large "
                             "(maximum allowed: %lu)"),
                            config->abook_per_page_max);
            return (NIL);
        }

        prefs->abook_per_page = value;
    }

    if ((s = assoc_lookup(h, "alt_addr"))) {
        prefs->alt_addr = pool_strdup(pool, string_trim_whitespace(s));

        if (prefs->alt_addr && prefs->alt_addr[0]) {
            if (!(addr=addr_parse_destructive(s, ""))) {
                session_message(session,
                                "Alt addresses invalid: %s", ml_errmsg());
                rc = NIL;
            } else {
                for (a = addr; a; a = a->next) {
                    if (!(a->host && a->host[0])) {
                        session_message(session,
                                        ("Unqualified address (%s) "
                                         "in Alt Address list"), a->mailbox);
                        rc = NIL;
                        break;
                    }
                }
                mail_free_address(&addr);
            }
        }
    }

    prefs->use_icons = (assoc_lookup(h, "use_icons")) ? T : NIL;
    prefs->use_tail_banner =
        (assoc_lookup(h, "use_tail_banner")) ? T : NIL;
    prefs->html_inline = (assoc_lookup(h, "html_inline")) ? T : NIL;
    prefs->html_inline_auto =
        (assoc_lookup(h, "html_inline_auto")) ? T : NIL;
    prefs->html_remote_images =
        (assoc_lookup(h, "html_remote_images")) ? T : NIL;
    prefs->preserve_mimetype =
        (assoc_lookup(h, "preserve_mimetype")) ? T : NIL;
    prefs->use_unread =
        (assoc_lookup(h, "use_unread")) ? T : NIL;

    if ((s = assoc_lookup(h, "folder_sort_mode"))) {
        if (!strncmp(s, "reverse-", strlen("reverse-"))) {
            prefs->sort_reverse = T;
            s += strlen("reverse-");
        } else
            prefs->sort_reverse = NIL;

        if (!strcmp(s, "arrival"))
            prefs->sort_mode = ARRIVAL;
        else if (!strcmp(s, "date"))
            prefs->sort_mode = DATE;
        else if (!strcmp(s, "from"))
            prefs->sort_mode = FROM;
        else if (!strcmp(s, "to"))
            prefs->sort_mode = TO;
        else if (!strcmp(s, "cc"))
            prefs->sort_mode = CC;
        else if (!strcmp(s, "size"))
            prefs->sort_mode = SIZE;
        else if (!strcmp(s, "subject"))
            prefs->sort_mode = SUBJECT;
        else if (!strcmp(s, "references"))
            prefs->sort_mode = REFERENCES;
        else if (!strcmp(s, "orderedsubject"))
            prefs->sort_mode = ORDEREDSUBJECT;
    }

    if ((s = assoc_lookup(h, "abook_sort_mode"))) {
        abook_sort_mode mode_original = prefs->abook_sort_mode;
        BOOL reverse_original = prefs->abook_sort_reverse;

        if (!strncmp(s, "reverse-", strlen("reverse-"))) {
            prefs->abook_sort_reverse = T;
            s += strlen("reverse-");
        } else
            prefs->abook_sort_reverse = NIL;

        if (!strcmp(s, "order"))
            prefs->abook_sort_mode = ABOOK_SORT_ORDER;
        else if (!strcmp(s, "alias"))
            prefs->abook_sort_mode = ABOOK_SORT_ALIAS;
        else if (!strcmp(s, "name"))
            prefs->abook_sort_mode = ABOOK_SORT_NAME;
        else if (!strcmp(s, "comment"))
            prefs->abook_sort_mode = ABOOK_SORT_COMMENT;
        else if (!strcmp(s, "email"))
            prefs->abook_sort_mode = ABOOK_SORT_EMAIL;

        if ((prefs->abook_sort_mode    != mode_original) ||
            (prefs->abook_sort_reverse != reverse_original)) {
            abook_set_sort(session->options->abook,
                           prefs->abook_sort_mode, prefs->abook_sort_reverse);
        }
    }

    return (rc);
}

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

/* Folder Preferences */

static void
generate_folder_form(struct session *session, struct prefs *prefs)
{
    struct template_vals *tvals = session->template_vals;
    struct request *request = session->request;
    struct buffer *b = request->write_buffer;

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

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

    template_vals_string(tvals, "maildir", prefs->maildir);
    template_vals_string(tvals, "sent_mail_folder", prefs->sent_mail_folder);
    template_vals_string(tvals, "postponed_folder", prefs->postponed_folder);

    session_seed_template(session, tvals);
    template_expand("prefs_folder", tvals, b);
}

static BOOL
process_folder_prefs(struct session *session,
                     struct prefs *prefs, struct assoc *h)
{
    struct pool *pool = prefs->pool;
    char *s;
    BOOL old_dotfiles = prefs->suppress_dotfiles;

    prefs->suppress_dotfiles =
        (assoc_lookup(h, "suppress_dotfiles")) ? T : NIL;

    if (prefs->suppress_dotfiles != old_dotfiles)
        folderlist_invalidate(session->folderlist);

    if ((s = assoc_lookup(h, "maildir"))) {
        s = string_trim_whitespace(s);

        if (strcmp(s, prefs->maildir) != 0)
            prefs->maildir = pool_strdup(pool, s);

        if (!string_filename_valid(s)) {
            session_message(session, "Invalid Mail Directory Name");
            return (NIL);
        }
        folderlist_invalidate(session->folderlist);
    }

    if ((s = assoc_lookup(h, "sent_mail_folder"))) {
        s = string_trim_whitespace(s);

        if (strcmp(s, prefs->sent_mail_folder) != 0)
            prefs->sent_mail_folder = pool_strdup(pool, s);

        if (*s == '\0') {
            session_message(session,
                            "Invalid (Empty) Sent Mail Folder Name");
            return (NIL);
        }

        if (!string_filename_valid(s)) {
            session_message(session, "Invalid Sent Mail Folder Name");
            return (NIL);
        }
    }

    if ((s = assoc_lookup(h, "postponed_folder"))) {
        s = string_trim_whitespace(s);

        if (strcmp(s, prefs->postponed_folder) != 0)
            prefs->postponed_folder = pool_strdup(pool, s);

        if (*s == '\0') {
            session_message(session,
                            "Invalid (Empty) Postponed Folder Name");
            return (NIL);
        }

        if (!string_filename_valid(s)) {
            session_message(session, "Invalid Postponed Folder Name");
            return (NIL);
        }
    }

    prefs->confirm_rm      = (assoc_lookup(h, "confirm_rm")) ? T : NIL;

    return (T);
}

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

/* Compose Preferences */

static void
generate_compose1_form(struct session *session, struct prefs *prefs)
{
    struct template_vals *tvals = session->template_vals;
    struct config *config = session->config;
    struct request *request = session->request;
    struct buffer *b = request->write_buffer;

    template_vals_string(tvals, "from_personal", prefs->from_personal);

    /* Calculate default from personal */
    if (!(prefs->from_personal && prefs->from_personal[0]) &&
        config->local_domain_list) {
        struct list_item *li;
        char *value;
        char *default_domain = prefs->default_domain;

        for (li = config->local_domain_list->head; li; li = li->next) {
            struct config_local_domain *cld =
                (struct config_local_domain *) li;

            if (cld->cdb_map && !strcmp(default_domain, cld->name) &&
                cdb_find(cld->cdb_map,
                         session->username, strlen(session->username),
                         &value)
                && value && value[0]
                && (value = string_trim_whitespace(value))) {
                template_vals_string(tvals, "default_from_personal", value);
                break;
            }
        }
    }

    if (config->fix_from_address)
        template_vals_ulong(tvals, "fix_from_address", 1);

    template_vals_string(tvals, "from_address", prefs->from_address);

    if (!(prefs->from_address && prefs->from_address[0])) {
        char *s = session->username;

        if (!strchr(session->username, '@')) {
            s = pool_printf(request->pool, "%s@%s",
                            session->username, prefs->default_domain);
        }
        template_vals_string(tvals, "default_from_address", s);
    }

    template_vals_string(tvals, "default_reply_to", prefs->default_reply_to);
    template_vals_string(tvals, "signature", prefs->signature);

    session_seed_template(session, tvals);
    template_expand("prefs_compose", tvals, b);
}

static BOOL
process_compose_prefs(struct session *session,
                      struct prefs *prefs, struct assoc *h)
{
    struct config *config = session->config;
    struct pool *pool = prefs->pool;
    char *s;
    ADDRESS *a = NIL;
    ADDRESS *addr = NIL;
    BOOL rc = T;

    if ((s = assoc_lookup(h, "from_personal")))
        prefs->from_personal =
            pool_strdup(pool, string_trim_whitespace(s));

    if (!config->fix_from_address && (s = assoc_lookup(h, "from_address"))) {
        prefs->from_address = pool_strdup(pool, string_trim_whitespace(s));
        if (prefs->from_address[0]) {
            if (!(addr=addr_parse_destructive(s, ""))) {
                session_message(session,
                                "From Address invalid: %s", ml_errmsg());
                rc = NIL;
            } else {
                if ((addr->next) || (addr->personal && addr->personal[0]) ||
                    (!(addr->host && addr->host[0]))) {
                    session_message(session,
                                    ("From Address must be single, simple "
                                     "and fully qualified email address"));
                    rc = NIL;
                }
                mail_free_address(&addr);
            }
        }
    }

    if ((s = assoc_lookup(h, "default_reply_to"))) {
        prefs->default_reply_to =
            pool_strdup(pool, string_trim_whitespace(s));

        if (prefs->default_reply_to[0]) {
            if (!(addr = addr_parse_destructive(s, ""))) {
                session_message(session,
                                "Default Reply-To Address invalid: %s",
                                ml_errmsg());
                rc = NIL;
            } else {
                for (a = addr; a; a = a->next) {
                    if (!(a->host && a->host[0])) {
                        session_message(session, ("Unqualified address in "
                                                  "Default Reply-To list"));
                        rc = NIL;
                        break;
                    }
                }
                mail_free_address(&addr);
            }
        }
    }

    if ((s = assoc_lookup(h, "signature")))
        prefs->signature = pool_strdup(pool, s);

    return (rc);
}

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

/* Compose2 Preferences */

static void
generate_compose2_form(struct session *session, struct prefs *prefs)
{
    struct template_vals *tvals = session->template_vals;
    struct config *config = session->config;
    struct request *request = session->request;
    struct buffer *b = request->write_buffer;

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

    if (config->aspell_path)
       template_vals_ulong(tvals, "have_aspell_path", 1);

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

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

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

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

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

    template_vals_ulong(tvals, "line_wrap_len", prefs->line_wrap_len);

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

    /* Start of default domain */
    template_vals_string(tvals, "default_domain", prefs->default_domain);
    if (config->local_domain_list) {
        unsigned long offset = 0;
        struct list_item *li;

        for (li = config->local_domain_list->head; li; li = li->next) {
            struct config_local_domain *cld =
                (struct config_local_domain *) li;

            template_vals_foreach_init(tvals, "@domains", offset);
            template_vals_foreach_string(tvals, "@domains", offset,
                                         "name", cld->name);
            offset++;
        }
    }

    template_vals_string(tvals, "ispell_language", prefs->ispell_language);
    if (config->ispell_language_list) {
        unsigned long offset = 0;
        struct list_item *li;

        for (li = config->ispell_language_list->head; li; li = li->next) {
            struct config_language *lang = (struct config_language *) li;

            template_vals_foreach_init(tvals, "@langs", offset);
            template_vals_foreach_string(tvals, "@langs", offset,
                                         "name", lang->name);
            template_vals_foreach_string(tvals, "@langs", offset,
                                         "desc", lang->desc);
            offset++;
        }
    }

    session_seed_template(session, tvals);
    template_expand("prefs_compose2", tvals, b);
}

static BOOL
process_compose2_prefs(struct session *session,
                       struct prefs *prefs, struct assoc *h)
{
    struct config *config = session->config;
    struct pool *pool = prefs->pool;
    char *s;
    int value;

    if ((s = assoc_lookup(h, "small_cols"))) {
        if (((value = atoi(s)) > 0) && (value <= 120)) {
            prefs->small_cols = value;
        } else {
            session_message(session, "Small columns option out of range");
            return (NIL);
        }
    }

    if ((s = assoc_lookup(h, "small_rows"))) {
        if (((value = atoi(s)) > 0) && (value <= 120)) {
            prefs->small_rows = value;
        } else {
            session_message(session, "Small rows option out of range");
            return (NIL);
        }
    }

    if ((s = assoc_lookup(h, "large_cols"))) {
        if (((value = atoi(s)) > 0) && (value <= 120)) {
            prefs->large_cols = value;
        } else {
            session_message(session, "Large columns option out of range");
            return (NIL);
        }
    }

    if ((s = assoc_lookup(h, "large_rows"))) {
        if (((value = atoi(s)) > 0) && (value <= 120)) {
            prefs->large_rows = value;
        } else {
            session_message(session, "Large rows option out of range");
            return (NIL);
        }
    }

    if ((s = assoc_lookup(h, "line_wrap_len"))) {
        if (((value = atoi(s)) > 40) && (value <= 120)) {
            prefs->line_wrap_len = value;
        } else {
            session_message(session,
                            "Line wrap length option out of range");
            return (NIL);
        }
    }

    if (config->aspell_path) {
        prefs->spell_skip_quoted
            = (assoc_lookup(h, "spell_skip_quoted")) ? T : NIL;
    }

    prefs->use_sent_mail = (assoc_lookup(h, "use_sent_mail")) ? T : NIL;

    prefs->line_wrap_on_reply
        = (assoc_lookup(h, "line_wrap_on_reply")) ? T : NIL;

    prefs->line_wrap_on_send
        = (assoc_lookup(h, "line_wrap_on_send")) ? T : NIL;

    prefs->line_wrap_on_spell
        = (assoc_lookup(h, "line_wrap_on_spell")) ? T : NIL;

    prefs->line_wrap_advanced
        = (assoc_lookup(h, "line_wrap_advanced")) ? T : NIL;

    if ((s = assoc_lookup(h, "default_domain")))
        prefs->default_domain =
            pool_strdup(pool, string_trim_whitespace(s));

    if ((s = assoc_lookup(h, "ispell_language")))
        prefs->ispell_language =
            pool_strdup(pool, string_trim_whitespace(s));

    return (T);
}

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

static void
generate_form(struct session *session, char *type, struct prefs *prefs)
{
    struct request *request = session->request;

    if (!strcmp(type, "general"))
        generate_general_form(session, prefs);
    else if (!strcmp(type, "display"))
        generate_display_form(session, prefs);
    else if (!strcmp(type, "folder"))
        generate_folder_form(session, prefs);
    else if (!strcmp(type, "compose1"))
        generate_compose1_form(session, prefs);
    else if (!strcmp(type, "compose2"))
        generate_compose2_form(session, prefs);

    response_html(request, 200);
}

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

void cmd_prefs(struct session *session)
{
    struct request *request = session->request;
    struct options *options = session->options;
    struct prefs *prefs;
    struct pool *pool;
    struct assoc *h = NIL;
    char *s;
    char *type;

    if (!options->prefs_work)
        options->prefs_work = prefs_copy(options->prefs);

    prefs = options->prefs_work;
    pool = prefs->pool;

    if (request->method != POST) {
        generate_form(session, "general", prefs);
        session_log(session, "[cmd_prefs] General Preferences");
        return;
    }

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

    /* Cancel doesn't need any preferences */
    if ((s = assoc_lookup(h, "sub_cancel"))) {
        /* Free up working preferences and return to list screen */
        prefs_free(options->prefs_work);
        options->prefs_work = NIL;

        session_redirect(session, request, "manage");
        return;
    }

    /* Make sure that working preferences updated before we go anywhere */
    if (!(type = assoc_lookup(h, "type")))
        type = "";

    if (!strcmp(type, "general")) {
        session_log(session, "[cmd_prefs] General Preferences");
        if (!process_general_prefs(session, prefs, h)) {
            generate_form(session, "general", prefs);
            return;
        }
    } else if (!strcmp(type, "display")) {
        session_log(session, "[cmd_prefs] Display Preferences");
        if (!process_display_prefs(session, prefs, h)) {
            generate_form(session, "display", prefs);
            return;
        }
    } else if (!strcmp(type, "folder")) {
        session_log(session, "[cmd_prefs] Folder Preferences");
        if (!process_folder_prefs(session, prefs, h)) {
            generate_form(session, "folder", prefs);
            return;
        }
    } else if (!strcmp(type, "compose1")) {
        session_log(session, "[cmd_prefs] Compose Preferences");
        if (!process_compose_prefs(session, prefs, h)) {
            generate_form(session, "compose1", prefs);
            return;
        }
    } else if (!strcmp(type, "compose2")) {
        session_log(session, "[cmd_prefs] Extra Compose Preferences");
        if (!process_compose2_prefs(session, prefs, h)) {
            generate_form(session, "compose2", prefs);
            return;
        }
    }

    /* Switch to other preference screens */
    if (assoc_lookup(h, "sub_general")) {
        generate_form(session, "general", prefs);
        return;
    }

    if (assoc_lookup(h, "sub_display")) {
        generate_form(session, "display", prefs);
        return;
    }

    if (assoc_lookup(h, "sub_folder")) {
        generate_form(session, "folder", prefs);
        return;
    }

    if (assoc_lookup(h, "sub_compose1")) {
        generate_form(session, "compose1", prefs);
        return;
    }

    if (assoc_lookup(h, "sub_compose2")) {
        generate_form(session, "compose2", prefs);
        return;
    }

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

    if (assoc_lookup(h, "sub_save")) {
        /* Update save preferences */
        if (options->prefs_save)
            prefs_free(options->prefs_save);
        options->prefs_save = prefs_copy(options->prefs_work);

        options->save = T;
    }

    /* Update live preferences */
    if (options->prefs)
        prefs_free(options->prefs);
    options->prefs = prefs_copy(options->prefs_work);
    session_use_prefs(session, prefs, NIL);

    /* Free up working preferences and return to list screen */
    prefs_free(options->prefs_work);
    options->prefs_work = NIL;

    session_redirect(session, request, "manage");
}
