rs@uunet.UU.NET (Rich Salz) (08/28/87)
Submitted-by: David Herron E-Mail Hack <david@ms.uky.edu> Posting-number: Volume 11, Issue 21 Archive-name: bsmtp BSMTP stands for "Batch Simple Mail Transfer Protocol". It is a form of SMTP for use over a network where you communicate by way of files rather than interactive channels. Both the UUCP network and BITNET are file oriented networks. This protocol was originally developed for BITNET by E. Alan Crosswell of Columbia University, and is documented in "CUVMB BSMTP" which is available from NETSERVE's on BITNET. I've included a copy of CUVMB BSMTP in this distribution; it is in it's original form (complete with fortran carraige control). The idea is to take the same sorts of things the sender says in an SMTP transaction, put it all in a file, and send that to the reciever. I'd sent you a copy of this a few months ago (April?) just before you changed jobs. Apparently it got lost in the shuffle, but no matter as it allowed for a couple of bugs to be fixed. -- David [ This is pitifully late because I seem to be unable to get E-mail to David, sigh. --r$ ] #! /bin/sh : This is a shell archive, meaning: : 1. Remove everything above the '#! /bin/sh' line. : 2. Save the resulting text in a file. : 3. Execute the file with /bin/sh '(not csh)' to create the files: : 'README' : 'CUVMB.BSMTP' : 'Makefile' : 'copyright' : 'ibsmtp.1' : 'ibsmtp.c' : 'sendbsmtp' : This archive created: 'Mon Jul 13 19:10:13 1987' : By: 'David Herron -- Resident E-mail Hack ()' export PATH; PATH=/bin:$PATH echo shar: extracting "'README'" '(5972 characters)' if test -f 'README' then echo shar: will not over-write existing file "'README'" else sed 's/^X//' >'README' <<'SHAR_EOF' X(C) Copyright 1987, X David Herron and the University of Kentucky Computer Science Dept. X XRights are granted to use, distribute, modify, and distribute Xmodifications of this work, provided that you subscribe to Xbsmtp-users@ms.uky.edu and describe there any modifications you Xmake. X--- X XThis package of files contains my BSMTP programs. X XBSMTP stands for "Batch Simple Mail Transfer Protocol". It is a form Xof SMTP for use over a network where you communicate by way of files Xrather than interactive channels. Both the UUCP network and BITNET are Xfile oriented networks. This protocol was originally developed for XBITNET by E. Alan Crosswell of Columbia University, and is documented Xin "CUVMB BSMTP" which is available from NETSERVE's on BITNET. I've Xincluded a copy of CUVMB BSMTP in this distribution; it is in it's Xoriginal form (complete with fortran carraige control). The idea is to Xtake the same sorts of things the sender says in an SMTP transaction, Xput it all in a file, and send that to the reciever. X XExample: X XHELO sending-domain XMAIL FROM: <reverse-path> # <path> is a regular rfc-822 route/addr XRCPT TO: <forward-path> X ... XDATA X ... RFC-822 style message (preferably) ... X. XQUIT X XThe other half of the conversation is to be returned to the sending Xprocess. Assumably the sending process will look through the file and Xsee if there are error codes, and return an error message to the human Xsender if there are any. To aid in this, Crosswell added two commands Xto the SMTP command set, "TICK <id-number>" and "VERBOSE {ON,OFF}". XThe id-number for the TICK command isn't the Message-ID:, but is Xsomething else and should be used to get back the message when an error Xreply comes back. The VERBOSE command controls the verbosity of the Xreply file. X XThe TICK and VERBOSE commands are seen and ignored because we don't Xgenerate the reply file. Instead we just give the message to the Xmail system and let it generate error messages as necessary. This Xis a policy decision that Crosswell saw in a different light than XI saw it. I do not know his reasoning behind deciding to generate Xreply files; possibly to keep the SMTP model as closely as possible; Xpossibly the crudeness of the systems at the time (1982). Nevertheless XI don't see any use for reply files so long as your underlying mail Xsystem is good, and can generate error messages back to the original Xsender as necessary. X XThe other "known difference" is that RFC-821 specifies that a XReturn-Path: header be added to the beginning of the message, and that Xeach host to handle the message add themselves to the beginning of the XReturn-Path:. Back in the dark ages when RFC-821 was written, that was Xprobably a useful thing to do. Nowadays that isn't so useful since Xdomains point exactly at the intended host without need to be able to Xtrace routes back through the network in order to reply. X X X X X XHOW TO USE "ibsmtp" X XRight now I'm only using it for receiving BSMTP from BITNET. I hacked Xon urep until we weren't using simple any more and using ibsmtp instead. X(Basically, the hacking involved changes in profile() in daemon/dafile.c Xand in mailer/damail.c, the sort of changes necessary should be obvious Xonce you've gotten to those sections of the code). X XOn BITNET, BSMTP files arrive in "pure" form. X XIn RFC-976, Mark suggests a different form for using BSMTP. XYou generate a UUCP envelope and RFC-822 envelope for a regular Xmessage headed for b-smtp@some.site.somewhere. The body of Xthis message contains a BSMTP envelope with #'s prepended to Xeach line. I might argue with some of his reasoning in deciding Xthat is the way to do this, however it is the documented method Xand will definitely do the job. X XYet this still leaves us with the problem of using BSMTP. We will Xhave to have some way of knowing who can do BSMTP. We will have Xto come up with a way for generating routes to the sites which Xcan do BSMTP. Also we will have to know what domains the sites Xwho can do BSMTP can service. X XAfter a few minutes thought it seems that we really need to meld this Xin with the map in some way. A possibility is to set a special cost of XBSMTP which isn't really a cost, as in: X Xsite .edu(BSMTP), .gov(BSMTP), ... X Xcould automagically generate a path ".edu ...!site!b-smtp". X XI think that a few people will want to implement bsmtp immediately. X(In fact, if I had someone who wanted to do that I'd be happy go ahead Xand implement it on our uucp side right now). BUT those sites will Xlikely be spread out meaning we'd have to make extra links just to do Xbsmtp over, or have some way to get across the network. To get started Xit will work to have hand generated routes and the like, but if it Xgrows big enough we'll want to have some sort of tools to help out. X X X XWHY YOU WANT TO USE "ibsmtp" X XThe main reason you want to use BSMTP format in your mail is Xto avoid sending addresses through command lines, and the resultant Xmucking that the shell can do. The addresses are hidden in the XBSMTP file which avoids handling by the shell, for most cases. X XThe other reason for using BSMTP is to bring the UUCP world Xcloser to using domains. X X X XFUTURE NEEDS X X1) Need to have this running under uucp systems. X2) Need to fix up an rmail which will handle b-smtp. I think that X it would be better to hack rmail to look at the address to see X if it is b-smtp rather than send it on into the mail system. X Sending it into the mail system will be extra overhead which X could get nasty if this stuff ever gets used a lot. X3) Tools to help us get bsmtp stuff across the net. X4) Also, some way of knowing the right bsmtp capable place to send X things for particular domains to. X5) "obsmtp" to create bsmtp packages. Right now I've got a silly X shell script which can be used. X6) A discussion group somewhere. To get the ball rolling I've created X an alias here "bsmtp-users" and "bsmtp-users-request". The first X is for discussion and the second is for administrivia. X SHAR_EOF if test 5972 -ne "`wc -c < 'README'`" then echo shar: error transmitting "'README'" '(should have been 5972 characters)' fi fi # end of overwriting check echo shar: extracting "'CUVMB.BSMTP'" '(12346 characters)' if test -f 'CUVMB.BSMTP' then echo shar: will not over-write existing file "'CUVMB.BSMTP'" else sed 's/^X//' >'CUVMB.BSMTP' <<'SHAR_EOF' X0 X0 X0 X0 X0 X BATCH SIMPLE MAIL TRANSFER PROTOCOL X+ BATCH SIMPLE MAIL TRANSFER PROTOCOL X+ BATCH SIMPLE MAIL TRANSFER PROTOCOL X0 X0 X0 X0 X0 X0 X0 X0 X0 X0 X E. Alan Crosswell X0 Columbia University Center for Computing Activities X0 X0 X0 X0 X0 X0 X0 Prepared for presentation at BITNET programmers' meeting X0 X 13 September 1982 X1 1 X0 X 1. Acknowledgements X+1. Acknowledgements X+1. Acknowledgements X0 ______ X+ The following paper is based on electronic mail discussions on BITNET and X0original ideas for a Batch SMTP implementation developed by Mike O'Dell on X0_______ X+ARPAnet. Thanks to Greg Minshall for making Mike's ideas known. X0 X02. Introduction X+2. Introduction X+2. Introduction X0 SMTP [1] is an interactive protocol that defines a transaction between X0two mail transport servers. One is defined to be the sender and the other X0the recipient. X0 X 1 2 X Using RSCS , UUCP , or any other store-and-forward transfer system where X0 ____ X+a file is the smallest unit of transmission it is clearly impossible to X0maintain such a full-duplex transaction. In RSCS, only files may be used X0to communicate between service machines and no guarantee can be made of the X0order of arrival of one file with respect to another. Also, communication X0with other than VM systems is required. Systems connected via HASP SML, X0NJE/NJI and other links exist (MVS, Unix, Tops-20, etc.). X0 X0 Why then should SMTP be used? Maybe it shouldn't. However, since ARPA X0supports it, it will be well defined and understood by the largest X0internet. Also, it seems relatively straightforward to create programs X0that will take a Batch SMTP (BSMTP) file and pass it to a SMTP system, X0 X0_______________ X0 1 X IBM VM/370 Remote Spooling Communications Subsystem X 2 X Unix to Unix Communication Program X1 2 X0 X simulating a real SMTP hookup. X0 X03. Batch SMTP operation X+3. Batch SMTP operation X+3. Batch SMTP operation X0 BSMTP uses seperate files for whole sections of a transaction rather than X0each line. The idea is to create a list of commands that would be issued X0assuming all previous commands had received OK replies. This transaction X0log could then be sent to the receiver BSMTP server which reads the file X0and logs its responses. The response log is then returned, etc. X0 X0 An example of an SMTP transaction is in in figure 1 and an example of the X0equivalent BSMTP transaction is in figures 2 and 3 below. X0 S: HELO CUVMB.BITNET ! begin session X R: 250 CUVMA.DECNET ! acknowledgement and ID X S: MAIL FROM:<EACUS@CUVMB.BITNET> ! request to send mail X R: 250 OK ! receiver agrees to accept X S: RCPT TO:<AC2US@CUVMA.BITNET> ! recipient #1 is named X R: 250 OK ! receiver will do it X S: RCPT TO:<FOO@CUVMA.BITNET> ! recipient #2 is named X R: 551 No such user ! denied this one X S: DATA ! ready to send data X R: Start mail input, end with record containing only "." X Date: Monday, 13-Sep-82 10:32 EST X From: Alan Crosswell <EACUS@CUVMB> X Subject: Hello X To: AC2US@CUVMA X0 Hello there.... X . X0 X Figure 1: X+ Figure 1: X+ Figure 1: Sample SMTP transaction X1 3 X0 X HELO CUVMB.BITNET X MAIL FROM:<EACUS@CUVMB.BITNET> X RCPT TO:<AC2US@CUVMA.BITNET> X RCPT TO:<FOO@CUVMA.BITNET> X DATA X Date: Monday, 13-Sep-82 10:32 EST X From: Alan Crosswell <EACUS@CUVMB> X Subject: Hello X To: AC2US@CUVMA X0 Hello there.... X . X QUIT X0 X Figure 2: X+ Figure 2: X+ Figure 2: BSMTP: File from CUVMB to CUVMA X0 X 220 CUVMA.BITNET Batch Simple Mail Transfer Service Ready X 250 CUVMA.BITNET X 250 OK X 250 OK X 551 No such user X 354 Start mail input. End with "." X 250 OK X 221 CUVMA.BITNET Service closing transmission... X0 X Figure 3: X+ Figure 3: X+ Figure 3: BSMTP: File from CUVMA to CUVMB X0 X04. Modifications to SMTP syntax X+4. Modifications to SMTP syntax X+4. Modifications to SMTP syntax X0 How does the server on CUVMB in the preceding example know which file in X0its reader is the reply to which file it sent? There's no way to be sure X0unless some sort of extra identification is imbedded in the files. The X0following proposed additions to the SMTP commands would solve this and the X0problem of coordinating processing of replies: X1 4 X0 X 4.1. Ticket command X+4.1. Ticket command X+4.1. Ticket command X0 X TICK X+ TICK X+ TICK <ticket-id> X0 X ______ X+ The Ticket command is used to mark a file with additional identifying X0information. This would be a unique message indentifier generated by the X0 ______ X+sending BSMTP system. By use of the ticket, the BSMTP sender would be able X0to identify a later BSMTP reply. X0 X 4.2. Verbose command X+4.2. Verbose command X+4.2. Verbose command X0 X X ON X+ ON X+ ON X VERB X+ VERB X+ VERB X OFF X+ OFF X+ OFF X X0 X _______ X+ The Verbose command solves the problem of coordinating BSMTP transactions X0further by specifying that all commands received by the receiver BSMTP X0system be echoed in its reply along with its usual reply codes (VERB ON). X0Data other than that on command lines need not be echoed, so the file X0overhead is not substantial. X0 X 4.3. Advantages X+4.3. Advantages X+4.3. Advantages X0 ______ _______ ______ X+ Use of Ticket and Verbose has several advantages, The ticket provides a X0unique identifier that the sender can use to keep track of the files it has X0shipped. When a reply arrives, the sender simply looks up the information X0 ______ X+it stored when it generated the ticket and uses the information to inform X0any interested parties (the person who sent the mail) of the status of the X0request. X1 5 X0 X _______ X+ The use of verbose takes the burden off the sender to remember and later X0synchronize the commands it sent with the replies it receives. Assuming X0the sender trusts the replying BSMTP system, it has all the information X0needed in the proper order. This information could very easily be "piped" X0into a program that simulates the SMTP full-duplex interaction. X0 X0 A very simple sender implementation would not even have to keep track of X0_______ _______ X+tickets as long as the verbose information is supplied to it. X0 X05. Example of BSMTP operations X+5. Example of BSMTP operations X+5. Example of BSMTP operations X0 A complete example would be as follows: X0 X 1. The CUVMB mailer receives a file from a local user to be sent to X a user at CUVMA. X0 ______ X+ 2. A unique ticket is created and a file is punched to CUVMA. The X ______ X+ ticket is stored for later recall. X0 3. The CUVMA mailer receives the file from CUVMB. X0 4. Mail is sent to the CUVMA user. X0 ________ ______ X+ 5. A verbsose reply file is generated which contains the ticket X supplied by CUVMB and is punched to the CUVMB mailer. X0 6. The CUVMB mailer receives the file. X0 7. The file is recognized as a reply rather than an original file X sent from CUVMA by the presence of reply codes rather than X commands. X0 8. Each code 250 line is read and skipped. X0 9. Each code 050 line (the echoed command) is processed to the X TICK X+ TICK X+ degree desired. In this case, the TICK is found and used to X ______ X+ access the ticket that was previously saved. X0 10. Other code 050 lines and their replies are processed and the X _____ X+ appropriate action is taken (i.e. EACUS is informed that user X ___ X+ FOO was not found). X1 6 X0 X HELO CUVMB.BITNET X VERB ON X TICK 0001 X MAIL FROM:<EACUS@CUVMB.BITNET> X RCPT TO:<AC2US@CUVMA.BITNET> X RCPT TO:<FOO@CUVMA.BITNET> X DATA X Date: Monday, 13-Sep-82 10:32 EST X From: Alan Crosswell <EACUS@CUVMB> X Subject: Hello X To: AC2US@CUVMA X0 Hello there.... X . X QUIT X0 X Figure 4: X+ Figure 4: X+ Figure 4: BSMTP: file generated by sender X0 X 220 CUVMA.BITNET Batch Simple Mail Transfer Service Ready X 250 CUVMA.BITNET X 050 VERB ON X 250 OK X 050 TICK 0001 X 250 OK X 050 MAIL FROM:<EACUS@CUVMB.BITNET> X 250 OK X 050 RCPT TO:<AC2US@CUVMA.BITNET> X 250 OK X 050 RCPT TO:<FOO@CUVMA.BITNET> X 551 No such user X 050 DATA X 354 Start mail input. End with "." X 250 OK X 050 QUIT X 221 CUVMB.BITNET closing transmission... X0 X Figure 5: X+ Figure 5: X+ Figure 5: BSMTP: file returned by recipient X0 X06. Compatibility with SMTP X+6. Compatibility with SMTP X+6. Compatibility with SMTP X0 ______ _______ X+ The Ticket and Verbose commands and 050 reply code are additions on top X0of the current SMTP specification. Assuming that they would be ignored by X0 _______ X+an SMTP process, compatibility is maintained. The Verbose command has even X0been suggested as a debugging aid for SMTP implementers. X1 7 X0 X REFERENCES X+ REFERENCES X+ REFERENCES X0 X [1] Jonathan B. Postel. X ______ ____ ________ ________ X+ Simple Mail Transfer Protocol. X ARPANET Request for Comments, No. 821; Information Sciences X Institute, University of Southern California, 4676 Admirality Way, X Marina del Rey, California 90291, 1982. X1 i X0 X Table of Contents X+Table of Contents X+Table of Contents X0 1. Acknowledgements 1 X 2. Introduction 1 X 3. Batch SMTP operation 2 X 4. Modifications to SMTP syntax 3 X 4.1. Ticket command 4 X 4.2. Verbose command 4 X 4.3. Advantages 4 X 5. Example of BSMTP operations 5 X 6. Compatibility with SMTP 6 X1 ii X0 X List of Figures X+List of Figures X+List of Figures X0 Figure 1: X+ Figure 1: X+ Figure 1: Sample SMTP transaction 2 X Figure 2: X+ Figure 2: X+ Figure 2: BSMTP: File from CUVMB to CUVMA 3 X Figure 3: X+ Figure 3: X+ Figure 3: BSMTP: File from CUVMA to CUVMB 3 X Figure 4: X+ Figure 4: X+ Figure 4: BSMTP: file generated by sender 6 X Figure 5: X+ Figure 5: X+ Figure 5: BSMTP: file returned by recipient 6 SHAR_EOF if test 12346 -ne "`wc -c < 'CUVMB.BSMTP'`" then echo shar: error transmitting "'CUVMB.BSMTP'" '(should have been 12346 characters)' fi fi # end of overwriting check echo shar: extracting "'Makefile'" '(1082 characters)' if test -f 'Makefile' then echo shar: will not over-write existing file "'Makefile'" else sed 's/^X//' >'Makefile' <<'SHAR_EOF' X# Makefile for BSMTP programs... X# X# Author: David Herron <david@ms.uky.edu> X# University of Kentucky, CS Dept. X# Patterson Office Tower, Room 915 X# Lexington, KY 40506-0027 X# X# (C) Copyright 1987, X# David Herron and the University of Kentucky Computer Science Dept. X# X# Rights are granted to use, distribute, modify, and distribute X# modifications of this work, provided that you subscribe to X# bsmtp-users@ms.uky.edu and describe there any modifications you X# make. X# X# Tue Apr 21 15:38:29 EST 1987 X XDESTDIR=/usr/lib/rscs X Xall: ibsmtp X Xinstall: all $(DESTDIR)/ibsmtp X X$(DESTDIR)/ibsmtp: ibsmtp X rm -f $(DESTDIR)/ibsmtp X cp ibsmtp $(DESTDIR)/ibsmtp Xibsmtp: ibsmtp.o X cc ibsmtp.o -o ibsmtp $(LDFLAGS) Xibsmtp.o: ibsmtp.c X cc -c ibsmtp.c $(CFLAGS) X Xnotes: X @echo X Xclean: X rm -f *.o ibsmtp obsmtp a.out core X Xuninstall: X rm -f $(DESTDIR)/ibsmtp X X# "addman" is a local command which basically copies the named X# file to /usr/man/manl ... but takes care of doing it right X# when we're doing it from the "wrong" host and it would fail X# due to the NFS. Xdoc: X addman ibsmtp.1 1 SHAR_EOF if test 1082 -ne "`wc -c < 'Makefile'`" then echo shar: error transmitting "'Makefile'" '(should have been 1082 characters)' fi fi # end of overwriting check echo shar: extracting "'copyright'" '(286 characters)' if test -f 'copyright' then echo shar: will not over-write existing file "'copyright'" else sed 's/^X//' >'copyright' <<'SHAR_EOF' X(C) Copyright 1987, X David Herron and the University of Kentucky Computer Science Dept. X XRights are granted to use, distribute, modify, and distribute Xmodifications of this work, provided that you subscribe to Xbsmtp-users@ms.uky.edu and describe there any modifications you Xmake. SHAR_EOF if test 286 -ne "`wc -c < 'copyright'`" then echo shar: error transmitting "'copyright'" '(should have been 286 characters)' fi fi # end of overwriting check echo shar: extracting "'ibsmtp.1'" '(1724 characters)' if test -f 'ibsmtp.1' then echo shar: will not over-write existing file "'ibsmtp.1'" else sed 's/^X//' >'ibsmtp.1' <<'SHAR_EOF' X.\" (C) Copyright 1987, X.\" David Herron and the University of Kentucky Computer Science Dept. X.\" X.\" Rights are granted to use, distribute, modify, and distribute X.\" modifications of this work, provided that you subscribe to X.\" bsmtp-users@ms.uky.edu and describe there any modifications you X.\" make. X.\" X.TH IBSMTP 8 "April 20, 1987" X.UC 4 X.SH NAME Xibsmtp \- Input BSMTP format mail into the mail system. X.SH SYNOPSIS X.B ibsmtp X[ X.B -c Xmmdf-channel ] [ X.B -d Xfile-name ] [ X.B -D Xour-domain ] [ file ... ] X.br X.SH DESCRIPTION X.B Ibsmtp Xreads the specified files X(or standard input if there are no files given), Xand extracts the RFC-822 style messages embedded in the BSMTP information. XOnce the message (or messages) are extracted, Xit is handed over to the mail system for delivery. X.IP -c XThis specifies which mmdf channel to bring the mail in through. XThis only makes sense for mmdf sites. X.IP -d XThis enables debugging output to the named file. X.IP -D XSpecifies what the domain name of this host is. X.PP XOther arguments are taken to be BSMTP files, Xthe standard input is specified by saying just "-". X.SH "SEE ALSO" Xsendmail(8), recvprog(8), mail(1) X.br XBSMTP is documented in the file "CUVMB BSMTP", Xavailable on the NETSERV's on BITNET. X.SH BUGS XGiving the domain name on the command line is bogus. XWe should look in the environment somewhere, Xbut there is no standard place to look. X.PP XFor MMDF we use recvprog to put the message into the system, Xinstead of the mm_*() routines. XThis is for ease of implementation, and version 2 Xmight use those routines. X.PP XInstead of specifying at compile time what mail system is Xon the host, Xthe program tries all of them in turn and uses the Xfirst one which works. SHAR_EOF if test 1724 -ne "`wc -c < 'ibsmtp.1'`" then echo shar: error transmitting "'ibsmtp.1'" '(should have been 1724 characters)' fi fi # end of overwriting check echo shar: extracting "'ibsmtp.c'" '(16498 characters)' if test -f 'ibsmtp.c' then echo shar: will not over-write existing file "'ibsmtp.c'" else sed 's/^X//' >'ibsmtp.c' <<'SHAR_EOF' Xchar *ProgramId = "(C) Copyright 1987,\ X David Herron and the University of Kentucky Computer Science Dept.\n\ X ibsmtp Version 1.0"; X X/* X * Rights are granted to use, distribute, modify, and distribute X * modifications of this work, provided that you subscribe to X * bsmtp-users@ms.uky.edu and describe there any modifications you X * make. X */ X X/* X * ibsmtp.c -- Input BSMTP style mail messages into the system. X * X * Originally written by David S. Herron, Thu Nov 20 16:47:29 EST 1986. X * X * My E-mail addresses are: X * {BITNET,UUCP}: david@ukma X * CSNET, Internet: david@ms.uky.edu X * X *-------------- X * This is the first official release of my BSMTP programs. They've X * been held up while mod.sources/comp.sources.unix were in flux, X * but that allowed for Sjoerd Mullender <sjoerd@cs.vu.nl> to find X * and fix a couple of bugs. (Thanks muchly)! X * David Herron, Mon Jul 13 19:08:04 EDT 1987 X */ X X#include <sys/types.h> X#include <time.h> X#include <stdio.h> X#include <ctype.h> X#include <sys/wait.h> X X/* X * Path names for the various possible mail daemons. X * If one fails, domail() tries the next one in line. X */ X#define RECVPROG "/usr/mmdf/chans/recvprog" X#define SENDMAIL "/usr/lib/sendmail" X#define BINMAIL "/bin/mail" X Xextern char **environ; X Xchar *ReversePath = NULL; Xchar *Channel = NULL; Xchar *SendingDomain = NULL; Xchar *OurDomain = NULL; X X/* X * C PURISTS BEWARE! This data structure is of a type which usually X * causes confusion. "ForwardPaths" is being used as a list (char *)'s. X * Normally this would be declared as "char *a[size];" but I didn't X * want to leave a limit on the number of ForwardPaths. ForwardPaths X * is manipulated ONLY in addpath(). Note the hook that if nForwardPaths X * is 0 but ForwardPaths points somewhere, that the memory allocated X * to it is free()'d. X */ Xchar **ForwardPaths = (char **)NULL; Xint nForwardPaths = 0; X Xint Errors = 0; /* bumped every time we have an error. X * If we have some, then we exit with X * an error status, and our calling shell X * script will be able to save the output X * for us. X */ X XFILE *dbgout; X X X/*********** First, some utility functions. *****************/ X X X/* X * arpadate() -- make a string saying what time it is as specified X * in RFC-822. Note the year is %'d by 100 because the spec says X * that the year is a 2DIGIT. Won't this cause problems in another X * 13 years or so? X * X * This routine was written on 4.3BSD, but using the SysVr3 manuals X * for reference. In particluar, the 4.3 manuals claim that time() X * returns a long instead of a time_t. Anyway, it works and it should X * work everywhere. X */ Xchar * Xarpadate() X{ X time_t now; X struct tm *tmnow; X static char buf[BUFSIZ]; X extern time_t time(); X static char *days[] = X { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL }; X static char *months[] = X { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", X "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; X X now = time((long *)0); X tmnow = gmtime(&now); X (void) sprintf(buf, "%s, %d %s %d %02d:%02d:%02d GMT", X days[tmnow->tm_wday], tmnow->tm_mday, months[tmnow->tm_mon], X tmnow->tm_year % 100, tmnow->tm_hour, tmnow->tm_min, X tmnow->tm_sec); X return(buf); X} X X/* X * killnl(s) -- kill a newline in s. X */ Xvoid Xkillnl(s) Xchar *s; X{ X char *s2, *index(); X X if ((s2=index(s, '\n')) != NULL) X *s2 = '\0'; X} X X/* X * copystr(s) -- make a copy of the string. X */ Xchar * Xcopystr(s) Xchar *s; X{ X char *rval, *malloc(), *strcpy(); X X if (!s) X return((char *)NULL); X rval = malloc((unsigned)(sizeof(char)*strlen(s)+1)); X return(strcpy(rval, s)); X} X X/* X * copypath(s) -- make a copy of a string, but strip leading '<' X * and trailing '>'. (i.e. suitable for copying <path> out of X * one of the BSMTP command lines. X */ Xchar * Xcopypath(s) Xchar *s; X{ X char *rval, *s2, *index(), *malloc(), *strcpy(); X X if (!s) X return(NULL); X rval = malloc((unsigned)(strlen(s)+1)); X if ((s2=index(s, '<')) != NULL) X (void) strcpy(rval, s2+1); X else X (void) strcpy(rval, s); X if ((s2=index(rval, '>')) != NULL) X *s2 = '\0'; X return(rval); X} X X/* X * stripsp(s) -- Strip out excess spacing from a string. X * (Only from the front and back of the string). X */ Xvoid Xstripsp(s) Xchar *s; X{ X char *outs, *ins; X X if (!s) X return; X outs = s; X ins = s; X while (*ins != '\0') { X if (*ins == '\t') X *ins = ' '; X if (*ins == ' ') { X if (*(ins-1) != ' ' && outs != s) X *outs++ = *ins; X } X else X *outs++ = *ins; X ins++; X } X *outs++ = '\0'; X} X X X/* X * split(s) -- split a character string into its fields. X * X * The return value is a group of pointers into a pair of X * static areas. The first (veclist) is a pointer to the X * base of a vector of pointers which point at various parts X * of the second area (buf). X * X * The upshot of that is: X * X * 1) Don't expect the data to remain after a call to split, X * so be prepared to make a copy of it. X * 2) Be nice and don't muck with split()'s copy. X * X * Oh, one last thing. Notice that the last element in veclist[] X * is marked by being NULL. X */ Xchar ** Xsplit(s) Xchar *s; X{ X static char *buf = (char *)NULL; X static int buflen = 0; X static char **veclist = (char **)NULL; X int nvecs; X int slen; X register char *sp, *lastp, *s2; X char *malloc(), *realloc(), *index(); X X if (!s) X return((char **)NULL); X if (veclist) { X free((char *)veclist); X } X veclist = (char **)malloc((unsigned)(sizeof(char *))); X veclist[0] = (char *)NULL; X nvecs = 1; X stripsp(s); X slen = strlen(s); X if (dbgout) fprintf(dbgout, "Original string \"%s\"\n", s); X if (buflen < (slen+1)) { X buflen = slen+1; X if (buf) X buf = realloc(buf, (unsigned)(sizeof(char)*buflen)); X else X buf = malloc((unsigned)(sizeof(char)*buflen)); X } X (void) strcpy(buf, s); X for (lastp = sp = buf; *sp != '\0'; sp++) { X if (*sp == ' ') { X nvecs++; X veclist = (char **)realloc((char *)veclist, X (unsigned)(sizeof(char *)*nvecs)); X (*(veclist+nvecs-1)) = (char *)NULL; X (*(veclist+nvecs-2)) = lastp; X *sp = '\0'; X if ((s2=index(lastp, '\n')) != NULL) X *s2 = '\0'; X if (dbgout) X fprintf(dbgout, X "Part %d \"%s\"\n", X nvecs-2, veclist[nvecs-2]); X lastp = sp+1; X } X } X nvecs++; X veclist = (char **)realloc((char *)veclist, X (unsigned)(sizeof(char *)*nvecs)); X veclist[nvecs-1] = (char *)NULL; X veclist[nvecs-2] = lastp; X if (dbgout) fprintf(dbgout, "Part %d \"%s\"\n", nvecs-2, lastp); X return(veclist); X} X X/* X * lexequ(s1, s2) -- strcmp of s1 and s2, but ignoring case. X */ Xint Xlexequ(s1, s2) Xregister char *s1, *s2; X{ X register char c1, c2; X X if (dbgout) fprintf(dbgout, "lexequ(\"%s\", \"%s\")\n", s1, s2); X if (!s1 && !s2) X return(0); X if (!s1 && s2) X return(-1); X if (s1 && !s2) X return(1); X while (*s1 && *s2) { X if (islower(*s1)) X c1 = toupper(*s1); X else X c1 = *s1; X if (islower(*s2)) X c2 = toupper(*s2); X else X c2 = *s2; X /* printf("<%c,0%o> == <%c,0%o>?\n", c1, c1, c2, c2); */ X if (c1 != c2) X break; X s1++; X s2++; X } X return(c1 - c2); X} X X/******** Next, specific stuff for bsmtp processing *******/ X X/* X * addpath(s) -- Add address in s to address list. X */ Xvoid Xaddpath(s) Xchar *s; X{ X int i; X char *malloc(), *realloc(); X X if (!s) X return; X if (!ForwardPaths || nForwardPaths==0) { X if (ForwardPaths) { X for (i=0; ForwardPaths[i]; i++) X if (ForwardPaths[i]) X free(ForwardPaths[i]); X free((char *)ForwardPaths); X } X nForwardPaths = 1; X ForwardPaths = (char **)malloc((unsigned)(sizeof(char *))); X ForwardPaths[0] = copypath(s); X if (dbgout) fprintf(dbgout, "addpath(%s, 0)\n", ForwardPaths[0]); X } X else { X nForwardPaths++; X ForwardPaths = (char **)realloc((char *)ForwardPaths, X (unsigned)(sizeof(char *)*nForwardPaths)); X ForwardPaths[nForwardPaths-1] = copypath(s); X if (dbgout) fprintf(dbgout, "addpath(%s, %d)\n", X ForwardPaths[nForwardPaths-1], nForwardPaths-1); X } X} X X/* X * domail(f) -- Process that part of the input stream which is the X * mail message (Between "DATA" and ".") and send it to the addresses X * which have already been saved away. X * X * According to RFC821, lines in the DATA part which should have X * a leading "." have the dot doubled. We need to remove the X * doubled dot here. X */ Xint Xdomail(f) XFILE *f; X{ X FILE *tmpfil; X char fname[40], buf[BUFSIZ], **argv; X int argc, argp, i, pid; X union wait status; X extern char *mktemp(); X X (void) strcpy(fname, "/tmp/bsmtxtXXXXXX"); X (void) mktemp(fname); X (void) close(creat(fname, 0644)); X tmpfil = fopen(fname, "w"); X fprintf(tmpfil, "Received: "); X if (SendingDomain) X fprintf(tmpfil, "from %s ", SendingDomain); X if (OurDomain) X fprintf(tmpfil, "by %s ", OurDomain); X if (ReversePath) X fprintf(tmpfil, "for %s ", ReversePath); X fprintf(tmpfil, "with BSMTP (1.0);\n\t%s\n", arpadate()); X while (fgets(buf, BUFSIZ, f) != NULL) { X if (buf[0] == '.') { X if (buf[1] == '\n' || buf[1] == '\0') X break; X else { X (void) fputs(&(buf[1]), tmpfil); X if (dbgout) (void) fputs(&(buf[1]), dbgout); X } X } X else { X (void) fputs(&(buf[0]), tmpfil); X if (dbgout) (void) fputs(&(buf[0]), dbgout); X } X } X (void) fclose(tmpfil); X if (nForwardPaths == 0) X return(1); X if ((pid=fork()) == 0) { X /* In child process */ X X /* Need to exec a mailer with the file on stdin */ X (void) fclose(stdin); X tmpfil = fopen(fname, "r"); X X/* First try RECVPROG */ X argc = 1 + /* "recvprog" */ X (Channel?2:0) + /* -c Channel */ X (SendingDomain?2:0) + /* -h SendingDomain */ X (ReversePath?2:0) + /* -s ReversePath */ X nForwardPaths; X argv = (char **)malloc((unsigned)(sizeof(char *)*(argc+1))); X argp = 0; X if (dbgout) fputs("recvprog", dbgout); X argv[argp++] = "recvprog"; X if (Channel) { X argv[argp++] = "-c"; X argv[argp++] = Channel; X if (dbgout) fputs(Channel, dbgout); X } X if (SendingDomain) { X argv[argp++] = "-h"; X argv[argp++] = SendingDomain; X if (dbgout) fputs(SendingDomain, dbgout); X } X if (ReversePath) { X argv[argp++] = "-s"; X argv[argp++] = ReversePath; X if (dbgout) fputs(ReversePath, dbgout); X } X for (i=0; i<nForwardPaths; i++) { X argv[argp++] = ForwardPaths[i]; X if (dbgout) fputs(argv[argp-1], dbgout); X } X argv[argp++] = NULL; X if (dbgout) (void) fflush(dbgout); X execve(RECVPROG, argv, environ); X X perror("recvprog failed, trying sendmail"); X X/* Gosh, it didn't work, try SENDMAIL */ X free((char *)argv); X argc = 1 + /* "sendmail" */ X (SendingDomain?1:0) + /* "-ffrom" */ X nForwardPaths; X argv = (char **)malloc((unsigned)(sizeof(char *)*(argc+1))); X argp = 0; X if (dbgout) fputs("sendmail", dbgout); X argv[argp++] = "sendmail"; X if (ReversePath) { X /* We won't need buf below here */ X (void) sprintf(buf, "-f%s", ReversePath); X argv[argp++] = buf; X if (dbgout) fputs(argv[argp-1], dbgout); X } X for (i=0; i<nForwardPaths; i++) { X argv[argp++] = ForwardPaths[i]; X if (dbgout) fputs(argv[argp-1], dbgout); X } X argv[argp++] = NULL; X if (dbgout) (void) fflush(dbgout); X execve(SENDMAIL, argv, environ); X X perror("sendmail failed, trying binmail"); X X/* Gosh, it still didn't work... next try BINMAIL */ X free((char *)argv); X argc = 1 + /* "mail" */ X (ReversePath?2:0) + /* -f from */ X nForwardPaths; X argv = (char **)malloc((unsigned)(sizeof(char *)*(argc+1))); X argp = 0; X if (dbgout) fputs("mail", dbgout); X argv[argp++] = "mail"; X if (ReversePath) { X /* We won't need buf below here */ X argv[argp++] = "-f"; X argv[argp++] = ReversePath; X if (dbgout) fputs(argv[argp-1], dbgout); X } X for (i=0; i<nForwardPaths; i++) { X argv[argp++] = ForwardPaths[i]; X if (dbgout) fputs(argv[argp-1], dbgout); X } X argv[argp++] = NULL; X if (dbgout) (void) fflush(dbgout); X execve(BINMAIL, argv, environ); X X perror("binmail failed, nowhere to turn"); X X /* Gosh, it STILL didn't work, but we tried everything! */ X if (dbgout) fputs("AAAARRRRGGGGHHHH!!!!", dbgout); X exit(1); X } X else { X /* In parent, first order of business is to wait for the child */ X while (wait(&status) != pid) X ; X /* X * Need to do something in case status indicates error. X * Right now, it'll just disappear into thin air. X * Well, not exactly... X */ X (void) unlink(fname); X if (status.w_status != 0) X return(1); X else X return(0); X } X return(1); X} X X X X X/* X * parse(f) -- parse the bsmtp thing on the given file. X */ Xint Xparse(f) XFILE *f; X{ X char **words; X char buf[BUFSIZ]; X Xinit_state: X if (fgets(buf, BUFSIZ, f) == NULL) { X /* early EOF */ X goto seen_QUIT; X } X killnl(buf); X words = split(buf); X if (lexequ("HELO", words[0]) == 0) { X if (dbgout) X fprintf(dbgout, "Howdy %s, how the hell are ya?\n", words[1]); X SendingDomain = copystr(words[1]); X goto seen_HELO; X } X else if (lexequ("NOOP", words[0]) == 0) X goto init_state; X else if (lexequ("RSET", words[0]) == 0) X goto init_state; X else X goto seen_ERROR; X Xseen_HELO: X if (fgets(buf, BUFSIZ, f) == NULL) X goto seen_QUIT; X killnl(buf); X words = split(buf); X if (lexequ("MAIL", words[0]) == 0) { X if (lexequ("FROM:", words[1])) { X /* MAIL FROM: <path> */ X ReversePath = copypath(words[2]); X } X else { X /* MAIL FROM:<path> */ X ReversePath = copypath(index(words[1], ':')); X } X if (dbgout) X fprintf(dbgout, "Oh, you're sending from %s eh?\n", ReversePath); X goto seen_MAIL; X } X else if (lexequ("NOOP", words[0]) == 0) X goto seen_HELO; X else if (lexequ("TICK", words[0]) == 0) X goto seen_HELO; X else if (lexequ("VERB", words[0]) == 0) X goto seen_HELO; X else if (lexequ("QUIT", words[0]) == 0) X goto seen_QUIT; X else if (lexequ("RSET", words[0]) == 0) X goto seen_RSET; X else X goto seen_ERROR; X Xseen_MAIL: X if (fgets(buf, BUFSIZ, f) == NULL) X goto seen_QUIT; X killnl(buf); X words = split(buf); X if (lexequ("RCPT", words[0]) == 0) { X if (lexequ("TO:", words[1])) { X /* RCPT TO: <path> */ X addpath(words[2]); X } X else { X /* RCTP TO:<path> */ X addpath(index(words[1], ':')); X } X goto seen_MAIL; X } X else if (lexequ("DATA", words[0]) == 0) X goto seen_DATA; X else if (lexequ("RSET", words[0]) == 0) X goto seen_RSET; X else if (lexequ("NOOP", words[0]) == 0) X goto seen_MAIL; X else if (lexequ("TICK", words[0]) == 0) X goto seen_MAIL; X else if (lexequ("VERB", words[0]) == 0) X goto seen_MAIL; X else if (lexequ("QUIT", words[0]) == 0) X goto seen_QUIT; X else X goto seen_ERROR; X Xseen_RSET: X if (ReversePath) { X free(ReversePath); X ReversePath = (char *)NULL; X } X nForwardPaths = 0; X goto seen_HELO; X Xseen_DATA: X if (domail(f) != 0) { X if (dbgout) fprintf(dbgout, "oops\n"); X Errors++; X } X goto seen_RSET; X Xseen_QUIT: X return(0); X Xseen_ERROR: X return(1); X} X X X/* X * doparse(s) -- open file named in s and run parse on it. X */ Xvoid Xdoparse(s) Xchar *s; X{ X char fname[BUFSIZ], buf[BUFSIZ]; X FILE *infile; X X if (!s) { X /* X * If the creat() fails then everything else will X * fail and the whole file will be dropped on the X * floor. What's a mother to do? X */ X (void) strcpy(fname, "/tmp/bsinXXXXXX"); X (void) mktemp(fname); X (void) close(creat(fname, 0600)); X infile = fopen(fname, "w"); X while (fgets(buf, BUFSIZ, stdin) != NULL) X fputs(buf, infile); X (void) fclose(infile); X infile = fopen(fname, "r"); X } X else { X infile = fopen(s, "r"); X } X if (infile && parse(infile)) { X Errors++; X } X else { X (void) unlink(fname); X } X} X X/* X * main(argc, argv) -- main program. X */ Xmain(argc, argv) Xint argc; Xchar **argv; X{ X int i; X int didanything = 0; X X Errors = 0; X for (i=1; i<argc; i++) { X if (argv[i][0] == '-') { X switch (argv[i][1]) { X case '\0': /* "-" read stdin */ X if (dbgout) fprintf(dbgout, "doparse(NULL)\n"); X didanything++; X doparse((char *)NULL); X break; X case 'c': /* -c channel specify a channel */ X if (dbgout) X fprintf(dbgout, "Channel = %s\n", argv[i+1]); X Channel = argv[i+1]; X i++; X break; X case 'd': /* -d file enable debugging messages */ X dbgout = fopen(argv[i+1], "w"); X if (!dbgout) perror("opening dbgout"); X i++; X break; X case 'D': /* -D domain specify the local domain name */ X if (dbgout) X fprintf(dbgout, "OurDomain = %s\n", argv[i+1]); X OurDomain = argv[i+1]; X i++; X break; X default: /* eh? */ X if (dbgout) X fprintf(dbgout, "Unknown option %c i=%d\n", X argv[i][1], i); X break; X } X } X else { /* anything else is a file to parse */ X if (dbgout) fprintf(dbgout, "doparse(%s)\n", argv[i]); X didanything++; X doparse(argv[i]); X } X } X if (!didanything) { X /* Specifying no files means to try stdin */ X if (dbgout) fprintf(dbgout, "doparse(stdin)\n"); X didanything++; X doparse((char *)NULL); X } X if (Errors > 0) X exit(1); X else X exit(0); X /*NOTREACHED*/ X} SHAR_EOF if test 16498 -ne "`wc -c < 'ibsmtp.c'`" then echo shar: error transmitting "'ibsmtp.c'" '(should have been 16498 characters)' fi fi # end of overwriting check echo shar: extracting "'sendbsmtp'" '(1458 characters)' if test -f 'sendbsmtp' then echo shar: will not over-write existing file "'sendbsmtp'" else sed 's/^X//' >'sendbsmtp' <<'SHAR_EOF' X#! /bin/sh X# sendbsmtp -- send a bsmtp packet given a destination and file. X# X# USAGE: sendbsmtp mailer dest [ file ] X XPATH=/bin:/usr/bin:/usr/local:/usr/ucb:. Xexport PATH X XFROM=`whoami`@ukma XMAILER=${1} XTO=${2} XFILE=${3} X X# NOTICE!!!! X# X# We're zapping the command line args here, so if you need them X# take care of getting them before now! Xset `date` XDAY=${1} XMONTH=${2} XDATE=${3} XTIME=${4} XTIMEZONE=${5} XYEAR=${6} XARPADATE="${DAY}, ${DATE} ${MONTH} ${YEAR} ${TIME} ${TIMEZONE}" X Xif test -z "${MAILER}" -o -z "${TO}"; then X echo Usage: sendbsmtp mailer dest \[ file \] X echo X echo where, mailer is user@host id of the mailer to send this to, and X echo dest is the address to send the mail to. X exit 1 Xfi X Xif test -z "${FILE}"; then X echo No file name given, stdin used Xfi X X( echo HELO ukma X echo MAIL FROM:\<${FROM}\> X echo RCPT TO:\<${TO}\> X echo DATA X echo "From: " ${FROM} X echo "To: " ${TO} X echo "Date: " ${ARPADATE} X echo -n "Subject: " File sent from ukma.bitnet \(or ms.uky.edu\) " " X if test -z "${FILE}"; then X echo -- Unknown name X else X echo -- ${FILE} X fi X echo " " X if test ! -z "${FILE}"; then X if test -f ${FILE} -a -r ${FILE}; then X # This sed command looks for lines beginning with a '.', X # and adds a '.' to the beginning of the line. This is X # as per the [B]SMTP spec. X sed '/^\./s/^\..*$/.&/' ${FILE} X fi X else X sed '/^\./s/^\..*$/.&/' X fi X echo . X echo QUIT ) >bsmtp.in X Xexit 0 SHAR_EOF if test 1458 -ne "`wc -c < 'sendbsmtp'`" then echo shar: error transmitting "'sendbsmtp'" '(should have been 1458 characters)' fi chmod +x 'sendbsmtp' fi # end of overwriting check : End of shell archive exit 0 -- ----- David Herron, Local E-Mail Hack, david@ms.uky.edu, david@ms.uky.csnet ----- {uunet,cbosgd}!ukma!david, david@UKMA.BITNET ----- bsmtp-users@ms.uky.edu for bsmtp discussion ----- bsmtp-users-request@ms.uky.edu for administrivia -- Rich $alz Cronus Project, BBN Labs rsalz@bbn.com Moderator, comp.sources.unix sources@uunet.uu.net