#include "util.h"
#include "mmdf.h"

#include <sys/stat.h>

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

/*  SUBMIT ADDRESS HANDLING                                             */

/*  Apr, 81 Dave Crocker    adr_check able to handle foo<addr> form
 *  Jun, 81 Dave Crocker    adr_check copy test addr, before parsing
 *                          loc_alsrch check for increment to lnk_nadrs
 *                          in case alias entry had no good addresses
 *  Jul 81 Dave Crocker     use separate variable to detect no valid
 *                          alias entries
 *                          fixed alias no-valid-address handling
 *                          lnk_nadrs no longer referenced
 *  May 82 Dave Crocker     adr_ghost use locname, if host field null
 *                          adr_check use chan name as host, if none given
 *                          adr_check added call to loc_gsrch
 *                          loc_g* routines added to use group names as list
 *  Jun 82 Dave Crocker     prevent alias cycling by saving source specs
 *  Aug 82 Dave Crocker     adr_check returns bad user/host distinction
 *  Dec 82 Doug Kingston    fixed error in design of lc_afin(), and lc_gfin().
 *  Feb 83 Doug Kingston    Modified adr_local to recurse through adr_check
 *                          when the local string contains a '%', '.'.
 *  Apr 83 Steve Kille      Add code to transparently deliver between machines
 *                              This uses a table of user to machine matches
 *                              Could use aliases or password instead
 *  May 83 Steve Kille      Change adr_check to handle domain routes
 */

#include <pwd.h>
#include "ch.h"
#include "dm.h"
#include "ap.h"

#define MAXLOOPS 10

extern struct ll_struct *logptr;
extern char *ch_dflnam;
extern char *locname;
extern char *locfullname;
extern char *supportaddr;
extern char *sitesignature;
extern char *locmachine;               /* local machine name           */
extern int mgt_inalias;
extern int ap_outtype;
extern short tracing;

				/* These two names are being retained for
				 * their mnemonic value. */
char *adr_fulldmn;              /* Name of 'full' domain                */
char *adr_fmc;                  /* name of 'full' machie                */
char *adr_orgspec;              /* original mailbox string              */

extern char *blt();
extern char *index();
extern char *rindex();
extern char mgt_dlname[];	/* name of the delay channel */
extern char *ap_p2s();

/***************  (adr_) PROCESS A SINGLE ADDRESS  *****************  */

LOCVAR char adr_gotone;		/* got at least one valid address     */
LOCVAR char adr_level = 0;	/* Level adr_check/adr_local recursion */

adr_check (local, domain, route) /* check & save an address            */
    AP_ptr  local,             /* beginning of local-part */
	    domain,            /* basic domain reference */
	    route;             /* beginning of 733 forward routing */
{
    extern Domain *dm_v2route ();
    Dmn_route dmrt;
    Dmn_route tmpdmrt;
    AP_ptr hptr;               /* 'next' host */
    Chan   *thechan;
    char    tstline[LINESIZE],
	    official[LINESIZE],
	    tmpstr[LINESIZE];
    int    i;
    int    nextchan;
    AP_ptr ap;
    char   *cp = (char *)0;
    int loopcnt = 0;
    int retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "adr_check (loc='%s', dom='%s', rt='%s')",
	  local -> ap_obvalue,
	  domain != (AP_ptr) 0 ? domain -> ap_obvalue : "[NIL]",
	  route != (AP_ptr) 0 ? route -> ap_obvalue : "[NIL]");
#endif

    /* Limit the number of time we loop looking up routes to domains */
    /* (Sort of a cop-out for foo.bar where foo isn't in the channel table */
    for (thechan = (Chan *) NOTOK; ; loopcnt++)
    {
	if (loopcnt > MAXLOOPS)
		return (RP_BHST);
	/*
	 * This if-then-else clause selects the nxt domain to be
	 * evaluated or calls adr_local if there are none left.
	 */
	if (route != (AP_ptr) 0) {	/* have explicit routing info */
	    hptr = route;		/* a list of fields             */
	    FOREVER
	    {
		switch (route -> ap_ptrtype) {
		    case APP_NIL:
		    case APP_NXT:
			route = (AP_ptr) 0;
			break;  /* no more route */

		    case APP_ETC:
			route = route -> ap_chain;
			if(route == (AP_ptr)0)
				break;
			switch (route -> ap_obtype) {
			    case APV_DLIT:
			    case APV_DOMN:
				break;

			    case APV_CMNT:
				continue;

			    default:
				route = (AP_ptr) 0;
				break;  /* no more route */
			}
		}
		break; /* no route */
	    }
	}
	else                    /* just use the primary reference */
	if (domain != (AP_ptr) 0) {	/* domain ref is a single field */
	    hptr = domain;
	    domain = (AP_ptr) 0;
	}
	else {
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "local address");
#endif
	    if(adr_level++ == 0)
		adr_orgspec = NULL;
	    retval = adr_local (local -> ap_obvalue);
	    adr_level--;
	    return (retval);
	}

