#include "util.h"
#include "mmdf.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
 *
 */

/*              SEND FROM DELIVER OVER TO CALLER (PASSIVE)              */

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

extern struct ll_struct    *logptr;

extern char *mmdflogin;
extern char *supportaddr;

extern char *strdup();
extern char *ap_p2s();

extern int ap_outtype;

LOCVAR char *sender = (char *)NULL;    /* return address for message         */
LOCVAR char *adr    = (char *)NULL;    /* recipient address                  */
LOCVAR struct rp_construct
			    rp_hend  =
{
    RP_NOOP, 'e', 'n', 'd', ' ', 'o', 'f', ' ', 'h', 'o', 's', 't', ' ',
    'i', 'g', 'n', 'o', 'r', 'e', 'd', '\0'
};
LOCVAR struct rp_construct
			    rp_skip =
{
    RP_NOOP, 's', 'k', 'i', 'p', ' ', 't', 'h', 'i', 's', ' ',
    'a', 'd', 'd', 'r', 'e', 's', 's', '\0'
};

LOCVAR Chan *q2p_chan;            /* what channel am I?                 */

LOCVAR int    q2p_uid;            /* user id of caller                  */

LOCVAR unsigned short q2p_nadrs;  /* number of valid addresses          */

LOCVAR char *q2p_info;            /* initialization info for message    */

LOCVAR char q2p_username[USERSIZE + 1];
				  /* name of caller                     */
LOCVAR char q2p_out;              /* at least one adr sent              */
char po_state = SND_RINIT;/* state of processing current msg    */
/**/

qu2po_send (chname)               /* overall mngmt for batch of msgs    */
    char chname[];                /* name of channel we are             */
{
    extern char *strdup ();
    short     result;
    char    info[LINESIZE],
	    sendbuf[ADDRSIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2po_send");
#endif

    q2p_gcinfo ();                /* get info on the caller             */

    q2p_chan = ch_nm2struct (chname);
    if (q2p_chan == (Chan *) NOTOK)
	err_abrt (RP_PARM, "unknown channel name '%s'", chname);
				  /* find out who I am                  */

    if (q2p_chan -> ch_login != 0)  /* only one login may pickup chan     */
    {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "%s =?= %s",
		    q2p_username, q2p_chan -> ch_login);
#endif
	if (!lexequ (q2p_username, q2p_chan -> ch_login) &&
	    !lexequ (q2p_username, mmdflogin))
	{
	    ll_log (logptr, LLOGFAT,
		    "'%s' not authorized to pickup for %s (%s)",
		    q2p_username, q2p_chan -> ch_table -> tb_name, 
		    q2p_chan -> ch_name);
	    return (RP_USER);     /* caller not authorized to pickup    */
	}
    }

    if (rp_isbad (result = qu_pkinit ()))
	return (result);

    if (rp_isbad (result = po_sbinit ()))
	return (result);

    po_state = SND_ABORT;
    for(;;){
	AP_ptr  loctree, domtree, routree, sendtree;
	AP_ptr  ap_s2tree(), ap_normalize();

	result = qu_rinit (info, sendbuf, q2p_chan->ch_apout);
	if(rp_gval(result) == RP_NS){
	    qu_rend();
	    continue;
	}
	else if(rp_gval(result) == RP_DONE)
	    break;

	if (rp_gval(result) == RP_FIO)          /* Can't open message file */
		continue;
	else if (rp_gval(result) != RP_OK)      /* Some other error */
		break;

	q2p_info = strdup (info); /* not sent until an address goes out */

	if (sender != (char *)NULL){
		free (sender);
		sender = NULL;
	}

	if ( sendbuf[0] == '\0' ||
	    (sendtree = ap_s2tree( sendbuf )) == (AP_ptr) NOTOK) {
		printx("return address unparseable, using Orphanage\n");
		fflush(stdout);
		if ((sendtree = ap_s2tree ( supportaddr )) == (AP_ptr) NOTOK) {
		    printx("Orphanage unparseable, using MMDF\n");
		    fflush(stdout);
		    if ((sendtree = ap_s2tree (mmdflogin)) == (AP_ptr) NOTOK) {
			result = RP_PARM;
			qu_rend();
			continue;
		    }
		}
	}
	ap_outtype = q2p_chan -> ch_apout;
	sendtree = ap_normalize (q2p_chan -> ch_lname,
		       q2p_chan  -> ch_ldomain, sendtree, q2p_chan);
	if(sendtree == (AP_ptr)MAYBE){
	    qu_rend();
	    continue;
	}
	ap_t2parts (sendtree, (AP_ptr *)0, (AP_ptr *)0, &loctree, &domtree, &routree);
	sender = ap_p2s ((AP_ptr)0, (AP_ptr)0, loctree, domtree, routree);
	if(sender == (char *)MAYBE){
	    qu_rend();	      
	    continue;
	}
	ap_sqdelete( sendtree, (AP_ptr) 0 );
	ap_free( sendtree );

	po_state = SND_RDADR;
	if (rp_isbad (result = q2p_admng ()))
	    return (result);      /* send the address list              */

	if (rp_gval (result) != RP_DONE)
	    break;                /* catch protocol errors              */

	result = q2p_txmng ();
	free (q2p_info);          /* don't have to zero                 */
	if (rp_isbad (result))
	    return (result);      /* send the message text              */
    }

    if (rp_gval (result) != RP_DONE)
    {
	ll_log (logptr, LLOGTMP, "not DONE (%s)", rp_valstr (result));
	return (RP_RPLY);         /* catch protocol errors              */
    }

    po_state = SND_ABORT;
    qu_pkend ();                  /* done getting messages              */
    return (po_sbend ());         /* done sending messages              */
}
/**/

