[net.sources] sendmail support for the SMTP send command

satz@CSL-Vax.ARPA (Greg Satz) (12/07/84)

The following shell archive will extract the necessary diffs and
programs to support sending messages between hosts via the SMTP send
command.  You need to add the following line to your sendmail.cf file:

  Mlocal,	P=/bin/mail, F=rlsDFmn, S=10, R=20, A=mail -d $u
  Mprog,	P=/bin/sh,   F=lsDFMeuP, S=10, R=20, A=sh -c $u
+ Mtty,		P=/usr/local/bin/to, F=rlsn, S=10, R=20, A=to $u, M=5000

It has been working here at Stanford for about a month. Enjoy!

------------------------------------------------------------------------
#! /bin/sh
: This is a shar archive.  Extract with sh, not csh.
echo x - to.c
cat > to.c << '19127!Funky!Stuff!'
/*
 * Send terminal messages locally and over the Internet
 */

#include <sys/types.h>
#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <utmp.h>
#include <pwd.h>
#include <sysexits.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define	MSG_SIZE 5000

struct	sockaddr_in  hisctladdr;
struct	utmp utmp, *utmpp;
struct	stat statb;
char    msg[MSG_SIZE], buffer[BUFSIZ];
char	tpath[] = "/dev/";
char	term[sizeof(tpath)+sizeof(utmp.ut_line)];
int	netfd, msglen;

char	*from,			  /* Remote user@host */
	*mytimestr,		  /* time string */
	*myname,		  /* Local username */
	*mytty,			  /* Local TTY */
	myhost[32];		  /* Local host name */

int	alarmed;
time_t	mytime;

char	*getlogin(), *index(), *ctime(), *malloc(), *ttyname();
int	nothing();
time_t	time();

main (argc, argv)
int argc;
char **argv;
{
    register char *p, *q;
    int ufd;
    char person[80];

    argc--, argv++;
    while (argc > 0 && **argv == '-')
	switch (*++*argv) {

	case 'r':
		argc--, argv++;
		if (argc > 0)
		    from = *argv;
		else
		    goto usage;
		argc--, argv++;
		break;

	case NULL:
	case 'd':
		argc--, argv++;
		break;			/* we can ignore this */

	default:
		goto usage;
	}

    if (argc < 1) {
usage:
	fprintf (stderr, "usage: to address[,...,address] [message]\n");
	exit (EX_USAGE);
    }
    signal(SIGALRM, nothing);
    if (stat("/etc/utmp", &statb) < 0) {
	perror("to: /etc/utmp");
	exit(EX_OSFILE);
    }
    utmpp = (struct utmp *) malloc((unsigned)statb.st_size);
    if ((ufd = open("/etc/utmp", 0)) < 0) {
	perror("to: /etc/utmp");
	exit(EX_OSFILE);
    }
    read(ufd, (char *)utmpp, (unsigned) statb.st_size);
    close(ufd);
    if ((myname = getlogin()) == NULL)
	myname =  "UNKNOWN";
    if ((mytty = ttyname(0)) != NULL)
	mytty += 5;
    mytime = time((time_t *) 0);
    mytimestr = ctime(&mytime);
    gethostname(myhost, sizeof myhost);
    if (argc == 1) {
	fputs ("Msg:\n", stdout);
	p = msg;
	while (p < &msg[MSG_SIZE] && gets(p) != NULL) {
	    p += strlen(p);
	    *p++ = '\r'; *p++ = '\n';
	}
	msglen = p - msg;
	p = *argv;
    } else {
	p = *argv;
	q = msg;
	while (--argc && q < &msg[MSG_SIZE]) {
	    strcpy(q, *++argv);
	    q += strlen(*argv);
	    *q++ = ' ';
	}
	*q++ = '\r';
	*q++ = '\n';
	msglen = q - msg;
    }
    for (; *p;) {
	for(q = person; *p && *p != ',';)
		*q++ = *p++;
	*q = '\0';
	if ((q = index(person, '@')) != NULL) {
	    *q++ = '\0';
	    netsend(person, q);
	} else
	    if (!locsend(person))	/* user not logged in */
		exit(EX_NOUSER);
    }
    exit(EX_OK);
}

/*
 * Send a message to a remote user
 */