/*
 *  a single domain reference is evaluated
 */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "testing '%s'", hptr -> ap_obvalue);
#endif

	if (thechan != (Chan *) NOTOK) {
	    /* Last time through was channel reference */
	    /* Check the specified table this time */
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "chan specified, checking '%s'",
		thechan -> ch_show);
#endif
	    /* SEK if lookup fails, may still be domain */
	    /* reference, so fall into code down below */
	    retval = tb_k2val(thechan->ch_table,TRUE,hptr->ap_obvalue, tstline);
	    if(retval == MAYBE)
		return(RP_NS);
	    else if(retval == OK)
	    {
		strcpy (tstline, hptr -> ap_obvalue);
		strcpy (official, tstline); /* no domain */
		goto storeit;
	    }
	}

	if (thechan == (Chan *) NOTOK)
	{
	    if (lexequ (hptr -> ap_obvalue, locname) ||
		lexequ (hptr -> ap_obvalue, adr_fulldmn) ||
		lexequ (hptr -> ap_obvalue, locmachine) ||
		lexequ (hptr -> ap_obvalue, adr_fmc)) {
#ifdef DEBUG
		 ll_log (logptr, LLOGFTR, "loc ref '%s' found", hptr ->
			ap_obvalue);
#endif
				/* SEK shortcut normalised address      */
		 continue;
	    }
	}

	switch ((int)dm_v2route (hptr -> ap_obvalue, official, &dmrt)) {
	    case MAYBE:
		return (RP_NS);
				  /* obtain a host reference            */
	    case OK:              /* 'tis us                            */
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "local domain reference");
#endif
		continue;         /* go to next domain reference        */

	    case NOTOK:		  /* Not a valid hostname */
				  /* SEK: first check for explicit       */
				  /* channel refs (somehow)              */
		if (cp = rindex(hptr -> ap_obvalue, '#')) {
		    *cp = 0;
		    if ((thechan = ch_nm2struct (hptr -> ap_obvalue))
						!= (Chan *) NOTOK) {
#ifdef DEBUG
			ll_log (logptr, LLOGFTR, "explicit chan spec '%s'",
				thechan -> ch_name);
#endif
			cp = 0;
			continue;
		    }
		}
		if ((thechan = ch_nm2struct("badhosts")) != (Chan *)NOTOK) {
		    strcpy (tstline, thechan->ch_host ? thechan->ch_host : "");
		    strcpy (official, hptr -> ap_obvalue);
		    goto storeit;
		}

		/*
		 * Bad name - really not known, and no place to send it.
		 */
		return (RP_BHST);

	    default:
		 if (thechan != (Chan *) NOTOK) {
#ifdef DEBUG
		    ll_log (logptr, LLOGFTR, "chan specified, checking '%s'",
			thechan -> ch_show);
#endif
		    if (dmrt.dm_argc != 1)
			return (RP_BHST);
		    switch(tb_k2val (thechan -> ch_table, TRUE,
				dmrt.dm_argv[0], tstline)) {
		    case MAYBE:
			return (RP_NS);
		    case OK:
			break;
		    default:
			return (RP_BHST);
		    }
		    strcpy (tstline, dmrt.dm_argv[0]);
		    goto storeit;                /* store it */
		}

		for (i = 0; i < (dmrt.dm_argc - 1); i++) {
				/* algorithm - first component in table is */
				/* last component in route, but before     */
				/* route in  address.                      */
				/* Thus take route components from the     */
				/* left, and add to front of explictit     */
				/* route                                   */
		    if (domain == (AP_ptr) 0) {
#ifdef DEBUG
		       ll_log (logptr,LLOGFTR,"Adding local (1) component '%s'",
					dmrt.dm_argv[i]);
#endif
			domain = ap_new (APV_DOMN, dmrt.dm_argv[i]);
		    }
		    else {
#ifdef DEBUG
		       ll_log (logptr, LLOGFTR, "Adding route component 1 '%s'",
					dmrt.dm_argv[i]);
#endif
			ap = ap_new (APV_DOMN, dmrt.dm_argv[i]);
			if (route != (AP_ptr) 0)
			    ap -> ap_ptrtype = APP_ETC;
			ap -> ap_chain = route;
			route = ap;
		    }
		}
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "Checking '%s' in channel tables",
				dmrt.dm_argv [dmrt.dm_argc - 1]);
