/*
 * $Header: /wrl/gen/gwsrc/mail11/d-1.7beta/RCS/smtp-pipe-sendmail.c,v 1.5 1992/04/28 20:57:46 vixie Exp $
 * Copyright (c) 1989 Keith Moore
 */
#include <stdio.h>
#include <varargs.h>
#include <sys/types.h>
#include <ctype.h>
#include "text.h"
#include "smtp.h"
#include "decnet.h"

extern char *UNIX_ErrorMessage();
extern int errno;
extern int sys_nerr;
#ifdef CAN_REDEFINE_SENDMAIL
char *getenv();
#endif

/*
 * speak to an SMTP protocol server via a pair of pipes to sendmail.
 */

static int fd;				/* socket to SMTP server */
static FILE *smtp_in;			/* buffered input stream */
static FILE *smtp_out;			/* buffered output stream */

/*
 * Build a single-line reply message to return to caller.
 */

static
struct smtp_reply *
SMTP_MakeReply (va_alist)
va_dcl
{
    static struct smtp_reply reply;
    va_list args;
    char *format;
    char buf[1000];

    va_start (args);
    TEXT_Erase (&reply.msg);
    format = va_arg (args, char *);
    reply.code =
	((format[0] - '0') * 16 + (format[1] - '0')) * 16 + (format[2] - '0');
    vsprintf (buf, format, args);
    TEXT_Append (&reply.msg, buf, strlen (buf));
    va_end (args);
    return &reply;
}

static
struct smtp_reply *
SMTP_Ok ()
{
    return SMTP_MakeReply ("200 Done.");
}


/*
 * Get a response to the command just issued.
 */
static
struct smtp_reply *
SMTP_GetResponse ()
{
    char c;
    char cont_flag;
    char *ptr;
    char message[1000];
    static struct smtp_reply reply;
    struct text_line *line;

    reply.code = 0;
    TEXT_Erase (&reply.msg);

    do {
	reply.code = 0;
	ptr = message;

	*ptr++ = c = getc (smtp_in);
	if (isdigit (c))
	    reply.code = reply.code * 16 + c - '0';
	*ptr++ = c = getc (smtp_in);
	if (isdigit (c))
	    reply.code = reply.code * 16 + c - '0';
	*ptr++ = c = getc (smtp_in);
	if (isdigit (c))
	    reply.code = reply.code * 16 + c - '0';
	*ptr++ = cont_flag = getc (smtp_in);

	do {
	    c = getc (smtp_in);
	    if (c == '\n') {
		DEBUG_Print ("<<< %.*s\n", ptr - message, message);
		TEXT_Append (&reply.msg, message, ptr - message);
		break;
	    }
	    else if (c == EOF)
		return SMTP_MakeReply ("451 Unexpected EOF on SMTP channel");
	    else if (ptr < message + sizeof message)
		*ptr++ = c;
	} while (1);
    } while (cont_flag == '-');
    return &reply;
}

/*
 * Write a line to the SMTP server.
 * Add the leading dot if necessary.
 */
void
SMTP_WriteLine (buf, length)
char *buf;
int length;
{
    if (length > 0 && toascii(*buf) == '.')
	putc ('.', smtp_out);
    while (length > 0) {
#ifndef	BITS_8_SMTP
	putc (*buf & 0x7f, smtp_out);
#else
	putc (*buf, smtp_out);
#endif
	++buf;
	--length;
    }
    putc ('\n', smtp_out);
    fflush (smtp_out);
}

static
SMTP_Putc (c)
{
#ifndef	BITS_8_SMTP
    putc (c & 0x7f, smtp_out);
#else
    putc (c, smtp_out);
#endif
}

static
SMTP_DEBUG_Putc (c)
{
#ifndef	BITS_8_SMTP
    putc (c & 0x7f, smtp_out);
#else
    putc (c, smtp_out);
#endif
    DEBUG_Putc (c);
}

/*
 * Do a subset of printf for the SMTP channel.  This is intended
 * for use in header generation, etc.  Unlike SMTP_Command, does NOT
 * automatically generate a newline at the end of each line.
 * You have to put \n in the format explicitly.
 *
 * Bug: doesn't check for leading dot.
 */
