/* $Cambridge: hermes/src/prayer/session/addr.c,v 1.3 2008/09/16 09:59:58 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"

/* Some utility routines for processing address objects */

/* est_size() function stolen directly from ALPINE 1.00: pith/bldaddr.c
 *
 * Compute an upper bound on the size of the array required by
 * rfc822_output_address_list() for this list of addresses.
 *
 * Args: adrlist -- The address list.
 *
 * Returns -- an integer giving the upper bound
 */

static int
est_size(ADDRESS *a)
{
    int cnt = 0;

    for (; a; a = a->next){
	/* two times personal for possible quoting */
	cnt   += 2 * (a->personal  ? (strlen(a->personal)+1)  : 0);
	cnt   += 2 * (a->mailbox  ? (strlen(a->mailbox)+1)    : 0);
	cnt   += (a->adl      ? strlen(a->adl)      : 0);
	cnt   += (a->host     ? strlen(a->host)     : 0);

	/*
	 * add room for:
         *   possible single space between fullname and addr
         *   left and right brackets
         *   @ sign
         *   possible : for route addr
         *   , <space>
	 *
	 * So I really think that adding 7 is enough.  Instead, I'll add 10.
	 */
	cnt   += 10;
    }

    return ((cnt > 50) ? cnt : 50);     /* just making sure */
}

long
dummy_soutr(void *stream, char *string)
{
    fatal("dummy_soutr unexpected call, caught overflow");
    return LONGT;
}

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

/* addr_text() **********************************************************
 *
 * Convert c-client ADDRESS structure to equivalent text in target pool
 *  pool: Target pool
 *  addr: ADDRESS to convert
 *
 * Returns: NULL terminated string
 ***********************************************************************/

char *addr_text(struct pool *pool, ADDRESS * addr)
{
    RFC822BUFFER rbuf;
    int esize = est_size(addr);

    char *buf = pool_alloc(pool, esize + 1);
    buf[0] = '\0';

    rbuf.f   = dummy_soutr;
    rbuf.s   = NULL;
    rbuf.beg = buf;
    rbuf.cur = buf;
    rbuf.end = &buf[esize];
    rfc822_output_address_list(&rbuf, addr, 0L, NULL);
    *rbuf.cur = '\0';

    return (buf);
}

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

/* addr_text_exclude() **************************************************
 *
 * Convert c-client ADDRESS structure to equivalent text in target pool,
 * exclusing any component which matches exclude list or session alt-addr
 * lists. Used to generate list of recipients for a particular message.
 * session:
 *    pool: Target pool
 *    addr: ADDRESS to convert
 * exclude: Address list to exclude.
 *
 * Returns: NULL terminated string
 ***********************************************************************/

char *addr_text_exclude(struct session *session, struct pool *pool,
                        ADDRESS * addr, ADDRESS * exclude)
{
    struct config *config = session->config;
    struct prefs *prefs = session->options->prefs;
    ADDRESS *a, *alt, *alt_addr = NIL;
    ADDRESS *head = NIL;
    ADDRESS *tail = NIL;
    ADDRESS *new;
    BOOL flag;
    char *text, *s;

    /* Need a temporary copy for rfc822_parse_adrlist() */
    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);

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

    for (a = addr; a; a = a->next) {
        if ((a->mailbox == NIL) || (a->host == NIL))
            continue;

        if (exclude &&
            !strcmp(a->mailbox, exclude->mailbox) &&
            !strcmp(a->host, exclude->host))
            continue;

        /* Alt address? */
        flag = NIL;
        for (alt = alt_addr; alt; alt = alt->next) {
            if (!strcasecmp(a->mailbox, alt->mailbox) &&
                !strcasecmp(a->host, alt->host)) {
                flag = T;
                break;
            }
        }
        if (flag)
            continue;

        if (!strcasecmp(a->mailbox, session->username) &&
            config->local_domain_list) {
            struct list_item *li;

            /* Check for username@[username_domain]. */
            flag = NIL;
            for (li = config->local_domain_list->head; li; li = li->next) {
                if (!strcasecmp(li->name, a->host)) {
                    flag = T;
                    break;
                }
            }
            if (flag)
                continue;
        }

        /* Create new address element */
        new = pool_alloc(pool, sizeof(ADDRESS));
        memcpy(new, a, sizeof(ADDRESS));
        new->next = NIL;

        if (head)
            tail = tail->next = new;    /* Add to end of list */
        else
            head = tail = new;  /* Start new list */
    }

    mail_free_address(&alt_addr);

    return (addr_text(pool, head));
}

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