#endif
		switch ((int)(thechan = ch_h2chan
			  (dmrt.dm_argv [dmrt.dm_argc -1], 1)))
		{
		    case MAYBE:
				/* NS failure */
				return(RP_NS);
		    case OK:      /* 'tis us                            */
				/* SEK first check for self to avoid    */
				/* loops due to references to           */
				/* non-existent local subdomains        */
			if (dmrt.dm_argc > 1)
			   if (lexequ (dmrt.dm_argv [dmrt.dm_argc - 2],
				official))
			    {
				/* Check first to see if previous entry is */
				/* in channel tables, for handling locmachine */
				switch ((int)(thechan = ch_h2chan
				  (dmrt.dm_argv [dmrt.dm_argc -2], 1)))
				{
				    case MAYBE:
					return(RP_NS);
				    case OK:
				    case NOTOK:
#ifdef DEBUG
					ll_log (logptr, LLOGTMP,
					  "Found unknown subdomain '%s'",
					  hptr -> ap_obvalue);
#endif
					return (RP_BHST);

				   default:
					/* Found host ref               */
					strcpy (tstline, dmrt.dm_argv [dmrt.dm_argc  - 2]);
					strcpy (official, tstline);
					/* Now remove first component of */
					/* route                         */
					if (route == (AP_ptr) 0) {
						ap_free (domain);
						domain = (AP_ptr) 0;
					}
					else {
						ap = route;
						route = ap -> ap_chain;
						ap_free (ap);
					}
					goto storeit;
				}
			    }
#ifdef DEBUG
			ll_log (logptr, LLOGFTR, "local host reference");
#endif
			thechan = (Chan *) NOTOK;               /* DPK */
			continue; /* go to next domain reference        */

		    case NOTOK:   /* hmmm, unknown                      */
			switch((int)dm_v2route (dmrt.dm_argv[dmrt.dm_argc -1],
				tmpstr, &tmpdmrt)) {
			case NOTOK:
			    return (RP_BHST);
			case MAYBE:
			    return (RP_NS);
			}
			thechan = (Chan *) NOTOK;
			ap = ap_new (APV_DOMN, dmrt.dm_argv[dmrt.dm_argc -1]);
			if (route != (AP_ptr) 0)
			    ap -> ap_ptrtype = APP_ETC;
			ap -> ap_chain = route;
			route = ap;
			continue;
		    default:
			strcpy (tstline,
				dmrt.dm_argv [dmrt.dm_argc - 1]);
			if (dmrt.dm_argc > 1)
			    strcpy(official, tstline);
				/* make sure official has full domain   */
			goto storeit;
		}
	}
    	/*NOTREACHED*/
    }

/*
 *  validated non-reflexive address is enqueued
 */
