
/*
 * addrsbr.c -- parse addresses 822-style
 *
 * $Id$
 */

#include <h/mh.h>
#include <h/addrsbr.h>
#include <zotnet/mf/mf.h>

/* High level parsing of addresses:

   The routines in zotnet/mf/mf.c parse the syntactic representations of
   addresses.  The routines in sbr/addrsbr.c associate semantics with those
   addresses.  

   If #ifdef DUMB is in effect, a full 822-style parser is called
   for syntax recongition.  This breaks each address into its components.
   Note however that no semantics are assumed about the parts or their
   totality.  This means that implicit hostnames aren't made explicit,
   and explicit hostnames aren't expanded to their "official" represenations.

   If DUMB is not in effect, then this module does some
   high-level thinking about what the addresses are.

   1. for MMDF systems:

	string%<uucp>@<local>	->	string

   2. for non-MMDF systems:

	string@host.<uucp>	->	host!string

   3. for any system, an address interpreted relative to the local host:

	string@<uucp>		->	string

   For cases (1) and (3) above, the leftmost host is extracted.  If it's not
   present, the local host is used.  If the tests above fail, the address is
   considered to be a real 822-style address.

   If an explicit host is not present, then MH checks for a bang to indicate
   an explicit UUCP-style address.  If so, this is noted.  If not, the host is
   defaulted, typically to the local host.  The lack of an explict host is
   also noted.

   If an explicit 822-style host is present, then MH checks to see if it
   can expand this to the official name for the host.  If the hostname is
   unknown, the address is so typed.

   To summarize, when we're all done, here's what MH knows about the address:

   DUMB	-	type:	local, uucp, or network
		host:	not locally defaulted, not explicitly expanded
		everything else

   other -	type:	local, uucp, network, unknown
		everything else
 */


static int  ingrp = 0;
static char *pers = NULL;
static char *mbox = NULL;
static char *host = NULL;
static char *route = NULL;
static char *grp = NULL;
static char *note = NULL;
static char err[BUFSIZ];
static char adr[BUFSIZ];

/*
 * external prototypes
 */
char *getusername (void);


char *
getname (char *addrs)
{
    struct adrx *ap;

    pers = mbox = host = route = grp = note = NULL;
    err[0] = '\0';

    if ((ap = getadrx (addrs ? addrs : "")) == NULL)
	return NULL;

    strncpy (adr, ap->text, sizeof(adr));
    pers = ap->pers;
    mbox = ap->mbox;
    host = ap->host;
    route = ap->path;
    grp = ap->grp;
    ingrp = ap->ingrp;
    note = ap->note;
    if (ap->err && *ap->err)
	strncpy (err, ap->err, sizeof(err));

    return adr;
}


