static char SccsId
[] = "@(#)deliver.c 1.11 10/27/80";
** DELIVER -- Deliver a message to a particular address.
** Compute receiving network (i.e., mailer), host, & user.
** If local, see if this is really a program name.
** Build argument for the mailer.
** Create pipe through edit fcn if appropriate.
** Parent: call editfcn if specified.
** Wait for mailer to finish.
** Interpret exit status.
** to -- the address to deliver the message to.
** editfcn -- if non-NULL, we want to call this function
** to output the letter (instead of just out-
** zero -- successfully delivered.
** else -- some failure, see ExitStat for more info.
** The standard input is passed off to someone.
** The standard input is shared amongst all children,
** including the file pointer. It is critical that the
** parent waits for the child to finish before forking
** standard input -- must be opened to the message to
register struct mailer
*m
;
extern struct passwd
*getpwnam();
extern char **buildargv();
** Compute receiving mailer, host, and to addreses.
** Do some initialization first. To is the to address
printf("deliver(%s [%d, `%s', `%s'])\n", To
, m
, host
, user
);
** Remove quote bits from user/host.
for (p
= user
; (*p
++ &= 0177) != '\0'; )
for (p
= host
; (*p
++ &= 0177) != '\0'; )
** Strip quote bits from names if the mailer wants it.
if (flagset(M_STRIPQ
, m
->m_flags
))
** See if this user name is "special".
** If the user is a program, diddle with the mailer spec.
** If the user name has a slash in it, assume that this
** is a file -- send it off without further ado.
** Note that this means that editfcn's will not
** be applied to the message.
if (index(user
, '/') != NULL
)
giveresponse(i
, TRUE
, m
);
** See if the user exists.
** Strictly, this is only needed to print a pretty
** >>>>>>>>>> This clause assumes that the local mailer
** >> NOTE >> cannot do any further aliasing; that
** >>>>>>>>>> function is subsumed by delivermail.
if (getpwnam(user
) == NULL
)
giveresponse(EX_NOUSER
, TRUE
, m
);
** If the mailer wants a From line, insert a new editfcn.
if (flagset(M_HDR
, m
->m_flags
) && editfcn
== NULL
)
** The argument vector gets built, pipes through 'editfcn'
** are created as necessary, and we fork & exec as
** appropriate. In the parent, we call 'editfcn'.
pvp
= buildargv(m
->m_argv
, m
->m_flags
, host
, user
, From
.q_paddr
);
/* create a pipe if we will need one */
if (editfcn
!= NULL
&& pipe(pvect
) < 0)
/* child -- set up input & exec mailer */
/* make diagnostic output be standard output */
syserr("Cannot dup to zero!");
if (!flagset(M_RESTR
, m
->m_flags
))
initlog(NULL
, 0, LOG_CLOSE
);
* We have to be careful with vfork - we can't mung up the
* memory but we don't want the mailer to inherit any extra
* open files. Chances are the mailer won't
* care about an extra file, but then again you never know.
* Actually, we would like to close(fileno(pwf)), but it's
* declared static so we can't. But if we fclose(pwf), which
* is what endpwent does, it closes it in the parent too and
* the next getpwnam will be slower. If you have a weird mailer
* that chokes on the extra file you should do the endpwent().
/* syserr fails because log is closed */
/* syserr("Cannot exec %s", m->m_mailer); */
/* arrange to write out header message if error */
signal(SIGPIPE
, pipesig
);
mfile
= fdopen(pvect
[1], "w");
** Wait for child to die and report status.
** We should never get fatal errors (e.g., segmentation
** violation), so we report those specially. For other
** errors, we choose a status message (into statmsg),
** and if it represents an error, we print it.
while ((i
= wait(&st
)) > 0 && i
!= pid
)
syserr("%s: stat %o", pvp
[0], st
);
ExitStat
= EX_UNAVAILABLE
;
giveresponse(i
, FALSE
, m
);
** GIVERESPONSE -- Interpret an error response from a mailer
** stat -- the status code from the mailer (high byte
** only; core dumps must have been taken care of
** force -- if set, force an error message output, even
** if the mailer seems to like to print its own
** m -- the mailer descriptor for this mailer.
** Errors may be incremented.
giveresponse(stat
, force
, m
)
register struct mailer
*m
;
if (i
< 0 || i
> N_SysEx
)
if (statmsg
== NULL
&& m
->m_badstat
!= 0)
if (i
< 0 || i
>= N_SysEx
)
syserr("Bad m_badstat %d", stat
);
usrerr("unknown mailer response %d", stat
);
else if (force
|| !flagset(M_QUIET
, m
->m_flags
))
** Log a record of the transaction. Compute the new
** ExitStat -- if we already had an error, stick with
sprintf(buf
, "error %d", stat
);
logmsg(LOG_INFO
, "%s->%s: %ld: %s", From
.q_paddr
, To
, MsgSize
, statmsg
);
** PUTHEADER -- insert the From header into some mail
** For mailers such as 'msgs' that want the header inserted
** into the mail, this edit filter inserts the From line and
** then passes the rest of the message through.
** fp -- the file pointer for the output.
** Puts a "From" line in UNIX format, and then
** outputs the rest of the message.
fprintf(fp
, "From %s %s", From
.q_paddr
, ctime(&tim
));
while (fgets(buf
, sizeof buf
, stdin
) != NULL
&& !ferror(fp
))
syserr("putheader: write error");
** PIPESIG -- Handle broken pipe signals
** This just logs an error.
** logs an error message.
signal(SIGPIPE
, SIG_IGN
);
** SENDTO -- Designate a send list.
** The parameter is a comma-separated list of people to send to.
** This routine arranges to send to all of them.
** list -- the send list.
** copyf -- the copy flag; passed to parse.
/* more keeps track of what the previous delimiter was */
/* find the end of this address */
while ((c
= *p
++) != '\0' && c
!= ',' && c
!= '\n')
if ((a
= parse(q
, (addrq
*) NULL
, copyf
)) == NULL
)
/* arrange to send to this person */
** RECIPIENT -- Designate a message recipient
** Saves the named person for future mailing.
** Designates a person as a recipient. This routine
** does the initial parsing, and checks to see if
** this person has already received the mail.
** It also supresses local network names and turns them into
** a -- the (preparsed) address header for the recipient.
** targetq -- the queue to add the name to.
register struct mailer
*m
;
printf("recipient(%s)\n", To
);
** Look up this person in the recipient list. If they
** are there already, return, otherwise continue.
for (q
= &SendQ
; (q
= nxtinq(q
)) != NULL
; )
if (sameaddr(q
, a
, FALSE
))
printf("(%s in SendQ)\n", a
->q_paddr
);
for (q
= &AliasQ
; (q
= nxtinq(q
)) != NULL
; )
if (sameaddr(q
, a
, FALSE
))
printf("(%s in AliasQ)\n", a
->q_paddr
);
** See if the user wants hir mail forwarded.
** `Forward' must do the forwarding recursively.
if (m
== &Mailer
[0] && !NoAlias
&& targetq
== &SendQ
&& forward(a
))
** Put the user onto the target queue.
** BUILDARGV -- Build an argument vector for a mail server.
** Using a template defined in config.c, an argv is built.
** The format of the template is already a vector. The
** items of this vector are copied, unless a dollar sign
** is encountered. In this case, the next character
** specifies something else to copy in. These can be
** The vector is built in a local buffer. A pointer to
** the static argv is returned.
** tmplt -- a template for an argument vector.
** flags -- the flags for this server.
** host -- the host name to send to.
** user -- the user name to send to.
** from -- the person this mail is from.
** Since the argv is staticly allocated, any subsequent
** calls will clobber the old argv.
buildargv(tmplt
, flags
, host
, user
, from
)
static char *pv
[MAXPV
+1];
** Do initial argv setup.
** Insert the mailer name. Notice that $x expansion is
** NOT done on the mailer name. Then, if the mailer has
** a picky -f flag, we insert it as appropriate. This
** code does not check for 'pv' overflow; this places a
** manifest lower limit of 4 for MAXPV.
/* insert -f or -r flag as appropriate */
if (flagset(M_FOPT
|M_ROPT
, flags
) && FromFlag
)
if (flagset(M_FOPT
, flags
))
** Build the rest of argv.
** For each prototype parameter, the prototype is
** scanned character at a time. If a dollar-sign is
** found, 'q' is set to the appropriate expansion,
** otherwise it is null. Then either the string
** pointed to by q, or the original character, is
** interpolated into the buffer. Buffer overflow is
for (mvp
= tmplt
; (p
= *++mvp
) != NULL
; )
syserr("Too many parameters to %s", pv
[0]);
/* q will be the interpolated quantity */
case 'f': /* from person */
case 'c': /* hop count */
sprintf(pbuf
, "%d", HopCount
);
** Interpolate q or output one character
** Strip quote bits as we proceed.....
while (bp
< &buf
[sizeof buf
- 1] && (*bp
++ = *q
++) != '\0')
else if (bp
< &buf
[sizeof buf
- 1])
if (bp
>= &buf
[sizeof buf
- 1])
printf("Interpolated argv is:\n");
for (mvp
= pv
; *mvp
!= NULL
; mvp
++)
** MAILFILE -- Send a message to a file.
** filename -- the name of the file to send to.
** The exit code associated with the operation.
f
= fopen(filename
, "a");
/* output the timestamp */
fprintf(f
, "From %s %s", From
.q_paddr
, ctime(&tim
));
while (fgets(buf
, sizeof buf
, stdin
) != NULL
)