storeit:

     if (domain == (AP_ptr) 0)
     {
#ifdef DEBUG
	 ll_log (logptr, LLOGFTR, "Adding local (2) component '%s'", official);
#endif
	 domain = ap_new (APV_DOMN, official);
     } else {
#ifdef BOTHEND
				/* SEK - at this point we pay for not */
				/* ap_normalize earlier, and must clean up */
				/* set thechan to map any looped local refs */
	if(ap_dmnormalize (domain,  thechan) == MAYBE){
		retval = RP_NS;
		goto bugout;
	}

	for(ap = route ; ap != (AP_ptr)0 ; ap = ap->ap_chain){
		switch(ap->ap_obtype){
		    case APV_DOMN:
			if(ap_dmnormalize (ap, thechan) == MAYBE){
				retval = RP_NS;
				goto bugout;
			}
		    case APV_DLIT:
		    case APV_CMNT:
			continue;
		}
		break; /* no more route */
	}
#endif /* BOTHEND */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "Adding route component 2 '%s'", official);
#endif
	ap = ap_new (APV_DOMN, official);
	if (route != (AP_ptr) 0)
	    ap -> ap_ptrtype = APP_ETC;
	ap -> ap_chain = route;
	route = ap;
     }
    /* there should be some code in here to delete spurious extra components
     * in the domain and the route, this can do nasty things on some sites
     */


    cp = ap_p2s ((AP_ptr) 0, (AP_ptr) 0, local, domain, route);
    if(cp == (char *)MAYBE){
	retval = RP_NS;
	goto bugout;
    }

    auth_uinit (cp);
    nextchan = 2;
    retval = RP_OK;

    while (mgt_aok (thechan, tstline, cp, "") == 0) {
	if((thechan = ch_h2chan(tstline, nextchan++)) == (Chan *)NOTOK){
		auth_bad();	/* build an error message */
		retval = RP_NAUTH;
#ifdef DEBUG
		ll_log(logptr, LLOGFTR, "No authorized route");
#endif
		break;
	}
	else if(thechan == (Chan *)MAYBE){
		retval = RP_NS;
		break;
	}
    }

    if(rp_gval(retval) == RP_OK) {
	free(cp);

	/* put the address into a form that can be used */
	i = ap_outtype;                 /* save this setting */
	ap_outtype = thechan -> ch_apout;
	cp = ap_p2s ((AP_ptr) 0, (AP_ptr) 0, local, domain, route);
	ap_outtype = i;
	if(cp != (char *)MAYBE){
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "linking for chan '%s'",thechan -> ch_name);
#endif
	    lnk_adinfo (thechan, tstline, cp);
	    adr_gotone = TRUE;
	    retval = RP_AOK;
	}
	else
	    retval = RP_NS;
    }

    auth_uend ();

bugout:
    if (domain != (AP_ptr) 0)
    {
	ap_sqdelete (domain, (AP_ptr) 0);
	ap_free (domain);
    }
    if (route != (AP_ptr) 0)
    {
	ap_sqdelete (route, (AP_ptr) 0);
	ap_free (route);
    }
    if (cp && cp != (char *)MAYBE)
	free (cp);
    return (retval);
}
/**/

adr_dsubmit(addr)
char	*addr;
{
    Chan    *thechan;
    extern  int mgt_nodelay;
    char    *mgt_dstgen();
    char    *xcp;

    if(mgt_nodelay == TRUE) /* can't use the delay channel */
	return(RP_NS);

    if(addr == (char *)MAYBE)	/* is this check needed ?? */
	return(RP_NS);

    if( (thechan = ch_nm2struct(mgt_dlname)) == (Chan *)NOTOK)
	return(RP_NS);      /* delay channel does not exist */

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "linking for chan '%s'",thechan -> ch_name);
#endif

    lnk_adinfo (thechan, (xcp = mgt_dstgen()), addr);
    free(xcp);
    adr_gotone = TRUE;
    return(RP_DOK);
}