netsend (person, host)
char *person, *host;
{
    register int n;
    struct servent *sp;
    struct hostent *hp;

#ifdef DEBUG
    printf("net user = %s@%s\n",person, host);
#endif
    hp = gethostbyname(host);
    if (hp == NULL) {
	fprintf(stderr, "to: %s: no such host\n", host);
	exit(EX_NOHOST);
    }
    sp = getservbyname("smtp", "tcp");
    if (sp == NULL) {
	fprintf(stderr, "to: smtp/tcp: service not found\n");
	exit(EX_UNAVAILABLE);
    }
    if ((netfd = socket(hp->h_addrtype, SOCK_STREAM, 0, 0)) < 0) {
	perror("to: socket");
	exit(EX_UNAVAILABLE);
    }
    if (bind(netfd, &hisctladdr, sizeof hisctladdr, 0) < 0) {
	perror("to: bind");
	exit(EX_UNAVAILABLE);
    }
    bcopy(hp->h_addr, (caddr_t)&hisctladdr.sin_addr, hp->h_length);
    hisctladdr.sin_family = hp->h_addrtype;
    hisctladdr.sin_port = sp->s_port;
    if (connect(netfd, &hisctladdr, sizeof hisctladdr, 0) < 0) {
        perror("to: connect");
	exit (EX_UNAVAILABLE);
    }
    n = getrply ();
    if (n != 220)		/* Got the site */
	goto error;
    sprintf (buffer, "HELO %s\r\n",myhost);
#ifdef DEBUG
    printf("buffer = %s\n",buffer);
#endif
    write(netfd, buffer, strlen (buffer));
    n = getrply ();
    if (n != 250)
	goto error;
    sprintf (buffer, "SEND FROM:<%s@%s>\r\n", myname, myhost);
#ifdef DEBUG
    printf("buffer = %s\n",buffer);
#endif
    write (netfd, buffer, strlen (buffer));
    n = getrply ();
    if (n != 250)
	goto error;
    sprintf (buffer, "RCPT TO:<%s@%s>\r\n", person, host);
#ifdef DEBUG
    printf("buffer = %s\n",buffer);
#endif
    write (netfd, buffer, strlen (buffer));
    n = getrply ();
    if (n != 250)
	goto error;
#ifdef DEBUG
    printf("buffer = DATA\n");
#endif
    write (netfd, "DATA\r\n", 6);
    n = getrply ();
    if (n != 354)
	goto error;
    write (netfd, msg, msglen);
#ifdef DEBUG
    printf(".\n");
#endif
    write (netfd, ".\r\n", 3);
    n = getrply ();
    if (n != 250)
	goto error;
#ifdef DEBUG
    printf("buffer = QUIT\n");
#endif
    write (netfd, "QUIT\r\n",6);
    n = getrply ();
    if (n != 221 && n != 220)
	goto error;
done:
    disconnect ();
    return;
error:
    fprintf(stderr, "to: network error: %s\n", buffer);
    goto done;
}

/*
 * Send a message to a local user
 */
locsend(person)
char   *person;
{
    char tbuf[MSG_SIZE+BUFSIZ];
    int count, found;
    FILE *tf;
    register struct utmp *up;

    count = statb.st_size / sizeof(struct utmp);
    found = 0;
    for (up = utmpp; up < &utmpp[count]; up++) {
	if (up->ut_name[0] == '\0' || strncmp(person, up->ut_name,
							sizeof(utmp.ut_name)))
	    continue;
	strcpy(term, tpath);
	strncat(term, up->ut_line, sizeof(utmp.ut_line));
	alarmed = 0;
	alarm(3);
	if ((tf = fopen(term, "w")) != NULL) {
	    alarm(0);
	    setbuf(tf, tbuf);
	    fprintf(tf, "\r\n\007%s,", from ? from : myname);
	    if (mytty)
		fprintf(tf, " %s,", mytty);
	    fprintf(tf, " %.7s%.4s%.9s\r\n%s",
		&mytimestr[4],
		&mytimestr[20],
		&mytimestr[10],
		msg);
	    alarm(5);
	    fflush(tf);
	    fclose(tf);
	    alarm(0);
	    if (!alarmed)
		found++;
	}
    }
    return(found);
}

/*
 * Disconnect from SMTP server
 */
disconnect ()
{
    write(netfd, "QUIT\r\n", 6);
    close(netfd);
}

/*
 * Read reply code from SMTP server
 */
