home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume11
/
smail3
/
part02
< prev
next >
Wrap
Text File
|
1987-09-21
|
55KB
|
2,449 lines
Subject: v11i070: Smail, UUCP domain mailer, Part02/03
Newsgroups: comp.sources.unix
Sender: sources
Approved: rs@uunet.UU.NET
Submitted-by: Larry Auton <clyde.ATT.COM!lda>
Posting-number: Volume 11, Issue 70
Archive-name: smail3/Part02
# 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:
# Makefile alias.c defs.h deliver.c getopt.c getpath.c headers.c
echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
# Makefile for smail (not a installation makefile)
# @(#)Makefile 2.5 (smail) 9/15/87
CFLAGS = -O
#
# System V Release 2.0 sites can use -lmalloc for a faster malloc
#
#LIBS = -lmalloc
OBJECTS = main.o map.o resolve.o deliver.o misc.o alias.o pw.o headers.o getpath.o str.o getopt.o
all: smail svbinmail lcasep pathproc mkfnames nptx
smail: $(OBJECTS)
cc $(CFLAGS) $(OBJECTS) -o smail $(LIBS)
$(OBJECTS): defs.h
cc $(CFLAGS) -c $<
svbinmail: svbinmail.c defs.h
cc $(CFLAGS) svbinmail.c -o svbinmail
lcasep: lcasep.c
cc $(CFLAGS) lcasep.c -o lcasep
pathproc: pathproc.sh
cp pathproc.sh pathproc
chmod 755 pathproc
mkfnames: mkfnames.sh
cp mkfnames.sh mkfnames
chmod 755 mkfnames
nptx: nptx.o pw.o str.o
cc $(CFLAGS) nptx.o pw.o str.o -o nptx $(LIBS)
nptx.o: nptx.c
cc $(CFLAGS) -c nptx.c
install: all
@echo read doc/Install
clean:
rm -f *.o *.ln a.out core
clobber: clean
rm -f smail rmail lcasep pathproc mkfnames svbinmail nptx
//E*O*F Makefile//
echo x - alias.c
cat > "alias.c" << '//E*O*F alias.c//'
#ifndef lint
static char *sccsid = "@(#)alias.c 2.5 (smail) 9/15/87";
#endif
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include "defs.h"
#include <ctype.h>
extern enum edebug debug; /* verbose and debug modes */
extern char hostdomain[];
extern char hostname[];
extern char *aliasfile;
/*
**
** Picture of the alias graph structure
**
** head
** |
** v
** maps -> mark -> gjm -> mel -> NNULL
** |
** v
** sys -> root -> ron -> NNULL
** |
** v
** root -> mark -> chris -> lda -> NNULL
** |
** v
** NNULL
*/
typedef struct alias_node node;
static struct alias_node {
char *string;
node *horz;
node *vert;
};
#ifndef SENDMAIL
static node aliases = {"", 0, 0}; /* this is the 'dummy header' */
#endif /* not SENDMAIL */
/*
** lint free forms of NULL
*/
#define NNULL ((node *) 0)
#define CNULL ('\0')
/*
** string parsing macros
*/
#define SKIPWORD(Z) while(*Z!=' ' && *Z!='\t' && *Z!='\n' && *Z!=',') Z++;
#define SKIPSPACE(Z) while(*Z==' ' || *Z=='\t' || *Z=='\n' || *Z==',') Z++;
static int nargc = 0;
static char *nargv[MAXARGS];
void add_horz();
void load_alias(), strip_comments();
int recipients();
node *pop();
#ifndef SENDMAIL
node *v_search(), *h_search();
char *tilde();
#endif /* not SENDMAIL */
/* our horizontal linked list looks like a stack */
#define push add_horz
#define escape(s) ((*s != '\\') ? (s) : (s+1))
char **
alias(pargc, argv)
int *pargc;
char **argv;
{
/*
** alias the addresses
*/
int i;
char domain[SMLBUF], ubuf[SMLBUF], *user;
node *addr, addrstk;
node *flist, fliststk, *u;
#ifndef SENDMAIL
FILE *fp;
node *a;
char *home, buf[SMLBUF];
int aliased;
struct stat st;
#endif /* not SENDMAIL */
#ifdef FULLNAME
char *res_fname(); /* Does fullname processing */
#endif
addr = &addrstk;
flist = &fliststk;
user = ubuf;
addr->horz = NNULL;
flist->horz = NNULL;
/*
** push all of the addresses onto a stack
*/
for(i=0; i < *pargc; i++) {
push(addr, argv[i]);
}
/*
** for each adress, check for included files, aliases,
** full name mapping, and .forward files
*/
while((nargc < MAXARGS) && ((u = pop(addr)) != NNULL)) {
#ifndef SENDMAIL
if(strncmpic(u->string, ":include:", 9) == 0) {
/*
** make sure it's a full path name
** don't allow multiple sourcing
** of a given include file
*/
char *p = u->string + 9;
if((*p == '/')
&& (h_search(flist, p) == NULL)) {
push(flist, p);
if((stat(p, &st) >= 0)
&&((st.st_mode & S_IFMT) == S_IFREG)
&&((fp = fopen(p, "r")) != NULL)) {
while(fgets(buf, sizeof buf, fp)) {
(void) recipients(addr, buf);
}
(void) fclose(fp);
}
}
continue;
}
#endif /* not SENDMAIL */
/*
** parse the arg to see if it's to be aliased
*/
if(islocal(u->string, domain, ubuf) == 0) {
goto aliasing_complete;
}
/*
** local form - try to alias user
** aliases file takes precedence over ~user/.forward
** since that's the way that sendmail does it.
*/
#ifdef LOWERLOGNAME
/* squish 'user' into lower case */
for(user = ubuf; *user ; user++) {
*user = lower(*user);
}
#endif
user = escape(ubuf);
(void) strcpy(u->string, user); /* local => elide domain */
#ifndef SENDMAIL
/*
** check for alias - all this complication is necessary
** to handle perverted aliases like these:
** # mail to 's' resolves to 't' 'm' and 'rmt!j'
** s t,g,j,m
** g j,m
** j rmt!j
** # mail to 'a' resolves to 'rmt!d'
** a b c
** b c
** c rmt!d
** # mail to x resolves to 'x'
** x local!x
** # mail to 'y' resolves to 'y' and 'z'
** y \y z
*/
if(((a = v_search(user)) != NNULL)) {
char dtmpb[SMLBUF], utmpb[SMLBUF], *ut;
int user_inalias = 0;
node *t = a;
for(a = a->horz; a != NNULL; a=a->horz) {
if(islocal(a->string, dtmpb, utmpb)) {
#ifdef LOWERLOGNAME
/* squish 'utmpb' into lower case */
for(ut = utmpb; *ut ; ut++) {
*ut = lower(*ut);
}
#endif
ut = escape(utmpb);
#ifdef CASEALIAS
if(strcmp(ut, user) == 0)
#else
if(strcmpic(ut, user) == 0)
#endif
{
user_inalias = 1;
} else {
push(addr, a->string);
}
} else {
push(addr, a->string);
}
}
t->horz = NNULL; /* truncate horz list of aliases */
if(user_inalias == 0) {
continue;
}
}
if((home = tilde(user)) != NULL) {
/* don't allow multiple sourcing
** of a given .forward file
*/
if((h_search(flist, home) != NULL)) {
continue;
}
push(flist, home);
/*
** check for ~user/.forward file
** must be a regular, readable file
*/
(void) sprintf(buf, "%s/%s", home, ".forward");
if((stat(buf, &st) >= 0)
&&((st.st_mode & S_IFMT) == S_IFREG)
&&((st.st_mode & 0444) == 0444)
&&((fp = fopen(buf, "r")) != NULL)) {
aliased = 0;
while(fgets(buf, sizeof buf, fp)) {
aliased |= recipients(addr, buf);
}
(void) fclose(fp);
if(aliased) {
continue;
}
}
}
#endif /* not SENDMAIL */
#ifdef FULLNAME
/*
** Do possible fullname substitution.
*/
#ifdef DOT_REQD
if (index(user, '.') != NULL)
#endif
{
static char t_dom[SMLBUF], t_unam[SMLBUF];
char *t_user = res_fname(user);
if (t_user != NULL) {
if(islocal(t_user, t_dom, t_unam) == 0) {
/* aliased to non-local address */
push(addr, t_user);
continue;
}
if(strcmp(t_unam, user) != 0) {
/* aliased to different local address */
push(addr, t_unam);
continue;
}
}
}
#endif
aliasing_complete:
user = escape(u->string);
for(i=0; i < nargc; i++) {
if(strcmpic(nargv[i], user) == 0) {
break;
}
}
if(i == nargc) {
nargv[nargc++] = user;
}
}
*pargc = nargc;
return(nargv);
}
#ifndef SENDMAIL
/*
** v_search
** given an string, look for its alias in
** the 'vertical' linked list of aliases.
*/
node *
v_search(user)
char *user;
{
node *head;
node *a;
static int loaded = 0;
head = &aliases;
if(loaded == 0) {
load_alias(head, aliasfile);
loaded = 1;
}
for(a = head->vert; a != NNULL; a = a->vert) {
#ifdef CASEALIAS
if(strcmp(a->string, user) == 0)
#else
if(strcmpic(a->string, user) == 0)
#endif
{
break;
}
}
if(a == NNULL) { /* not in graph */
return(NNULL);
}
return(a);
}
/*
** h_search
** given an string, look for it in
** a 'horizontal' linked list of strings.
*/
node *
h_search(head, str)
node *head;
char *str;
{
node *a;
for(a = head->horz; a != NNULL; a = a->horz) {
#ifdef CASEALIAS
if(strcmp(a->string, str) == 0)
#else
if(strcmpic(a->string, str) == 0)
#endif
{
break;
}
}
return(a);
}
#endif /* not SENDMAIL */
/*
** load_alias
** parse an 'aliases' file and add the aliases to the alias graph.
** Handle inclusion of other 'aliases' files.
*/
void
load_alias(head, filename)
node *head;
char *filename;
{
FILE *fp;
node *v, *h, *add_vert();
char domain[SMLBUF], user[SMLBUF];
char *p, *b, buf[SMLBUF];
if((fp = fopen(filename,"r")) == NULL) {
DEBUG("load_alias open('%s') failed\n", filename);
return;
}
while(fgets(buf, sizeof buf, fp) != NULL) {
p = buf;
if((*p == '#') || (*p == '\n')) {
continue;
}
/*
** include another file of aliases
*/
if(strncmp(p, ":include:", 9) == 0) {
char *nl;
p += 9;
if((nl = index(p, '\n')) != NULL) {
*nl = CNULL;
}
DEBUG("load_alias '%s' includes file '%s'\n", filename, p);
load_alias(head, p);
continue;
}
/*
** if the first char on the line is a space or tab
** then it's a continuation line. Otherwise,
** we start a new alias.
*/
if(*p != ' ' && *p != '\t') {
b = p;
SKIPWORD(p);
*p++ = CNULL;
/*
** be sure that the alias is in local form
*/
if(islocal(b, domain, user) == 0) {
/*
** non-local alias format - skip it
*/
continue;
}
/*
** add the alias to the (vertical) list of aliases
*/
if((h = add_vert(head, user)) == NNULL) {
DEBUG("load_alias for '%s' failed\n", b);
return;
}
}
/*
** Next on the line is the list of recipents.
** Strip out each word and add it to the
** horizontal linked list.
*/
(void) recipients(h, p);
}
(void) fclose(fp);
/*
** strip out aliases which have no members
*/
for(v = head; v->vert != NNULL; ) {
if(v->vert->horz == NNULL) {
v->vert = v->vert->vert;
} else {
v = v->vert;
}
}
}
/*
** add each word in a string (*p) of recipients
** to the (horizontal) linked list associated with 'h'
*/
recipients(h, p)
node *h;
char *p;
{
char *b, d[SMLBUF], u[SMLBUF];
int ret = 0;
strip_comments(p); /* strip out stuff in ()'s */
SKIPSPACE(p); /* skip leading whitespace on line */
while((*p != NULL) && (*p != '#')) {
b = p;
if(*b == '"') {
if((p = index(++b, '"')) == NULL) {
/* syntax error - no matching quote */
/* skip the rest of the line */
return(ret);
}
} else {
SKIPWORD(p);
}
if(*p != CNULL) {
*p++ = CNULL;
}
/* don't allow aliases of the form
** a a
*/
if((islocal(b, d, u) == 0)
|| (strcmpic(h->string, u) != 0)) {
add_horz(h, b);
ret = 1;
}
SKIPSPACE(p);
}
return(ret);
}
/*
** some aliases may have comments on the line like:
**
** moderators moderator@somehost.domain (Moderator's Name)
** moderator@anotherhost.domain (Another Moderator's Name)
**
** strip out the stuff in ()'s
**
*/
void
strip_comments(p)
char *p;
{
char *b;
while((p = index(p, '(')) != NULL) {
b = p++; /*
** save pointer to open parenthesis
*/
if((p = index(p, ')')) != NULL) {/* look for close paren */
(void) strcpy(b, ++p); /* slide string left */
} else {
*b = CNULL; /* no paren, skip rest of line */
break;
}
}
}
/*
** add_vert - add a (vertical) link to the chain of aliases.
*/
node *
add_vert(head, str)
node *head;
char *str;
{
char *p, *malloc();
void free();
node *new;
/*
** strip colons off the end of alias names
*/
if((p = index(str, ':')) != NULL) {
*p = CNULL;
}
if((new = (node *) malloc(sizeof(node))) != NNULL) {
if((new->string = malloc((unsigned) strlen(str)+1)) == NULL) {
free(new);
new = NNULL;
} else {
(void) strcpy(new->string, str);
new->vert = head->vert;
new->horz = NNULL;
head->vert = new;
/*DEBUG("add_vert %s->%s\n", head->string, new->string);/* */
}
}
return(new);
}
/*
** add_horz - add a (horizontal) link to the chain of recipients.
*/
void
add_horz(head, str)
node *head;
char *str;
{
char *malloc();
node *new;
if((new = (node *) malloc(sizeof(node))) != NNULL) {
if((new->string = malloc((unsigned) strlen(str)+1)) == NULL) {
free(new);
new = NNULL;
} else {
(void) strcpy(new->string, str);
new->horz = head->horz;
new->vert = NNULL;
head->horz = new;
}
/*DEBUG("add_horz %s->%s\n", head->string, new->string);/* */
}
}
node *
pop(head)
node *head;
{
node *ret = NNULL;
if(head != NNULL) {
ret = head->horz;
if(ret != NNULL) {
head->horz = ret->horz;
}
}
return(ret);
}
//E*O*F alias.c//
echo x - defs.h
cat > "defs.h" << '//E*O*F defs.h//'
/*
**
** Defs.h: header file for rmail/smail.
**
** Configuration options for rmail/smail.
** default configuration is:
** full domain name is 'hostname.uucp' (get registered!)
** path file is /usr/lib/uucp/paths.
** no log, no record, use sendmail.
**
** You can change these in the next few blocks.
**
*/
/*
** @(#)defs.h 2.5 (smail) 9/15/87
*/
#ifndef VERSION
#define VERSION "smail2.5"
#endif
/*#define BSD /* if system is a Berkeley system */
/*#define SENDMAIL "/usr/lib/sendmail" /* Turn off to use /bin/(l)mail only */
#ifdef BSD
#define GETHOSTNAME /* use gethostname() */
#else
#define UNAME /* use uname() */
#endif
/* if defined, HOSTNAME overrides UNAME and GETHOSTNAME */
/*#define HOSTNAME "host" /* literal name */
/*#define HOSTDOMAIN "host.dom" /* overrides default HOSTNAME.MYDOM */
/*
* .UUCP here is just for testing, GET REGISTERED in COM, EDU, etc.
* See INFO.REGISTRY for details.
*/
#define MYDOM ".UUCP" /* literal domain suffix */
/*
* WARNING: DOMGATE is only for qualified gateways - use caution.
* If you don't fully understand it - don't use it!
* If you are not completely sure you need it - don't use it!
* If you are not prepared to handle all addresses to MYDOM - don't use it!
*
* if defined, DOMGATE (DOMain GATEway) will cause addresses of the form
*
* user@MYDOM or MYDOM!user
*
* (with and without the leading '.' on MYDOM)
* to be seen treated simply 'user' - a purely local address.
* Then, it is left to the aliasing code to map it back to a
* non-local address if necessary.
*/
/*#define DOMGATE /* Act as Domain Gateway */
/*
* HIDDENHOSTS allows hosts that serve as domain gateways to hide
* the subdomains beneath them. Mail that originates at any of
* the hosts in the subdomain will appear to come from the gateway host.
* Hence, mail from
*
* anything.hostdomain!user
*
* will appear to come from
*
* hostdomain!user
*
* A consequence is that return mail to hostdomain!user would need to
* be forwarded to the proper subdomain via aliases or other forwarding
* facilities.
*
* If you're using sendmail, then if defined here,
* it should be used in ruleset 4 of the sendmail.cf, too.
*/
/*#define HIDDENHOSTS /* hide subdomains of hostdomain */
/*
* Mail that would otherwise be undeliverable will be passed to the
* aliased SMARTHOST for potential delivery.
*
* Be sure that the host you specify in your pathalias input knows that you're
* using it as a relay, or you might upset somebody when they find out some
* other way. If you're using 'foovax' as your relay, and below you have
* #define SMARTHOST "smart-host", then the pathalias alias would be:
*
* smart-host = foovax
*/
#define SMARTHOST "smart-host" /* pathalias alias for relay host */
/*
** ALIAS and CASEALIAS are used only if SENDMAIL is NOT defined.
** Sites using sendmail have to let sendmail do the aliasing.
** LOWERLOGNAME maps all local login names into lower case. This
** helps sites who have only upper case send mail to mixed case sites.
*/
#define ALIAS "/usr/lib/aliases" /* location of mail aliases */
/*#define CASEALIAS /* make aliases case sensitive */
/*#define LOWERLOGNAME /* map local logins to lower case */
/*
* defining FULLNAME means that Full Name resolution
* will be attempted when necessary.
*
* the Full Name information will be taken from a
* list of {Full Name, address} pairs.
* The names in the list must be sorted
* without regard to upper/lower case.
*
* defining DOT_REQD says that the user name must contain a '.' for
* the Full Name search to be done.
*
* All full name searches are case insensitive.
*
*/
#define FULLNAME "/usr/lib/fullnames"
/* list of Full Name, address pairs */
/*#define DOT_REQD /* Must be George.P.Burdell@gatech.EDU
** not just Burdell@gatech.EDU
*/
/*
** PATHS is name of pathalias file. 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" /* location of the path database */
#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.
*/
#ifndef UUX
#define UUX "/usr/bin/uux" /* location of uux command */
#endif
#ifndef SMAIL
#define SMAIL "/bin/smail" /* location of smail command */
#endif
/*
** command used to retry failed mail, flag is used to set the routing level.
*/
#define VFLAG ((debug == VERBOSE)?"-v":"")
#define RETRY(flag) "%s %s %s -f %s ", SMAIL, VFLAG, flag, spoolfile
/*
** use the -a if you have it. This sometimes helps failed mail and warning
** messages get back to where the mail originated.
**
** some versions of uux can't do '-a' - pick one of the next two definitions
*/
#define RMAIL(flags,from,sys) "%s -a%s %s - %s!rmail",UUX,from,flags,sys /* */
/*#define RMAIL(flags,from,sys) "%s %s - %s!rmail",UUX,flags,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 LMAIL(frm,sys) "%s -em -f%s",SENDMAIL,frm
#define LARG(user) " '%s'",postmaster(user)
#define RLARG(sys,frm) " '%s!%s'",sys,frm
#define LFROM(frm,now,host) "From %s %.24s\n",frm,now
#else
#define HANDLE ALL
#define ROUTING JUSTDOMAIN
#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'",postmaster(user)
#define RLARG(sys,frm) " '%s!%s'",sys,frm
#define LFROM(frm,now,host) "From %s %.24s\n",frm,now
#endif
/*
** The following definitions affect the queueing algorithm for uux.
**
** DEFQUEUE if defined the default is to queue uux mail
**
** QUEUECOST remote mail with a cost of less than QUEUECOST
** will be handed to uux for immediate delivery.
**
** MAXNOQUEUE don't allow more than 'n' immediate delivery
** jobs to be started on a single invocation of smail.
**
** GETCOST if defined, the paths file will be searched for
** each address to discover the cost of the route.
** this allows informed decisions about whether to
** use the queue flags when calling uux. The price
** is in the overhead of a paths file search for
** addresses that are not going to be routed.
*/
#define DEFQUEUE /* default is to queue uux jobs */
#define QUEUECOST 100 /* deliver immediately if the cost
/* is DEDICATED+LOW or better */
#define MAXNOQUEUE 2 /* max UUX_NOQUEUE jobs */
#define GETCOST /* search for cost */
#define UUX_QUEUE "-r" /* uux flag for queueing */
#define UUX_NOQUEUE "" /* uux with immediate delivery */
/*
** Normally, all mail destined for the local host is delivered with a single
** call to the local mailer, and all remote mail is delivered with one call
** to the remote mailer for each remote host. This kind of 'batching' saves
** on the cpu overhead.
**
** MAXCLEN is used to limit the length of commands that are exec'd by smail.
** This is done to keep other program's buffers from overflowing, or to
** allow for less intelligent commands which can take only one argument
** at a time (e.g., 4.1 /bin/mail). To disable the batching, set MAXCLEN
** a small value (like 0).
*/
#define MAXCLEN 128 /* longest command allowed (approx.)
/* this is to keep other's buffers
** from overflowing
*/
/*
** PLEASE 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 500 /* 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 or domain!user */
UUCP, /* host!address */
ROUTE, /* intermediate form - to be routed */
SENT /* sent to a mailer on a previous pass */
};
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 <strings.h>
#include <sysexits.h>
#else
#include <string.h>
#include "sysexits.h"
#define index strchr
#define rindex strrchr
#endif
extern void exit(), perror();
extern unsigned sleep();
//E*O*F defs.h//
echo x - deliver.c
cat > "deliver.c" << '//E*O*F deliver.c//'
/*
** Deliver.c
**
** Routines to effect delivery of mail for rmail/smail.
**
*/
#ifndef lint
static char *sccsid="@(#)deliver.c 2.5 (smail) 9/15/87";
#endif
# include <stdio.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <ctype.h>
# include <signal.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 char hostdomain[]; /* our host's domain */
extern enum ehandle handle; /* what we handle */
extern enum erouting routing; /* how we're routing addresses */
extern char *uuxargs; /* arguments given to uux */
extern int queuecost; /* threshold for queueing mail */
extern int maxnoqueue; /* max number of uucico's */
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 char nows[]; /* local time in ctime(3) format*/
extern char arpanows[]; /* local time in arpadate format*/
char stderrfile[20]; /* error file for stderr traping*/
/*
**
** deliver(): hand the letter to the proper mail programs.
**
** 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.
**
** 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, costv)
int argc; /* number of addresses */
char *hostv[]; /* host names */
char *userv[]; /* user names */
enum eform formv[]; /* form for each address */
int costv[]; /* cost vector */
{
FILE *out; /* pipe to mailer */
FILE *popen(); /* to fork a mailer */
#ifdef RECORD
void record(); /* record all transactions */
#endif
#ifdef LOG
void log();
#endif
char *mktemp();
char from[SMLBUF]; /* accumulated from argument */
char lcommand[SMLBUF]; /* local command issued */
char rcommand[SMLBUF]; /* remote command issued */
char scommand[SMLBUF]; /* retry command issued */
char *command; /* actual command */
char buf[SMLBUF]; /* copying rest of the letter */
enum eform form; /* holds form[i] for speed */
long size; /* number of bytes of message */
char *flags; /* flags for uux */
char *sflag; /* flag for smail */
int i, j, status, retrying;
char *c, *postmaster();
int failcount = 0;
int noqcnt = 0; /* number of uucico's started */
char *uux_noqueue = UUX_NOQUEUE;/* uucico starts immediately */
char *uux_queue = UUX_QUEUE; /* uucico job gets queued */
off_t message;
struct stat st;
/*
** rewind the spool file and read the collapsed From_ line
*/
(void) fseek(spoolfp, 0L, 0);
(void) fgets(from, sizeof(from), spoolfp);
if((c = index(from, '\n')) != 0) *c = '\0';
message = ftell(spoolfp);
/*
** We pass through the list of addresses.
*/
stderrfile[0] = '\0';
for(i = 0; i < argc; i++) {
char *lend = lcommand;
char *rend = rcommand;
char *send = scommand;
/*
** If we don't have sendmail, arrange to trap standard error
** for inclusion in the message that is returned with failed mail.
*/
(void) unlink(stderrfile);
(void) strcpy(stderrfile, "/tmp/stderrXXXXXX");
(void) mktemp(stderrfile);
(void) freopen(stderrfile, "w", stderr);
if(debug != YES) {
(void) freopen(stderrfile, "w", stdout);
}
*lend = *rend = *send = '\0';
/*
** If form == ERROR, the address was bad
** If form == SENT, it has been sent on a previous pass.
*/
form = formv[i];
if (form == SENT) {
continue;
}
/*
** Build the command based on whether this is local mail or uucp mail.
** By default, don't allow more than 'maxnoqueue' uucico commands to
** be started by a single invocation of 'smail'.
*/
if(uuxargs == NULL) { /* flags not set on command line */
if(noqcnt < maxnoqueue && costv[i] <= queuecost) {
flags = uux_noqueue;
} else {
flags = uux_queue;
}
} else {
flags = uuxargs;
}
retrying = 0;
if(routing == JUSTDOMAIN) {
sflag = "-r";
} else if(routing == ALWAYS) {
sflag = "-R";
} else {
sflag = "";
}
(void) sprintf(lcommand, LMAIL(from, hostv[i]));
(void) sprintf(rcommand, RMAIL(flags, 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.
*/
/* we initialized lend (rend) to point at the
* beginning of its buffer, so that at
* least one address will be used regardless
* of the length of lcommand (rcommand).
*/
for (j = i; j < argc; j++) {
if ((formv[j] != form)
|| (strcmpic(hostv[i], hostv[j]) != 0)
|| ((lend - lcommand) > MAXCLEN)
|| ((rend - rcommand) > MAXCLEN)) {
continue;
}
/*
** seek to the end of scommand
** and add on a 'smail' command
** multiple commands are separated by ';'
*/
send += strlen(send);
if(send != scommand) {
*send++ = ';' ;
}
(void) sprintf(send, RETRY(sflag));
send += strlen(send);
lend += strlen(lend);
rend += strlen(rend);
if (form == LOCAL) {
(void) sprintf(lend, LARG(userv[j]));
(void) sprintf(send, LARG(userv[j]));
} else {
(void) sprintf(lend, RLARG(hostv[i], userv[j]));
(void) sprintf(send, RLARG(hostv[i], userv[j]));
}
(void) sprintf(rend, RARG(userv[j]));
formv[j] = SENT;
}
retry:
/*
** rewind the spool file and read the collapsed From_ line
*/
(void) fseek(spoolfp, message, 0);
/* if the address was in a bogus form (usually DOMAIN),
** then don't bother trying the uux.
**
** Rather, go straight to the next smail routing level.
*/
if(form == ERROR) {
static char errbuf[SMLBUF];
(void) sprintf(errbuf,
"address resolution ('%s' @ '%s') failed",
userv[i], hostv[i]);
command = errbuf;
size = 0;
goto form_error;
}
if (retrying) {
command = scommand;
} else if (form == LOCAL) {
command = lcommand;
} else {
command = rcommand;
if(flags == uux_noqueue) {
noqcnt++;
}
}
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.
*/
/*
** We may try to write on a broken pipe, if the uux'd host
** is unknown to us. Ignore this signal, since we can use the
** return value of the pclose() as our indication of failure.
*/
(void) signal(SIGPIPE, SIG_IGN);
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 smail.
*/
(void) sleep(60);
} while (++failcount < 10);
}
if(out == NULL) {
exitstat = EX_UNAVAILABLE;
(void) printf("couldn't execute %s.\n", command);
continue;
}
size = 0;
if(fstat(fileno(spoolfp), &st) >= 0) {
size = st.st_size - message;
}
/*
** Output our From_ line.
*/
if (form == LOCAL) {
#ifdef SENDMAIL
(void) sprintf(buf, LFROM(from, nows, hostname));
size += strlen(buf);
(void) fputs(buf, out);
#else
char *p;
if((p=index(from, '!')) == NULL) {
(void) sprintf(buf,
LFROM(from, nows, hostname));
size += strlen(buf);
(void) fputs(buf, out);
} else {
*p = NULL;
(void) sprintf(buf, RFROM(p+1, nows, from));
size += strlen(buf);
(void) fputs(buf, out);
*p = '!';
}
#endif
} else {
(void) sprintf(buf, RFROM(from, nows, hostname));
size += strlen(buf);
(void) fputs(buf, out);
}
#ifdef SENDMAIL
/*
** If using sendmail, insert a Received: line only for mail
** that is being passed to uux. If not using sendmail, always
** insert the received line, since sendmail isn't there to do it.
*/
if(command == rcommand && handle != ALL)
#endif
{
(void) sprintf(buf,
"Received: by %s (%s)\n\tid AA%05d; %s\n",
hostdomain, VERSION,
getpid(), arpanows);
size += strlen(buf);
(void) fputs(buf, out);
}
/*
** Copy input.
*/
while(fgets(buf, sizeof(buf), spoolfp) != NULL) {
(void) fputs(buf, out);
}
/*
** Get exit status and if non-zero, set global exitstat so when we exit
** we can indicate an error.
*/
form_error:
if (debug != YES) {
if(form == ERROR) {
exitstat = EX_NOHOST;
} else if (status = pclose(out)) {
exitstat = status >> 8;
}
/*
* The 'retrying' check prevents a smail loop.
*/
if(exitstat != 0) {
/*
** the mail failed, probably because the host
** being uux'ed isn't in L.sys or local user
** is unknown.
*/
if((retrying == 0) /* first pass */
&& (routing != REROUTE) /* have higher level */
&& (form != LOCAL)) { /* can't route local */
/*
** Try again using a higher
** level of routing.
*/
ADVISE("%s failed (%d)\ntrying %s\n",
command, exitstat, scommand);
exitstat = 0;
retrying = 1;
form = SENT;
goto retry;
}
/*
** if we have no other routing possibilities
** see that the mail is returned to sender.
*/
if((routing == REROUTE)
|| (form == LOCAL)) {
/*
** if this was our last chance,
** return the mail to the sender.
*/
ADVISE("%s failed (%d)\n",
command, exitstat);
(void) fseek(spoolfp, message, 0);
#ifdef SENDMAIL
/* if we have sendmail, then it
** was handed the mail, which failed.
** sendmail returns the failed mail
** for us, so we need not do it again.
*/
if(form != LOCAL)
#endif
{
return_mail(from, command);
}
exitstat = 0;
}
}
# ifdef LOG
else {
if(retrying == 0) log(command, from, size); /* */
}
# endif
}
}
/*
** Update logs and records.
*/
# ifdef RECORD
(void) fseek(spoolfp, message, 0);
record(command, from, size);
# endif
/*
** close spool file pointer.
** if we created it, then unlink file.
*/
(void) fclose(spoolfp);
if(spoolmaster) {
(void) unlink(spoolfile);
}
(void) unlink(stderrfile);
}
/*
** return mail to sender, as determined by From_ line.
*/
return_mail(from, fcommand)
char *from, *fcommand;
{
char buf[SMLBUF];
char domain[SMLBUF], user[SMLBUF];
char *r;
FILE *fp, *out, *popen();
int i = 0;
r = buf;
(void) sprintf(r, "%s %s", SMAIL, VFLAG);
r += strlen(r);
if(islocal(from, domain, user)) {
(void) sprintf(r, LARG(user));
} else {
(void) sprintf(r, RLARG(domain, user));
}
i = 0;
do {
out = popen(buf, "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 smail.
*/
(void) sleep(60);
} while (++i < 10);
if(out == NULL) {
(void) printf("couldn't execute %s.\n", buf);
return;
}
(void) fprintf(out, "Date: %s\n", arpanows);
(void) fprintf(out, "From: MAILER-DAEMON@%s\n", hostdomain);
(void) fprintf(out, "Subject: failed mail\n");
(void) fprintf(out, "To: %s\n", from);
(void) fprintf(out, "\n");
(void) fprintf(out, "======= command failed =======\n\n");
(void) fprintf(out, " COMMAND: %s\n\n", fcommand);
(void) fprintf(out, "======= standard error follows =======\n");
(void) fflush(stderr);
if((fp = fopen(stderrfile, "r")) != NULL) {
while(fgets(buf, sizeof(buf), fp) != NULL) {
(void) fputs(buf, out);
}
}
(void) fclose(fp);
(void) fprintf(out, "======= text of message follows =======\n");
/*
** Copy input.
*/
(void) fprintf(out, "From %s\n", from);
while(fgets(buf, sizeof(buf), spoolfp) != NULL) {
(void) fputs(buf, out);
}
(void) pclose(out);
}
//E*O*F deliver.c//
echo x - getopt.c
cat > "getopt.c" << '//E*O*F getopt.c//'
/*
** @(#)getopt.c 2.5 (smail) 9/15/87
*/
/*
* 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 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 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);
}
//E*O*F getopt.c//
echo x - getpath.c
cat > "getpath.c" << '//E*O*F getpath.c//'
#ifndef lint
static char *sccsid="@(#)getpath.c 2.5 (smail) 9/15/87";
#endif
# include <stdio.h>
# include <sys/types.h>
# include <ctype.h>
# include "defs.h"
extern enum edebug debug; /* how verbose we are */
extern char *pathfile; /* location of path database */
/*
**
** getpath(): look up key in ascii sorted path database.
**
*/
getpath( key, path , cost)
char *key; /* what we are looking for */
char *path; /* where the path results go */
int *cost; /* where the cost results go */
{
long pos, middle, hi, lo;
static long pathlength = 0;
register char *s;
int c;
static FILE *file;
int flag;
DEBUG("getpath: looking for '%s'\n", key);
if(pathlength == 0) { /* 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 != 0)
while(((c = getc(file)) != EOF) && (c != '\n'))
; /* go to beginning of next line */
if(c == EOF) {
return(EX_NOHOST);
}
for( flag = 0, s = path; flag == 0; s++ ) { /* match??? */
if( *s == '\0' ) {
goto solved;
}
if((c = getc(file)) == EOF) {
return(EX_NOHOST);
}
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 != '\t') && (c != '\n')) {
*path++ = c;
}
*path = '\0';
/*
** See if the next field on the line is numeric.
** If so, use it as the cost for the route.
*/
if(c == '\t') {
int tcost = -1;
while(((c = getc(file)) != EOF) && isdigit(c)) {
if(tcost < 0) tcost = 0;
tcost *= 10;
tcost += c - '0';
}
if(tcost >= 0) *cost = tcost;
}
return (EX_OK);
}
//E*O*F getpath.c//
echo x - headers.c
cat > "headers.c" << '//E*O*F headers.c//'
/*
** message spooing, header and address parsing and completion
** functions for smail/rmail
*/
#ifndef lint
static char *sccsid="@(#)headers.c 2.5 (smail) 9/15/87";
#endif
# include <stdio.h>
# include <sys/types.h>
# include <time.h>
# include <ctype.h>
# include <pwd.h>
# include "defs.h"
extern enum edebug debug; /* how verbose we are */
extern char hostname[]; /* */
extern char hostdomain[]; /* */
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 time_t now; /* time */
extern char nows[], arpanows[]; /* time strings */
extern struct tm *gmt, *loc; /* time structs */
extern char *from_addr; /* replacement fromaddr with -F */
static char toline[SMLBUF];
static char fromline[SMLBUF];
static char dateline[SMLBUF];
static char midline[SMLBUF];
static char *ieof = "NOTNULL";
struct reqheaders {
char *name;
char *field;
char have;
};
static struct reqheaders reqtab[] = {
"Message-Id:" , midline , 'N' ,
"Date:" , dateline , 'N' ,
"From:" , fromline , 'N' ,
"To:" , toline , 'N' ,
NULL , NULL , 'N'
};
/*
**
** 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 @domain_a,@domain_b:user@domain_c, ...
*/
if(*address == '@')
#ifdef SENDMAIL
/*
** hand it to sendmail
*/
{
goto local;
}
#else
/*
** no sendmail, convert it into a bang path: domain_a!domain_b!domain_c!user
*/
{
char buf[SMLBUF], *p;
char t_dom[SMLBUF], t_user[SMLBUF];
(void) strcpy(buf, address+1); /* elide leading '@' */
for(p=buf; *p != '\0' ; p++) { /* search for ',' or ':' */
if(*p == ':') { /* reached end of route */
break;
}
if(*p == ',') { /* elide ','s */
(void) strcpy(p, p+1);
}
if(*p == '@') { /* convert '@' to '!' */
*p = '!';
}
}
if(*p != ':') { /* bad syntax - punt */
goto local;
}
*p = '\0';
if(parse(p+1, t_dom, t_user) != LOCAL) {
(void) strcat(buf, "!");
(void) strcat(buf, t_dom);
}
(void) strcat(buf, "!");
(void) strcat(buf, t_user);
/* munge the address (yuk)
** it's OK to copy into 'address', because the machinations
** above don't increase the string length of the address.
*/
(void) strcpy(address, buf);
/* re-parse the address */
return(parse(address, domain, user));
}
#endif
/*
** Try splitting at @. If it works, 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);
}
/*
** 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);
}
/*
** Done trying. This must be just a user name, form LOCAL.
*/
local:
(void) strcpy(user, address);
(void) strcpy(domain, "");
return(LOCAL); /* user */
}
build(domain, user, form, result)
char *domain;
char *user;
enum eform form;
char *result;
{
switch((int) 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;
}
}
/*
** 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);
}
/*
** Determine whether an address is a local address
*/
islocal(addr, domain, user)
char *addr, *domain, *user;
{
enum eform form, parse();
extern char hostuucp[];
/*
** parse the address
*/
form = parse(addr, domain, user);
if((form == LOCAL) /* user */
||(strcmpic(domain, hostdomain) == 0) /* user@hostdomain */
||(strcmpic(domain, hostname) == 0) /* user@hostname */
#ifdef DOMGATE
||(strcmpic(domain, &MYDOM[0]) == 0) /* user@MYDOM w/ dot */
||(strcmpic(domain, &MYDOM[1]) == 0) /* user@MYDOM no dot */
#endif
||(strcmpic(domain, hostuucp) == 0)) {/* user@hostuucp */
return(1);
}
return(0);
}
/*
** spool - message spooling module
**
** (1) get dates for headers, etc.
** (2) if the message is on the standard input (no '-f')
** (a) create a temp file for spooling the message.
** (b) collapse the From_ headers into a path.
** (c) if the mail originated locally, then
** (i) establish default headers
** (ii) scan the message headers for required header fields
** (iii) add any required message headers that are absent
** (d) copy rest of the message to the spool file
** (e) close the spool file
** (3) open the spool file for reading
*/
void
spool(argc, argv)
int argc;
char **argv;
{
static char *tmpf = "/tmp/rmXXXXXX"; /* temp file name */
char *mktemp();
char buf[SMLBUF];
static char splbuf[SMLBUF];
char from[SMLBUF], domain[SMLBUF], user[SMLBUF];
void rline(), scanheaders(), compheaders();
/*
** if the mail has already been spooled by
** a previous invocation of smail don't respool.
** check the file name to prevent things like
** rmail -f /etc/passwd badguy@dreadfuldomain
*/
if((spoolfile != NULL)
&& (strncmp(spoolfile, tmpf, strlen(tmpf) - 6) != 0)) {
error(EX_TEMPFAIL, "spool: bad file name '%s'\n", spoolfile);
}
/*
** set dates in local, arpa, and gmt forms
*/
setdates();
/*
** If necessary, copy stdin to a temp file.
*/
if(spoolfile == NULL) {
spoolfile = strcpy(splbuf, tmpf);
(void) mktemp(spoolfile);
if((spoolfp = fopen(spoolfile, "w")) == NULL) {
error(EX_CANTCREAT, "can't create %s.\n", spoolfile);
}
spoolmaster = 1;
/*
** rline reads the standard input,
** collapsing the From_ and >From_
** lines into a single uucp path.
** first non-from_ line is in buf[];
*/
rline(from, buf);
/*
** if the mail originated here, we parse the header
** and add any required headers that are missing.
*/
if(islocal(from, domain, user) || (from_addr != NULL)) {
/*
** initialize default headers
*/
def_headers(argc, argv, from);
/*
** buf has first, non-from_ line
*/
scanheaders(buf);
/*
** buf has first, non-header line,
*/
compheaders();
if(buf[0] != '\n') {
(void) fputs("\n", spoolfp);
}
}
/*
** now, copy the rest of the letter into the spool file
** terminate on either EOF or '^.$'
*/
while(ieof != NULL) {
(void) fputs(buf, spoolfp);
if((fgets(buf, SMLBUF, stdin) == NULL)
|| (buf[0] == '.' && buf[1] == '\n')) {
ieof = NULL;
}
}
/*
** close the spool file, and the standard input.
*/
(void) fclose(spoolfp);
(void) fclose(stdin); /* you don't see this too often! */
}
if((spoolfp = fopen(spoolfile, "r")) == NULL) {
error(EX_TEMPFAIL, "can't open %s.\n", spoolfile);
}
}
/*
**
** rline(): collapse From_ and >From_ lines.
**
** Same idea as the old rmail, but also turns user@domain to domain!user.
**
*/
void
rline(from, retbuf)
char *from;
char *retbuf;
{
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(); /* " " " */
char *c;
int nhops, i;
char buf[SMLBUF], tmp[SMLBUF], *hop[128], *e, *b;
char *pwuid();
if(spoolmaster == 0) return;
buf[0] = from[0] = addr[0] = '\0';
/*
** 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(;;) {
(void) strcpy(retbuf, buf);
if(ieof == NULL) {
break;
}
if((fgets(buf, sizeof(buf), stdin) == NULL)
|| (buf[0] == '.' && buf[1] == '\n')) {
ieof = NULL;
break;
}
if (strncmp("From ", buf, 5)
&& strncmp(">From ", buf, 6)) {
break;
}
/*
** Crack the line apart using ssplit.
*/
if(c = index(buf, '\n')) {
*c = '\0';
}
parts = ssplit(buf, ' ', partv);
/*
** Tack host! onto the from argument if "remote from host" is present.
*/
if((parts > 3)
&& (strncmp("remote from ", partv[parts-3], 12) == 0)) {
(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.
*/
if(parts < 2) {
break;
} else {
char *x = partv[1];
char *q = index(x, ' ');
if(q != NULL) {
*q = '\0';
}
(void) strcpy(addr, x);
}
(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[0] == '\0') {
char *login;
if ((login = pwuid(getuid())) == NULL) {
(void) strcpy(from, "nobody"); /* bad news */
} else {
(void) strcpy(from, login);
}
}
/* split the from line on '!'s */
nhops = ssplit(from, '!', hop);
for(i = 0; i < (nhops - 1); i++) {
b = hop[i];
if(*b == '\0') {
continue;
}
e = hop[i+1];
e-- ;
*e = '\0'; /* null terminate each path segment */
e++;
#ifdef HIDDENHOSTS
/*
** Strip hidden hosts: anything.hostname.MYDOM -> hostname.MYDOM
*/
for(p = b;(p = index(p, '.')) != NULL; p++) {
if(strcmpic(hostdomain, p+1) == 0) {
(void) strcpy(b, hostdomain);
break;
}
}
#endif
/*
** Strip useless MYDOM: hostname.MYDOM -> hostname
*/
if(strcmpic(hop[i], hostdomain) == 0) {
(void) strcpy(hop[i], hostname);
}
}
/*
** Now strip out any redundant information in the From_ line
** a!b!c!c!d => a!b!c!d
*/
for(i = 0; i < (nhops - 2); i++) {
b = hop[i];
e = hop[i+1];
if(strcmpic(b, e) == 0) {
*b = '\0';
}
}
/*
** Reconstruct the From_ line
*/
tmp[0] = '\0'; /* empty the tmp buffer */
for(i = 0; i < (nhops - 1); i++) {
if((hop[i][0] == '\0') /* deleted this hop */
||((tmp[0] == '\0') /* first hop == hostname */
&&(strcmpic(hop[i], hostname) == 0))) {
continue;
}
(void) strcat(tmp, hop[i]);
(void) strcat(tmp, "!");
}
(void) strcat(tmp, hop[i]);
(void) strcpy(from, tmp);
(void) strcpy(retbuf, buf);
(void) fprintf(spoolfp, "%s\n", from);
}
void
scanheaders(buf)
char *buf;
{
int inheader = 0;
while(ieof != NULL) {
if(buf[0] == '\n') {
break; /* end of headers */
}
/*
** header lines which begin with whitespace
** are continuation lines
*/
if((inheader == 0)
|| ((buf[0] != ' ' && buf[0] != '\t'))) {
/* not a continuation line
** check for header
*/
if(isheader(buf) == 0) {
/*
** not a header
*/
break;
}
inheader = 1;
haveheaders(buf);
}
(void) fputs(buf, spoolfp);
if((fgets(buf, SMLBUF, stdin) == NULL)
|| (buf[0] == '.' && buf[1] == '\n')) {
ieof = NULL;
}
}
if(isheader(buf)) {
buf[0] = '\0';
}
}
/*
** complete headers - add any required headers that are not in the message
*/
void
compheaders()
{
struct reqheaders *i;
/*
** look at the table of required headers and
** add those that are missing to the spooled message.
*/
for(i = reqtab; i->name != NULL; i++) {
if(i->have != 'Y') {
(void) fprintf(spoolfp, "%s\n", i->field);
}
}
}
/*
** look at a string and determine
** whether or not it is a valid header.
*/
isheader(s)
char *s;
{
char *p;
/*
** header field names must terminate with a colon
** and may not be null.
*/
if(((p = index(s, ':')) == NULL) || (s == p)) {
return(0);
}
/*
** header field names must consist entirely of
** printable ascii characters.
*/
while(s != p) {
if((*s < '!') || (*s > '~')) {
return(0);
}
s++;
}
/*
** we hit the ':', so the field may be a header
*/
return(1);
}
/*
** compare the header field to those in the required header table.
** if it matches, then mark the required header as being present
** in the message.
*/
haveheaders(s)
char *s;
{
struct reqheaders *i;
for(i = reqtab; i->name != NULL; i++) {
if(strncmpic(i->name, s, strlen(i->name)) == 0) {
if((strncmpic("From:", s, 5) == 0)
&& (from_addr != NULL)) {
(void) sprintf(s, "From: %s\n", from_addr);
}
i->have = 'Y';
break;
}
}
}
/*
** create default headers for the message.
*/
def_headers(argc, argv, from)
int argc;
char **argv;
char *from;
{
def_to(argc, argv); /* default To: */
def_date(); /* default Date: */
def_from(from); /* default From: */
def_mid(); /* default Message-Id: */
}
/*
** default Date: in arpa format
*/
def_date()
{
(void) strcpy(dateline, "Date: ");
(void) strcat(dateline, arpanows);
}
/*
** default Message-Id
** Message-Id: <yymmddhhmm.AAppppp@hostdomain>
**
** yy year
** mm month
** dd day
** hh hour
** mm minute
** ppppp process-id
**
** date and time are set by GMT
*/
def_mid()
{
(void) sprintf(midline, "Message-Id: <%02d%02d%02d%02d%02d.AA%05d@%s>",
gmt->tm_year,
gmt->tm_mon+1,
gmt->tm_mday,
gmt->tm_hour,
gmt->tm_min,
getpid(),
hostdomain);
}
/*
** default From:
** From: user@hostdomain (Full Name)
*/
def_from(from)
char *from;
{
char *nameptr;
char name[SMLBUF];
char *getenv(), *login;
char *pwfnam(), *pwuid();
if (from_addr != NULL) {
(void) sprintf(fromline, "From: %s", from_addr);
return;
}
name[0] = '\0';
if((nameptr = getenv("NAME")) != NULL) {
(void) strcpy(name, nameptr);
} else if((login = pwuid(getuid())) != NULL) {
if((nameptr = pwfnam(login)) != NULL) {
(void) strcpy(name, nameptr);
}
}
if(name[0] != '\0') {
(void) sprintf(fromline,
"From: %s@%s (%s)", from, hostdomain, name);
} else {
(void) sprintf(fromline,
"From: %s@%s", from, hostdomain);
}
}
/*
** default To:
** To: recip1, recip2, ...
**
** lines longer than 50 chars are continued on another line.
*/
def_to(argc, argv)
int argc;
char **argv;
{
int i, n;
char *bol;
bol = toline;
(void) strcpy(bol, "To: ");
for(n = i = 0; i < argc; i++) {
(void) strcat(bol, argv[i]);
if((index(argv[i], '!') == NULL)
&& (index(argv[i], '@') == NULL)) {
(void) strcat(bol, "@");
(void) strcat(bol, hostdomain);
}
if(i+1 < argc) {
n = strlen(bol);
if(n > 50) {
(void) strcat(bol, ",\n\t");
bol = bol + strlen(bol);
*bol = '\0';
n = 8;
} else {
(void) strcat(bol, ", ");
}
}
}
}
//E*O*F headers.c//
exit 0