struct mailname *
getm (char *str, char *dfhost, int dftype, int wanthost, char *eresult)
{
    char *pp;
    struct mailname *mp;
#ifndef	DUMB
    char *dp;
#endif /* not DUMB */

    if (err && err[0]) {
	if (eresult)
	    strcpy (eresult, err);
	else
	    if (wanthost == AD_HOST)
		admonish (NULL, "bad address '%s' - %s", str, err);
	return NULL;
    }
    if (pers == NULL
	    && mbox == NULL && host == NULL && route == NULL
	    && grp == NULL) {
	if (eresult)
	    strcpy (eresult, "null address");
	else
	    if (wanthost == AD_HOST)
		admonish (NULL, "null address '%s'", str);
	return NULL;
    }
    if (mbox == NULL && grp == NULL) {
	if (eresult)
	    strcpy (eresult, "no mailbox in address");
	else
	    if (wanthost == AD_HOST)
		admonish (NULL, "no mailbox in address '%s'", str);
	return NULL;
    }

    if (dfhost == NULL) {
	dfhost = LocalName ();
	dftype = LOCALHOST;
    }

    mp = (struct mailname *) calloc ((size_t) 1, sizeof(*mp));
    if (mp == NULL) {
	if (eresult)
	   strcpy (eresult, "insufficient memory to represent address");
	else
	    if (wanthost == AD_HOST)
		adios (NULL, "insufficient memory to represent address");
	return NULL;
    }

    mp->m_next = NULL;
    mp->m_text = getcpy (str);
    if (pers)
	mp->m_pers = getcpy (pers);

    if (mbox == NULL) {
	mp->m_type = BADHOST;
	mp->m_nohost = 1;
	mp->m_ingrp = ingrp;
	mp->m_gname = getcpy (grp);
	if (note)
	    mp->m_note = getcpy (note);
	return mp;
    }

    if (host) {
	mp->m_mbox = getcpy (mbox);
	mp->m_host = getcpy (host);
    }
    else {
	if ((pp = strchr(mbox, '!'))) {
	    *pp++ = '\0';
	    mp->m_mbox = getcpy (pp);
	    mp->m_host = getcpy (mbox);
	    mp->m_type = UUCPHOST;
	}
	else {
	    mp->m_nohost = 1;
	    mp->m_mbox = getcpy (mbox);
#ifdef DUMB
	    if (route == NULL && dftype == LOCALHOST) {
		mp->m_host = NULL;
		mp->m_type = dftype;
	    }
	    else
#endif /* DUMB */
	    {
		mp->m_host = route ? NULL : getcpy (dfhost);
		mp->m_type = route ? NETHOST : dftype;
	    }
	}
	goto got_host;
    }

    if (wanthost == AD_NHST)
	mp->m_type = !strcasecmp (LocalName (), mp->m_host)
	    ? LOCALHOST : NETHOST;
#ifdef DUMB
    else
	mp->m_type = strcasecmp (LocalName(), mp->m_host) ?  NETHOST : LOCALHOST;
#else /* not DUMB */
    else
	if (pp = OfficialName (mp->m_host)) {
    got_real_host: ;
	    free (mp->m_host);
	    mp->m_host = getcpy (pp);
	    mp->m_type = strcasecmp (LocalName(), mp->m_host) ? NETHOST : LOCALHOST;
	}
	else {
	    if (dp = strchr(mp->m_host, '.')) {
		*dp = NULL;
		if (pp = OfficialName (mp->m_host))
		    goto got_real_host;
		*dp = '.';
	    }
	    mp->m_type = BADHOST;
	}
#endif /* not DUMB */

got_host: ;
    if (route)
	mp->m_path = getcpy (route);
    mp->m_ingrp = ingrp;
    if (grp)
	mp->m_gname = getcpy (grp);
    if (note)
	mp->m_note = getcpy (note);

    return mp;
}


void
mnfree (struct mailname *mp)
{
    if (!mp)
	return;

    if (mp->m_text)
	free (mp->m_text);
    if (mp->m_pers)
	free (mp->m_pers);
    if (mp->m_mbox)
	free (mp->m_mbox);
    if (mp->m_host)
	free (mp->m_host);
    if (mp->m_path)
	free (mp->m_path);
    if (mp->m_gname)
	free (mp->m_gname);
    if (mp->m_note)
	free (mp->m_note);

    free ((char *) mp);
}


#define empty(s) ((s) ? (s) : "")

char *
auxformat (struct mailname *mp, int extras)
{
    static char addr[BUFSIZ];
    static char buffer[BUFSIZ];

#ifdef DUMB
	if (mp->m_nohost)
	    strncpy (addr, mp->m_mbox ? mp->m_mbox : "", sizeof(addr));
	else
#endif /* DUMB */

#ifndef	BANG
	if (mp->m_type != UUCPHOST)
	    snprintf (addr, sizeof(addr), mp->m_host ? "%s%s@%s" : "%s%s",
		empty(mp->m_path), empty(mp->m_mbox), mp->m_host);
	else
#endif /* not BANG */
	    snprintf (addr, sizeof(addr), "%s!%s", mp->m_host, mp->m_mbox);

    if (!extras)
	return addr;

    if (mp->m_pers || mp->m_path)
	if (mp->m_note)
	    snprintf (buffer, sizeof(buffer), "%s %s <%s>",
		    legal_person (mp->m_pers ? mp->m_pers : mp->m_mbox),
		    mp->m_note, addr);
	else
	    snprintf (buffer, sizeof(buffer), "%s <%s>",
		    legal_person (mp->m_pers ? mp->m_pers : mp->m_mbox),
		    addr);
    else
	if (mp->m_note)
	    snprintf (buffer, sizeof(buffer), "%s %s", addr, mp->m_note);
	else
	    strncpy (buffer, addr, sizeof(buffer));

    return buffer;
}


/*
 * address specific "sprintf"
 */

char *
adrsprintf (char *local, char *domain)
{
    static char addr[BUFSIZ];

    if (local == NULL)
#ifdef REALLYDUMB
	return getusername ();
    else
#endif /* REALLYDUMB */
	local = getusername ();

    if (domain == NULL)
#ifdef REALLYDUMB
	return local;
    else
#endif /* REALLYDUMB */
	domain = LocalName ();

#ifndef	BANG
    snprintf (addr, sizeof(addr), "%s@%s", local, domain);
#else /* BANG */
    snprintf (addr, sizeof(addr), "%s!%s", domain, local);
#endif /* BANG */

    return addr;
}