LOCFUN
	q2p_admng ()              /* send address list                  */
{
    struct rp_bufstruct thereply;
    short     result;
    int       len;
    char    host[ADDRSIZE];
    char    adrbuf[ADDRSIZE];

    AP_ptr  loctree, domtree, routree, adrtree;
    AP_ptr  ap_s2tree(), ap_normalize();


#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "q2p_admng");
#endif

    q2p_nadrs = 0;                /* reset for each new messages        */
    q2p_out = FALSE;
    FOREVER                       /* iterate thru list                  */
    {                             /* we already have and adr            */
	po_state = SND_ABORT;
	result = qu_radr (host, adrbuf);
	if (rp_isbad (result))
	    return (result);      /* get address from Deliver           */

	if (rp_gval (result) == RP_HOK)
	{                         /* no-op the sub-list indication      */
	    qu_wrply ((struct rp_bufstruct *) &rp_hend, rp_conlen (rp_hend));
	    continue;
	}

	if (rp_gval (result) == RP_DONE)
	    break;                /* end of address list                */

	po_state = SND_ARPLY;
	if (!q2p_mayadr (host))
	{                         /* not authorized to go out           */
	    qu_wrply ((struct rp_bufstruct *) &rp_skip, rp_conlen (rp_skip));
	    continue;
	}

	if (!q2p_out)             /* first one to go out                */
	{
	    q2p_out = TRUE;
	    if (rp_isbad (result =
			    po_winit ((char *) 0, q2p_info, sender)))
		return (result);
	}

	if (adr != (char *)NULL)
		free (adr);

	if ( (adrtree = ap_s2tree(adrbuf)) == (AP_ptr) NOTOK)  {
		adr = strdup( adrbuf );
	} else {
		ap_outtype = q2p_chan -> ch_apout;
		adrtree = ap_normalize (q2p_chan -> ch_lname,
				q2p_chan  -> ch_ldomain, adrtree, q2p_chan);
		if(adrtree == (AP_ptr)MAYBE)
		    return(RP_NS);
		ap_t2parts (adrtree, (AP_ptr *)0, (AP_ptr *)0, &loctree, &domtree, &routree);
		adr = ap_p2s ((AP_ptr)0, (AP_ptr)0, loctree, domtree, routree);
		if(adr == (char *)MAYBE)
		    return(RP_NS);
		ap_sqdelete( adrtree, (AP_ptr) 0 );
		ap_free( adrtree );
	  }

	if (rp_isbad (result = po_wadr (host, adr)))
	    return (result);      /* give to remote site                */

	if (rp_isbad (result = po_rrply (&thereply, &len)))
	    return (result);      /* how did remote like it?            */

	switch (rp_gval (thereply.rp_val))
	{                         /* was address acceptable?            */
	    case RP_AOK:          /* address ok, text not yet sent      */
		q2p_nadrs++;
		po_state = SND_ABORT;
		qu_wrply (&thereply, len);
		break;

	    case RP_NO:           /* remaining acceptible responses     */
		thereply.rp_val = RP_NDEL;
	    case RP_USER: 
	    case RP_NDEL: 
	    case RP_AGN: 
	    case RP_NOOP: 
		po_state = SND_ABORT;
		qu_wrply (&thereply, len);
		break;

	    default:              /* responses which force abort        */
		if (rp_isbad (thereply.rp_val))
		    return (thereply.rp_val);
		return (RP_RPLY);
	}
    }
    po_state = SND_TRPLY;
    if (q2p_out)                  /* at least one went out              */
	return (po_waend ());    /* tell remote of address list end    */
    else
	return (RP_DONE);
}
/**/