void
SMTP_Printf (va_alist)
va_dcl
{
    va_list args;
    char *format;
    va_start (args);
    format = va_arg (args, char *);
    vfformat (SMTP_Putc, format, args);
    va_end (args);
}

struct smtp_reply *
SMTP_Open ()
{
    int child_pid;
    int out_pipe[2];
    int in_pipe[2];
    char *smtp_command;

#define PARENT_OUT out_pipe[1]
#define PARENT_IN  in_pipe[0]
#define CHILD_OUT  in_pipe[1]
#define CHILD_IN   out_pipe[0]

#ifdef CAN_REDEFINE_SENDMAIL
    if ((smtp_command = getenv ("SMTP_COMMAND")) == NULL)
#endif
	smtp_command = "/usr/lib/sendmail";

    if (pipe (out_pipe) == EOF)
	return SMTP_MakeReply ("451 SMTP_Open: pipe(out_pipe): %s",
			       UNIX_ErrorMessage ());
    if (pipe (in_pipe) == EOF) {
	close (out_pipe[0]);
	close (out_pipe[1]);
	return SMTP_MakeReply ("451 SMTP_Open: pipe(in_pipe): %s",
			       UNIX_ErrorMessage ());
    }
    if ((smtp_in = fdopen (PARENT_IN, "r")) == NULL ||
	(smtp_out = fdopen (PARENT_OUT, "w")) == NULL) {
	close (in_pipe[0]);
	close (in_pipe[1]);
	close (out_pipe[0]);
	close (out_pipe[1]);
	return SMTP_MakeReply ("451 SMTP_Open: fdopen failed: %s",
			       UNIX_ErrorMessage ());
    }

    if ((child_pid = vfork ())) {
	/* parent */
	if (child_pid < 0) {
	    close (in_pipe[0]);
	    close (in_pipe[1]);
	    close (out_pipe[0]);
	    close (out_pipe[1]);
	    return SMTP_MakeReply ("451 SMTP_Open: vfork failed: %s",
				   UNIX_ErrorMessage ());
	}
	close (CHILD_IN);
	close (CHILD_OUT);
	return SMTP_GetResponse ();
    }
    else {
	/* child */
	extern char **environ;
	static char *child_argv[] = {
	    "sendmail",
	    "-bs",
	    "-oem",
	    NULL
	};

	/*
	 * Set up input and output for child before leaving.
	 * Remember: in_pipe is parent's input and out_pipe is
	 * parent's output.
	 */
	close (PARENT_IN);		/* close parent's input */

	close (1);			/* make stdout = output pipe */
	dup (CHILD_OUT);
	close (CHILD_OUT);

	close (2);			/* make stderr = output pipe */
	dup (1);

	close (PARENT_OUT);		/* close parent's output */

	close (0);			/* make stdin = input pipe */
	dup (CHILD_IN);
	close (CHILD_IN);

	execve (smtp_command, child_argv, environ);
	printf ("451-UTK-MAIL11D Error on DECnet node %s:\n", DECNET_NodeName);
	printf ("451-Cannot exec SMTP server %s\n", smtp_command);
	if (errno < sys_nerr)
	    printf ("451 Message is: %s\n", UNIX_ErrorMessage ());
	else
	    printf ("451 Error number is %d\n", errno);
	exit (1);
    }
}

struct smtp_reply *
SMTP_Close ()
{
    if (fclose (smtp_out) < 0) 
	return SMTP_MakeReply ("451 Error in SMTP_Close: %s",
			       UNIX_ErrorMessage ());
    else {
	fclose (smtp_in);
	return SMTP_MakeReply ("221 Y'all come back now, ya' heah?");
    }
}


struct smtp_reply *
SMTP_Command (va_alist)
va_dcl
{
    va_list args;
    char *format;

    va_start (args);
    format = va_arg (args, char *);
    DEBUG_Print (">>> ");
    vfformat (SMTP_DEBUG_Putc, format, args);
    va_end (args);
    putc ('\n', smtp_out);
    fflush (smtp_out);
    DEBUG_Print ("\n");
    return SMTP_GetResponse ();
}