adr_local (local)                       /* process host-less reference */
    char *local;
{
    Chan *thechan;
    char    qualstr[LINESIZE],
	    tmpstr[LINESIZE];
    char    old_gotone;           /* stacked value of adr_gotone        */
    char    *cp;
    int     retval, bypass = 0;

    strcpy (tmpstr, local);

    /*
     *  Important Note:
     *      The order of the following conditions determines
     *      precedence for the separators @, ., %, and !.
     *      This assumes standard (from K&R) evaluation.
     *      If your compiler is non-standard, you're screwed.
     */
    if ( (cp = rindex (tmpstr, '@')) != 0
	  || (cp = rindex (tmpstr, '%')) != 0
	  || (cp = index (tmpstr, '!')) != 0
#ifdef LEFTDOTS
	  || (cp = rindex (tmpstr, '.')) != 0
#endif
	)
				/* SEK handle quoted @, and quoted or   */
				/* unquoted . or %.  If JNT Mail, do not*/
				/* handle ".".  In this case this is    */
				/* only needed for quoted @ and %       */
    {
	AP_ptr  adrtree;
	AP_ptr  loctree, domtree, routree;
	int     rtnval;

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "recursing on local part: '%s'", local);
#endif
	/*
	 *  We recurse after changing the % to an `@` after
	 *  running ap_normalize on the resultant string.
	 */
	if (*cp != '!')
	    *cp = '@';
	else
	{
	    char   tbuf [LINESIZE];
	    *cp++ = '\0';
	    sprintf (tbuf, "%s@%s", cp, tmpstr);
	    strcpy (tmpstr, tbuf);
	}
	if ((adrtree = ap_s2tree (tmpstr)) != (AP_ptr) NOTOK) {
	    if(ap_normalize ((char *) 0, (char *) 0, adrtree, (Chan *) 0) ==
						(AP_ptr)MAYBE){
		ap_sqdelete (adrtree, (AP_ptr) 0);
		ap_free (adrtree);   /* delete full tree */
		return(RP_NS);
	    }
	    ap_t2parts (adrtree, (AP_ptr *)0, (AP_ptr *)0,
			&loctree, &domtree, &routree);
	    rtnval = adr_check( loctree, domtree, routree );  /* RECURSE */
	    ap_sqdelete (adrtree, (AP_ptr) 0);
	    ap_free (adrtree);   /* delete full tree */
	    return( rtnval );
	}
    }

    if (ch_dflnam[0] == '\0') /* no local delivery && no hostname   */
	return (RP_BHST);     /*    => address not legal            */

    if ((thechan = ch_nm2struct (ch_dflnam)) == (Chan *) NOTOK)
    {
	ll_log (logptr, LLOGFTR, "default channel '%s' unknown",
		    ch_dflnam);
	return (RP_BHST);
    }
    strcpy (tmpstr, local);
			    /* local will be untouched version    */
    qualstr[0] = '\0';
    adr_gparm (tmpstr, qualstr);

    if (bypass = (local[0] == '~')) {    /* bypass alias search   */
	strcpy (tmpstr, &tmpstr[1]);
	strcpy (local, &local[1]);      /* need both if qualstr     */
    }

    old_gotone = adr_gotone;
    if ((retval = lc_search (tmpstr, qualstr, bypass)) != RP_NO)
    {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "alias = %s", local);
#endif
	if (adr_gotone)   /* in alias file & has valid addrs    */
	    return (RP_AOK);

	adr_gotone = old_gotone;
	if (retval == RP_NAUTH) {
	    ll_log (logptr, LLOGTMP, "*** No authorized addrs: alias '%s'", tmpstr);
	    return (RP_NAUTH);
	}
	if (retval == RP_NS)
	    return (RP_NS);

	ll_log (logptr, LLOGTMP, "*** No valid addrs: alias '%s'", tmpstr);
	return (RP_USER);
    }
    adr_gotone = old_gotone;

    if (lc_pwsrch (tmpstr))    /* not even a login name.  too bad  */
    {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "local addr='%s',parm='%s'",
		    tmpstr, qualstr);
#endif
	auth_uinit (tmpstr);
	if (qualstr[0] == '\0' && mgt_aok (thechan, "", tmpstr, ""))
	{                       /* try it without the argument      */
	    lnk_adinfo (thechan, "", tmpstr);
				/* acceptable -> insert into list   */
				/* source string NOT used           */
	    adr_gotone = TRUE;
	    auth_uend ();
	    return (RP_AOK);
	}
	else if (mgt_aok (thechan, "", tmpstr, qualstr))
	{                       /* have to check the parm, too      */
	    lnk_adinfo (thechan, "", local);
				/* acceptable -> insert into list   */
				/* note that SOURCE string is used  */
	    adr_gotone = TRUE;
	    auth_uend ();
	    return (RP_AOK);
	}
	auth_bad ();
	auth_uend ();
	ll_log (logptr, LLOGFST, "Local address '%s' not authorized",
			tmpstr);
	return (RP_NAUTH);
    }

    /*
     *  Last chance:  See if we have a forwarding channel
     */
    if( (thechan = ch_nm2struct("badusers")) != (Chan *)NOTOK) {
	if (!mgt_aok (thechan, "", local, ""))
	    return(RP_NO);

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "linking for chan '%s'", thechan -> ch_name);
#endif

	lnk_adinfo (thechan,
	    thechan->ch_host ? thechan->ch_host : "", local);
	adr_gotone = TRUE;
	return(RP_OK);
    }

    ll_log (logptr, LLOGFST, "Unknown user '%s'", tmpstr);

    return (RP_USER);         /* bad ref        */
}
/**/

adr_gparm (buf, to)               /* get local-mailbox parameter        */
    char   *buf;                  /* the buffer holding the text        */
    char   *to;                   /* put parameter into here            */
{
    register char  *strptr;

    for (strptr = buf ;; strptr++)
	 switch (*strptr)
	 {
	    case '\0':
		to[0] = '\0';
		return (FALSE);

	    case '/':
	    case '|':
	    case '=':
		strcpy (to, strptr);
		*strptr = '\0'; /* terminate the mailbox portion */
		return (TRUE);
	 }

     /* NOTREACHED */
}

