fix botch in SIGPIPE catching; map diagnostic to std out in mailers
[unix-history] / usr / src / usr.sbin / sendmail / src / deliver.c
CommitLineData
b3cbe40f
EA
1# include <stdio.h>
2# include <pwd.h>
3# include <signal.h>
4# include "dlvrmail.h"
5# ifdef LOG
6# include <log.h>
7# endif LOG
8
902c5f15 9static char SccsId[] = "@(#)deliver.c 1.10 %G%";
916b3375 10
b3cbe40f
EA
11/*
12** DELIVER -- Deliver a message to a particular address.
13**
14** Algorithm:
15** Compute receiving network (i.e., mailer), host, & user.
16** If local, see if this is really a program name.
17** Build argument for the mailer.
18** Create pipe through edit fcn if appropriate.
19** Fork.
20** Child: call mailer
21** Parent: call editfcn if specified.
22** Wait for mailer to finish.
23** Interpret exit status.
24**
25** Parameters:
26** to -- the address to deliver the message to.
27** editfcn -- if non-NULL, we want to call this function
28** to output the letter (instead of just out-
29** putting it raw).
30**
31** Returns:
32** zero -- successfully delivered.
33** else -- some failure, see ExitStat for more info.
34**
35** Side Effects:
36** The standard input is passed off to someone.
37**
38** WARNING:
39** The standard input is shared amongst all children,
40** including the file pointer. It is critical that the
41** parent waits for the child to finish before forking
42** another child.
43**
b3cbe40f
EA
44** Called By:
45** main
46** savemail
47**
48** Files:
f895a45d 49** standard input -- must be opened to the message to
b3cbe40f 50** deliver.
b3cbe40f
EA
51*/
52
53deliver(to, editfcn)
54 addrq *to;
55 int (*editfcn)();
56{
57 register struct mailer *m;
58 char *host;
59 char *user;
60 extern struct passwd *getpwnam();
61 char **pvp;
62 extern char **buildargv();
63 auto int st;
64 register int i;
65 register char *p;
66 int pid;
67 int pvect[2];
68 extern FILE *fdopen();
69 extern int errno;
70 FILE *mfile;
71 extern putheader();
72 extern pipesig();
73
74 /*
75 ** Compute receiving mailer, host, and to addreses.
76 ** Do some initialization first. To is the to address
77 ** for error messages.
78 */
79
80 To = to->q_paddr;
81 m = to->q_mailer;
82 user = to->q_user;
83 host = to->q_host;
d916f0ca 84 Errors = 0;
b3cbe40f
EA
85 errno = 0;
86# ifdef DEBUG
87 if (Debug)
88 printf("deliver(%s [%d, `%s', `%s'])\n", To, m, host, user);
89# endif DEBUG
90
91 /*
92 ** Remove quote bits from user/host.
93 */
94
95 for (p = user; (*p++ &= 0177) != '\0'; )
96 continue;
97 if (host != NULL)
98 for (p = host; (*p++ &= 0177) != '\0'; )
99 continue;
100
101 /*
102 ** Strip quote bits from names if the mailer wants it.
103 */
104
105 if (flagset(M_STRIPQ, m->m_flags))
106 {
107 stripquotes(user);
108 stripquotes(host);
109 }
110
111 /*
112 ** See if this user name is "special".
113 ** If the user is a program, diddle with the mailer spec.
114 ** If the user name has a slash in it, assume that this
115 ** is a file -- send it off without further ado.
116 ** Note that this means that editfcn's will not
117 ** be applied to the message.
118 */
119
120 if (m == &Mailer[0])
121 {
122 if (*user == '|')
123 {
124 user++;
125 m = &Mailer[1];
126 }
127 else
128 {
129 if (index(user, '/') != NULL)
130 {
131 i = mailfile(user);
132 giveresponse(i, TRUE, m);
133 return (i);
134 }
135 }
136 }
137
b3cbe40f 138 /*
cb590f52
EA
139 ** See if the user exists.
140 ** Strictly, this is only needed to print a pretty
141 ** error message.
142 **
143 ** >>>>>>>>>> This clause assumes that the local mailer
144 ** >> NOTE >> cannot do any further aliasing; that
145 ** >>>>>>>>>> function is subsumed by delivermail.
b3cbe40f
EA
146 */
147
148 if (m == &Mailer[0])
149 {
150 if (getpwnam(user) == NULL)
151 {
152 giveresponse(EX_NOUSER, TRUE, m);
153 return (EX_NOUSER);
154 }
155 }
b3cbe40f
EA
156
157 /*
158 ** If the mailer wants a From line, insert a new editfcn.
159 */
160
161 if (flagset(M_HDR, m->m_flags) && editfcn == NULL)
162 editfcn = putheader;
163
164 /*
165 ** Call the mailer.
166 ** The argument vector gets built, pipes through 'editfcn'
167 ** are created as necessary, and we fork & exec as
168 ** appropriate. In the parent, we call 'editfcn'.
169 */
170
171 pvp = buildargv(m->m_argv, m->m_flags, host, user, From.q_paddr);
172 if (pvp == NULL)
173 {
174 usrerr("name too long");
175 return (-1);
176 }
177 rewind(stdin);
178
179 /* create a pipe if we will need one */
180 if (editfcn != NULL && pipe(pvect) < 0)
181 {
182 syserr("pipe");
183 return (-1);
184 }
c9b9c7a2
MH
185# ifdef VFORK
186 pid = vfork();
187# else
b3cbe40f 188 pid = fork();
c9b9c7a2 189# endif
b3cbe40f
EA
190 if (pid < 0)
191 {
192 syserr("Cannot fork");
193 if (editfcn != NULL)
194 {
195 close(pvect[0]);
196 close(pvect[1]);
197 }
198 return (-1);
199 }
200 else if (pid == 0)
201 {
202 /* child -- set up input & exec mailer */
902c5f15
EA
203 /* make diagnostic output be standard output */
204 close(2);
205 dup(1);
b3cbe40f
EA
206 signal(SIGINT, SIG_IGN);
207 if (editfcn != NULL)
208 {
209 close(0);
210 if (dup(pvect[0]) < 0)
211 {
212 syserr("Cannot dup to zero!");
a16e99bc 213 _exit(EX_OSERR);
b3cbe40f
EA
214 }
215 close(pvect[0]);
216 close(pvect[1]);
217 }
218 if (!flagset(M_RESTR, m->m_flags))
219 setuid(getuid());
220# ifdef LOG
221 initlog(NULL, 0, LOG_CLOSE);
222# endif LOG
c9b9c7a2
MH
223# ifndef VFORK
224 /*
225 * We have to be careful with vfork - we can't mung up the
226 * memory but we don't want the mailer to inherit any extra
227 * open files. Chances are the mailer won't
228 * care about an extra file, but then again you never know.
229 * Actually, we would like to close(fileno(pwf)), but it's
230 * declared static so we can't. But if we fclose(pwf), which
231 * is what endpwent does, it closes it in the parent too and
232 * the next getpwnam will be slower. If you have a weird mailer
233 * that chokes on the extra file you should do the endpwent().
234 */
b3cbe40f 235 endpwent();
c9b9c7a2 236# endif
b3cbe40f
EA
237 execv(m->m_mailer, pvp);
238 /* syserr fails because log is closed */
239 /* syserr("Cannot exec %s", m->m_mailer); */
a16e99bc 240 _exit(EX_UNAVAILABLE);
b3cbe40f
EA
241 }
242
243 /* arrange to write out header message if error */
244 if (editfcn != NULL)
245 {
246 close(pvect[0]);
247 signal(SIGPIPE, pipesig);
248 mfile = fdopen(pvect[1], "w");
249 (*editfcn)(mfile);
250 fclose(mfile);
251 }
252
253 /*
254 ** Wait for child to die and report status.
255 ** We should never get fatal errors (e.g., segmentation
256 ** violation), so we report those specially. For other
257 ** errors, we choose a status message (into statmsg),
258 ** and if it represents an error, we print it.
259 */
260
261 while ((i = wait(&st)) > 0 && i != pid)
262 continue;
263 if (i < 0)
264 {
265 syserr("wait");
266 return (-1);
267 }
268 if ((st & 0377) != 0)
269 {
270 syserr("%s: stat %o", pvp[0], st);
21ec078e 271 ExitStat = EX_UNAVAILABLE;
b3cbe40f
EA
272 return (-1);
273 }
274 i = (st >> 8) & 0377;
275 giveresponse(i, FALSE, m);
276 return (i);
277}
278\f/*
279** GIVERESPONSE -- Interpret an error response from a mailer
280**
281** Parameters:
282** stat -- the status code from the mailer (high byte
283** only; core dumps must have been taken care of
284** already).
285** force -- if set, force an error message output, even
286** if the mailer seems to like to print its own
287** messages.
288** m -- the mailer descriptor for this mailer.
289**
290** Returns:
291** none.
292**
293** Side Effects:
d916f0ca 294** Errors may be incremented.
b3cbe40f
EA
295** ExitStat may be set.
296**
b3cbe40f
EA
297** Called By:
298** deliver
b3cbe40f
EA
299*/
300
301giveresponse(stat, force, m)
302 int stat;
303 int force;
304 register struct mailer *m;
305{
306 register char *statmsg;
307 extern char *SysExMsg[];
308 register int i;
309 extern int N_SysEx;
310
311 i = stat - EX__BASE;
312 if (i < 0 || i > N_SysEx)
313 statmsg = NULL;
314 else
315 statmsg = SysExMsg[i];
316 if (stat == 0)
317 statmsg = "ok";
318 else
319 {
d916f0ca 320 Errors++;
b3cbe40f
EA
321 if (statmsg == NULL && m->m_badstat != 0)
322 {
323 stat = m->m_badstat;
324 i = stat - EX__BASE;
325# ifdef DEBUG
326 if (i < 0 || i >= N_SysEx)
327 syserr("Bad m_badstat %d", stat);
328 else
329# endif DEBUG
330 statmsg = SysExMsg[i];
331 }
332 if (statmsg == NULL)
333 usrerr("unknown mailer response %d", stat);
334 else if (force || !flagset(M_QUIET, m->m_flags))
335 usrerr("%s", statmsg);
336 }
337
338 /*
339 ** Final cleanup.
340 ** Log a record of the transaction. Compute the new
341 ** ExitStat -- if we already had an error, stick with
342 ** that.
343 */
344
345# ifdef LOG
346 if (statmsg == NULL)
347 logmsg(LOG_INFO, "%s->%s: error %d", From.q_paddr, To, stat);
348 else
349 logmsg(LOG_INFO, "%s->%s: %s", From.q_paddr, To, statmsg);
350# endif LOG
cb590f52 351 setstat(stat);
b3cbe40f
EA
352 return (stat);
353}
354\f/*
355** PUTHEADER -- insert the From header into some mail
356**
357** For mailers such as 'msgs' that want the header inserted
358** into the mail, this edit filter inserts the From line and
359** then passes the rest of the message through.
360**
361** Parameters:
362** fp -- the file pointer for the output.
363**
364** Returns:
365** none
366**
367** Side Effects:
368** Puts a "From" line in UNIX format, and then
369** outputs the rest of the message.
370**
b3cbe40f
EA
371** Called By:
372** deliver
b3cbe40f
EA
373*/
374
375putheader(fp)
376 register FILE *fp;
377{
378 char buf[MAXLINE + 1];
379 long tim;
380 extern char *ctime();
381
382 time(&tim);
383 fprintf(fp, "From %s %s", From.q_paddr, ctime(&tim));
384 while (fgets(buf, sizeof buf, stdin) != NULL && !ferror(fp))
385 fputs(buf, fp);
386 if (ferror(fp))
387 {
388 syserr("putheader: write error");
389 setstat(EX_IOERR);
390 }
391}
392\f/*
393** PIPESIG -- Handle broken pipe signals
394**
395** This just logs an error.
396**
397** Parameters:
398** none
399**
400** Returns:
401** none
402**
403** Side Effects:
404** logs an error message.
b3cbe40f
EA
405*/
406
407pipesig()
408{
409 syserr("Broken pipe");
902c5f15 410 signal(SIGPIPE, SIG_IGN);
b3cbe40f
EA
411}
412\f/*
413** SENDTO -- Designate a send list.
414**
415** The parameter is a comma-separated list of people to send to.
416** This routine arranges to send to all of them.
417**
418** Parameters:
419** list -- the send list.
420** copyf -- the copy flag; passed to parse.
421**
422** Returns:
423** none
424**
425** Side Effects:
426** none.
427**
b3cbe40f
EA
428** Called By:
429** main
430** alias
b3cbe40f
EA
431*/
432
433sendto(list, copyf)
434 char *list;
435 int copyf;
436{
437 register char *p;
438 register char *q;
439 register char c;
440 addrq *a;
441 extern addrq *parse();
442 bool more;
443
444 /* more keeps track of what the previous delimiter was */
445 more = TRUE;
446 for (p = list; more; )
447 {
448 /* find the end of this address */
449 q = p;
450 while ((c = *p++) != '\0' && c != ',' && c != '\n')
451 continue;
452 more = c != '\0';
453 *--p = '\0';
454 if (more)
455 p++;
456
457 /* parse the address */
458 if ((a = parse(q, (addrq *) NULL, copyf)) == NULL)
459 continue;
460
461 /* arrange to send to this person */
462 recipient(a, &SendQ);
463 }
464 To = NULL;
465}
466\f/*
467** RECIPIENT -- Designate a message recipient
468**
469** Saves the named person for future mailing.
470**
471** Designates a person as a recipient. This routine
472** does the initial parsing, and checks to see if
473** this person has already received the mail.
474** It also supresses local network names and turns them into
475** local names.
476**
477** Parameters:
478** a -- the (preparsed) address header for the recipient.
479** targetq -- the queue to add the name to.
480**
481** Returns:
482** none.
483**
484** Side Effects:
485** none.
486**
b3cbe40f
EA
487** Called By:
488** sendto
489** main
b3cbe40f
EA
490*/
491
492recipient(a, targetq)
493 register addrq *a;
494 addrq *targetq;
495{
496 register addrq *q;
497 register struct mailer *m;
498 register char **pvp;
499 extern char *xalloc();
500 extern bool forward();
501 extern int errno;
502 extern bool sameaddr();
503
504 To = a->q_paddr;
505 m = a->q_mailer;
506 errno = 0;
507# ifdef DEBUG
508 if (Debug)
509 printf("recipient(%s)\n", To);
510# endif DEBUG
511
b3cbe40f
EA
512 /*
513 ** Look up this person in the recipient list. If they
514 ** are there already, return, otherwise continue.
515 */
516
517 if (!ForceMail)
518 {
519 for (q = &SendQ; (q = nxtinq(q)) != NULL; )
520 if (sameaddr(q, a, FALSE))
521 {
522# ifdef DEBUG
523 if (Debug)
524 printf("(%s in SendQ)\n", a->q_paddr);
525# endif DEBUG
526 return;
527 }
528 for (q = &AliasQ; (q = nxtinq(q)) != NULL; )
529 if (sameaddr(q, a, FALSE))
530 {
531# ifdef DEBUG
532 if (Debug)
533 printf("(%s in AliasQ)\n", a->q_paddr);
534# endif DEBUG
535 return;
536 }
537 }
538
539 /*
540 ** See if the user wants hir mail forwarded.
541 ** `Forward' must do the forwarding recursively.
542 */
543
544 if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a))
545 return;
546
547 /*
548 ** Put the user onto the target queue.
549 */
550
551 if (targetq != NULL)
552 {
553 putonq(a, targetq);
554 }
555
556 return;
557}
558\f/*
559** BUILDARGV -- Build an argument vector for a mail server.
560**
561** Using a template defined in config.c, an argv is built.
562** The format of the template is already a vector. The
563** items of this vector are copied, unless a dollar sign
564** is encountered. In this case, the next character
565** specifies something else to copy in. These can be
566** $f The from address.
567** $h The host.
568** $u The user.
569** $c The hop count.
570** The vector is built in a local buffer. A pointer to
571** the static argv is returned.
572**
573** Parameters:
574** tmplt -- a template for an argument vector.
575** flags -- the flags for this server.
576** host -- the host name to send to.
577** user -- the user name to send to.
578** from -- the person this mail is from.
579**
580** Returns:
581** A pointer to an argv.
582**
583** Side Effects:
584** none
585**
586** WARNING:
587** Since the argv is staticly allocated, any subsequent
588** calls will clobber the old argv.
589**
b3cbe40f
EA
590** Called By:
591** deliver
b3cbe40f
EA
592*/
593
594char **
595buildargv(tmplt, flags, host, user, from)
596 char **tmplt;
597 int flags;
598 char *host;
599 char *user;
600 char *from;
601{
602 register char *p;
603 register char *q;
604 static char *pv[MAXPV+1];
605 char **pvp;
606 char **mvp;
607 static char buf[512];
608 register char *bp;
609 char pbuf[30];
610
611 /*
612 ** Do initial argv setup.
613 ** Insert the mailer name. Notice that $x expansion is
614 ** NOT done on the mailer name. Then, if the mailer has
615 ** a picky -f flag, we insert it as appropriate. This
616 ** code does not check for 'pv' overflow; this places a
617 ** manifest lower limit of 4 for MAXPV.
618 */
619
620 pvp = pv;
621 bp = buf;
622
623 *pvp++ = tmplt[0];
624
625 /* insert -f or -r flag as appropriate */
626 if (flagset(M_FOPT|M_ROPT, flags) && FromFlag)
627 {
628 if (flagset(M_FOPT, flags))
629 *pvp++ = "-f";
630 else
631 *pvp++ = "-r";
632 *pvp++ = From.q_paddr;
633 }
634
635 /*
636 ** Build the rest of argv.
637 ** For each prototype parameter, the prototype is
638 ** scanned character at a time. If a dollar-sign is
639 ** found, 'q' is set to the appropriate expansion,
640 ** otherwise it is null. Then either the string
641 ** pointed to by q, or the original character, is
642 ** interpolated into the buffer. Buffer overflow is
643 ** checked.
644 */
645
646 for (mvp = tmplt; (p = *++mvp) != NULL; )
647 {
648 if (pvp >= &pv[MAXPV])
649 {
650 syserr("Too many parameters to %s", pv[0]);
651 return (NULL);
652 }
653 *pvp++ = bp;
654 for (; *p != '\0'; p++)
655 {
656 /* q will be the interpolated quantity */
657 q = NULL;
658 if (*p == '$')
659 {
660 switch (*++p)
661 {
662 case 'f': /* from person */
663 q = from;
664 break;
665
666 case 'u': /* user */
667 q = user;
668 break;
669
670 case 'h': /* host */
671 q = host;
672 break;
673
674 case 'c': /* hop count */
675 sprintf(pbuf, "%d", HopCount);
676 q = pbuf;
677 break;
678 }
679 }
680
681 /*
682 ** Interpolate q or output one character
683 ** Strip quote bits as we proceed.....
684 */
685
686 if (q != NULL)
687 {
688 while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0')
689 continue;
690 bp--;
b3cbe40f
EA
691 }
692 else if (bp < &buf[sizeof buf - 1])
693 *bp++ = *p;
694 }
695 *bp++ = '\0';
696 if (bp >= &buf[sizeof buf - 1])
697 return (NULL);
698 }
699 *pvp = NULL;
700
701# ifdef DEBUG
702 if (Debug)
703 {
704 printf("Interpolated argv is:\n");
705 for (mvp = pv; *mvp != NULL; mvp++)
706 printf("\t%s\n", *mvp);
707 }
708# endif DEBUG
709
710 return (pv);
711}
712\f/*
713** MAILFILE -- Send a message to a file.
714**
715** Parameters:
716** filename -- the name of the file to send to.
717**
718** Returns:
719** The exit code associated with the operation.
720**
721** Side Effects:
722** none.
723**
b3cbe40f
EA
724** Called By:
725** deliver
b3cbe40f
EA
726*/
727
728mailfile(filename)
729 char *filename;
730{
731 char buf[MAXLINE];
732 register FILE *f;
733 auto long tim;
734 extern char *ctime();
735
736 f = fopen(filename, "a");
737 if (f == NULL)
738 return (EX_CANTCREAT);
739
740 /* output the timestamp */
741 time(&tim);
742 fprintf(f, "From %s %s", From.q_paddr, ctime(&tim));
743 rewind(stdin);
744 while (fgets(buf, sizeof buf, stdin) != NULL)
745 {
746 fputs(buf, f);
747 if (ferror(f))
748 {
749 fclose(f);
750 return (EX_IOERR);
751 }
752 }
753 fputs("\n", f);
754 fclose(f);
755 return (EX_OK);
756}