LOCFUN
	q2p_txmng ()              /* send message text                  */
{
    struct rp_bufstruct thereply;
    int     len;
    short   result;
    char    buffer[BUFSIZ];
    static char faktxt[] = "   ";       /* to keep dial happy   */

/*  the main portion handles normal transmission, when there have been
 *  some addresses accepted.  when no addresses have been accepted, then
 *  the text will be ignored by the receiving side.
 */
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "q2p_txmng");
#endif

    if (!q2p_out)
    {                             /* no addresses tried                 */
	ll_log (logptr, LLOGGEN, "no addrs sent");
	qu_wrply ((struct rp_bufstruct *) &rp_skip, rp_conlen (rp_skip));
	return (RP_OK);
    }

    if (q2p_nadrs == 0)           /* no valid addresses                 */
    {                             /* send some text to keep things happy*/
	if (rp_isbad (result = po_wtxt (faktxt, (sizeof faktxt) - 1)))
	    return (result);
	if (rp_gval (result) != RP_OK)
	    return (RP_RPLY);
    }
    else
    {
	qu_rtinit (0L);           /* get ready to read text             */
    	len = sizeof(buffer);
	while ((rp_gval (result = qu_rtxt (buffer, &len))) == RP_OK)
	{
	    if (rp_isbad (result = po_wtxt (buffer, len)))
		return (result);
	    if (rp_gval (result) != RP_OK)
		return (RP_RPLY);
	    len = sizeof(buffer);
	}

	if (rp_isbad (result))
	    return (result);
	if (rp_gval (result) != RP_DONE)
	    return (RP_RPLY);     /* didn't get it all across?          */
    }


    if (rp_isbad (result = po_wtend ()))
	return (result);          /* flag end of message                */

    if (rp_isbad (result = po_rrply (&thereply, &len)))
	return (result);          /* problem getting reply?             */

    switch (rp_gval (thereply.rp_val))
    {                             /* was text acceptable?               */
	case RP_OK: 
	    thereply.rp_val = RP_MOK;
	case RP_MOK:              /* text was accepted                  */
	    qu_wrply (&thereply, len);
	    break;

	case RP_NO:               /* remaining acceptible responses     */
	    thereply.rp_val = RP_NDEL;
	case RP_NDEL: 
	case RP_AGN: 
	case RP_NOOP: 
	    qu_wrply (&thereply, len);
	    break;

	default:                  /* responses which force abort        */
	    if (rp_isbad (thereply.rp_val))
		return (thereply.rp_val);
	    return (RP_RPLY);
    }
    return (thereply.rp_val);     /* just quote remote                  */
}
/**/

LOCFUN
	q2p_gcinfo ()             /* who is the caller?                 */
{
    struct passwd *getpwuid ();
    int   effecid;
    register struct passwd  *pwdptr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "q2p_gcinfo ()");
#endif

    getwho (&q2p_uid, &effecid);
    pwdptr = getpwuid (q2p_uid);

    strcpy (q2p_username, pwdptr -> pw_name);
}
/**/

LOCFUN
	q2p_mayadr (host)         /* may caller get this message?       */
char    host[];                   /* destination host                   */
{
    char    adrline[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "q2p_mayadr");
#endif

/* PICKUP POLICY:   Who may pickup messages?
 *
 *  Two situations will be most common:  pickup net & relay
 *
 *  A "pickup net" is a network of callers who each will have
 *  mail to pickup.  Each caller is a single recipient (machine).
 *  Hence, a "host" equates to a single receiver (machine).  The
 *  hostname, therefore, must equal the name (unix login name) of
 *  the caller.
 *
 *  A "relay" is a single caller, retrieving mail for a set of
 *  hosts.  Here, the hostname has nothing to do with the
 *  caller's id and is part of the address that is passed on.
 *  (For local, arpanet, and pickup-net mail, the hostname part
 *  of the address is not passed on to the recipient.)  In this
 *  case, the id of the authorized caller is hard-wired into a
 *  variable.
 *
 *  The authorization test, for relaying, is done in qu2ph_send's
 *  startup.  The test for pickup-net is done in this routine.
 */

    if (q2p_chan -> ch_login == 0)
    {                             /* login => host, not chan            */
	if (tb_k2val (q2p_chan -> ch_table, TRUE, host, adrline)!=OK)
	{
	    ll_log (logptr, LLOGTMP, "pobox host '%s' not in table!", host);
	    return (FALSE);       /* map name -> address                  */
	}

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "%s =?= %s", adrline, q2p_username);
#endif

	if (!lexequ (adrline, q2p_username))
	    return (FALSE);       /* "address" == this user's id?         */
    }

    return (TRUE);                /* OK to send                           */
}