getrply ()
{
    char temp[BUFSIZ];
    register int i, n;

    while((i = read(netfd, temp, sizeof temp)) == 0)
	;
    temp[i] = NULL;
#ifdef DEBUG
    printf("temp=\"%s\"\n",temp);
#endif
    for (i = 0, n = 0; temp[i] != NULL; i++)
	if (temp[i] != '\n' && temp[i] != '\r')	{
	    buffer[n++] = temp[i];
	}
    buffer[n] = NULL;
    n = 0;
    for (i = 0; i < strlen (buffer); i++) {
	if (isdigit (buffer[i]))
	    n = (n * 10) + (buffer[i] - '0');
	else
	    break;
    }
#ifdef DEBUG
    printf("n=%d temp=\"%s\"\n",n, temp);
#endif
    return (n);
}

nothing()
{
    alarmed++;
}
19127!Funky!Stuff!
echo x - sendmail.diffs
cat > sendmail.diffs << '19127!Funky!Stuff!'
*** sendmail.h_	Fri Jul  6 18:14:12 1984
--- sendmail.h	Sun Nov  4 23:53:14 1984
***************
*** 129,134
  
  EXTERN MAILER	*LocalMailer;		/* ptr to local mailer */
  EXTERN MAILER	*ProgMailer;		/* ptr to program mailer */
  /*
  **  Header structure.
  **	This structure is used internally to store header items.

--- 129,135 -----
  
  EXTERN MAILER	*LocalMailer;		/* ptr to local mailer */
  EXTERN MAILER	*ProgMailer;		/* ptr to program mailer */
+ EXTERN MAILER	*TTYMailer;		/* ptr to tty mailer */
  /*
  **  Header structure.
  **	This structure is used internally to store header items.
***************
*** 217,222
  #define EF_KEEPQUEUE	000100		/* keep queue files always */
  #define EF_RESPONSE	000200		/* this is an error or return receipt */
  #define EF_RESENT	000400		/* this message is being forwarded */
  
  EXTERN ENVELOPE	*CurEnv;	/* envelope currently being processed */
  /*

--- 218,224 -----
  #define EF_KEEPQUEUE	000100		/* keep queue files always */
  #define EF_RESPONSE	000200		/* this is an error or return receipt */
  #define EF_RESENT	000400		/* this message is being forwarded */