/* Support routine for addr_parse */

static BOOL
addr_parse_check_single(ADDRESS *a)
{
    char *s;

    if ((a->mailbox == NIL) || (a->mailbox[0] == '\0') || (a->host == NIL)) {
        ml_set_errmsg("Empty address");
        return (NIL);
    }

    if (a->mailbox && (a->mailbox[0] == '\'')) {
        ml_errmsg_printf("Invalid \' character in address: %s@%s",
                         a->mailbox, a->host);
        return (NIL);
    }

    if (a->host && (a->host[0] == '\'')) {
        ml_errmsg_printf("Invalid \' character in address: %s@%s",
                         a->mailbox, a->host);
        return (NIL);
    }

    for (s = a->host ; *s ; s++) {
        if ((s[0] == '.') && (s[1] == '.')) {
            ml_errmsg_printf("Invalid .. sequence in address: %s@%s",
                             a->mailbox, a->host);
            return (NIL);
        }

        if (*s == '_') {
            ml_errmsg_printf("Invalid '_' in address: %s@%s",
                             a->mailbox, a->host);
            return (NIL);
        }

        if (s[1] != '\0')
            continue;

        /* Test trailing character */
        if (s[0] == '.') {
            ml_errmsg_printf("Invalid trailing '.' in address: %s@%s",
                             a->mailbox, a->host);
            return (NIL);
        }
        if (s[0] == '\'') {
            ml_errmsg_printf("Invalid \' character in address: %s@%s",
                             a->mailbox, a->host);
            return (NIL);
        }
    }
    return(T);
}

/* addr_parse_destructive() ************************************************
 *
 * Wrapper to c-client rfc822_parse_adrlist() (converts RFC822 address list
 * into parsed ADDRESS format) which does some additional checks. This
 * interface trashs the text parameter.
 *
 * text: Address to parse
 * default_domain: Domain to use on unqualified addresses
 *
 **************************************************************************/

ADDRESS *
addr_parse_destructive(char *text, char *default_domain)
{
    ADDRESS *addr = NIL;
    ADDRESS *a;
    char *s;

    ml_clear_errmsg();
    rfc822_parse_adrlist(&addr, text, default_domain);

    if ((s = ml_errmsg()) && s[0]) {
        mail_free_address(&addr);
        return (NIL);
    }

    if (addr == NIL) {
        ml_set_errmsg("Empty address");
        return (NIL);
    }

    for (a = addr; a; a = a->next) {
        if (!addr_parse_check_single(a)) {
            mail_free_address(&addr);
            return (NIL);
        }
    }
    return(addr);
}

/* addr_parse() ************************************************************
 *
 * Wrapper to c-client rfc822_parse_adrlist() (converts RFC822 address list
 * into parsed ADDRESS format) which does some additional checks. This
 * interface preserves the text parameter.
 *
 * pool: Pool for scratch copy of address
 * text: Address to parse
 * default_domain: Domain to use on unqualified addresses
 *
 **************************************************************************/

ADDRESS *
addr_parse(struct pool *pool, char *text, char *default_domain)
{
    text = pool_strdup(pool, text);  /* Generate Scratch copy */

    return(addr_parse_destructive(text, default_domain));
}

/* addr_check_valid() ******************************************************
 *
 * Check whether text corresponds to valid ADDRESS (+ additional checks).
 *
 * pool: Pool for scratch copy of address
 * text: Address to parse
 *
 **************************************************************************/

BOOL
addr_check_valid(struct pool *pool, char *text)
{
    ADDRESS *addr;

    text = pool_strdup(pool, text);  /* Generate Scratch copy */

    if (!(addr = addr_parse_destructive(text, "")))
        return(NIL);

    mail_free_address(&addr);
    return(T);
}