/**************  (lc_)  LOCAL NAME TABLE SEARCHING  **************** */

extern Alias *al_list;            /* list of the aliases tables */

LOCVAR
struct lc_alstruct               /* previous aposinfo's are stored on  */
{                                 /*    lc_alsrch's stack when         */
				  /*    lc_alsrch needs to recurse     */
    char   *abufpos;              /* current position in alias buf      */
    char    aliasbuf[LINESIZE];   /* address-part of alias entry        */
}                  *lc_cralias;

LOCFUN
lc_afin ()                /* alst_proc input routine for file   */
{                         /* parse already-read line            */
    char    c;

    switch (c = *(lc_cralias->abufpos))
    {
#ifndef LINUX
	case 0377:
#endif /* LINUX */
	case 0:
	    return( 0 );

	case '\n':
	    c = ',';
    }
    lc_cralias->abufpos++;
    return ( c );
}
/**/
lc_search (mbox, qualstr, bypass)
char	*mbox;
char	*qualstr;
int	bypass;
{
    register Alias *alp;
#ifdef DOTFORWARD
    Alias tal;
#endif
    struct lc_alstruct *oldalias, newalias;
    char *oldspec;
    int	oldinalias;
    register int retval;
    int badretval = RP_NO;
    char *badlist = (char *) 0;
    char tmpbuf[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lc_search(%s, %d)", mbox, bypass);
#endif
    if ((qualstr[0] != 0) && (qualstr[0] != '='))
	return(RP_NO);  /* Skip the alias search for user/file, user|prog */

    if (adr_orgspec != NULL)		/* we have been here before ... */
	if(lexequ(mbox, adr_orgspec))	/* this is getting boring ... */
	     return (RP_NO);		/* Pretend we didn't find the alias */

    /*
     *  This could almost use the library routine aliasfetch() except
     *  we need the al_flags information down below (AL_TRUSTED?).
     */
    newalias.abufpos = newalias.aliasbuf;
    for (alp = al_list; alp != (Alias *)0; alp = alp->al_next) {
	if (bypass && !(alp->al_flags & AL_NOBYPASS))
	    continue;
	if ((retval = tb_k2val (alp->al_table, TRUE, mbox,
				newalias.abufpos)) == OK)
	    break;
	if (retval == MAYBE)
	    badretval = RP_NS;	/* NS timeout? */
    }
#ifdef DOTFORWARD
    if (alp == (Alias *)0) {
	if (lc_forward(mbox, newalias.abufpos, bypass, &tal) != OK)
		return (badretval);
	alp = &tal;
	alp->al_flags = AL_TRUSTED;
    }
#else
    if (alp == (Alias *)0)
	return (badretval);
#endif

    strcpy (tmpbuf, mbox);
    strcat (tmpbuf, qualstr);

    if (lnk_adinfo ((Chan *) 0, "", tmpbuf) == OK) {
	adr_gotone = TRUE;
	return (RP_AOK);       /* already did this alias              */
    }

#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "lc_search, newalias '%s'", newalias.abufpos);
#endif
    if (tracing)
	printf("alias: %s => %s\n", mbox, newalias.abufpos);
    if (qualstr[0] != '\0')
    {
	char *p;
				/* Found alias for username.  If username   */
				/* was part of "user=foo", check that alias */
				/* was just a simple username (otherwise    */
				/* alias=foo will make no sense).           */
	if ((index (newalias.aliasbuf, '|') != 0) ||
	    (index (newalias.aliasbuf, '/') != 0) ||
	    (index (newalias.aliasbuf, ',') != 0)) {
	    ll_log (logptr, LLOGTMP, "Illegal to use = in alias '%s:%s'",
			mbox,  newalias.aliasbuf);
	    return (RP_NO);
	}
	if ((p = rindex (newalias.aliasbuf, '@')) == 0)
	    strcat (newalias.aliasbuf, qualstr);
	else {
	    *p++ = '\0';
	    sprintf (tmpbuf, "%s%s@%s", newalias.aliasbuf, qualstr, p);
	    strcpy (newalias.aliasbuf, tmpbuf);
	}
    }

    /* so save previous info on stack     */
    oldspec = adr_orgspec;
    oldalias = lc_cralias;
    oldinalias = mgt_inalias;

    adr_orgspec = mbox;
    lc_cralias = &newalias;  /*   to allow processing new list     */
    mgt_inalias = (alp->al_flags & AL_TRUSTED ? TRUE : FALSE);

    adr_gotone = FALSE;
    if (rp_isbad (retval=alst_proc(lc_afin, FALSE, (int (*)()) 0, &badlist))) {
	char        linebuf[LINESIZE];

	ll_log (logptr, LLOGTMP, "Bad address in alias %s", mbox);
	if (tracing)
	    printf ("Bad address in alias %s\n", mbox);
	sprintf (linebuf, "%s %s", locfullname, sitesignature);
	ml_1adr (NO, NO, linebuf, "Bad address in alias", supportaddr);
	sprintf (linebuf, "Found bad address in alias '%s'.\n", mbox);
	ml_txt (linebuf);
	if (retval == RP_NAUTH)
	    ml_txt ("    (Authorization problem with valid address)\n");
	sprintf (linebuf, "The alias was '%s'.\n\n", newalias.aliasbuf);
	ml_txt (linebuf);
	if (badlist) {
	    sprintf (linebuf, "There were problems with:\n");
	    ml_txt (linebuf);
	    ml_txt (badlist);
	    free (badlist);
	    sprintf (linebuf, 
	"\nThe remaining addresses in the alias were used for submission.\n\n");
	    ml_txt (linebuf);
	}
	if (ml_end(OK) != OK)
	    ll_log (logptr, LLOGFAT, "Can't send to supportaddr");
    }
    adr_orgspec = oldspec;
    lc_cralias = oldalias;   /* pop previous info off stack        */
    mgt_inalias = oldinalias;

    if ((retval == RP_NAUTH) || (retval == RP_NS))
	return (retval);
    return (RP_AOK);
}

lc_pwsrch (name)                 /* search login names (password file) */
char  *name;                      /* search key                         */
{
    extern struct passwd *getpwmid ();
    char namebuf[LINESIZE];
    register int ind;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lc_pwsrch(%s)", name);
#endif


    if (getpwmid (name) != NULL)   /* case-independent name search       */
	return (TRUE);            /*   found an entry                   */

    if ((ind = strindex ("|", name)) >= 0 ||
	(ind = strindex ("/", name)) >= 0   )
    {                             /* piped msg or stored into file      */
	blt (name, namebuf, ind);
	namebuf[ind] = '\0';
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "lc_pwsrch base part (%s)", namebuf);
#endif
	return ((getpwmid (namebuf) != NULL) ? TRUE : FALSE);
    }

    return (FALSE);               /* return failure                     */
}