+ #define	EF_TTYSEND	001000		/* this message go to tty mailer */
  
  EXTERN ENVELOPE	*CurEnv;	/* envelope currently being processed */
  /*
*** collect.c_	Fri Jul  6 18:14:45 1984
--- collect.c	Tue Nov  6 12:40:07 1984
***************
*** 181,187
  	*/
  
  	if (hvalue("to") == NULL && hvalue("cc") == NULL &&
! 	    hvalue("bcc") == NULL && hvalue("apparently-to") == NULL)
  	{
  		register ADDRESS *q;
  

--- 181,188 -----
  	*/
  
  	if (hvalue("to") == NULL && hvalue("cc") == NULL &&
! 	    hvalue("bcc") == NULL && hvalue("apparently-to") == NULL &&
! 	    !bitset(EF_TTYSEND, CurEnv->e_flags))
  	{
  		register ADDRESS *q;
  
*** deliver.c_	Sat Jul 28 17:20:41 1984
--- deliver.c	Sun Nov  4 23:53:34 1984
***************
*** 1204,1209
  	{
  		extern int QueueLA;
  
  		if (getla() > QueueLA)
  			mode = SM_QUEUE;
  		else

--- 1204,1212 -----
  	{
  		extern int QueueLA;
  
+ 		if (bitset(EF_TTYSEND, e->e_flags))
+ 			mode = SM_QUICKD;
+ 		else
  		if (getla() > QueueLA)
  			mode = SM_QUEUE;
  		else
*** main.c_	Fri Jul  6 18:14:01 1984
--- main.c	Sun Nov  4 23:52:58 1984
***************
*** 375,381
  	expand("\001j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
  	HostName = jbuf;
  
! 	/* the indices of local and program mailers */
  	st = stab("local", ST_MAILER, ST_FIND);
  	if (st == NULL)
  		syserr("No local mailer defined");

--- 375,381 -----
  	expand("\001j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
  	HostName = jbuf;
  
! 	/* the indices of local and program and tty mailers */
  	st = stab("local", ST_MAILER, ST_FIND);
  	if (st == NULL)
  		syserr("No local mailer defined");
***************
*** 386,391
  		syserr("No prog mailer defined");
  	else
  		ProgMailer = st->s_mailer;
  
  	/* operate in queue directory */
  	if (chdir(QueueDir) < 0)

--- 386,396 -----
  		syserr("No prog mailer defined");
  	else
  		ProgMailer = st->s_mailer;
+ 	st = stab("tty", ST_MAILER, ST_FIND);
+ 	if (st == NULL)
+ 		syserr("No tty mailer defined");
+ 	else
+ 		TTYMailer = st->s_mailer;
  
  	/* operate in queue directory */
  	if (chdir(QueueDir) < 0)
*** recipient.c_	Sun Nov  4 21:01:25 1984
--- recipient.c	Tue Nov  6 13:53:33 1984
***************
*** 180,187
  	stripquotes(buf, TRUE);
  
  	/* do sickly crude mapping for program mailing, etc. */
! 	if (m == LocalMailer && buf[0] == '|')
! 	{
  		a->q_mailer = m = ProgMailer;
  		a->q_user++;
  		if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail)

--- 180,187 -----
  	stripquotes(buf, TRUE);
  
  	/* do sickly crude mapping for program mailing, etc. */
! 	if (m == LocalMailer)
! 	    if (buf[0] == '|') {
  		a->q_mailer = m = ProgMailer;
  		a->q_user++;
  		if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail)
***************
*** 189,195
  			usrerr("Cannot mail directly to programs");
  			a->q_flags |= QDONTSEND;
  		}
! 	}
  
  	/*
  	**  Look up this person in the recipient list.

--- 189,198 -----
  			usrerr("Cannot mail directly to programs");
  			a->q_flags |= QDONTSEND;
  		}
! 	    } else
! 		if (bitset(EF_TTYSEND, CurEnv->e_flags))
! 			a->q_mailer = m = TTYMailer;
! 	
  
  	/*
  	**  Look up this person in the recipient list.
***************
*** 252,258
  	**  the user (which is probably correct anyway).
  	*/
  
! 	if (!bitset(QDONTSEND, a->q_flags) && m == LocalMailer)
  	{
  		struct stat stb;
  		extern bool writable();

--- 255,262 -----
  	**  the user (which is probably correct anyway).
  	*/
  
! 	if (!bitset(QDONTSEND, a->q_flags) && (m == LocalMailer ||
! 					       m == TTYMailer))
  	{
  		struct stat stb;
  		extern bool writable();
*** srvrsmtp.c_	Fri Jul  6 18:45:22 1984
--- srvrsmtp.c	Mon Nov  5 00:05:06 1984
***************
*** 48,53
  # define CMDDBGWIZ	14	/* wiz -- become a wizard */
  # define CMDONEX	15	/* onex -- sending one transaction only */
  # define CMDDBGSHELL	16	/* shell -- give us a shell */
  
  static struct cmd	CmdTab[] =
  {

--- 48,54 -----
  # define CMDDBGWIZ	14	/* wiz -- become a wizard */
  # define CMDONEX	15	/* onex -- sending one transaction only */
  # define CMDDBGSHELL	16	/* shell -- give us a shell */
+ # define CMDSEND	17	/* send -- designate sender of a send */
  
  static struct cmd	CmdTab[] =
  {
***************
*** 52,57
  static struct cmd	CmdTab[] =
  {
  	"mail",		CMDMAIL,
  	"rcpt",		CMDRCPT,
  	"data",		CMDDATA,
  	"rset",		CMDRSET,

--- 53,59 -----
  static struct cmd	CmdTab[] =
  {
  	"mail",		CMDMAIL,
+ 	"send",		CMDSEND,
  	"rcpt",		CMDRCPT,
  	"data",		CMDDATA,
  	"rset",		CMDRSET,
***************
*** 184,189
  				HostName, p);
  			break;
  
  		  case CMDMAIL:		/* mail -- designate sender */
  			/* force a sending host even if no HELO given */
  			if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)

--- 186,192 -----
  				HostName, p);
  			break;
  
+ 		  case CMDSEND:
  		  case CMDMAIL:		/* mail -- designate sender */
  			/* force a sending host even if no HELO given */
  			if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
***************
*** 211,216
  			if (p == NULL)
  				break;
  			setsender(p);
  			if (Errors == 0)
  			{
  				message("250", "Sender ok");

--- 214,221 -----
  			if (p == NULL)
  				break;
  			setsender(p);
+ 			if (c->cmdcode == CMDSEND)
+ 				CurEnv->e_flags |= EF_TTYSEND;
  			if (Errors == 0)
  			{
  				message("250", "Sender ok");
19127!Funky!Stuff!