/*
 *     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 TO ARPANET MACHINE
 *
 *  Jun 81  D. Crocker    Officially added SRI's header-transform code
 *                        Removed MLFL-related code
 *                        Add HDRMOD conditional compilation for code
 *                        that transforms offnet addresses
 *                        Moved unlink just after reopen, in d2a_send()
 *  Jul 81  D. Crocker    Only call am_cend if txtcpy was successful
 *  Aug 81  D. Crocker    d2a_send() had fclose's w/out fp validity test
 *
 *      -----------------------------------------------------------
 *
 *  Feb 83  Doug Kingston       *** Total Rewrite, Some Fragments Used ***
 *  Nov 83  Steve Kille         Add throughput and UCL changes
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "ap.h"
#include "smtp.h"

extern struct ll_struct   *logptr;
extern Chan *chanptr;
extern char *supportaddr;

extern char *sm_curname;

extern long qu_msglen;
extern long sm_hostid();
extern struct sm_rstruct sm_rp;
extern char *strdup();
extern char *strncpy();
extern char *ap_p2s();
extern char *blt();
extern time_t time();

extern int ap_outtype;

LOCFUN qu2sm_each(), qu2sm_txtcpy();

LOCVAR time_t start_time;
LOCVAR short sm_fromsent;
LOCVAR int nadrs;
LOCVAR char *sender = (char *)NULL;

LOCVAR struct rp_construct
	rp_bdrem =
{
    RP_BHST, 'B', 'a', 'd', ' ', 'r', 'e', 's', 'p', 'o', 'n', 's', 'e',
    ' ', 'f', 'r', 'o', 'm', ' ', 'r', 'e', 'm', 'o', 't', 'e', ' ',
    's', 'i', 't', 'e', '\0'
}          ,
	rp_adr  =
{
    RP_AOK, 'a', 'd', 'd', 'r', 'e', 's', 's', ' ', 'o', 'k', '\0'
},
	rp_gdtxt =
{
    RP_MOK, 't', 'e', 'x', 't', ' ', 's', 'e', 'n', 't', ' ', 'o', 'k', '\0'
},
	rp_noop =
{
    RP_NOOP, 's', 'u', 'b', '-', 'l', 'i', 's', ' ', 'n', 'o', 't', ' ',
    's', 'p', 'e', 'c', 'i', 'a', 'l', '\0'
},
	rp_dhst =
{
    RP_DHST, 'R', 'e', 'm', 'o', 't', 'e', ' ', 'h', 'o', 's', 't', ' ',
    'u', 'n', 'a', 'v', 'a', 'i', 'l', 'a', 'b', 'l', 'e', '\0'
},
	rp_kild =
{
    RP_DHST, 'n', 'o', ' ', 'v', 'a', 'l', 'i', 'd', ' ', 'a', 'd', 'd',
    'r', 'e', 's', 's', 'e', 's', '\0'
};

/**/

qu2sm_send ()             /* overall mngmt for batch of msgs    */
{
    short   result;
    char    info[LINESIZE];
    char    sendbuf[ADDRSIZE];

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

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

    sm_curname = (char *) 0;

    /*
     *  While there are messages to process ...
     */
    for(;;){
	AP_ptr  loctree, domtree, routree, sendtree;
	AP_ptr  ap_s2tree(), ap_normalize();

	result = qu_rinit (info, sendbuf, chanptr -> 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;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "from qu_rinit (%s)", sendbuf);
#endif
	if (sender != (char *)NULL)
		free (sender);

	if ( sendbuf[0] == '\0' ||
	    (sendtree = ap_s2tree( sendbuf )) == (AP_ptr) NOTOK) {
		printx("return address unparseable, using Orphanage\n");
		fflush(stdout);
		sender = strdup (supportaddr);
	} else {
		sendtree = ap_normalize (chanptr -> ch_lname,
				chanptr  -> ch_ldomain, sendtree, chanptr);
		if(sendtree == (AP_ptr)MAYBE){
		    result = RP_NS;
		    goto bugout;
		}
		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){
		    result = RP_NS;
		    goto bugout;
		}
		ap_outtype = chanptr -> ch_apout;
		ap_sqdelete( sendtree, (AP_ptr) 0 );
		ap_free( sendtree );
	}

	if (rp_isbad (result = qu2sm_each ()))
	    break;

	qu_rend();              /* Cleans up after header conversion... */
    }
    qu_rend();

    if (sm_curname != (char *) 0)
	sm_nclose (OK);

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

    qu_pkend ();                  /* done getting messages              */
    return (result);

