/* $Cambridge: hermes/src/prayer/servers/prayer.c,v 1.6 2010/07/07 15:02:11 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_shared.h"
#include "prayer.h"
#include "prayer_server.h"

/* Support routines for "prayer" frontend HTTP server and proxy */

/* prayer_create() ******************************************************
 *
 * Create a prayer structure, used to bind all other global state
 *   config: Prayer configuration
 *
 * Returns: Ptr to prayer structure, using own pool.
 ***********************************************************************/

struct prayer *prayer_create(struct config *config)
{
    struct pool *pool = pool_create(sizeof(struct prayer));
    struct prayer *prayer = pool_alloc(pool, sizeof(struct prayer));

    prayer->pool = pool;
    prayer->config = config;
    prayer->request = NIL;
    prayer->accesslog = log_create(config, NIL);
    prayer->http_port_list = list_create(pool, T);
    prayer->select_lock_fd = -1;

    /* Use session timeouts for initial exchange */
    prayer->ipaddr = ipaddr_create(pool);
    prayer->port = 0L;
    prayer->use_ssl = NIL;
    prayer->is_session = T;
    prayer->template_vals = NIL;

    return (prayer);
}

/* prayer_free() ********************************************************
 *
 * Free a prayer instance. Closes log fd if open as well 
 *   prayer: Prayer structure
 ***********************************************************************/

void prayer_free(struct prayer *prayer)
{
    log_free(prayer->accesslog);
    pool_free(prayer->pool);
}

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

/* prayer_url_prefix() **************************************************
 *
 * Generate a URL prefix from prayer structure for current active port.
 *    prayer:
 *     tpool: Target pool
 *
 * Returns: URL prefix
 ***********************************************************************/

char *prayer_url_prefix(struct prayer *prayer, struct pool *tpool)
{
    struct config *config = prayer->config;

    if (prayer->use_ssl) {
        if (prayer->port == 443)
            return (pool_printf(tpool, "https://%s", config->hostname));

        return (pool_printf(tpool, "https://%s:%d",
                            config->hostname, prayer->port));
    }

    if (prayer->port == 80)
        return (pool_printf(tpool, "http://%s", config->hostname));

    return (pool_printf(tpool, "http://%s:%d",
                        config->hostname, prayer->port));
}

/* prayer_template_setup() *********************************************
 *
 * Initialise prayer->template_vals if we are going to template_expand()
 *    prayer:
 *
 ***********************************************************************/

void
prayer_template_setup(struct prayer *prayer)
{
    struct request *request = prayer->request;
    struct config  *config  = prayer->config;

    prayer->template_vals = template_vals_create
        (request->pool,
         config->template_path,
         config->template_set,
         config->template_use_compiled, T,
         template_vals_urlstate_create(request->pool));

    template_vals_string(prayer->template_vals,
                         "$url_prefix",
                         prayer_url_prefix(prayer, request->pool));

    if (config->login_service_name)
        template_vals_string(prayer->template_vals, "g_service_name",
                             config->login_service_name);
}

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

/* prayer_log_open() ****************************************************
 *
 * Open log file (but only after setup procedure complete and running
 * as correct user and group!)
 *   prayer: Prayer structure
 ***********************************************************************/

BOOL prayer_log_open(struct prayer * prayer)
{
    return (log_open(prayer->accesslog, "access_log"));
}

/* prayer_log_ping() ****************************************************
 *
 * Reopen log file.
 *   prayer: Prayer structure
 ***********************************************************************/

BOOL prayer_log_ping(struct prayer * prayer)
{
    return (log_ping(prayer->accesslog));
}

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

/* prayer_http_port_open() **********************************************
 *
 * Open HTTP port
 *   prayer: Prayer structure
 *     port: Port to bind
 *  use_ssl: Flag that this port will be used for SSL connections when
 *           active.
 * interface:Interface to bind to.
 *
 * Returns: T on success. NIL => couldn't bind to port
 ***********************************************************************/

BOOL
prayer_http_port_open(struct prayer * prayer,
                      unsigned long port, BOOL use_ssl, char *interface)
{
    int *sockfds, i;
    struct prayer_http_port *fhp;

    if (!(sockfds = os_bind_inet_socket(port, interface)))
        return (NIL);

    /* Result is list of sockfds, terminated by negative number */
    for (i=0; sockfds[i] >= 0; i++) {
        fhp = pool_alloc(prayer->pool, sizeof(struct prayer_http_port));
        fhp->port = port;
        fhp->use_ssl = use_ssl;
        fhp->sockfd = sockfds[i];

        list_push(prayer->http_port_list, (struct list_item *) fhp, NIL);
    }
    return (T);
}

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

/* prayer_http_port_alloc() *********************************************
 *
 * Add prayer_http_port to end of active list. Used by prayer to reproduce
 * the work above if it execs itself after ports are opened and setuid()
 * called in order to lose root process coredump paranoia.
 *  prayer:  Prayer structure
 *  use_ssl: Indicate that this will be a SSL port
 *   sockfd: Socket that is bound to this port
 ***********************************************************************/

BOOL
prayer_http_port_alloc(struct prayer * prayer,
                       unsigned long port, BOOL use_ssl, int sockfd)
{
    struct prayer_http_port *fhp;

    fhp = pool_alloc(prayer->pool, sizeof(struct prayer_http_port));
    fhp->port = port;
    fhp->use_ssl = use_ssl;
    fhp->sockfd = sockfd;

    list_push(prayer->http_port_list, (struct list_item *) fhp, NIL);
    return (T);
}

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

/* prayer_http_port_close() *********************************************
 *
 * Close all HTTP ports bound by this prayer structure.
 *  prayer: Prayer structure
 ***********************************************************************/

BOOL prayer_http_port_close(struct prayer * prayer)
{
    struct list_item *li;

    if (prayer->http_port_list) {
        for (li = prayer->http_port_list->head; li; li = li->next) {
            struct prayer_http_port *fhp = (struct prayer_http_port *) li;

            close(fhp->sockfd);
        }
    }

    return (T);
}

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

/* Interface to logging subsystem that picks up on prayer state */

void prayer_accesslog(struct prayer *prayer, struct request *request)
{
    unsigned long size;
    char *http_request
        = (request->log_entry) ? request->log_entry : request->request;

    if (request->gzip_buffer)
        size = buffer_size(request->gzip_buffer);
    else
        size = buffer_size(request->write_buffer);

    log_here(prayer->accesslog,
             "[%s] (%lu) %s -> %lu %lu %s",
             ipaddr_text(prayer->ipaddr),
             prayer->port, http_request, request->status, size,
             ((request->use_http_1_1) ? "1.1" : "1.0"));
}

/* Compatibility Interface to new fangled panic log system... */

void prayer_paniclog(struct prayer *prayer, char *fmt, ...)
{
    unsigned long len;
    va_list ap;

    va_start(ap, fmt);
    len = log_entry_size(fmt, ap);
    va_end(ap);

    va_start(ap, fmt);
    log_panic_ap(prayer->config, NIL, len, fmt, ap);
    va_end(ap);
}

void prayer_fatal(struct prayer *prayer, char *fmt, ...)
{
    unsigned long len;
    va_list ap;

    va_start(ap, fmt);
    len = log_entry_size(fmt, ap);
    va_end(ap);

    va_start(ap, fmt);
    log_panic_ap(prayer->config, NIL, len, fmt, ap);
    va_end(ap);

    /* Generate core dump */
    abort();
}
