Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!rsalz From: rsalz@uunet.UU.NET (Rich Salz) Newsgroups: comp.sources.unix Subject: v11i071: Smail, UUCP domain maielr, Part03/03 Message-ID: <1842@uunet.UU.NET> Date: Wed, 23-Sep-87 00:15:21 EDT Article-I.D.: uunet.1842 Posted: Wed Sep 23 00:15:21 1987 Date-Received: Fri, 25-Sep-87 02:48:00 EDT Organization: UUNET Communications Services, Arlington, VA Lines: 2200 Approved: rs@uunet.UU.NET Submitted-by: Larry Auton Posting-number: Volume 11, Issue 71 Archive-name: smail3/Part03 # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # lcasep.c main.c make.cf.sh map.c misc.c mkfnames.sh nptx.c patchlevel # pathproc.sh pw.c resolve.c smail.prompt str.c svbinmail.c sysexits.h # template.cf echo x - lcasep.c cat > "lcasep.c" << '//E*O*F lcasep.c//' /* ** convert the host name on a pathalias line to lower case */ #ifndef lint static char *sccsid="@(#)lcasep.c 2.5 (smail) 9/15/87"; #endif #include #include # define lower(c) ( isupper(c) ? c-'A'+'a' : c ) void exit(), perror(); main(argc, argv) int argc; char *argv[]; { FILE *ifp, *ofp; char buf[BUFSIZ]; register char *p; int c; extern int optind; extern char *optarg; ifp = stdin; ofp = stdout; while((c = getopt(argc, argv, "f:o:")) != EOF) { switch(c) { case 'f': if((ifp = fopen(optarg, "r")) == NULL) { (void) fprintf(stderr, "%s: can't open %s: ", argv[0], optarg); perror(""); exit(1); } break; case 'o': if((ofp = fopen(optarg, "w")) == NULL) { (void) fprintf(stderr, "%s: can't open %s: ", argv[0], optarg); perror(""); exit(1); } break; default: (void) fprintf(stderr, "usage: %s [-f file] [-o outfile]\n", argv[0]); exit(1); /* NOTREACHED */ break; } } while(fgets(buf, sizeof(buf), ifp) != NULL) { for(p = buf; *p != '\t' && *p != '\0' ; p++) { (void) fputc(lower(*p), ofp); } (void) fputs(p, ofp); } return(0); } //E*O*F lcasep.c// echo x - main.c cat > "main.c" << '//E*O*F main.c//' /* ** ** rmail/smail - UUCP mailer with automatic routing. ** ** Christopher Seiwald /+\ ** chris@cbosgd.att.com +\ ** January, 1985 \+/ ** */ #ifndef lint static char *sccsid="@(#)main.c 2.5 (smail) 9/15/87"; #endif /* ** ** usage: rmail [options] address... ** smail [options] address... ** options: ** -d debug - verbose and don't invoke mailers. ** -v verbose - just verbose. ** -A print mapped addresses. don't invoke mailers. ** -h hostname set hostname ** -H hostdomain set hostdomain (default hostname.MYDOM) ** -p pathfile path database filename ** -r force routing of host!address ** -R reroute even explicit path!user ** -l user@domain goes to local mailer ** -L all mail goes local ** -q number mail queueing cost threshold ** -m number limit on number of uux_noqueue jobs ** -u string string of flags for uux ** -F address name to substitute in From: line ** -a aliasfile aliases filename (not used with SENDMAIL) ** -n namelist list of full names for simple aliases */ #include #include #include "defs.h" 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 hostuucp[SMLBUF] = ""; /* built with hostname+".UUCP" */ char *pathfile = PATHS; /* or set by -p */ char *uuxargs = NULL; /* or set by -u */ char *aliasfile = #ifdef ALIAS ALIAS; /* or set by -a */ #else NULL; #endif char *fnlist = #ifdef FULLNAME FULLNAME; /* or set by -n */ #else NULL; #endif int queuecost = QUEUECOST; /* or set by -q */ char *from_addr = NULL; /* or set by -F */ int maxnoqueue = MAXNOQUEUE; /* or set by -m */ int getcost = #ifdef GETCOST 1; /* get cost of path even if not routing */ #else 0; #endif char *spoolfile = NULL; /* name of the file containing letter */ FILE *spoolfp; /* file pointer to spoolfile */ int spoolmaster = 0; /* indicates 'control' of spoolfile */ void spool(); /* ** ** rmail/smail: mail stdin letter to argv addresses. ** ** After processing command line options and finding our host and domain ** names, we map addresses into sets. Then we deliver. ** */ main(argc, argv) int argc; char *argv[]; { char *hostv[MAXARGS]; /* UUCP neighbor */ char *userv[MAXARGS]; /* address given to host */ int costv[MAXARGS]; /* cost of resolved route */ enum eform formv[MAXARGS]; /* invalid, local, or uucp */ char *p; int c; int printaddr = 0; /* or set by -A */ int nargc; char **nargv, **alias(); char *optstr = "cdvArRlLH:h:p:u:q:a:n:m:f:F:"; 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 */ while ((c = getopt(argc, argv, optstr)) != EOF) { switch ( c ) { case 'd': debug = YES; break; case 'v': debug = VERBOSE; break; case 'A': printaddr = 1; break; case 'F': from_addr = optarg; break; case 'r': routing = ALWAYS; break; case 'R': routing = REROUTE; break; case 'l': handle = JUSTUUCP; break; case 'L': handle = NONE; break; case 'f': spoolfile = optarg; break; case 'p': pathfile = optarg; break; case 'u': uuxargs = optarg; break; case 'a': aliasfile = optarg; break; case 'n': fnlist = optarg; break; case 'H': (void) strcpy(hostdomain, optarg); break; case 'h': (void) strcpy(hostname, optarg); break; case 'm': if(isdigit(*optarg)) { maxnoqueue = atoi(optarg); } break; case 'c': getcost = 1; break; case 'q': if(isdigit(*optarg)) { queuecost = atoi(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(); /* ** Spool the letter in a temporary file. */ nargc = argc - optind; if(printaddr == 0) { spool(nargc, &argv[optind]); } /* ** Do aliasing and fullname resolution */ nargv = alias(&nargc, &argv[optind]); /* ** Map argv addresses to . */ map(nargc, nargv, hostv, userv, formv, costv); /* ** If all we want it mapped addresses, print them and exit. */ if(printaddr) { int i; char abuf[SMLBUF]; for(i=nargc-1; i >= 0; i--) { if(formv[i] == ERROR) { (void) strcpy(abuf, nargv[i]); } else { build(hostv[i], userv[i], formv[i], abuf); } (void) fputs(abuf, stdout); if(i != 0) (void) putchar(' '); } (void) putchar('\n'); exit(0); } /* ** Deliver. */ deliver(nargc, hostv, userv, formv, costv); /* ** Exitstat was set if any resolve or deliver failed, otherwise 0. */ return( exitstat ); } //E*O*F main.c// echo x - make.cf.sh cat > "make.cf.sh" << '//E*O*F make.cf.sh//' #! /bin/sh # # @(#)make.cf.sh 2.5 (smail) 9/15/87 # cat < sendmail.cf //E*O*F make.cf.sh// echo x - map.c cat > "map.c" << '//E*O*F map.c//' #ifndef lint static char *sccsid="@(#)map.c 2.5 (smail) 9/15/87"; #endif # include # include # include "defs.h" extern int queuecost; /* ** ** map(): map addresses into sets. ** ** 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, costv) 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 costv[]; /* cost vector */ { int i, cost; enum eform resolve(); char *c; static char userbuf[BIGBUF], *userz; static char hostbuf[BIGBUF], *hostz; userz = userbuf; hostz = hostbuf; for( i=0; i "misc.c" << '//E*O*F misc.c//' /* ** Miscellaneous support functions for smail/rmail */ #ifndef lint static char *sccsid="@(#)misc.c 2.5 (smail) 9/15/87"; #endif # include # include # include # include "defs.h" #ifdef BSD # include # include #else # include # include #endif extern int exitstat; /* set if a forked mailer fails */ extern enum edebug debug; /* how verbose we are */ extern enum ehandle handle; /* what we handle */ extern char *uuxargs; /* arguments given to uux */ extern int queuecost; /* threshold for queueing mail */ extern int maxnoqueue; /* max number of uucico's */ extern enum erouting routing; /* when to route addresses */ extern char hostdomain[]; /* */ extern char hostname[]; /* */ extern char hostuucp[]; /* */ extern char *pathfile; /* location of path database */ extern char *spoolfile; /* file name of spooled message */ extern FILE *spoolfp; /* file ptr to spooled message */ extern int spoolmaster; /* set if creator of spoolfile */ extern struct tm *localtime(); struct tm *gmt, *loc; /* GMT and local time structure */ time_t now; /* current system time */ char nows[50]; /* time in ctime format */ char arpanows[50]; /* time in arpa format */ # ifdef LOG void log(command, from, size) char *command, *from; long size; { FILE *fd; char *logtime, tbuf[50]; int cmask; logtime = strcpy(tbuf, nows); logtime[16] = '\0'; logtime += 4; cmask = umask(0); fd = fopen(LOG, "a"); (void) umask(cmask); if (fd != NULL) { (void) fprintf(fd, "%s\t%ld\t%s\t%s\n", logtime, size, from, command); (void) fclose(fd); } } # endif # ifdef RECORD FILE * record(command, from, size) char *command, *from; long size; { FILE *fd; char *logtime, buf[SMLBUF]; int cmask; logtime = strcpy(buf, nows); 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); } while(fgets(buf, sizeof(buf), spoolfp) != NULL) { (void) fputs(buf, fd); } (void) fclose(fd); } # endif setdates() { time_t time(); struct tm *gmtime(); char *ctime(), *arpadate(); (void) time(&now); (void) strcpy(nows, ctime(&now)); gmt = gmtime(&now); loc = localtime(&now); (void) strcpy(arpanows, arpadate(nows)); } /* ** Note: This routine was taken from sendmail ** ** ARPADATE -- Create date in ARPANET format ** ** Parameters: ** ud -- unix style date string. if NULL, one is created. ** ** Returns: ** pointer to an ARPANET date field ** ** Side Effects: ** none ** ** WARNING: ** date is stored in a local buffer -- subsequent ** calls will overwrite. ** ** Bugs: ** Timezone is computed from local time, rather than ** from whereever (and whenever) the message was sent. ** To do better is very hard. ** ** Some sites are now inserting the timezone into the ** local date. This routine should figure out what ** the format is and work appropriately. */ char * arpadate(ud) register char *ud; { register char *p; register char *q; static char b[40]; extern char *ctime(); register int i; #ifndef BSD extern char *tzname[]; time_t t, time(); #else /* V7 and 4BSD */ struct timeb t; extern struct timeb *ftime(); extern char *timezone(); #endif /* ** Get current time. ** This will be used if a null argument is passed and ** to resolve the timezone. */ #ifndef BSD (void) time(&t); if (ud == NULL) ud = ctime(&t); #else /* V7 or 4BSD */ ftime(&t); if (ud == NULL) ud = ctime(&t.time); #endif /* ** Crack the UNIX date line in a singularly unoriginal way. */ q = b; p = &ud[8]; /* 16 */ if (*p == ' ') p++; else *q++ = *p++; *q++ = *p++; *q++ = ' '; p = &ud[4]; /* Sep */ *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' '; p = &ud[22]; /* 1979 */ *q++ = *p++; *q++ = *p++; *q++ = ' '; p = &ud[11]; /* 01:03:52 */ for (i = 8; i > 0; i--) *q++ = *p++; /* -PST or -PDT */ #ifndef BSD p = tzname[localtime(&t)->tm_isdst]; #else p = timezone(t.timezone, localtime(&t.time)->tm_isdst); #endif if (p[3] != '\0') { /* hours from GMT */ p += 3; *q++ = *p++; if (p[1] == ':') *q++ = '0'; else *q++ = *p++; *q++ = *p++; p++; /* skip ``:'' */ *q++ = *p++; *q++ = *p++; } else { *q++ = ' '; *q++ = *p++; *q++ = *p++; *q++ = *p++; } p = &ud[0]; /* Mon */ *q++ = ' '; *q++ = '('; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ')'; *q = '\0'; return (b); } /* * The user name "postmaster" must be accepted regardless of what * combination of upper and lower case is used. This function is * used to convert all case variants of "postmaster" to all lower * case. If the user name passed in is not "postmaster", it is * returned unchanged. */ char * postmaster(user) char *user; { static char *pm = "postmaster"; if(strcmpic(user, pm) == 0) { return(pm); } else { return(user); } } /* * Return 1 iff the string is "UUCP" (ignore case). */ isuucp(str) char *str; { if(strcmpic(str, "UUCP") == 0) { return(1); } else { return(0); } } /* ** sform(form) returns a pointer to a string that tells what 'form' means */ char * sform(form) enum eform form; { if(form == ERROR) return("ERROR"); if(form == LOCAL) return("LOCAL"); if(form == DOMAIN) return("DOMAIN"); if(form == UUCP) return("UUCP"); if(form == ROUTE) return("ROUTE"); return("UNKNOWN"); } /* ** ** 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, SMLBUF - 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); (void) strcat(strcpy(hostuucp, hostname), ".UUCP"); } //E*O*F misc.c// echo x - mkfnames.sh cat > "mkfnames.sh" << '//E*O*F mkfnames.sh//' #! /bin/sh # # @(#)mkfnames.sh 2.5 (smail) 9/15/87 # if test $# = 0 then sed 's/\(.*\):.*:.*:.*:\(.*\):.*:.*/\1 \2/' /etc/passwd else cat $* fi | # at this point, we have a list of login\tFull Name pairs nptx | lcasep | sort -u +0 -1 //E*O*F mkfnames.sh// echo x - nptx.c cat > "nptx.c" << '//E*O*F nptx.c//' #ifndef lint static char *sccsid = "@(#)nptx.c 2.5 (smail) 9/15/87"; #endif #include #include #include #include #include "defs.h" #include char *malloc(), *index(), *fullname(); void nptx(), free(), dotspace(); enum edebug debug; /* not used by nptx */ char *fnlist = NULL; /* not used by nptx */ main() { int i; char buf[SMLBUF], *p, *name, *last, *nick, *ctmp; while(gets(buf) != NULL) { /* line should be in form ** ** login First Last ** or ** login First Last(Nickname) ** */ if((p = index(buf, '\t')) == NULL) { (void) fprintf(stderr, "format error: %s\n", buf); continue; } *p++ = '\0'; name = fullname(p); dotspace(name); if (last = rindex(name, '.')) { last++; } else { last = NULL; } if ((nick = index(p, '(')) != NULL) { nick++; if ((ctmp = index(nick, ')')) == NULL) { nick = NULL; } else { *ctmp = '\0'; } } nptx(buf, name); if((last != NULL) && (nick != NULL)) { i=strlen(nick) + strlen(last) + 2; if((name = malloc(i)) != NULL) { (void) strcpy(name, nick); (void) strcat(name, "."); (void) strcat(name, last); dotspace(name); nptx(buf, name); free(name); } } } return(0); } void dotspace(s) char *s; { register char *p, *t; /* turn whitespace to '.' */ for(p = s; *p != '\0'; p++) { if((*p == ' ') || (*p == '\t')) { *p = '.'; } } /* elide leading '.'s */ for(p = s; *p == '.' ; p++) ; /* elide mulitple '.'s and all "'"s */ for(t = s; *p != '\0'; p++, t++) { *t = *p; if(*t == '\'') { t--; continue; } if(*p == '.') { while(*(++p) == '.') ; p--; } } *t = '\0'; /* elide trailing '.' */ if((t > s) && (*(--t) == '.')) *t = '\0'; } void nptx(login, name) char *login, *name; { int i,j,k,N,lim,mask; int ii,ji,ki,Ni,limi,maski; char nl[11][100], il[11][100]; char *pi, *p, *rindex(); char buf[100]; char bufi[100]; if((name == NULL) || (*name == '\0')) { return; } for(i=0; i < 10; i++) { if((p = rindex(name, '.')) == NULL) break; (void) strcpy(nl[i], p+1); *p = NULL; } (void) strcpy(nl[i], name); while((strcmpic(nl[i], "Mr" ) == 0) || (strcmpic(nl[i], "Dr" ) == 0) || (strcmpic(nl[i], "Mrs" ) == 0) || (strcmpic(nl[i], "Miss") == 0) || (strcmpic(nl[i], "Ms" ) == 0)) { i--; } while((strcmpic(nl[0], "Jr") == 0) || (strcmpic(nl[0], "Sr") == 0)) { for(j=0; j < i; j++) { (void) strcpy(nl[j], nl[j+1]); } i--; } N = i; lim = 1 << (N+1); for(mask = 1 << N ; mask < lim ; mask++) { buf[0] = '\0'; for(j = 1, k = N; j < lim; j <<=1, k--) { if(j & mask) { (void) strcat(buf, nl[k]); (void) strcat(buf, "."); } } if((p = rindex(buf, '.')) != NULL) { *p = '\0'; } for(ii=0; ii < 10; ii++) { if((pi = rindex(buf, '.')) == NULL) break; (void) strcpy(il[ii], pi+1); *pi = NULL; } (void) strcpy(il[ii], buf); Ni = ii; limi = 1 << (Ni+1); for(maski = 1 << Ni /* 0 */ ; maski < limi ; maski++) { bufi[0] = '\0'; for(ji = 1, ki = Ni; ji < limi; ji <<=1, ki--) { if(ji & maski) { (void) strcat(bufi, il[ki]); } else { char init[3]; init[0] = il[ki][0]; init[1] = '\0'; (void) strcat(bufi, init); } (void) strcat(bufi, "."); } if((pi = rindex(bufi, '.')) != NULL) { *pi = '\0'; } #ifdef DOT_REQD if(index(bufi, '.') == NULL) { continue; } #endif /* DOT_REQD */ (void) printf("%s\t%s\n",bufi, login); /* */ } } } //E*O*F nptx.c// echo x - patchlevel cat > "patchlevel" << '//E*O*F patchlevel//' Patch #: 00 //E*O*F patchlevel// echo x - pathproc.sh cat > "pathproc.sh" << '//E*O*F pathproc.sh//' # # @(#)pathproc.sh 2.5 (smail) 9/15/87 # # This script will do all that's necessary for # transforming the output of pathalias -f into # the format of a 'paths' file for smail. # # format of the pathalias -f output is # cost host route # # format of a 'paths' file for smail is # host route first_hop_cost # # move cost field to end of line # sed 's/\(.*\) \(.*\) \(.*\)/\2 \3 \1/'| # # convert target domain/host to lower case # lcasep | # # sort the stream # sort //E*O*F pathproc.sh// echo x - pw.c cat > "pw.c" << '//E*O*F pw.c//' #ifndef lint static char *sccsid = "@(#)pw.c 2.5 (smail) 9/15/87"; #endif #include #include #include #include #include "defs.h" #include char *malloc(); void free(); typedef struct pw_node pwlist; struct pw_node { char *lname; /* login name */ char *fname; /* full name */ int uid; /* user-id */ char *home; /* login name */ pwlist *vlink; /* link to next item */ }; pwlist *pwhead; /* head of linked list */ pwlist *pwparse(); /* head of linked list */ #define PNULL ((pwlist *) 0) char * pwfnam(user) char *user; { pwlist *f; /* ** check for previously cached user */ for(f=pwhead; f != NULL; f=f->vlink) { if(strcmp(user, f->lname) == 0) { return(f->fname); } } /* ** not found parse the password file */ while((f=pwparse()) != PNULL) { if(strcmp(user, f->lname) == 0) { return(f->fname); } } return(NULL); } char * pwuid(uid) int uid; { pwlist *f; /* ** check for previously cached user */ for(f=pwhead; f != NULL; f=f->vlink) { if(uid == f->uid) { return(f->lname); } } /* ** not found parse the password file */ while((f=pwparse()) != PNULL) { if(uid == f->uid) { return(f->lname); } } return(NULL); } #ifndef SENDMAIL char * tilde(user) char *user; { pwlist *f; /* ** check for previously cached user */ for(f=pwhead; f != NULL; f=f->vlink) { if(strcmp(user, f->lname) == 0) { return(f->home); } } /* ** not found parse the password file */ while((f=pwparse()) != PNULL) { if(strcmp(user, f->lname) == 0) { return(f->home); } } return(NULL); } #endif /* not SENDMAIL */ char * fullname(gecos) char *gecos; { static char fname[SMLBUF]; register char *cend; (void) strcpy(fname, gecos); if (cend = index(fname, ',')) *cend = '\0'; if (cend = index(fname, '(')) *cend = '\0'; /* ** Skip USG-style 0000-Name nonsense if necessary. */ if (isdigit(*(cend = fname))) { if ((cend = index(fname, '-')) != NULL) cend++; else /* ** There was no `-' following digits. */ cend = fname; } return (cend); } pwlist * pwparse() { pwlist *f; char *p, *name; struct passwd *pwent, *getpwent(); unsigned int i; static int pw_eof = 0; if((pw_eof == 1) || ((pwent = getpwent()) == (struct passwd *) NULL)) { pw_eof = 1; return(PNULL); } /* ** Get an entry from the password file. ** Parse relevant strings. */ f = (pwlist *) malloc(sizeof(pwlist)); if(f == PNULL) return(PNULL); f->vlink = pwhead; pwhead = f; f->uid = pwent->pw_uid; i=strlen(pwent->pw_name)+1; p = malloc(i); if(p == NULL) return(PNULL); f->lname = strcpy(p, pwent->pw_name); i=strlen(pwent->pw_dir)+1; p = malloc(i); if(p == NULL) return(PNULL); f->home = strcpy(p, pwent->pw_dir); name = fullname(pwent->pw_gecos); i=strlen(name)+1; p = malloc(i); if(p == NULL) return(PNULL); f->fname = strcpy(p, name); return(f); } #ifdef FULLNAME /* ** Resolve a full name to a login name. ** Not too much smarts here. */ char * res_fname(user) register char *user; { long pos, middle, hi, lo; static long pathlength = 0; register char *s; int c; static FILE *file; int flag; char namebuf[SMLBUF], *path; extern enum edebug debug; extern char *fnlist; DEBUG("res_fname: looking for '%s'\n", user); if(pathlength == 0) { /* open file on first use */ if((file=fopen(fnlist, "r")) == NULL) { DEBUG( "can't access %s.\n", fnlist); pathlength = -1; } else { (void) fseek(file, 0L, 2); /* find length */ pathlength = ftell(file); } } if(pathlength == -1 ) return(NULL); lo = 0; hi = pathlength; path = namebuf; (void) strcpy( path, user ); (void) strcat( path, "\t" ); for( ;; ) { pos = middle = ( hi+lo+1 )/2; (void) fseek( file, pos, 0 ); /* find midpoint */ if (pos != 0) /* to beginning of next line */ while( ( c=getc( file ) ) != EOF && c != '\n' ); for( flag = 0, s = path; flag == 0; s++ ) { /* match??? */ if ( *s == '\0' ) { goto solved; } c = getc( file ); flag = lower( c ) - lower( *s ); } if (lo >= middle) /* failure? */ return(NULL); 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 != '\t') && (c != '\n')) { *path++ = c; } if(path == namebuf) { /* NULL alias field */ return(NULL); } *path = '\0'; if((path = malloc((unsigned) strlen(namebuf)+1)) == NULL) { return(NULL); /* sorry, no memory */ } (void) strcpy(path, namebuf); return(path); } #endif /* FULLNAME */ //E*O*F pw.c// echo x - resolve.c cat > "resolve.c" << '//E*O*F resolve.c//' /* ** ** Resolve.c ** ** Routes then resolves addresses into UUCP or LOCAL. ** */ #ifndef lint static char *sccsid="@(#)resolve.c 2.5 (smail) 9/15/87"; #endif #include #include #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[]; /* */ extern char hostname[]; /* */ extern char *pathfile; /* location of path database */ extern int getcost; /* get path cost even if not routing */ char *sform(); /* ** ** rsvp(): how to resolve addresses. ** ** After parsing an address into
, 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 justuucp none */ { ERROR, ERROR, ERROR }, /* error */ { LOCAL, LOCAL, LOCAL }, /* local */ { ROUTE, LOCAL, LOCAL }, /* domain */ { UUCP, UUCP, LOCAL }, /* uucp */ { ERROR, ERROR, ERROR }}; /* route */ /* ** ** resolve(): resolve addresses to . ** ** This is a gnarly piece of code, but it does it all. Each section ** is documented. ** */ enum eform resolve( address, domain, user , cost) char *address; /* the input address */ char *domain; /* the returned domain */ char *user; /* the returned user */ int *cost; /* the returned cost */ { 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. Otherwise, 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 of the address. */ for( i = parts - 1; i >= 0; i-- ) { /* ** Parse the address. */ (void) strcpy( temp, partv[i] ); form = parse( temp, domain, user ); DEBUG("resolve: parse address '%s' = '%s' @ '%s' (%s)\n", temp,user,domain,sform(form)); /* ** If we are looking at a substring (that's not the entire string) ** which parses to a LOCAL address, we skip to the next larger substring. */ if((i != 0) && (form == LOCAL)) continue; /* ** Routing, when required, is the next step. ** We route the address if we have a ROUTE form ** or if we have a UUCP form and we are told to ** route ALWAYS or REROUTE (i.e., routing != JUSTDOMAIN) */ if((rsvp( form ) == ROUTE) ||((rsvp( form ) == UUCP) && (routing != JUSTDOMAIN ))) { int look_smart = 0; if((routing == REROUTE) && (i == 0)) { look_smart = 1; /* last chance */ } /* route() puts the new route in 'temp' */ if(route(domain,user,look_smart,temp,cost) != EX_OK) { continue; /* If routing fails, try /* next larger substring. /* */ } /* ** After routing, reparse the new route into domain and user. */ form = parse( temp, domain, user ); DEBUG("resolve: parse route '%s' = '%s' @ '%s' (%s)\n", temp,user,domain,sform(form)); } else if((getcost) && (rsvp(form) == UUCP)) { /* get the cost of the route ** even if we're not going route the mail. ** this allows smart decisions about using ** the -r flag to uux when we're not routing. */ char junk[SMLBUF]; if(route(domain,user,0,junk,cost) != EX_OK) { continue; /* If routing fails, try /* next larger substring. /* */ } } break; /* route is resolved */ } /* ** For LOCAL mail in non-local format, we rewrite the full address into ** and leave 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 an address but failed (form == ERROR), ** or after routing we are left with an address that still needs to ** be routed (rsvp( form ) == ROUTE), complain. */ if ((form == ERROR) || (rsvp( form ) == ROUTE )) { exitstat = EX_NOHOST; ADVISE("resolve failed '%s' = '%s' @ '%s' (%s)\n", address, user, domain, sform(form)); form = ERROR; } else { ADVISE("resolve '%s' = '%s' @ '%s' (%s)\n", address, user, domain, sform(form)); } return ( form ); } /* ** ** route(): route domain, plug in user. ** ** Less complicated than it looks. Each section is documented. ** */ route(domain, user, look_smart, result, cost) char *domain; /* domain or host name */ char *user; /* user name */ int look_smart; /* do we try to route through a smarter host? */ char *result; /* output route */ int *cost; /* cost of output route */ { int uucpdom = 0; 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. */ temp[0] = '.'; (void) strcpy(temp+1, domain ); domains = ssplit( temp+1, '.', domainv ); /* ** check target domain for the local host name and host domain. ** if it matches, then skip the lookup in the database. ** this prevents mail loops for cases where SMARTHOST is defined ** in the routing table, but the local host is not. It also is ** a little faster when the local host is the target domain. */ if((strcmpic(domain, hostname) == 0) || (strcmpic(domain, hostdomain) == 0)) { step = 0; *cost = 0; (void) strcpy(path, "%s"); DEBUG("route: '%s' is local\n", domain); goto route_complete; } /* If the domain ends in .UUCP, trim that off. */ if((domains > 0) && isuucp(domainv[domains-1])) { domains--; domainv[domains][-1] = '\0'; uucpdom = 1; } /* ** 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? ) ** SMARTHOST ** Returns with error if we find no path. */ for(step = 0; (step < domains); step++) { if((getpath(domainv[step]-1, path, cost) == EX_OK) /* w/ dot */ || (getpath(domainv[step] , path, cost) == EX_OK))/* no dot */ break; } if(step == domains) { /* ** we've looked at each component of the domain without success */ /* ** If domain is a UUCP address, look for a UUCP gateway. */ if((uucpdom == 0) || (getpath(".UUCP", path, cost) != EX_OK)) { /* ** The domain not is a UUCP address, or we can't ** find a UUCP gateway. If this is our last chance, ** look for a smarter host to deliver the mail. */ if((look_smart == 0) || (getpath(SMARTHOST, path, cost) != EX_OK)) { /* ** All our efforts have been in vain. ** Tell them the bad news. */ DEBUG("route '%s' failed\n", domain); return( EX_NOHOST ); } } } route_complete: DEBUG("route: '%s' (%s) = '%s' (%d)\n", domain, domainv[step]?domainv[step]:"NULL", path, *cost); /* ** If we matched on the entire domain name, this address is fully resolved, ** and we plug into it. If we matched on only part of the domain ** name, we plug ! in. */ build(domain, user, (step == 0) ? LOCAL : UUCP, temp); (void) sprintf(result, path, temp); return( EX_OK ); } //E*O*F resolve.c// echo x - smail.prompt cat > "smail.prompt" << '//E*O*F smail.prompt//' # # @(#)smail.prompt 2.5 (smail) 9/15/87 # 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 //E*O*F smail.prompt// echo x - str.c cat > "str.c" << '//E*O*F str.c//' #ifndef lint static char *sccsid="@(#)str.c 2.5 (smail) 9/15/87"; #endif #include "defs.h" #include /* ** strncmpic: string compare, ignore case, stop after 'n' chars */ strncmpic(s1, s2, n) char *s1, *s2; int n; { register char *u = s1; register char *p = s2; while((n > 0) && (*p != '\0')) { /* chars match or only case different */ if(lower(*u) == lower(*p)) { p++; /* examine next char */ u++; } else { break; /* no match - stop comparison */ } n--; } if(n > 0) { return(lower(*u) - lower(*p)); /* return "difference" */ } else { return(0); } } /* ** strcmpic: string compare, ignore case */ strcmpic(s1, s2) char *s1, *s2; { register char *u = s1; register char *p = s2; while(*p != '\0') { /* chars match or only case different */ if(lower(*u) == lower(*p)) { p++; /* examine next char */ u++; } else { break; /* no match - stop comparison */ } } return(lower(*u) - lower(*p)); /* return "difference" */ } //E*O*F str.c// echo x - svbinmail.c cat > "svbinmail.c" << '//E*O*F svbinmail.c//' #ifndef lint static char *sccsid = "@(#)svbinmail.c 2.5 (smail) 9/15/87"; #endif /* */ /* 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 #include "defs.h" #ifdef SENDMAIL #define SENDER SENDMAIL #else #define SENDER "/bin/rmail" #endif #define READER "/bin/lmail" #define TRUE 1 #define FALSE 0 char prog[128]; void perror(), exit(), usage(); 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; j < 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); } //E*O*F svbinmail.c// echo x - sysexits.h cat > "sysexits.h" << '//E*O*F sysexits.h//' /* ** @(#)sysexits.h 2.5 (smail) 9/15/87 */ # 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 */ # define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ //E*O*F sysexits.h// echo x - template.cf cat > "template.cf" << '//E*O*F template.cf//' ############################################################ # # SENDMAIL CONFIGURATION FILE # # supports internet style addressing # over UUCP and ethernet links. # # A product of the UUCP Project. # # @(#)template.cf 2.5 (smail) 9/15/87 # ############################################################ ############################################################ # # Local configuration options - HINTS # # Host name and domain name macros. # # Dw sets $w # DD sets $D # CD sets $=D # # $D and $=D list all domains in which this host sits. # $D goes into outbound addresses, i.e. "user@$w.$D". # $A is another domain for which this host is 'authoritative' # it will will be turned into $D. CF_HOST CF_DOMAIN CF_AUTHORITY CF_DCLASS # Preemptive ether 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 # 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 # ############################################################ DVsmail2.5/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 OWvoidpasswords ############################### # 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 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 precedence 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 precedence below @. Add yours here. R$+^$+ $1!$2 convert ^ to ! 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 # ############################################################ S0 # first make canonical R$*<$*>$* $1$2$3 defocus R$+ $:$>3$1 make canonical # handle special cases..... R@ $#local$:MAILER-DAEMON handle <> form R$*<@[$+]>$* $#ether$@[$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 ruleset 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$*<$*$=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$*<@$=E$+.ETHER>$* $#ether$@$2$:$1@$2$4 known etherhost 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 forward to $2 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$:unknown address $1$2$3 don't hand anywhere # local delivery R$+ $#local$:$1 user ############################################################ # # Local and Program Mailer specification # ############################################################ 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 CF_HIDDENHOSTSR$+<@$+.$j>$* $1<@$j>$3 hide anydom.$j under $j S20 ############################################################ # # UUCP Mailer specification # ############################################################ Muux, P=/bin/smail, F=sDFMhum, S=14, R=24, M=100000, A=smail -vH$j $h!$u S14 R$+<@$=E> $1 user@etherhost -> user R$*<@$+>$* $@$1<@$2>$3 already ok CF_HIDDENHOSTSR$+<@$+.$j>$* $1<@$j>$3 hide anydom.$j under $j R$+ $@$1<@$j> add our full address S24 ############################################################ # # SMTP ethernet mailer # ############################################################ Mether, P=[IPC], F=msDFMuCXP, S=11, R=21, A=IPC $h S11 R$*<@$+>$* $@$1<@$2>$3 already ok R$+ $@$1<@$w> add our hostname S21 ################################# # 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 canonical //E*O*F template.cf// exit 0