bugout:
    qu_rend();
    if (sm_curname != (char *) 0)
	sm_nclose (OK);
    qu_pkend ();                  /* done getting messages              */
    return (result);
}
/**/

LOCFUN
	qu2sm_each ()             /* send copy of text per address      */
{
    RP_Buf  thereply;
    short   result;
    short   len;
    char    host[ADDRSIZE];
    char    adr[ADDRSIZE];
    char     *newname;

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

    time (&start_time);

    sm_fromsent = FALSE;
    nadrs = 0;

    /*
     *  For every address in the message ...
     */
    while( 1 ) {
	result = qu_radr (host, adr);
	if (rp_isbad (result))
	    return (result);      /* get address from Deliver           */

/*  check for end of address sub-list.  if channel is relay, then
 *  sub-list reference is not meaningful.  otherwise, different
 *  sub-lists reference different hosts to connect to.
 */
	switch (rp_gval (result))
	{
	    case RP_HOK:          /* end of sub-list */
		if (chanptr -> ch_host != NORELAY)
		{                 /* different host refs => diff dests  */
		    qu_wrply ((RP_Buf *) &rp_noop, sizeof rp_noop);
		    break;
		}                 /* else DROP ON THROUGH */
	    case RP_DONE:         /* end of full address list           */
		if (nadrs == 0)
		{
		    /*
		     *  Theoretically we would break the connection here since
		     *  the next host is a different host, except that it may
		     *  be flaged and the host after that might be this one.
		     *  Basic rule: don't break a connection until you have to.
		     */
		    qu_wrply ((RP_Buf *) &rp_kild, sizeof rp_kild);

		    if (sm_curname != (char *) 0)
		    {
			if (rp_isbad (sm_cmd ("RSET", SM_RTIME)))
			    break;
/********* should be:   if (sm_rp.sm_rval != 250) *******/
			if (sm_rp.sm_rval < 200 || sm_rp.sm_rval > 299)
			    sm_nclose( NOTOK ); /* Give up on it for now */
		    }
		}
		else if (sm_curname != (char *) 0)
		{
		    nadrs = 0;      /* reset, in case this is only a hend */
		    qu2sm_txtcpy(&thereply);
		    ll_log(logptr, LLOGFTR, "txtcpy reply (%o) (%s)",
			    thereply.rp_val, thereply.rp_line);
		    qu_wrply (&thereply, sizeof (thereply.rp_val)
			      + strlen(thereply.rp_line));
		} else {
		    /* Some addrs were send, but connection went bad */
		    qu_wrply((RP_Buf *)&rp_bdrem, sizeof rp_bdrem);
		}
		sm_fromsent = FALSE;
		if (rp_gval (result) == RP_HOK)
		    continue;           /* more to process  */

		return (RP_OK);         /* END for this message */

	    default:            /* actually have an address */
		if(( newname =  (chanptr->ch_host == NORELAY ?
		   host : chanptr->ch_host)) == (char *) 0)
		{
		    qu_wrply ((RP_Buf *)&rp_dhst, sizeof rp_dhst);
		    break;
		}

		if (!lexequ (sm_curname, newname))
		{
		    if (sm_curname != (char *) 0)
			sm_nclose (OK);

		    if( rp_isbad(sm_nopen( newname)))
		    {
			qu_wrply((RP_Buf *)&rp_dhst, sizeof rp_dhst);
			break;
		    }

		}

		if( sm_fromsent == FALSE ) {
		    if (rp_isbad (sm_wfrom (sender))) {
			if (rp_isbad (sm_rpcpy(&thereply, &len)))
			    qu_wrply((RP_Buf *)&rp_bdrem, sizeof rp_bdrem);
			else
			    qu_wrply (&thereply, len);
			break;
		    }
		    sm_fromsent = TRUE;
		}

		if (sm_wto (host, adr) == RP_DHST
		  || rp_isbad (sm_rpcpy (&thereply, &len))) {
		    qu_wrply ((RP_Buf *) &rp_bdrem, sizeof rp_bdrem);
		    break;
		}
		switch (rp_gval (thereply.rp_val))
		{                 /* was address acceptable?            */
		    case RP_AOK:
			nadrs++;
			qu_wrply ((RP_Buf *) &rp_adr, sizeof rp_adr);
			continue;

		    case RP_PARM:
		    case RP_USER:
		    case RP_NO:
			break;    /* report failure and continue        */

		    case RP_RPLY:
			ll_log (logptr, LLOGFAT, "unusual return: (%s)%s",
				rp_valstr (thereply.rp_val), thereply.rp_line);
			break;    /* notify deliver */
		}

		qu_wrply (&thereply, len);
				  /* notify deliver */
	}
    }
    /* NOTREACHED */
}
/**/