#ifdef DOTFORWARD
/*
 * See if there's a ~user/.forward file.  If not, return NOTOK.  If so
 * pass that back to our caller in "buf".
 *
 * SECURITY CHECK:  stat() the file and check that it's owned by
 * the user --OR-- owned by one of MMDF's priveledged user-id's.
 * This is a little more lenient than SendMail which only checks
 * the ownership & readability.  But I can see where it might be
 * useful for an administrator to install an alias the luser can't
 * get rid of ...
 *
 * (Our readability check is in the fopen())
 *
 *	-- DSH (suggested by Marc Rouleau <mer6g@virginia.acc.virginia.edu>)
 */
lc_forward(mbox, buf, bypass)
    char *mbox, *buf;
    int bypass;
{
    extern struct passwd *getpwmid();
    struct passwd *pw;
    extern int effecid;
    char dotforward[LINESIZE];
    FILE *fd;
    struct stat sbuf;

    if (bypass)
	return (NOTOK);
    if ((pw = getpwmid(mbox)) == NULL)
	return (NOTOK);
    sprintf (dotforward, "%s/.forward", pw->pw_dir);

    if (stat(dotforward, &sbuf) == -1)
	return(NOTOK);
    if (pw->pw_uid != sbuf.st_uid && !PRIV_USER(sbuf.st_uid)) {
	ll_log (logptr, LLOGFAT,
		"lc_forward forward file (%s) exists but owned by %d, not %d",
		dotforward, sbuf.st_uid, pw->pw_uid);
	return(NOTOK);
    }
    
    if ((fd = fopen(dotforward, "r")) == NULL)
	return (NOTOK);
    *buf='\0';
    fgets(buf, LINESIZE, fd);
    fclose(fd);
    return(OK);
}
#endif
