#ifndef lint
static char *RCSid = "$Header: /home/jsellens/src/users/biffer/RCS/doprog.c,v 1.14 1996/02/14 17:39:47 rbutterworth Exp $";
#endif

#if 0
#include <mfcf/libc/errno.h>
#include <mfcf/libc/stdlib.h>
#include <mfcf/libc/stdio.h>
#include <mfcf/libc/signal.h>
#include <mfcf/libc/unistd.h>
#endif

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>


#include "biffer.h"
#include "bits.h"
#include <syslog.h>
#include <string.h>
#include <setjmp.h>

static jmp_buf timeout;

	static SignalType
wakeup(signum)
{
    longjmp( timeout, 1 );
}



/* return -1 on detectable failure i.e. fork() fails, 0 otherwise */

int
doprog( opt, isnetwork, termtype, terminal, rhost, prog, uid )
biffopt *opt;
int isnetwork;
char *termtype;
char *terminal;
char *rhost;
char *prog;
int uid;
{
    FILE *term;
    char *p;

    /* Set up our execution environment for the coming popen() */
    switch( fork() ){
    case -1:
	/* avoid ugly complaints if you get too much mail */
	if ( errno != EAGAIN )
	    eprintf( "couldn't fork(): %s", strerror(errno) );
	return( -1 );		/* caller tries to do tty style */
    case 0:
	break;			/* child goes on to exec program */
    default:
	return( 0 );		/* parent goes back for more */
    }
    setpgid(0,getpid());	/* so kill(0,) works below */
    /*
     * I'm about to exec a program written by "uid", so I
     * want to run this process as that user, not as me.
     * But unless biffer was called from root, I can't setuid().
     * So this biff will fail for non-root uses of biffer.
     * Maybe biffer should run setuid root?  -IAN!
     */
    if( setuid(uid) == -1 )
	exit(1);
    if( chdir(home) == -1 )
	exit(1);

    if( termtype && termtype[0] != '\0' )
	setenv("BIFFER_TERM",termtype,1);
    if( terminal && terminal[0] != '\0' )
	setenv("BIFFER_TTY",terminal,1);
    if( rhost && rhost[0] != '\0' )
	setenv("BIFFER_REMOTEHOST",rhost,1);

    /*
     * Parse the header of the message and set environment
     * variables with just the text portion.
     */
#define EQ(str,env,save) \
    if( strncmp(p,str,sizeof(str)-1) == 0 ){\
	register char *endp = strchr(p,'\n');\
	if( endp != NULL )\
		*endp = '\0';\
	strcpy(save,p+sizeof(str)-1);\
	setenv(env,save,1);\
	if( endp != NULL ){\
		*endp = '\n';\
		p = endp + 1;\
	} else\
		p = "";\
	continue;\
    }

    for ( p=msgbuf; *p && *p != '\n'; ) {
	EQ("From: ",    "BIFFER_FROM",    savefromline);
	EQ("To: ",      "BIFFER_TO",      savetoline);
	EQ("Subject: ", "BIFFER_SUBJECT", savesubjline);
	while( *p && *p++ != '\n' )
	    ;
    }

    /* and make sure we have a PATH that we hope is reasonable */
#ifdef ADDPATH
    {
	char pbuf[512];
	char *currp = getenv( "PATH" );	/* should always be set, but */
	if ( currp ) {
	    strcpy( pbuf, currp );
	    strcat( pbuf, ":" );
	    strcat( pbuf, ADDPATH );
	} else
	    strcpy( pbuf, ADDPATH );	/* best we can do, but not enough? */
	(void) setenv( "PATH", pbuf, 1 ); /* failure is no big problem */
    }
#endif
#ifdef ABSPATH
    if ( setenv( "PATH", ABSPATH, 1 ) != 0 )
	exit( 1 );	/* out of memory or something */
#endif
    if ( debugFlag )
	debug( "PATH is '%s'", getenv( "PATH" ) );

    /* and set HOME because some things (notably x stuff that needs
       .Xdefaults and/or .Xauthority) need to know HOME */
    (void) setenv ( "HOME", home, 1 );

    /*
     * Strip leading blank lines from the text.
     */
    while( *p && *p == '\n' )
	++p;

    /* slime warning - we want the child we start to have stdout and stderr
       open, for those few programs that bother to check the returns of
       write, so we open two files, and hope that those are the right ones */
    (void) open( "/dev/null", O_WRONLY );
    (void) open( "/dev/null", O_WRONLY );

    if ( debugFlag )
	debug( "About to popen '%s', uid=(%d,%d) gid=(%d,%d)",
	    prog,  getuid(), geteuid(),  getgid(), getegid());
    /*
     * Write the rest of the text to the user's process.
     * Time out after 5 minutes and SIGTERM us all.
     */
    if ( setjmp( timeout ) == 0 ) {
	(void) signal( SIGALRM, wakeup );
	(void) alarm( 5 * 60 );	/* 5 minute timeout */
	if ( (term=popen(prog,"w")) == NULL ) {
	    auto errnum = errno;
	    /* if we're out of processes, don't complain.  If you get
	       too much mail, this can be the case, but what else
	       can we do? And it's too late to do tty style. */
	    if (errno != EAGAIN)
		eprintf( "couldn't popen '%s': %s", prog, strerror(errnum) );
	    if (debugFlag)
		debug("Couldn't popen \"%s\": %s", prog, strerror(errnum));
	    exit(1);
	}
	if (debugFlag)
	    debug("Popened \"%s\"", prog);
	/*
	 * Print the header lines the user wants into the program.
	 * Then write out the text body into the program.
	 */
	if (debugFlag) {
	    debug("savetoline=\"%s\"", savetoline);
	    debug("savefromline=\"%s\"", savefromline);
	    debug("savesubjline=\"%s\"", savesubjline);
	    debug("p=\"%.10s...\"", p);
	}
#define DO(mask,nam,str) \
	if( ((isnetwork && SHOWNET(mask)) || \
	   (!isnetwork && SHOWNON(mask))) && str[0] != '\0' ) \
		fprintf(term, "%s%s\n", nam, str);
	DO(opt->showtoline,   "To: ",      savetoline);
	DO(opt->showfromline, "From: ",    savefromline);
	DO(opt->showsubjline, "Subject: ", savesubjline);
	if (debugFlag) {
	    if (ferror(term))
		debug("DO failed: %s", strerror(errno));
	}
	errno = 0;
	if (strlen(p) != fwrite(p,1,strlen(p),term) ) {
	    if (debugFlag)
		debug("fwrite failed: %s", strerror(errno));
	}

	{
	    auto int status;
	    status = pclose(term); /* this waits for child */
	    if (debugFlag)
		debug("pclose returned %#x: %s", status, strerror(errno));
	}
    } else {
	if ( debugFlag )
	    debug( "doprog - timeout" );
	kill(0,SIGTERM);	/* signal this process group */
    }
    if (debugFlag)
	debug("done doprog");
    exit(0);
}