LOCFUN
	qu2sm_txtcpy (rp)     /* copy the text of the message       */
RP_Buf *rp;
{
    int       len;
    short     result;
    char      buffer[BUFSIZ];
    long      bytecount;
    time_t    start_txt,
		end_txt,
		time_eff,
		time_txt;

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

    qu_rtinit (0L);
    sm_fromsent = FALSE;
    nadrs = 0;
    if (rp_isbad (sm_cmd ("DATA", SM_DTIME))) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }

    switch( sm_rp.sm_rval ) {
    case 354:
	break;          /* Go ahead and send mail */

    case 500:
    case 501:
    case 503:
    case 554:
	rp->rp_val = RP_NDEL;
	if (sm_rp.sm_rgot)
		strncpy (rp->rp_line, sm_rp.sm_rstr, sm_rp.sm_rlen);
	else
		strcpy (rp->rp_line, "Unknown Problem");
	return;

    case 421:
    case 451:
    default:
	rp->rp_val = RP_AGN;
	if (sm_rp.sm_rgot)
		strncpy (rp->rp_line, sm_rp.sm_rstr, sm_rp.sm_rlen);
	else
		strcpy (rp->rp_line, "Unknown Problem");
	return;
    }

    printx ("sending...:");
    fflush (stdout);

    bytecount = 0;
    time (&start_txt);

    len = sizeof(buffer);
    while ((rp_gval (result = qu_rtxt (buffer, &len))) == RP_OK)
    {
	if (setjmp(timerest)) {
	    break;
	}
	s_alarm (SM_BTIME);
	result = sm_wstm (buffer, len);
	s_alarm (0);
	bytecount = bytecount + len;
	if (rp_isbad (result))
	    break;
	printx (".");
	fflush (stdout);
	if (rp_gval (result) != RP_OK) {
	    blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	    return;
	}
	len = sizeof(buffer);
    }

    bytecount = bytecount +  len;

    if (rp_isbad (result) || rp_gval (result) != RP_DONE) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }

    /* Kludge: the sm_wstm function will make sure the last char is a NL */
    if (setjmp(timerest)) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }
    s_alarm (SM_TTIME);
    result = sm_wstm ((char *)0, 0);    /* MAGIC options */
    s_alarm( 0 );
    if (rp_isbad (result)) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }

    printx (" ");
    if( rp_isbad( sm_cmd( ".", SM_PTIME + (3*nadrs) ))) {
	rp->rp_val = RP_BHST;
	strcpy (rp->rp_line, "Bad response to final dot");
	return;
    }

    time (&end_txt);
    time_txt = end_txt - start_txt;
    time_eff = end_txt - start_time;

    ll_log (logptr, LLOGFST,
		"tpt: data = (%ld) bytes/sec, eff =  (%ld) bytes/sec",
		bytecount / (time_txt ? time_txt : 1), 
		bytecount / (time_eff ? time_eff : 1));


    time (&start_time);

    switch( sm_rp.sm_rval ) {
    case 250:
    case 251:
	blt ((char *)&rp_gdtxt, (char *)rp, sizeof rp_gdtxt);
	return;

    case 552:
    case 554:
	rp->rp_val = RP_NDEL;
	if (sm_rp.sm_rgot)
		strncpy (rp->rp_line, sm_rp.sm_rstr, sm_rp.sm_rlen);
	else
		strcpy (rp->rp_line, "Unknown Problem");
	return;

    case 421:
    case 451:
    case 452:
    default:
	rp->rp_val = RP_AGN;
	if (sm_rp.sm_rgot)
		strncpy (rp->rp_line, sm_rp.sm_rstr, sm_rp.sm_rlen);
	else
		strcpy (rp->rp_line, "Unknown Problem");
	return;
    }
}
