sources-request@mirror.UUCP (08/28/86)
Submitted by: Mark Horton <mark@cbosgd.ATT.COM> Mod.sources: Volume 7, Issue 10 Archive-name: smail/Part02 # To unpack the enclosed files, please use this file as input to the # Bourne (sh) shell. This can be most easily done by the command; # sh < thisfilename # This archive contains; # Makefile binmail.c defs.h deliver.c # getopt.c main.c resolve.c sendmail.att # sendmail.leaf smail.cf.fix smail.cf.form smail.cf.sh # smail.prompt sysexits.h u.Path.local u.Path.top.1 # u.Path.uucp.1 # ---------- file Makefile ---------- filename="Makefile" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file Makefile... fi cat << 'END-OF-FILE' > $filename # Makefile for smail (not a installation makefile) # @(#)Makefile 1.6 (UUCP-Project/CS) 6/27/86 # If you have compressed files in this dir, uncompress them # and point UUMAP at the directory with uncompressed files, # or fiddle with zcat. UUMAP=/usr/spool/news/maps/mod.map PATHS = /usr/lib/uucp/paths CFLAGS = -O -DPATHS=\"$(PATHS)\" OBJECTS = main.o resolve.o deliver.o getopt.o all: smail binmail smail: ${OBJECTS} cc ${CFLAGS} ${OBJECTS} -o smail ${OBJECTS}: defs.h binmail: install: all @echo read INFO.INSTALL paths: pathalias u.Path.* $(UUMAP)/u.* | sort -f > path.out cp path.out $(PATHS) clean: rm -f *.o a.out core clobber: clean rm -f smail rmail binmail END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 691 ] then echo $filename changed - should be 691 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file binmail.c ---------- filename="binmail.c" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file binmail.c... fi cat << 'END-OF-FILE' > $filename /* */ /* This program will be used in place of /bin/mail on SVR2 sites. /* It looks at the arguments and decides whether to call /* SENDER for sending mail, or READER for reading mail. /* /* before installing as /bin/mail, move the stock /bin/mail to /bin/lmail /* /* */ #include <stdio.h> #include "defs.h" #ifdef SENDMAIL #define SENDER "/usr/lib/sendmail" #else #define SENDER "/bin/rmail" #endif #define READER "/bin/lmail" #define TRUE 1 #define FALSE 0 char prog[128]; void perror(), exit(), usage(); char *strcpy(); main(argc, argv) int argc; char *argv[]; { extern int optind; extern char *optarg; int i, j, c; int reading, sending; reading = sending = FALSE; (void) strcpy(prog, argv[0]); if(argc == 1) { reading = TRUE; } else { while((c = getopt(argc, argv, "epqrtf:")) != EOF) { switch(c) { case 'e': case 'p': case 'q': case 'r': case 'f': reading = TRUE; break; case 't': sending = TRUE; break; default: usage(); return(1); } } } /* any arguments left over -> sending */ if(argc > optind) { sending = TRUE; } if((reading == TRUE) && (sending == TRUE)) { usage(); return(1); } if(sending == TRUE) { argv[0] = SENDER; for(i = 1, j = optind; i < argc; i++, j++) { argv[i] = argv[j]; } argv[i] = NULL; } else { argv[0] = READER; } (void) execvp(argv[0], argv); (void) fprintf(stderr, "%s: execvp(\"%s\", argv) failed: ", prog, argv[0]); perror(""); return(1); } void usage() { (void) fprintf(stderr, "usage:\t%s [ -epqr ] [ -f file ]\n", prog); (void) fprintf(stderr, "\t%s [ -t ] persons\n", prog); } END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 1630 ] then echo $filename changed - should be 1630 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file defs.h ---------- filename="defs.h" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file defs.h... fi cat << 'END-OF-FILE' > $filename /* ** ** Defs.h: header file for rmail/smail. ** ** Configuration options for rmail/smail. As distributed, these are ** your options: ** use gethostname() to determine hostname. ** full domain name is 'hostname.uucp'. ** path file is /usr/lib/uucp/paths. ** no log, no record, no dbm, use sendmail. ** ** You can change these in the next few blocks. ** */ /* ** @(#)defs.h 1.12 (UUCP-Project/CS) 6/29/86 */ /* ** If you select SENDMAIL below, you needn't worry about hostdomain here, ** as rmail does no domain routing with a SNOOPY sendmail, and sendmail ** itself sets hostdomain for smail. ** ** Determining hostname: ** To use gethostname(), define GETHOSTNAME. ** To use uname(), define UNAME. ** To use a literal string, define HOSTNAME "host" ** ** Determining hostdomain: ** To use hostname.mydomain, define MYDOM "mydomain" ** To use a literal string, define HOSTDOMAIN "host.domain" ** Otherwise, it will be "hostname" (not advised). ** ** THE TOP LEVEL OF YOUR DOMAIN NAME IS IMPLICIT IN THE PATHALIAS DATABASE */ # define BSD /* if system is a Berkeley system */ # define SENDMAIL 1 /* Turn off to use /bin/(l)mail only */ # ifdef BSD # define GETHOSTNAME /* use gethostname() */ # else # define UNAME /* use uname() */ # endif /* # define HOSTNAME "host" /* literal name */ /* * .UUCP here is just for testing, GET REGISTERED in COM, EDU, etc. * See INFO.REGISTRY for details. */ # define MYDOM ".UUCP" /* literal domain suffix */ /* # define HOSTDOMAIN "host.dom" /* replacement for HOSTNAME.MYDOM */ /* ** Locations of files: ** PATHS is where the pathalias output is. This is mandatory. ** Define LOG if you want a log of mail. This can be handy for ** debugging and traffic analysis. ** Define RECORD for a copy of all mail. This uses much time and ** space and is only used for extreme debugging cases. */ #ifndef PATHS # define PATHS "/usr/lib/uucp/paths" /* where the path database is */ #endif /*# define LOG "/usr/spool/uucp/mail.log" /* log of uucp mail */ /*# define RECORD "/tmp/mail.log" /* record of uucp mail */ /* ** Mailer options: ** RMAIL is the command to invoke rmail on machine sys. ** RARG is how to insulate metacharacters from RMAIL. ** LMAIL is the command to invoke the local mail transfer agent. ** LARG is how to insulate metacharacters from LMAIL. ** RLARG is LARG with host! on the front - to pass a uux addr to sendmail. ** SENDMAIL selects one of two sets of defines below for either ** using sendmail or /bin/lmail. */ /* add -r for queueing */ # define RMAIL(from,sys) "/usr/bin/uux - -r %s!rmail",sys # define RARG(user) " '(%s)'",user # define RFROM(frm,now,host) "From %s %.24s remote from %s\n",frm,now,host #ifdef SENDMAIL # define HANDLE JUSTUUCP /* see HANDLE definition below */ # define ROUTING JUSTDOMAIN /* see ROUTING definition below */ /* # define ROUTING REROUTE /* reroute everything (sets -R flag) */ # define LMAIL(frm,sys) "/usr/lib/sendmail -em -f%s",frm # define LARG(user) " '%s'",user # define RLARG(sys,frm) " '%s!%s'",sys,frm # define LFROM(frm,now,host) "" #else # define HANDLE ALL # define ROUTING JUSTDOMAIN /* */ /* # define ROUTING REROUTE /* reroute everything (sets -R flag) */ #ifdef BSD # define LMAIL(frm,sys) "/bin/mail" /* BSD local delivery agent */ #else # define LMAIL(frm,sys) "/bin/lmail" /* SV local delivery agent */ #endif # define LARG(user) " '%s'",user # define RLARG(sys,frm) " '%s!%s'",sys,frm # define LFROM(frm,now,host) "From %s %.24s\n",frm,now #endif SENDMAIL /* ** DON'T TOUCH THE REST */ # define SMLBUF 512 /* small buffer (handle one item) */ # define BIGBUF 4096 /* handle lots of items */ # define MAXPATH 32 /* number of elements in ! path */ # define MAXDOMS 16 /* number of subdomains in . domain */ # define MAXARGS 32 /* number of arguments */ #ifndef NULL # define NULL 0 #endif # define DEBUG if (debug==YES) (void) printf # define ADVISE if (debug!=NO) (void) printf # define error(stat,msg,a) { (void) fprintf(stderr, msg, a); exit(stat); } # define lower(c) ( isupper(c) ? c-'A'+'a' : c ) enum eform { /* format of addresses */ ERROR, /* bad or invalidated format */ LOCAL, /* just a local name */ DOMAIN, /* user@domain */ UUCP, /* host!address */ ROUTE }; /* intermediate form - to be routed */ enum ehandle { /* what addresses can we handle? (don't kick to LMAIL) */ ALL, /* UUCP and DOMAIN addresses */ JUSTUUCP, /* UUCP only; set by -l */ NONE }; /* all mail is LOCAL; set by -L */ enum erouting { /* when to route A!B!C!D */ JUSTDOMAIN, /* route A if A is a domain */ ALWAYS, /* route A always; set by -r */ REROUTE }; /* route C, B, or A (whichever works); set by -R */ enum edebug { /* debug modes */ NO, /* normal deliver */ VERBOSE, /* talk alot */ YES }; /* talk and don't deliver */ # ifdef BSD # include <sysexits.h> # include <strings.h> # else # include "sysexits.h" # include <string.h> # define index strchr # define rindex strrchr # endif extern void exit(), perror(); extern unsigned sleep(); END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 5060 ] then echo $filename changed - should be 5060 bytes, not $size bytes fi chmod 444 $filename fi # ---------- file deliver.c ---------- filename="deliver.c" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file deliver.c... fi cat << 'END-OF-FILE' > $filename /* ** Deliver.c ** ** Routines to effect delivery of mail for rmail/smail. ** */ #ifndef lint static char *sccsid="@(#)deliver.c 1.13 (UUCP-Project/CS) 7/6/86"; #endif # include <sys/types.h> # include <stdio.h> # include <pwd.h> # include "defs.h" extern int exitstat; /* set if a forked mailer fails */ extern enum edebug debug; /* how verbose we are */ extern char hostname[]; /* our uucp hostname */ extern enum ehandle handle; /* what we handle */ /* ** ** deliver(): hand the letter to the proper mail programs. ** ** Copies stdin to a temp file, if more than one mailer ** will be invoked. Issues one command for each different ** host of <hostv>, constructing the proper command for ** LOCAL or UUCP mail. Note that LOCAL mail has blank ** host names. The <userv> names for each host are arguments ** to the command. Reads the "From_" and ">From_" lines ** of the letter to build a <from> argument. Finally, ** prepends a "From" line to the letter just before going ** out, with a "remote from <hostname>" if it is a UUCP letter. ** */ deliver( argc, hostv, userv, formv ) int argc; /* number of addresses */ char *hostv[]; /* host names */ char *userv[]; /* user names */ enum eform formv[]; /* form for each address */ { FILE *file; /* source (stdin or temp file) */ FILE *out; /* pipe to mailer */ FILE *popen(); /* to fork a mailer */ #ifdef RECORD FILE *record(); /* snoopy mailer */ #endif char *tmpf = "/tmp/rmXXXXXX"; /* temp file name */ char lcommand[SMLBUF]; /* local command issued */ char rcommand[SMLBUF]; /* remote command issued */ char *command; /* actual command */ char from[SMLBUF]; /* accumulated from argument */ char line[SMLBUF]; /* one line of the letter */ int plural = 0; /* more than one mailer? */ enum eform form; /* holds form[i] for speed */ long size; /* number of bytes of message */ time_t now, time(); /* the time */ char *nows; /* the time in a string */ int i, j, stat; char *c, *ctime(); int failcount = 0; (void) strcpy( from, "" ); (void) time( &now ); /* set the time */ nows = ctime( &now ); /* ** Decide if we should copy the letter to a temp file. We do this if we ** are sending to more than one mailer or if we are making a record. We ** check to see if there is more than one different host in hostv. */ # ifdef RECORD plural++; # else for( i = 1; i < argc; i++ ) if( strcmp( hostv[0], hostv[i] ) && ++plural ) break; /* shouldn't be case sensitive */ # endif /* ** If plural (more than one copy), copy stdin to a file. Otherwise, just ** set file pointer to stdin. This way, we avoid an extra copy of the ** file for the 99% of the cases with only one destination. We also assume ** that if we must return a failure to the sender, that stdin is a file ** we can rewind and seek on, which is true of uuxqt. */ if( plural ) { char *mktemp(); ( void ) mktemp( tmpf ); if( ( file = fopen( tmpf, "w+" ) ) == NULL ) error( EX_CANTCREAT, "can't create %s.\n", tmpf ); while( fgets( line, SMLBUF, stdin ) != NULL ) (void) fputs( line, file ); } else { file = stdin; } /* ** We pass through the list of addresses. */ for( i = 0; i < argc; i++ ) { /* ** If form == ERROR, either the address was bad or it has been sent on a ** previous pass. So we break out. */ form = formv[i]; if ( form == ERROR ) continue; /* ** Rewind the input file for multiple copies (won't be stdin). Call ** rline() to read the first few lines to collapse a From argument ** from the From_ and >From_ lines. Rline() leaves the first unused ** line in line. */ if ( plural ) rewind( file ); rline( file, line, from ); /* ** Build the command name based on whether this is local mail or ** uucp mail. Someday, this will use a form->mailer table. */ (void) sprintf( lcommand, LMAIL( from, hostv[i] ) ); (void) sprintf( rcommand, RMAIL( from, hostv[i] ) ); /* ** For each address with the same host name and form, append the user ** name to the command line, and set form = ERROR so we skip this address ** on later passes. */ for ( j = argc - 1; j >= i; j-- ) { if ( formv[j] != form || strcmp( hostv[i], hostv[j] ) ) continue; c = lcommand + strlen( lcommand ); if (form == LOCAL) (void) sprintf( c, LARG( userv[j] ) ); else (void) sprintf( c, RLARG(hostv[i], userv[j]) ); c = rcommand + strlen( rcommand ); (void) sprintf( c, RARG( userv[j] ) ); formv[j] = ERROR; } retry: if (form == LOCAL) command = lcommand; else command = rcommand; ADVISE( "COMMAND: %s\n", command ); /* ** Fork the mailer and set it up for writing so we can send the mail to it, ** or for debugging divert the output to stdout. */ if ( debug == YES ) out = stdout; else { failcount = 0; do { out = popen( command, "w" ); if (out) break; /* * Fork failed. System probably overloaded. * Wait awhile and try again 10 times. * If it keeps failing, probably some * other problem, like no uux or sendmail. */ (void) sleep(60); } while (++failcount < 10); } if( out == NULL ) { exitstat = EX_UNAVAILABLE; (void) printf( "couldn't execute %s.\n", command ); continue; } /* ** Output our From_ line. */ if ( form == LOCAL ) { #ifdef SENDMAIL (void) fprintf( out, LFROM( from, nows, hostname ) ); #else char *p; if((p=index(from, '!')) == NULL) { (void) fprintf( out, LFROM( from, nows, hostname ) ); } else { *p = NULL; (void) fprintf(out, RFROM( p+1, nows, from)); *p = '!'; } #endif } else { (void) fprintf( out, RFROM( from, nows, hostname ) ); } /* ** Copy input. Remember that line from rline(). */ size = 0; do { (void) fputs( line, out ); size += strlen( line ); } while( fgets( line, SMLBUF, file ) != NULL ); /* ** Get exit status and if non-zero, set global exitstat so when we exit ** we can indicate an error. */ if ( debug != YES ) { if ( stat = pclose( out ) ) exitstat = stat >> 8; /* * handle==ALL means we're smail, else we're rmail. * The check here prevents a smail<=>sendmail loop. * The form check prevents an internal smail loop. */ if (handle != ALL && form != LOCAL && exitstat != 0) { /* * RMAIL failed, probably because the host * being uux'ed isn't in L.sys. Try again * using sendmail. If there is no sendmail, * we should include code ala the old rmail * which mails it back to the sender. */ rewind(file); /* assume uuxqt file */ rline( file, line, from ); form = LOCAL; exitstat = 0; /* don't bother uuxqt */ ADVISE("uux failed %d, trying sendmail\n", exitstat); goto retry; } } } /* ** Update logs and records. Blech. */ # ifdef LOG log( command, from, size ); /* */ # endif # ifdef RECORD rewind( file ); out = record( command, from, size ); if(out != NULL) { while( fgets( line, SMLBUF, file ) != NULL ) { (void) fputs( line, out ); } (void) fclose( out ); } # endif /* ** Close and unlink temp file if we made one. */ if ( plural ) { (void) fclose( file ); (void) unlink( tmpf ); } } /* ** ** rline(): collapse From_ and >From_ lines. ** ** Same idea as the old rmail, but also turns user@domain to domain!user. ** Leaves the first line of the letter in "line". ** */ rline( file, line, from ) FILE *file; /* source file */ char *line; /* return first unused line */ char *from; /* return accumulated from arg */ { int parts; /* for cracking From_ lines ... */ char *partv[16]; /* ... apart using ssplit() */ char user[SMLBUF]; /* for rewriting user@host */ char domain[SMLBUF]; /* " " " */ char addr[SMLBUF]; /* " " " */ enum eform form, parse(); /* " " " */ extern build(); /* " " " */ struct passwd *pwent, *getpwuid(); /* to get default user */ char *c; (void) strcpy( from, "" ); (void) strcpy( addr, "" ); (void) strcpy( line, "" ); /* ** Read each line until we hit EOF or a line not beginning with "From " ** or ">From " (called From_ lines), accumulating the new path in from ** and stuffing the actual sending user (the user name on the last From_ ** line) in addr. */ for( ;; ) { if ( fgets( line, SMLBUF, file )==NULL ) break; if ( strncmp( "From ", line, 5 ) && strncmp( ">From ", line, 6 ) ) break; /* ** Crack the line apart using ssplit. */ if( c = index( line, '\n' ) ); *c = '\0'; parts = ssplit( line, ' ', partv ); /* ** Tack host! onto the from argument if "remote from host" is present. */ if ( parts > 3 && !strncmp( "remote from ", partv[parts-3], 12 ) ) { (void) strcat( from, partv[parts-1] ); (void) strcat( from, "!" ); } /* ** Stuff user name into addr, overwriting the user name from previous ** From_ lines, since only the last one counts. Then rewrite user@host ** into host!user, since @'s don't belong in the From_ argument. */ (void) strncpy( addr, partv[1], partv[2]-partv[1]-1 ); addr[partv[2]-partv[1]-1] = '\0'; /* ugh */ (void) parse( addr, domain, user ); if(*domain == '\0') { form = LOCAL; } else { form = UUCP; } build( domain, user, form, addr ); } /* ** Now tack the user name onto the from argument. */ (void) strcat( from, addr ); /* ** If we still have no from argument, we have junk headers, but we try ** to get the user's name using /etc/passwd. */ if ( !*from ) if ( ( pwent = getpwuid( getuid() ) ) == NULL ) (void) strcpy( from, "nowhere" ); /* bad news */ else (void) strcpy( from, pwent->pw_name ); } # ifdef LOG log(command, from, size) char *command, *from; long size; { FILE *fd; char *logtime, *ctime(); time_t t; int cmask; ( void ) time( &t ); logtime = ctime( &t ); logtime[16] = 0; logtime += 4; cmask = umask(0); fd = fopen( LOG, "a" ); (void) umask(cmask); if ( fd != NULL ) { (void) fprintf( fd, "%s: %s, from %s, %ld bytes\n", logtime, command, from, size); (void) fclose( fd ); } } # endif # ifdef RECORD FILE * record( command, from, size) char *command, *from; long size; { FILE *fd; char *logtime, *ctime(); long t; int cmask; ( void ) time( &t ); logtime = ctime( &t ); logtime[16] = 0; logtime += 4; cmask = umask(0); fd = fopen( RECORD, "a" ); (void) umask(cmask); if ( fd != NULL ) { (void) fprintf( fd, "%s: %s, from %s, %ld bytes\n", logtime, command, from, size); } return(fd); } # endif END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 10558 ] then echo $filename changed - should be 10558 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file getopt.c ---------- filename="getopt.c" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file getopt.c... fi cat << 'END-OF-FILE' > $filename /* * Here's something you've all been waiting for: the AT&T public domain * source for getopt(3). It is the code which was given out at the 1985 * UNIFORUM conference in Dallas. I obtained it by electronic mail * directly from AT&T. The people there assure me that it is indeed * in the public domain. * * There is no manual page. That is because the one they gave out at * UNIFORUM was slightly different from the current System V Release 2 * manual page. The difference apparently involved a note about the * famous rules 5 and 6, recommending using white space between an option * and its first argument, and not grouping options that have arguments. * Getopt itself is currently lenient about both of these things White * space is allowed, but not mandatory, and the last option in a group can * have an argument. That particular version of the man page evidently * has no official existence, and my source at AT&T did not send a copy. * The current SVR2 man page reflects the actual behavor of this getopt. * However, I am not about to post a copy of anything licensed by AT&T. */ /* This include is needed only to get "index" defined as "strchr" on Sys V. */ #include "defs.h" /*LINTLIBRARY*/ #define NULL 0 #define EOF (-1) #define ERR(s, c) if(opterr){\ extern int strlen(), write();\ char errbuf[2];\ errbuf[0] = c; errbuf[1] = '\n';\ (void) write(2, argv[0], (unsigned)strlen(argv[0]));\ (void) write(2, s, (unsigned)strlen(s));\ (void) write(2, errbuf, 2);} extern int strcmp(); extern char *index(); int opterr = 1; int optind = 1; int optopt; char *optarg; int getopt(argc, argv, opts) int argc; char **argv, *opts; { static int sp = 1; register int c; register char *cp; if(sp == 1) if(optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') return(EOF); else if(strcmp(argv[optind], "--") == NULL) { optind++; return(EOF); } optopt = c = argv[optind][sp]; if(c == ':' || (cp=index(opts, c)) == NULL) { ERR(": illegal option -- ", c); if(argv[optind][++sp] == '\0') { optind++; sp = 1; } return('?'); } if(*++cp == ':') { if(argv[optind][sp+1] != '\0') optarg = &argv[optind++][sp+1]; else if(++optind >= argc) { ERR(": option requires an argument -- ", c); sp = 1; return('?'); } else optarg = argv[optind++]; sp = 1; } else { if(argv[optind][++sp] == '\0') { sp = 1; optind++; } optarg = NULL; } return(c); } END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 2456 ] then echo $filename changed - should be 2456 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file main.c ---------- filename="main.c" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file main.c... fi cat << 'END-OF-FILE' > $filename /* ** ** Rmail/Smail - UUCP mailer with automatic routing. ** ** Christopher Seiwald /+\ ** cbosgd!chris +\ ** January, 1985 \+/ ** */ #ifndef lint static char *sccsid="@(#)main.c 1.5 (UUCP-Project/CS) 6/9/86"; #endif /* ** ** usage: rmail [options] address... ** smail [options] address... ** options: ** -d debug - verbose and don't invoke mailers. ** -v verbose - just verbose. ** -h hostname set hostname (default GETHOSTNAME, ** UNAME, or HOSTNAME) ** -H hostdomain set hostdomain (default hostname.MYDOM) ** -p pathfile path database filename ** (without both -r and -R, only user@domain gets routed) ** -r force routing of host!address ** -R reroute even explicit path!user ** (without both -l and -L, only local mail goes local) ** -l user@domain goes to local mailer ** -L all mail goes local ** ** -r, -R, -l, and -L can be preset in defs.h Smail clears -l and -L. ** */ #include <stdio.h> #include <ctype.h> #include "defs.h" #ifdef UNAME #include <sys/utsname.h> #endif int exitstat = 0; /* exit status, set by resolve, deliver */ enum edebug debug = NO; /* set by -d or -v option */ enum ehandle handle = HANDLE; /* which mail we can handle, see defs.h */ enum erouting routing = ROUTING;/* to route or not to route, see defs.h */ char hostname[SMLBUF] = ""; /* set by -h, defaults in defs.h */ char hostdomain[SMLBUF] = ""; /* set by -H, defaults in defs.h */ char *pathfile = PATHS; /* or set by -p */ /* ** ** Rmail/Smail: mail stdin letter to argv addresses. ** ** After processing command line options and finding our host and domain ** names, we map addresses into <host,user,form> triples. Then we deliver. ** */ main( argc, argv ) int argc; char **argv; { char *hostv[MAXARGS]; /* UUCP neighbor */ char *userv[MAXARGS]; /* address given to host */ enum eform formv[MAXARGS]; /* invalid, local, or uucp */ char *p; int c; char *optstr = "dvrRlLH:h:p:"; extern char *optarg; extern int optind; /* ** see if we aren't invoked as rmail */ if((p = rindex(argv[0], '/')) == NULL) { p = argv[0]; } else { p++; } if(*p != 'r' ) { handle = ALL; } /* ** Process command line arguments ( maybe getopt()? ). */ while ((c = getopt(argc, argv, optstr)) != EOF) { switch ( c ) { case 'd': debug = YES; break; case 'v': debug = VERBOSE; break; case 'r': routing = ALWAYS; break; case 'R': routing = REROUTE; break; case 'l': handle = JUSTUUCP; break; case 'L': handle = NONE; break; case 'H': (void) strcpy( hostdomain, optarg ); break; case 'h': (void) strcpy( hostname, optarg ); break; case 'p': pathfile = optarg; break; default: error( EX_USAGE, "valid flags are %s\n", optstr); } } if ( argc <= optind ) { error( EX_USAGE, "usage: %s [flags] address...\n", argv[0] ); } /* ** Get our default hostname and hostdomain. */ getmynames(); /* ** Map argv addresses to <host, user, form>. */ map( (argc - optind), &argv[optind], hostv, userv, formv ); /* ** Deliver. */ deliver( (argc - optind), hostv, userv, formv ); /* ** Exitstat was set if any resolve or deliver failed, otherwise 0. */ exit( exitstat ); } /* ** ** map(): map addresses into <host, user, form> triples. ** ** Calls resolve() for each address of argv. The result is hostv and ** userv arrays (pointing into buffers userz and hostz), and formv array. ** */ map( argc, argv, hostv, userv, formv ) int argc; /* address count */ char **argv; /* address vector */ char *hostv[]; /* remote host vector */ char *userv[]; /* user name vector */ enum eform formv[]; /* address format vector */ { int i; enum eform resolve(); char *c, *malloc(); char *userz = malloc( BIGBUF ); char *hostz = malloc( BIGBUF ); for( i=0; i<argc; i++ ) { userv[i] = userz; /* put results here */ hostv[i] = hostz; if ( **argv == '(' ) /* strip () */ { ++*argv; c = index( *argv, ')' ); if (c) *c = '\0'; } /* here it comes! */ formv[i] = resolve( *argv++, hostz, userz ); userz += strlen( userz ) + 1; /* skip past \0 */ hostz += strlen( hostz ) + 1; } } /* ** ** getmynames(): what is my host name and host domain? ** ** Hostname set by -h, failing that by #define HOSTNAME, failing ** that by gethostname() or uname(). ** ** Hostdomain set by -h, failing that by #define HOSTDOMAIN, ** failing that as hostname.MYDOM, or as just hostname. ** ** See defs.h for the inside story. ** */ getmynames() { #ifdef HOSTNAME if ( !*hostname ) (void) strcpy( hostname, HOSTNAME ); #endif #ifdef GETHOSTNAME if ( !*hostname ) gethostname( hostname, sizeof( hostname ) - 1 ); #endif #ifdef UNAME if ( !*hostname ) { struct utsname site; if ( uname( &site ) < 0 ) error( EX_SOFTWARE, "uname() call failed", 0 ); (void) strcpy( hostname, site.nodename ); } #endif if ( !*hostname ) error( EX_SOFTWARE, "can't determine hostname.\n", 0 ); #ifdef HOSTDOMAIN if ( !*hostdomain ) (void) strcpy( hostdomain, HOSTDOMAIN ); #endif #ifdef MYDOM if ( !*hostdomain ) (void) strcat( strcpy( hostdomain, hostname ), MYDOM ); #endif if ( !*hostdomain ) (void) strcpy( hostdomain, hostname ); } /* ** ssplit(): split a line into array pointers. ** ** Each pointer wordv[i] points to the first character after the i'th ** occurence of c in buf. Note that each wordv[i] includes wordv[i+1]. ** */ ssplit( buf, c, ptr ) register char *buf; /* line to split up */ char c; /* character to split on */ char **ptr; /* the resultant vector */ { int count = 0; int wasword = 0; for( ; *buf; buf++ ) { if ( !wasword ) count++, *ptr++ = buf; wasword = ( c != *buf ); } if ( !wasword ) count++, *ptr++ = buf; *ptr = NULL; return( count ); } END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 5792 ] then echo $filename changed - should be 5792 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file resolve.c ---------- filename="resolve.c" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file resolve.c... fi cat << 'END-OF-FILE' > $filename /* ** ** Resolve.c ** ** Routes then resolves addresses into UUCP or LOCAL. ** */ #ifndef lint static char *sccsid="@(#)resolve.c 1.9 (UUCP-Project/CS) 6/27/86"; #endif #include <ctype.h> #include <stdio.h> #include "defs.h" extern int exitstat; /* set if address doesn't resolve */ extern enum ehandle handle; /* what mail we can handle */ extern enum edebug debug; /* verbose and debug modes */ extern enum erouting routing; /* when to route addresses */ extern char hostdomain[]; /* for qualifying abbreviated addr's */ extern char *pathfile; /* location of path database */ /* ** ** rsvp(): how to resolve addresses. ** ** After parsing an address into <form>, the resolved form will be ** rsvp( form ). If == ROUTE, we route the parsed address and parse again. ** */ # define rsvp(a) table[(int)a][(int)handle] enum eform table[5][3] = { /* all uucponly none */ { ERROR, ERROR, ERROR }, /* error */ { LOCAL, LOCAL, LOCAL }, /* local */ { ROUTE, LOCAL, LOCAL }, /* domain */ { UUCP, UUCP, LOCAL }, /* uucp */ { ERROR, ERROR, ERROR }}; /* route */ /* ** NOTE: in this module <domainv> replaces <hostv>. <domainv> contains ** the domain part of each address, though by the time it leaves here it ** can only be a host name. */ /* ** ** resolve(): resolve addresses to <host, user, form>. ** ** This is a gnarly piece of code, but it does it all. Each section ** is documented. ** */ enum eform resolve( address, domain, user ) char *address; /* the input address */ char *domain; /* the returned domain */ char *user; /* the returned user */ { enum eform form; /* the returned form */ enum eform parse(); /* to crack addresses */ int parts; /* to ssplit addresses */ char *partv[MAXPATH]; /* " " " */ char temp[SMLBUF]; /* " " " */ int i; /* ** If we set REROUTE and are prepared to deliver UUCP mail, we split the ** address apart at !'s and try to resolve successively larger righthand ** substrings until we succeed. Regularly, we just resolve the whole thing ** once. */ if ( routing == REROUTE && rsvp( UUCP ) == UUCP ) parts = ssplit( address, '!', partv ); else parts = 1, partv[0] = address; /* ** This for( i ) loop selects successively larger righthand substrings ** for BULLYing, see above. */ for( i = parts - 1; i >= 0; i-- ) { /* ** Parse the address. If we are BULLYing and our substring parses to ** the LOCAL address, we skip to the next larger. */ (void) strcpy( temp, partv[i] ); form = parse( temp, domain, user ); DEBUG("parse address '%s' = %s @ %s (%d)\n",temp,user,domain,form); if ( i && form==LOCAL ) continue; /* ** Routing is next step, so we break out if we don't have a UUCP form (if ** we are set to route ALWAYS or REROUTE) or a ROUTE form. */ if ( rsvp( form ) != ROUTE && ( rsvp( form ) != UUCP || routing == JUSTDOMAIN ) ) break; /* ** Apply router. If BULLYing and routing failed, try next larger substring. */ if ( route( form, domain, user, temp ) ) continue; /* ** After routing, reparse and resolve. */ form = parse( temp, domain, user ); DEBUG("parse route '%s' = %s @ %s (%d)\n",temp,user,domain,form); break; } /* ** For LOCAL mail in non-local format, we rewrite the full address into ** <user> and leave <domain> blank. */ if ( rsvp( form ) == LOCAL && form != LOCAL ) { build( domain, user, form, temp ); (void) strcpy( user, temp ); (void) strcpy( domain, "" ); form = LOCAL; } /* ** If we were supposed to route and address but failed (form == ERROR), ** or after routing once we are left with an address that still needs to ** be routed (rsvp( form ) == ROUTE), complain. It is possible that we ** may want to resolve this address more than once (if the routing tables ** return a domain-style address), but most likely this address partially ** resolved to this host. */ if ( form == ERROR || rsvp( form ) == ROUTE ) { exitstat = EX_NOHOST; (void) printf( "%s...couldn't resolve %s.\n", address, domain ); form = ERROR; } ADVISE("resolve '%s' = %s @ %s (%d)\n",address,user,domain,form); return ( form ); } /* ** ** parse(): parse <address> into <domain, user, form>. ** ** input form ** ----- ---- ** user LOCAL ** domain!user DOMAIN ** user@domain DOMAIN ** @domain,address LOCAL (just for sendmail) ** host!address UUCP ** */ enum eform parse( address, domain, user ) char *address; /* input address */ char *domain; /* output domain */ char *user; /* output user */ { int parts; char *partv[MAXPATH]; /* to crack address */ /* ** If this is route address form @hosta,@hostb:user@hostd, break for ** LOCAL since only sendmail would want to eat it. */ if ( *address == '@' ) goto local; /* ** Try splitting at !. If it works, see if the piece before the ! has ** a . in it (domain!user, form DOMAIN) or not (host!user, form UUCP). */ if ( ssplit( address, '!', partv ) > 1 ) { (void) strcpy( user, partv[1] ); (void) strncpy( domain, partv[0], partv[1]-partv[0]-1 ); domain[partv[1]-partv[0]-1] = '\0'; if( ( parts = ssplit( domain, '.', partv ) ) < 2 ) return( UUCP ); if( partv[parts-1][0] == '\0' ) partv[parts-1][-1] = '\0'; /* strip trailing . */ return ( DOMAIN ); } /* ** Try splitting at @. If it work, this is user@domain, form DOMAIN. ** Prefer the righthand @ in a@b@c. */ if ( ( parts = ssplit( address, '@', partv ) ) >= 2 ) { (void) strcpy( domain, partv[parts-1] ); (void) strncpy( user, partv[0], partv[parts-1]-partv[0]-1 ); user[partv[parts-1]-partv[0]-1] = '\0'; return ( DOMAIN ); } /* ** Done trying. This must be just a user name, form LOCAL. */ local: (void) strcpy( user, address ); (void) strcpy( domain, "" ); return( LOCAL ); /* user */ } /* ** ** route(): route domain, plug in user. ** ** Less complicated than it looks. Each section is documented. ** */ route( form, domain, user, result ) enum eform form; /* domain is UUCP host? */ char *domain; /* domain or host name */ char *user; /* user name */ char *result; /* output route */ { int domains, step; /* to split domain */ char *domainv[MAXDOMS]; /* " " " */ char temp[SMLBUF], path[SMLBUF]; /* ** Fully qualify the domain, and then strip the last (top level domain) ** component off, so that we look it up separately. */ (void) strcpy( temp, "."); (void) strcat( temp, domain ); domains = ssplit( temp+1, '.', domainv ); /* If the domain ends in .UUCP, trim that off. */ if ( domains && isuucp(domainv[domains-1])) domainv[domains-1][-1] = '\0'; /* ** Try to get the path for successive components of the domain. ** Example for osgd.cb.att.uucp: ** osgd.cb.att ** cb.att ** att ** uucp ( remember stripping top level? ) ** Returns with error if we find no path. */ step = 0; while ( step<domains && getpath( domainv[step]-1, path ) /* w/dot */ && getpath( domainv[step] , path ) )/* no dot */ step++; if ( step == domains ) { DEBUG( "getpath '%s' failed\n", domain ); return( EX_NOHOST ); } DEBUG("getpath '%s' (%s) = %s\n",domain,domainv[step],path); /* ** If we matched on the entire domain name, this address is fully resolved, ** and we plug <user> into it. If we matched on only part of the domain ** name, we plug <domain>!<user> in. */ build( domain, user, step ? UUCP:LOCAL, temp+1 ); (void) sprintf( result, path, temp+1 ); return( EX_OK ); } /* * Return 1 iff the string is "UUCP" (ignore case). */ isuucp(str) char *str; { if (lower(*str) != 'u') return 0; ++str; if (lower(*str) != 'u') return 0; ++str; if (lower(*str) != 'c') return 0; ++str; if (lower(*str) != 'p') return 0; ++str; if (*str != '\0') return 0; return 1; } /* ** ** qualifydomain(): turn domain into full domain name. ** ** Best explained by examples, if hostdomain = a.b.c.UUCP ** host.domain.UUCP -> host.domain.UUCP ** host.b -> host.b.c.UUCP ** host.x -> host.x.UUCP ** */ /* qualifydomain(): * Taken out 3/21/86 by MRH - if hostdomain is, say. a.b.c.COM, * and domain is x.UUCP, it turns it into x.UUCP.COM and then * barfs on it. I don't see a way to handle PQN's this easily. */ build( domain, user, form, result ) char *domain; char *user; enum eform form; char *result; { switch( form ) { case LOCAL: (void) sprintf( result, "%s", user ); break; case UUCP: (void) sprintf( result, "%s!%s", domain, user ); break; case DOMAIN: (void) sprintf( result, "%s@%s", user, domain ); break; } } /* ** ** getpath(): look up key in ascii sorted path database. ** ** Binary searches a la look(1). Sort -f to fold cases. ** */ getpath( key, path ) char *key; /* what we are looking for */ char *path; /* where the results go */ { long pos, middle, hi, lo; static long pathlength = 0; register char *s; int c; static FILE *file; int flag; if( !pathlength ) /* open file on first use */ { if( ( file=fopen( pathfile, "r" ) ) == NULL ) { (void) printf( "can't access %s.\n", pathfile ); pathlength = -1; } else { (void) fseek( file, 0L, 2 ); /* find length */ pathlength = ftell( file ); } } if( pathlength == -1 ) return( EX_OSFILE ); lo = 0; hi = pathlength; (void) strcpy( path, key ); (void) strcat( path, "\t" ); /* ** "Binary search routines are never written right the first time around." ** - Robert G. Sheldon. */ for( ;; ) { pos = middle = ( hi+lo+1 )/2; (void) fseek( file, pos, 0 ); /* find midpoint */ if ( pos ) /* to beginning of next line */ while( ( c=getc( file ) ) != EOF && c != '\n' ); for( flag = 0, s = path; !flag; s++ ) /* match??? */ { if ( *s == '\0' ) goto solved; c = getc( file ); flag = lower( c ) - lower( *s ); } if ( lo>=middle ) /* failure? */ return( EX_NOHOST ); if ( c != EOF && flag < 0 ) /* close window */ lo = middle; else hi = middle - 1; } /* ** Now just copy the result. */ solved: while( ( c=getc( file ) ) != EOF && c != '\n' ) *path++ = c; *path = '\0'; return ( EX_OK ); } END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 10038 ] then echo $filename changed - should be 10038 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file sendmail.att ---------- filename="sendmail.att" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file sendmail.att... fi cat << 'END-OF-FILE' > $filename This is a diff between the distributed sendmail.cf file in this directory and the actual sendmail.cf run on cbosgd. The only changes are filling in ATT.COM in place of orgname.COM, and our local convention for CEM. Other AT&T sites may wish to support the CEM syntax by handing off to ihnp4 (as this does) or otherwise. This doesn't apply to non-AT&T sites, which should only fill in orgname. The sendmail.cf file here makes the host a gateway into and out of the organization. This is probably appropriate for the first machine to bring up smail, but for less well-known machines you may prefer the sendmail.leaf file or a simpler locally developed sendmail.cf. *** sendmail.cf Mon Jun 9 16:14:53 1986 --- /usr/lib/sendmail.cf Mon Jun 9 16:16:50 1986 *************** *** 29,37 # Until you register you can use orgname.UUCP for testing. #Dwhost ! DDorgname.COM ! DAorgname.UUCP ! CDUUCP orgname # Preemptive ether and UUCP connections. We prefer these connections # over both designated transport mechanisms and the general depository. --- 29,37 ----- # Until you register you can use orgname.UUCP for testing. #Dwhost ! DDATT.COM ! DAATT.UUCP ! CDUUCP ATT # Preemptive ether and UUCP connections. We prefer these connections # over both designated transport mechanisms and the general depository. *************** *** 280,285 #R$*<@$*>$* $#uux$@$R$:$1@$2$3 hand to uucp relay #R$*<@$*>$* $#ether$@$R$:$1@$2$3 hand to ether relay #R$*<$*>$* $#error$:unkown address $1$2$3 don't hand anywhere # local delivery R$+ $#local$:$1 user --- 280,288 ----- #R$*<@$*>$* $#uux$@$R$:$1@$2$3 hand to uucp relay #R$*<@$*>$* $#ether$@$R$:$1@$2$3 hand to ether relay #R$*<$*>$* $#error$:unkown address $1$2$3 don't hand anywhere + + # AT&T CEM f.m.last /CBOSGD + R$-.$+ $#uux$@ihnp4$:$1.$2 AT&T CEM f.m.last # local delivery R$+ $#local$:$1 user END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 1919 ] then echo $filename changed - should be 1919 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file sendmail.leaf ---------- filename="sendmail.leaf" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file sendmail.leaf... fi sed 's/^X//' << 'END-OF-FILE' > $filename XDate: Thu, 29 May 86 14:28:47 edt XFrom: avolio@decuac.DEC.COM (Frederick M. Avolio) X XThese changes are useful for a simple UUCP site with only one neighbor, Xwhich does not run smail but does run sendmail. It passes all mail to Xthe neighbor, which must run smail. X X---------- X*** ORIGsendmail.cf Sat Feb 15 23:27:33 1986 X--- sendmail.cf Thu May 29 14:20:06 1986 X ############################### X ### Message precedences ### X*************** X*** 304,309 X # if you just want general disposition. X X #R$+<@$-.UUX> $#smail$@$:$2!$1 any uucp via smail X R$*<@$+.ETHER>$* $#ether$@$2$:$1$3 etherhost X X # Send all foreign mail to general disposition now, and everything else X X--- 304,311 ----- X # if you just want general disposition. X X #R$+<@$-.UUX> $#smail$@$:$2!$1 any uucp via smail X+ R$+<@$=U.UUX> $#uux$@$2$:$1 any uucp via uux X+ R$+<@$-.UUX> $#uux$@$R$:$2!$1 X R$*<@$+.ETHER>$* $#ether$@$2$:$1$3 etherhost X X # Send all foreign mail to general disposition now, and everything else X*************** X*** 322,327 X X R$+ $#smail$@$:$1 hand to smail X #R$+ $#smail$@$:$R!$1 hand to uucp relay X #R$+ $#ether$@$R$:$1 hand to ether relay X #R$+ $#error$:unknown address $1 don't hand anywhere X X X--- 324,331 ----- X X R$+ $#smail$@$:$1 hand to smail X #R$+ $#smail$@$:$R!$1 hand to uucp relay X+ R$*@$* $#uux$@$R$:$2!$1 X+ R$+ $#uux$@$R$:$1 hand to uucp relay X #R$+ $#ether$@$R$:$1 hand to ether relay X #R$+ $#error$:unknown address $1 don't hand anywhere END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 1509 ] then echo $filename changed - should be 1509 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file smail.cf.fix ---------- filename="smail.cf.fix" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file smail.cf.fix... fi cat << 'END-OF-FILE' > $filename Here is a note regarding the changes made at randvax to help handle 822 domain issues. I don't claim this is a 100% solution, but it should be worth some study. This is supposed to be a fix to the problem that vanilla 4.2 and 4.3BSD sendmails add their name and a ! to a legal RFC822 address, turning it into an illegal 822 address. ----- ----- [terry%owl] Here's the change I made: S13 R$+ $:$>5$1 convert to old style R$+<@$=w> $:$1 strip @localhost (trw) R$+@$=w $:$1 strip @localhost (trw) R$+@$=w.arpa $:$1 strip @localhost (trw) # trw: don't append our hostname to addresses with @s, # or to uucp addresses we don't talk to--class X # contains a list of sites we talk to. R$+@$+ $@$1@$2 stop evaluation R$=U!$+ $2 strip local name R$=X!$+ $@$U!$1!$2 stick on our host name R$+!$+ $@$1!$2 don't prepend our host R$+ $:$U!$1 stick on our host name END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 902 ] then echo $filename changed - should be 902 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file smail.cf.form ---------- filename="smail.cf.form" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file smail.cf.form... fi cat << 'END-OF-FILE' > $filename ############################################################ ############################################################ ##### ##### SENDMAIL CONFIGURATION FILE ##### ##### UUCP project prototype: supports domain style ##### addressing over UUCP and ethernet links. ##### ##### A product of the UUCP Project. ##### ##### @(#)smail.cf.form 1.11 (UUCP-Project/CS) 7/13/86 ##### ############################################################ ############################################################ ############################################################ ### ### Local configuration options - HINTS ### # Host name and domain name macros. Dw set $w, DD sets $D, and # CD sets $=D. Most sendmails set $w to `hostname`, so you can # probably leave out Dw. $D and $=D lists all domains in which this # host sits, and $D goes into outbound addresses, i.e. "user@$w.$D". # $A is another domain for which this host is authoratative, which # will be turned into $D. CF_HOST CF_DOMAIN CF_AUTHORITY CF_DCLASS # Preemptive ether and UUCP connections. We prefer these connections # over both designated transport mechanisms and the general depository. # You can add more classes (here and in S0). # /etc/hosts.smtp might be a link to /etc/hosts CF_SMTP FU/dev/null %s # Mock top-level domain names. These name designate a transport mechanism # and appear internally only, set in S3, used in S0, and removed in S4 and # (possibly) the ruleset for the particular mailer. CTETHER UUX # Relay host. Used at the end of S0 as the general depository for # addresses which didn't resolve locally. DRrelay ### ### End Local configuration options ### ############################################################ ############################################################ # # General configuration information # # This information is basically just "boiler-plate"; it must be # there, but is essentially constant. # # Information in this file should be independent of location -- # i.e., although there are some policy decisions made, they are # not specific to Berkeley per se. # # @(#)base.m4 4.3 8/30/83 # ############################################################ DVUUCP-Project/rel-1.0/CF_DATE ########################## ### Special macros ### ########################## # official hostname Dj$w.$D # my name DnMAILER-DAEMON # UNIX header format DlFrom $g $d # delimiter (operator) characters Do.:%@!^=/[] # format of a total name Dq$g$?x ($x)$. # SMTP login message De$j Sendmail $v/$V ready at $b ################### ### Options ### ################### # location of alias file OA/usr/lib/aliases # default delivery mode (deliver in background) Odbackground # (don't) connect to "expensive" mailers #Oc # temporary file mode OF0644 # default GID Og1 # location of help file OH/usr/lib/sendmail.hf # log level OL9 # default messages to old style Oo # queue directory OQ/usr/spool/mqueue # read timeout -- violates protocols Or2h # status file OS/usr/lib/sendmail.st # queue up everything before starting transmission Os # default timeout interval OT3d # time zone names (V6 only) OtPST,PDT # default UID Ou1 # wizard's password OWtZWoIRijHq0EQ ############################### ### Message precedences ### ############################### Pfirst-class=0 Pspecial-delivery=100 Pjunk=-100 ######################### ### Trusted users ### ######################### Troot Tdaemon Tuucp Tnetwork ############################# ### Format of headers ### ############################# #H?P?Return-Path: <$g> HReceived: $?sfrom $s $.by $j ($v/$V) id $i; $b H?D?Resent-Date: $a H?D?Date: $a H?F?Resent-From: $q H?F?From: $q H?x?Full-Name: $x HSubject: # HPosted-Date: $a # H?l?Received-Date: $b H?M?Resent-Message-Id: <$t.$i@$j> H?M?Message-Id: <$t.$i@$j> ############################################################ ############################################################ ##### ##### REWRITING RULES ##### ########################### # Name Canonicalization # ########################### S3 # basic textual canonicalization R<> $@@ turn into magic token R$*<$+>$* $2 basic RFC821/822 parsing R$+ at $+ $1@$2 "at" -> "@" for RFC 822 R$*<$*>$* $1$2$3 in case recursive # handle route-addr <@a,@b,@c:user@d> R@$+,$+ @$1:$2 change all "," to ":" R@$+:$+ $@<@$1>:$2 handle <route-addr> R$+:$*;@$+ $@$1:$2;@$3 list syntax # Rewrite address into a domain-based address. Any special mock domain names # (like UUX) should be defined on the CT line and removed (if necessary) # in S4. You can use them in S0 for designated transport mechanisms. # Delimiters with precendence over @. Add yours here. # The @ delimiter. Leave this alone. R$+@$+ $:$1<@$2> focus on domain R$+<$+@$+> $1$2<@$3> move gaze right R$+<@$+> $@$1<@$2> already canonical # Delimiters with precendece below @. Add yours here. R$+^$+ $1!$2 convert ^ to ! R$+!$-.$+!$+ $:$2.$3!$4 prefer domain /CBOSGD R$-!$+ $@$2<@$1.UUX> resolve uucp names R$+.!$+ $@$2<@$1> domain.!host R$+!$+ $@$2<@$1> domain!host # % is a low precedence @. R$*%$* $@$>3$1@$2 %->@ and retry ############################################################ ############################################################ ##### ##### RULESET ZERO PREAMBLE ##### ##### UUCP Project 11/01/85 ##### ############################################################ ############################################################ S0 # first make canonical R$*<$*>$* $1$2$3 defocus R$+ $:$>3$1 make canonical # handle special cases..... R@ $#local$:MAILER-DAEMON handle <> form R$*<@[$+]>$* $#tcp$@[$2]$:$1@[$2]$3 numeric internet spec # strip local stuff R$*<$*$w.$D>$* $1<$2>$3 thishost.mydom CF_GATEWAYR$*<$*$D>$* $1<$2>$3 mydom R$*<$*$w.$=D>$* $1<$2>$4 thishost.anydom R$*<$*$w.$A>$* $1<$2>$3 thishost.anotherdom R$*<$*$A>$* $1<$2>$3 anotherdom R$*<$*$w.$=T>$* $1<$2>$4 thishost.mockdom CF_GATEWAYR$*<$*$w>$* $1<$2>$3 thishost R$*<$*.>$* $1<$2>$3 drop trailing dot R<@>:$+ $@$>0$1 strip null route, retry R$+<@> $@$>0$1 strip null addr, retry ############################################### ### Machine dependent part of rulset zero ### ############################################### # Preemption: for a host on a known link turn the domain spec into a # mock domain indicating the link. One set of these rules for each of # the F classes listed in the local configuration options. R$*<$*$=U.$D>$* $:$1<$2$3.UUX>$4 uuxhost.mydomain R$*<$*$=U.$=D>$* $:$1<$2$3.UUX>$5 uuxhost.anydomain R$*<$*$=U.$A>$* $:$1<$2$3.UUX>$4 uuxhost.anotherdomain R$*<$*$=U.$=T>$* $:$1<$2$3.UUX>$5 uuxhost.mock-domain R$*<$*$=U>$* $:$1<$2$3.UUX>$4 uuxhost R$*<$*$=E.$D>$* $:$1<$2$3.ETHER>$4 etherhost.mydomain R$*<$*$=E.$=D>$* $:$1<$2$3.ETHER>$5 etherhost.anydomain R$*<$*$=E.$A>$* $:$1<$2$3.ETHER>$4 etherhost.anotherdomain R$*<$*$=E.$=T>$* $:$1<$2$3.ETHER>$5 etherhost.mock-domain R$*<$*$=E>$* $:$1<$2$3.ETHER>$4 etherhost # Designated delivery: use the indicated transport mechanism. One of # these rules for each of the mock domains defined in $=T. You can # remove these if you just want general disposition. HINTS. # Designated delivery: R$*<@$=U.UUX>$* $#uux$@$2$:$1$3 known uucphost R$*<@$+.ETHER>$* $#ether$@$2$:$1@$2$3 etherhost # throw out mock domain name now R$*<$*.$=T>$* $1<$2>$4 # General disposition of remote mail (comment out all but one). You # might add to this list, if you have other "smarter" mailers. HINTS. R$*<@$*>$* $#uux$@$2$:$1$3 hand to uucp #R$*<@$*>$* $#uux$@$R$:$1@$2$3 hand to uucp relay #R$*<@$*>$* $#ether$@$R$:$1@$2$3 hand to ether relay #R$*<$*>$* $#error$:unkown address $1$2$3 don't hand anywhere # local delivery R$+ $#local$:$1 user ############################################################ ############################################################ ##### ##### Local and Program Mailer specification ##### ##### @(#)localm.m4 4.1 7/25/83 ##### ############################################################ ############################################################ CF_SVMAILMlocal, P=CF_LOCALMAIL, F=lsDFMhumSU, S=10, R=20, A=rmail $u CF_BSMAILMlocal, P=CF_LOCALMAIL, F=rlsDFMmn, S=10, R=20, A=mail -d $u Mprog, P=/bin/sh, F=lsDFMe, S=10, R=20, A=sh -c $u S10 R@ MAILER-DAEMON errors to mailer-daemon ############################################################ ############################################################ ##### ##### UUCP Mailer specification ##### ##### UUCP Project 11/01/85 ##### ############################################################ ############################################################ # Top two lines use uux, bottom two use smail. For UUCP to handle domain # style addressing, you must use smail. HINTS. #Muux, P=/usr/bin/uux, F=sDFMuU, S=13, R=23, M=65535, # A=uux - $h!rmail ($u) Muux, P=/bin/smail, F=sDFMhum, S=14, R=24, M=100000, A=smail -r -vH$j $h!$u # S13 rewrites return addresses for uux as smail would: turn "user" into # "host!user" and "user@domain" to "host!domain!user". Unfortunately, # this also affects the From: line (which is wrong). S13 R$+<@$w.UUX> $@$1<@$w.UUX> intercept 2nd pass R$+ $:$>4$1 externalize R$*@$* $2!$1 a@b -> b!a R$+ $@$1<@$w.UUX> tack on our name S14 R$+<@$=E> $1 u@cbpavo->u/CBOSGD R$*<@$+>$* $@$1<@$2>$3 already ok #R$+ $@$1<@$w> tack on our hostname R$+ $@$1<@$j> tack on our full address ############################################################ ############################################################ ##### ##### SMTP ethernet mailer ##### ##### UUCP Project 11/01/85 ##### ############################################################ ############################################################ Mether, P=[IPC], F=msDFMuCXP, S=11, R=21, A=IPC $h S11 R$*<@$+>$* $@$1<@$2>$3 already ok R$+ $@$1<@$w> tack on our hostname #R$+ $@$1<@$j> tack on our full address ################################# # Final Output Post-rewriting # ################################# # This rewrites the internal $=T mock domains into their external form. # The default is to replace the mock domain name with $D. The last # two lines are stock. S4 R@ $@ handle <> error addr R$+<@$-.UUX> $2!$1 u@host.UUX => host!u R$*<$*$=T>$* $:$1<$2$D>$4 change local info R$*<$+>$* $1$2$3 defocus R@$+:$+:$+ $@@$1,$2:$3 <route-addr> canonical END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 10487 ] then echo $filename changed - should be 10487 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file smail.cf.sh ---------- filename="smail.cf.sh" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file smail.cf.sh... fi cat << 'END-OF-FILE' > $filename cat <<!EOM! This script will prompt you for the automatically configurable parameters in the stock version of the sendmail configuration file. Naturally, any local extensions will have to be added manually. Below is a trace of the session that configured the sendmail.cf on clyde.ATT.COM. This is a System V machine with sendmail. === !EOM! echo "press return to continue"; read foo cat <<!EOM! Enter Date (MM-DD-YY): 06-24-86 Enter This Host's Name: clyde Enter This Host's Official Domain: ATT.COM Enter Any Equivalent Domain Classes: ATT Enter Any Domains For Which This Host Is An Authority: ATT.UUCP Does This Host Have SMTP Connections (y/n)? no Enter Full Path to Executable That Will Provide Local Mail Delivery: /bin/lmail Is /bin/lmail A Berkeley Mailer [i.e., use -r to specify sender] (y/n)? no Will This Host Act As A Gateway Between Domains (y/n)? yes === !EOM! # get date of configuration CF_DATE=`/bin/sh ./smail.prompt string "Enter Date (MM-DD-YY):"` # get host name CF_HOST=`/bin/sh ./smail.prompt string "Enter This Host's Name:"` # get host domain CF_DOMAIN=`/bin/sh ./smail.prompt string "Enter This Host's Official Domain:"` # get domain classes CF_DCLASS=`/bin/sh ./smail.prompt string "Enter Any Equivalent Domain Classes:"` # get domain authority CF_AUTHORITY=`/bin/sh ./smail.prompt string "Enter Any Domains For Which This Host Is An Authority:"` CF_SMTP=`/bin/sh ./smail.prompt yesno "Does This Host Have SMTP Connections (y/n)?"` if test "$CF_SMTP" = "yes" then #get list of local SMTP connections CF_SMTP=`/bin/sh ./smail.prompt file "Enter Full Path to File that Contains List of SMTP Connections:"` CF_SMTP="FE$CF_SMTP %s" else CF_SMTP="" fi # get path to local delivery agent CF_LOCALMAIL=`/bin/sh ./smail.prompt file "Enter Full Path to Executable That Will Provide Local Mail Delivery:"` CF_SYSTEM=`/bin/sh ./smail.prompt yesno "Is $CF_LOCALMAIL A Berkeley Mailer [i.e., use -r to specify sender] (y/n)?"` if test "$CF_SYSTEM" = "yes" then CF_SVMAIL="#" CF_BSMAIL="" else CF_SVMAIL="" CF_BSMAIL="#" fi CF_GATEWAY=`/bin/sh ./smail.prompt yesno "Will This Host Act As A Gateway Between Domains(y/n)?"` if test "$CF_GATEWAY" = "yes" then CF_GATEWAY="" else CF_GATEWAY="#" fi sed \ -e "s/CF_HOST/Dw$CF_HOST/" \ -e "s/CF_DOMAIN/DD$CF_DOMAIN/" \ -e "s/CF_AUTHORITY/DA$CF_AUTHORITY/" \ -e "s/CF_DCLASS/CDUUCP $CF_DCLASS/" \ -e "s;CF_SMTP;$CF_SMTP;" \ -e "s;CF_DATE;$CF_DATE;" \ -e "s;CF_LOCALMAIL;$CF_LOCALMAIL;" \ -e "s;CF_BSMAIL;$CF_BSMAIL;" \ -e "s;CF_SVMAIL;$CF_SVMAIL;" \ -e "s;CF_GATEWAY;$CF_GATEWAY;" \ smail.cf.form > sendmail.cf END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 2604 ] then echo $filename changed - should be 2604 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file smail.prompt ---------- filename="smail.prompt" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file smail.prompt... fi cat << 'END-OF-FILE' > $filename loop=true while test $loop = true do case "$1" in string) echo "$2" 1>&2 read ans if test ! -z "$ans" then echo $ans loop=false; fi ;; file) echo "$2" 1>&2 read ans case "$ans" in /*) if test -f "$ans" then echo $ans loop=false; else echo "file '$ans' not found" 1>&2 fi ;; *) echo "must give FULL PATH to file" 1>&2 ;; esac ;; yesno) echo "$2" 1>&2 read ans case "$ans" in y|Y|yes|Yes|YES) echo "yes" loop=false ;; n|N|no|No|NO) echo "no" loop=false ;; *) echo "Please enter yes or no" 1>&2 ;; esac ;; *) echo "usage: $0 string|yesno prompt_message" 1>&2 echo BOGUS_PROMPT_STRING loop=false ;; esac done END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 717 ] then echo $filename changed - should be 717 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file sysexits.h ---------- filename="sysexits.h" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file sysexits.h... fi cat << 'END-OF-FILE' > $filename # define EX_OK 0 /* successful termination */ # define EX_USAGE 64 /* command line usage error */ # define EX_NOHOST 68 /* host name unknown */ # define EX_UNAVAILABLE 69 /* service unavailable */ # define EX_SOFTWARE 70 /* internal software error */ # define EX_OSFILE 72 /* critical OS file missing */ # define EX_CANTCREAT 73 /* can't create (user) output file */ END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 368 ] then echo $filename changed - should be 368 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file u.Path.local ---------- filename="u.Path.local" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file u.Path.local... fi cat << 'END-OF-FILE' > $filename # Put local aliases in this file that you don't want to bother # the whole world with (e.g. pc's) or that you want to keep secret. # Change the name of the file to u.Path.`hostname` myname topsecret(DIRECT), 4urisonly(LOCAL), mypc(LOCAL) END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 238 ] then echo $filename changed - should be 238 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file u.Path.top.1 ---------- filename="u.Path.top.1" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file u.Path.top.1... fi cat << 'END-OF-FILE' > $filename # # DOMAIN GATEWAY LIST FOR UUCP AND OTHER TOP-LEVEL DOMAINS # This file is pathalias input for UUCP domain addressing. # # All gateway hosts (those in this file) must understand domain!user. # Note that most lines are still commented out, because many hosts don't # recognise domain!user. # # Problems and changes to domains@registry.UUCP # 9/20/85 # TOP LEVEL DOMAINS AND THEIR GATEWAYS # UUCP top level domain cbosgd .uucp ihnp4 .uucp seismo .uucp ucbvax .uucp harvard .uucp mcvax .uucp decuac .uucp # ARPANET and related domains seismo .arpa, .com, .gov, .edu, .org, .net, .us ucbvax .arpa, .com, .gov, .edu, .org, .net, .us harvard .arpa, .com, .gov, .edu, .org, .net, .us talcott .arpa, .com, .gov, .edu, .org, .net, .us # CSNET harvard .csnet talcott .csnet # MAILNET harvard .mailnet talcott .mailnet # BITNET psuvax1 .bitnet talcott .bitnet # United Kingdom/Great Britain ucl-cs .uk, .gb ukc .uk, .gb # Israel humus .il, .israel # Korea kaist .kr, .korea # Australia munnari .au, .oz, .oz.au mulga .au, .oz, .oz.au # Canada ubc-vision .ca, .cdn, .canada watmath .ca, .cdn, .canada garvield .ca, .cdn, .canada # Japan kddlab .jp, .junet, .jpn END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 1168 ] then echo $filename changed - should be 1168 bytes, not $size bytes fi chmod 644 $filename fi # ---------- file u.Path.uucp.1 ---------- filename="u.Path.uucp.1" if [ -f $filename ] then echo File \"$filename\" already exists\! Skipping... filename=/dev/null # throw it away else echo extracting file u.Path.uucp.1... fi cat << 'END-OF-FILE' > $filename # # DOMAIN GATEWAY LIST FOR UUCP AND OTHER TOP-LEVEL DOMAINS # This file is pathalias input for UUCP domain addressing. # # All gateway hosts (those in this file) must understand domain!user. # # Problems and changes to domains@registry.UUCP # 9/20/85 # TOP LEVEL REGISTRY ALIAS (.UUCP implied) cbosgd = registry # UUCP 2nd level domains and their gateways. #O AT&T cbosgd .att.com, .att # will include ihnp4 as soon as ihnp4 handles subdom.mach.att.com #O BellCore - Bell Communications Research bellcore .bellcore.com mouton .bellcore.com #O Digital Equipment Corp decuac .dec.com, .dec decwrl .dec.com, .dec #O Concurrent Computer Corporation peora .ccur.uucp END-OF-FILE if [ "$filename" != "/dev/null" ] then size=`wc -c < $filename` if [ $size != 674 ] then echo $filename changed - should be 674 bytes, not $size bytes fi chmod 644 $filename fi echo done exit 0