#define	W_NIL	0x0000
#define	W_MBEG	0x0001
#define	W_MEND	0x0002
#define	W_MBOX	(W_MBEG | W_MEND)
#define	W_HBEG	0x0004
#define	W_HEND	0x0008
#define	W_HOST	(W_HBEG | W_HEND)
#define	WBITS	"\020\01MBEG\02MEND\03HBEG\04HEND"

/*
 * Check if this is my address
 */

int
ismymbox (struct mailname *np)
{
    int oops;
    register int len, i;
    register char *cp;
    register char *pp;
    char buffer[BUFSIZ];
    struct mailname *mp;
    static char *am = NULL;
    static struct mailname mq={NULL};

    /*
     * If this is the first call, initialize
     * list of alternate mailboxes.
     */
    if (am == NULL) {
	mq.m_next = NULL;
	mq.m_mbox = getusername ();
	if ((am = context_find ("alternate-mailboxes")) == NULL)
	    am = getusername();
	else {
	    mp = &mq;
	    oops = 0;
	    while ((cp = getname (am))) {
		if ((mp->m_next = getm (cp, NULL, 0, AD_NAME, NULL)) == NULL) {
		    admonish (NULL, "illegal address: %s", cp);
		    oops++;
		} else {
		    mp = mp->m_next;
		    mp->m_type = W_NIL;
		    if (*mp->m_mbox == '*') {
			mp->m_type |= W_MBEG;
			mp->m_mbox++;
		    }
		    if (*(cp = mp->m_mbox + strlen (mp->m_mbox) - 1) == '*') {
			mp->m_type |= W_MEND;
			*cp = '\0';
		    }
		    if (mp->m_host) {
			if (*mp->m_host == '*') {
			    mp->m_type |= W_HBEG;
			    mp->m_host++;
			}
			if (*(cp = mp->m_host + strlen (mp->m_host) - 1) == '*') {
			    mp->m_type |= W_HEND;
			    *cp = '\0';
			}
		    }
		    if ((cp = getenv ("MHWDEBUG")) && *cp)
			fprintf (stderr, "mbox=\"%s\" host=\"%s\" %s\n",
			    mp->m_mbox, mp->m_host,
			    snprintb (buffer, sizeof(buffer), (unsigned) mp->m_type, WBITS));
		}
	    }
	    if (oops)
		advise (NULL, "please fix the %s: entry in your %s file",
			"alternate-mailboxes", mh_profile);
	}
    }

    if (np == NULL) /* XXX */
	return 0;
    
    switch (np->m_type) {
	case NETHOST:
	    len = strlen (cp = LocalName ());
	    if (!uprf (np->m_host, cp) || np->m_host[len] != '.')
		break;
	    goto local_test;

	case UUCPHOST:
	    if (strcasecmp (np->m_host, SystemName()))
		break;		/* fall */
	case LOCALHOST:
local_test: ;
	    if (!strcasecmp (np->m_mbox, mq.m_mbox))
		return 1;
	    break;

	default:
	    break;
    }

    /*
     * Now scan through list of alternate
     * mailboxes, and check for a match.
     */
    for (mp = &mq; mp->m_next;) {
	mp = mp->m_next;
	if (!np->m_mbox)
	    continue;
	if ((len = strlen (cp = np->m_mbox))
		< (i = strlen (pp = mp->m_mbox)))
	    continue;
	switch (mp->m_type & W_MBOX) {
	    case W_NIL: 
		if (strcasecmp (cp, pp))
		    continue;
		break;
	    case W_MBEG: 
		if (strcasecmp (cp + len - i, pp))
		    continue;
		break;
	    case W_MEND: 
		if (!uprf (cp, pp))
		    continue;
		break;
	    case W_MBEG | W_MEND: 
		if (stringdex (pp, cp) < 0)
		    continue;
		break;
	}

	if (mp->m_nohost)
	    return 1;
	if (np->m_host == NULL)
	    continue;
	if ((len = strlen (cp = np->m_host))
		< (i = strlen (pp = mp->m_host)))
	    continue;
	switch (mp->m_type & W_HOST) {
	    case W_NIL: 
		if (strcasecmp (cp, pp))
		    continue;
		break;
	    case W_HBEG: 
		if (strcasecmp (cp + len - i, pp))
		    continue;
		break;
	    case W_HEND: 
		if (!uprf (cp, pp))
		    continue;
		break;
	    case W_HBEG | W_HEND: 
		if (stringdex (pp, cp) < 0)
		    continue;
		break;
	}
	return 1;
    }

    return 0;
}
