static char SccsId
[] = "@(#)main.c 3.15 %G%";
** SENDMAIL -- Post mail to a set of destinations.
** This is the basic mail router. All user mail programs should
** call this routine to actually deliver mail. Sendmail in
** turn calls a bunch of mail servers that do the real work of
** Sendmail is driven by tables defined in conf.c. This
** file will be different from system to system, but the rest
** of the code will be the same. This table could be read in,
** but it seemed nicer to have it compiled in, since deliver-
** mail will potentially be exercised a lot.
** /etc/sendmail [-f name] [-a] [-q] [-v] [-n] [-m] addr ...
** Positional Parameters:
** addr -- the address to deliver the mail to. There
** -f name The mail is from "name" -- used for
** the header in local mail, and to
** deliver reports of failures to.
** -r name Same as -f; however, this flag is
** reserved to indicate special processing
** for remote mail delivery as needed
** in the future. So, network servers
** -Ffullname Select what the full-name should be
** -a This mail should be in ARPANET std
** -n Don't do aliasing. This might be used
** when delivering responses, for
** -em Mail back a response if there was an
** error in processing. This should be
** used when the origin of this message
** -ew Write back a response if the user is
** still logged in, otherwise, act like
** -eq Don't print any error message (just
** -ep (default) Print error messages
** -ee Send BerkNet style errors. This
** is equivalent to MailBack except
** that it has gives zero return code
** (unless there were errors during
** returning). This used to be
** "EchoBack", but you know how the old
** -m In group expansion, send to the
** sender also (stands for the Mail metoo
** -i Do not terminate mail on a line
** -s Save UNIX-like "From" lines on the
** -v Give blow-by-blow description of
** everything that happens.
** -Cfilename Use alternate configuration file.
** -DXvalue Define macro X to have value.
** As defined in <sysexits.h>.
** These codes are actually returned from the auxiliary
** mailers; it is their responsibility to make them
** LOG -- if set, everything is logged.
** Compilation Instructions:
** cc -c -O main.c conf.c deliver.c parse.c
** It ought to collect together messages that are
** destined for a single host and send these
** to the auxiliary mail server together.
** It should take "user at host" as three separate
** parameters and combine them into one address.
** Eric Allman, UCB/INGRES
bool ArpaFmt
; /* mail is expected to be in ARPANET format */
bool FromFlag
; /* from person is explicitly specified */
bool MailBack
; /* mail back response on error */
bool BerkNet
; /* called from BerkNet */
bool WriteBack
; /* write back response on error */
bool HasXscrpt
; /* if set, the transcript file exists */
bool NoAlias
; /* don't do aliasing */
bool ForceMail
; /* mail even if already sent a copy */
bool MeToo
; /* send to the sender also if in a group expansion */
bool SaveFrom
; /* save From lines on the front of messages */
bool IgnrDot
; /* if set, ignore dot when collecting mail */
bool SuprErrs
; /* supress errors if set */
bool Verbose
; /* set if blow-by-blow desired */
int Debug
; /* debug level */
int Errors
; /* count of errors */
char InFileName
[] = "/tmp/mailtXXXXXX";
char Transcript
[] = "/tmp/mailxXXXXXX";
ADDRESS From
; /* the from person */
char *To
; /* the target person */
int HopCount
; /* hop count */
int ExitStat
; /* the exit status byte */
HDR
*Header
; /* header list */
char *Macro
[128]; /* macros */
long CurTime
; /* current time */
char FromLine
[80]; /* holds From line (UNIX style header) */
char nbuf
[MAXLINE
]; /* holds full name */
char pbuf
[10]; /* holds pid */
char tbuf
[10]; /* holds "current" time */
char cbuf
[5]; /* holds hop count */
char dbuf
[30]; /* holds ctime(tbuf) */
if (signal(SIGINT
, SIG_IGN
) != SIG_IGN
)
(void) signal(SIGINT
, finis
);
(void) signal(SIGTERM
, finis
);
setbuf(stdout
, (char *) NULL
);
if ((i
= open(DEBUGFILE
, 1)) > 0)
cfname
= "/usr/lib/sendmail.cf";
while (--argc
> 0 && (p
= *++argv
)[0] == '-')
case 'r': /* obsolete -f flag */
case 'f': /* from address */
if (--argc
<= 0 || *p
== '-')
syserr("No \"from\" person");
syserr("More than one \"from\" person");
case 'F': /* set full name */
case 'h': /* hop count */
if (--argc
<= 0 || *p
< '0' || *p
> '9')
syserr("Bad hop count (%s)", p
);
case 'e': /* error message disposition */
case 'p': /* print errors normally */
case 'q': /* be silent about it */
(void) freopen("/dev/null", "w", stdout
);
case 'm': /* mail back */
case 'e': /* do berknet error processing */
case 'w': /* write back (or mail) */
printf("Version %s Debug %d\n", Version
, Debug
);
case 'D': /* redefine internal macro */
case 'C': /* select configuration file */
case 'n': /* don't alias */
case 'm': /* send to me too */
case 'i': /* don't let dot stop me */
case 'a': /* arpanet format */
case 's': /* save From lines in headers */
case 'v': /* give blow-by-blow description */
/* at Eric Schmidt's suggestion, this will not be an error....
syserr("Unknown flag %s", p);
... seems that upward compatibility will be easier. */
if (from
!= NULL
&& ArpaFmt
)
syserr("-f and -a are mutually exclusive");
** Read control file and initialize system macros.
** Collect should be called first, so that the time
** corresponds to the time that the messages starts
** getting sent, rather than when it is first composed.
(void) sprintf(pbuf
, "%d", getpid());
(void) sprintf(cbuf
, "%d", HopCount
);
/* time as integer, unix time, arpa time */
(void) sprintf(tbuf
, "%ld", &CurTime
);
(void) strcpy(dbuf
, ctime(&CurTime
));
*index(dbuf
, '\n') = '\0';
define('a', arpadate(dbuf
));
(void) expand("$z/.mailcf", cfbuf
, &cfbuf
[sizeof cfbuf
- 1]);
if (access(cfbuf
, 2) == 0)
if (locname == NULL || locname[0] == '\0')
extern struct passwd *getpwuid();
syserr("Who are you? (uid=%d)", uid);
extern struct passwd *getpwnam();
syserr("Who are you? (name=%s)", p);
if (p == NULL || p[0] == '\0' || pw == NULL)
/* extract full name from passwd file */
if ((fullname
== NULL
|| fullname
[0] == '\0') &&
pw
!= NULL
&& pw
->pw_gecos
!= NULL
)
while (*p
!= '\0' && *p
!= ',' && *p
!= ';')
(void) strcpy(nb
, realname
);
if (!ArpaFmt
&& from
== NULL
&& nbuf
[0] != '\0')
if (fullname
!= NULL
&& fullname
[0] != '\0')
(void) expand("$l", FromLine
, &FromLine
[sizeof FromLine
- 1]);
printf("From person = \"%s\"\n", From
.q_paddr
);
usrerr("Usage: /etc/sendmail [flags] addr...");
** The Hop count tells us how many times this message has
** been processed by sendmail. If it exceeds some
** fairly large threshold, then we assume that we have
** an infinite forwarding loop and die.
syserr("Infinite forwarding loop (%s->%s)", From
.q_paddr
, *argv
);
** Scan argv and deliver the message to everyone.
for (; argc
-- > 0; argv
++)
if (argc
>= 2 && p
[2] == '\0' &&
(p
[0] == 'a' || p
[0] == 'A') &&
(p
[1] == 't' || p
[1] == 'T'))
if (strlen(argv
[0]) + strlen(argv
[2]) + 2 > sizeof nbuf
)
usrerr("address overflow");
(void) strcpy(nbuf
, argv
[0]);
(void) strcat(nbuf
, "@");
(void) strcat(nbuf
, argv
[2]);
/* if we have had errors sofar, drop out now */
if (Errors
> 0 && ExitStat
== EX_OK
)
** First arrange that the person who is sending the mail
** will not be expanded (unless explicitly requested).
From
.q_flags
|= QDONTSEND
;
** Actually send everything.
for (i
= 0; Mailer
[i
] != NULL
; i
++)
for (q
= Mailer
[i
]->m_sendq
; q
!= NULL
; q
= q
->q_next
)
(void) deliver(q
, (fnptr
) NULL
);
** SETFROM -- set the person who this message is from
** Under certain circumstances allow the user to say who
** s/he is (using -f or -r). These are:
** 1. The user's uid is zero (root).
** 2. The user's login name is "network" (mail from
** 3. The user's login name is "uucp" (mail from the
** 4. The address the user is trying to claim has a
** "!" character in it (since #3 doesn't do it for
** us if we are dialing out).
** A better check to replace #3 & #4 would be if the
** effective uid is "UUCP" -- this would require me
** to rewrite getpwent to "grab" uucp as it went by,
** make getname more nasty, do another passwd file
** scan, or compile the UID of "UUCP" into the code,
** all of which are reprehensible.
** Assuming all of these fail, we figure out something
** from -- the person it is from.
** realname -- the actual person executing sendmail.
** sets sendmail's notion of who the from person is.
if (strcmp(realname
, "network") != 0 && strcmp(realname
, "uucp") != 0 &&
index(from
, '!') == NULL
&& getuid() != 0)
/* network sends -r regardless (why why why?) */
/* syserr("%s, you cannot use the -f flag", realname); */
if (from
== NULL
|| parse(from
, &From
, 0) == NULL
)
(void) parse(from
, &From
, 0);
** Rewrite the from person to dispose of possible implicit
pvp
= prescan(from
, '\0');
syserr("cannot prescan from (%s)", from
);
(void) strcat(frombuf
, *pvp
++);
define('f', newstr(frombuf
));
** FINIS -- Clean up and exit.
** via signal on interrupt.
** It may be that it should only remove the input
** file if there have been no errors.
/* mail back the transcript on errors */
(void) unlink(Transcript
);
(void) unlink(InFileName
);
** OPENXSCRPT -- Open transcript file
** Creates a transcript file for possible eventual mailing or
** Turns the standard output into a special file
(void) mktemp(Transcript
);
if (freopen(Transcript
, "w", stdout
) == NULL
)
syserr("Can't create %s", Transcript
);
(void) chmod(Transcript
, 0600);
setbuf(stdout
, (char *) NULL
);