/* $Cambridge: hermes/src/prayer/servers/session_exchange.c,v 1.7 2011/01/11 15:27:24 dpc22 Exp $ */
/************************************************
 *    Prayer - a Webmail Interface              *
 ************************************************/

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

#include "server.h"

/* session_exchange() ***************************************************
 *
 * Manage single HTTP exchange with user agent (can be initiated by
 * proxy frontend or direct connection to session server).
 ***********************************************************************/

BOOL session_exchange(struct session * session)
{
    struct prefs  *prefs  = session->options->prefs;
    struct config *config = session->config;
    struct request *request = session->request;
    struct user_agent *user_agent = request->user_agent;
    char *sequence;
    struct template_vals *tvals;
    char *template_set = config->template_set;

    /* Do something useful with HTTP/0.9 */
    if (request->major == 0) {
        response_0_9_error(request);
        return (NIL);
    }

    /* Check for valid host if one supplied */
    if (request->url_host) {
        BOOL rc = NIL;

        if (!strcasecmp(request->url_host, "localhost"))
            rc = T;
        else if (config->hostname &&
                 !strcasecmp(request->url_host, config->hostname))
            rc = T;
        else if (config->hostname_service &&
                 !strcasecmp(request->url_host, config->hostname_service))
            rc = T;

        if (!rc) {
            response_error(request, 403);       /* Forbidden */
            return (NIL);
        }
    }

    request_parse_argv(request);
    request_parse_charset(request);

    if (session->use_gzip) {
        request->allow_gzip = T;
        request_parse_encoding(request);
    } else
        request->allow_gzip = NIL;

    /* argv: /session/username/session/sequence... */

    if ((request->argc < 5) || (strcmp(request->argv[0], "session") != 0)) {
        response_error(request, 404);   /* Not found */
        return (NIL);
    }

    sequence = request->argv[3];
    request->argv = &request->argv[4];
    request->argc -= 4;

    session->sequence_okay = T;

    if (strcmp(sequence, "NOSEQ") != 0) {
        BOOL error = NIL;

        if (session->sequence != session_sequence(sequence))
            session->sequence_okay = NIL;

        session_bump_sequence(session);
        session_update_sequence(session);

        if (user_agent->use_substitution) {
            /* Check that the sequence number is correct:
             *   Browser back button doesn't work with page substituton enabled
             *   Spell checker and browser buttons don't get on at all well
             *    (flag forall methods would be sensible)
             */
            if (!session->sequence_okay)
                error = T;
        } else {
            /* Browser back button works fine without page substition
             * apart from folder changes.
             */

            if (session->sequence_last_change > session_sequence(sequence))
                error = T;
        }

        if (error) {
            char *last_cmd = session_last_cmd(session);

            if (last_cmd && last_cmd[0]) {
                session_message(session,
                                ("Browser history disabled as unsafe: "
                                 "use Webmail navigation icons and links"));
                session_redirect(session, request, last_cmd);
                return (T);
            }

            session_log(session, ("[session_exchange] "
                                  "Internal error: last command not recorded"));
            session_redirect(session, request, "restart");
            return (T);
        }
    } else {
        /* Just in case NOSEQ URL generates a redirect */
        session_update_sequence(session);
    }

    /* Only safe to call session_redirect() after this point */

    /* Check IMAP streams before processing each session request
     * (reopens streams if required + ping every once in a while
     * Have to do this here rather than in parent routines so that we
     * have a HTTP connection back to client for restart page...
     *
     * Don't make this test if it would cause infinite loop of redirects
     */
    if ((strcmp(request->argv[0], "restart") != 0) &&
        (strcmp(request->argv[0], "exit") != 0)) {
        if (!session_streams_check(session)) {
            session_redirect(session, request, "restart");
            return (T);
        }
    }

    /* Reopen local domains every once in a while */
    session_config_local_domain_ping(session->config);

    session_log(session, "[session_exchange] %s", request->argv[0]);

    if (config->template_list &&
        list_lookup_byname(config->template_list, prefs->template_set))
        template_set = prefs->template_set;
    else
        template_set = config->template_set;   /* Safe default */

    /* Set up template_vars ready for dispatch */
    session->template_vals = tvals
        = template_vals_create(request->pool,
                               config->template_path,
                               template_set,
                               config->template_use_compiled, T,
                               session_extract_urlstate(session));

    template_vals_string(tvals, "g_cmd", request->argv[0]);

    /* Look for defined session method */
    if (cmd_dispatch(session, request->argv[0]))
        return (request->persist);

    if ((request->method == GET) || (request->method == HEAD))
        response_error(request, 404);   /* Not found */
    else
        response_error(request, 501);   /* Not implemented */

    return (NIL);
}
