BSD 4 release
[unix-history] / usr / src / cmd / delivermail / 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
31cef89c 9static char SccsId[] = "@(#)deliver.c 1.11 10/27/80";
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;
afcbcc6e
EA
310 extern long MsgSize;
311 char buf[30];
b3cbe40f
EA
312
313 i = stat - EX__BASE;
314 if (i < 0 || i > N_SysEx)
315 statmsg = NULL;
316 else
317 statmsg = SysExMsg[i];
318 if (stat == 0)
319 statmsg = "ok";
320 else
321 {
d916f0ca 322 Errors++;
b3cbe40f
EA
323 if (statmsg == NULL && m->m_badstat != 0)
324 {
325 stat = m->m_badstat;
326 i = stat - EX__BASE;
327# ifdef DEBUG
328 if (i < 0 || i >= N_SysEx)
329 syserr("Bad m_badstat %d", stat);
330 else
331# endif DEBUG
332 statmsg = SysExMsg[i];
333 }
334 if (statmsg == NULL)
335 usrerr("unknown mailer response %d", stat);
336 else if (force || !flagset(M_QUIET, m->m_flags))
337 usrerr("%s", statmsg);
338 }
339
340 /*
341 ** Final cleanup.
342 ** Log a record of the transaction. Compute the new
343 ** ExitStat -- if we already had an error, stick with
344 ** that.
345 */
346
b3cbe40f 347 if (statmsg == NULL)
afcbcc6e
EA
348 {
349 sprintf(buf, "error %d", stat);
350 statmsg = buf;
351 }
352
353# ifdef LOG
354 logmsg(LOG_INFO, "%s->%s: %ld: %s", From.q_paddr, To, MsgSize, statmsg);
b3cbe40f 355# endif LOG
cb590f52 356 setstat(stat);
b3cbe40f
EA
357 return (stat);
358}
359\f/*
360** PUTHEADER -- insert the From header into some mail
361**
362** For mailers such as 'msgs' that want the header inserted
363** into the mail, this edit filter inserts the From line and
364** then passes the rest of the message through.
365**
366** Parameters:
367** fp -- the file pointer for the output.
368**
369** Returns:
370** none
371**
372** Side Effects:
373** Puts a "From" line in UNIX format, and then
374** outputs the rest of the message.
375**
b3cbe40f
EA
376** Called By:
377** deliver
b3cbe40f
EA
378*/
379
380putheader(fp)
381 register FILE *fp;
382{
383 char buf[MAXLINE + 1];
384 long tim;
385 extern char *ctime();
386
387 time(&tim);
388 fprintf(fp, "From %s %s", From.q_paddr, ctime(&tim));
389 while (fgets(buf, sizeof buf, stdin) != NULL && !ferror(fp))
390 fputs(buf, fp);
391 if (ferror(fp))
392 {
393 syserr("putheader: write error");
394 setstat(EX_IOERR);
395 }
396}
397\f/*
398** PIPESIG -- Handle broken pipe signals
399**
400** This just logs an error.
401**
402** Parameters:
403** none
404**
405** Returns:
406** none
407**
408** Side Effects:
409** logs an error message.
b3cbe40f
EA
410*/
411
412pipesig()
413{
414 syserr("Broken pipe");
902c5f15 415 signal(SIGPIPE, SIG_IGN);
b3cbe40f
EA
416}
417\f/*
418** SENDTO -- Designate a send list.
419**
420** The parameter is a comma-separated list of people to send to.
421** This routine arranges to send to all of them.
422**
423** Parameters:
424** list -- the send list.
425** copyf -- the copy flag; passed to parse.
426**
427** Returns:
428** none
429**
430** Side Effects:
431** none.
432**
b3cbe40f
EA
433** Called By:
434** main
435** alias
b3cbe40f
EA
436*/
437
438sendto(list, copyf)
439 char *list;
440 int copyf;
441{
442 register char *p;
443 register char *q;
444 register char c;
445 addrq *a;
446 extern addrq *parse();
447 bool more;
448
449 /* more keeps track of what the previous delimiter was */
450 more = TRUE;
451 for (p = list; more; )
452 {
453 /* find the end of this address */
454 q = p;
455 while ((c = *p++) != '\0' && c != ',' && c != '\n')
456 continue;
457 more = c != '\0';
458 *--p = '\0';
459 if (more)
460 p++;
461
462 /* parse the address */
463 if ((a = parse(q, (addrq *) NULL, copyf)) == NULL)
464 continue;
465
466 /* arrange to send to this person */
467 recipient(a, &SendQ);
468 }
469 To = NULL;
470}
471\f/*
472** RECIPIENT -- Designate a message recipient
473**
474** Saves the named person for future mailing.
475**
476** Designates a person as a recipient. This routine
477** does the initial parsing, and checks to see if
478** this person has already received the mail.
479** It also supresses local network names and turns them into
480** local names.
481**
482** Parameters:
483** a -- the (preparsed) address header for the recipient.
484** targetq -- the queue to add the name to.
485**
486** Returns:
487** none.
488**
489** Side Effects:
490** none.
491**
b3cbe40f
EA
492** Called By:
493** sendto
494** main
b3cbe40f
EA
495*/
496
497recipient(a, targetq)
498 register addrq *a;
499 addrq *targetq;
500{
501 register addrq *q;
502 register struct mailer *m;
503 register char **pvp;
504 extern char *xalloc();
505 extern bool forward();
506 extern int errno;
507 extern bool sameaddr();
508
509 To = a->q_paddr;
510 m = a->q_mailer;
511 errno = 0;
512# ifdef DEBUG
513 if (Debug)
514 printf("recipient(%s)\n", To);
515# endif DEBUG
516
b3cbe40f
EA
517 /*
518 ** Look up this person in the recipient list. If they
519 ** are there already, return, otherwise continue.
520 */
521
522 if (!ForceMail)
523 {
524 for (q = &SendQ; (q = nxtinq(q)) != NULL; )
525 if (sameaddr(q, a, FALSE))
526 {
527# ifdef DEBUG
528 if (Debug)
529 printf("(%s in SendQ)\n", a->q_paddr);
530# endif DEBUG
531 return;
532 }
533 for (q = &AliasQ; (q = nxtinq(q)) != NULL; )
534 if (sameaddr(q, a, FALSE))
535 {
536# ifdef DEBUG
537 if (Debug)
538 printf("(%s in AliasQ)\n", a->q_paddr);
539# endif DEBUG
540 return;
541 }
542 }
543
544 /*
545 ** See if the user wants hir mail forwarded.
546 ** `Forward' must do the forwarding recursively.
547 */
548
549 if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a))
550 return;
551
552 /*
553 ** Put the user onto the target queue.
554 */
555
556 if (targetq != NULL)
557 {
558 putonq(a, targetq);
559 }
560
561 return;
562}
563\f/*
564** BUILDARGV -- Build an argument vector for a mail server.
565**
566** Using a template defined in config.c, an argv is built.
567** The format of the template is already a vector. The
568** items of this vector are copied, unless a dollar sign
569** is encountered. In this case, the next character
570** specifies something else to copy in. These can be
571** $f The from address.
572** $h The host.
573** $u The user.
574** $c The hop count.
575** The vector is built in a local buffer. A pointer to
576** the static argv is returned.
577**
578** Parameters:
579** tmplt -- a template for an argument vector.
580** flags -- the flags for this server.
581** host -- the host name to send to.
582** user -- the user name to send to.
583** from -- the person this mail is from.
584**
585** Returns:
586** A pointer to an argv.
587**
588** Side Effects:
589** none
590**
591** WARNING:
592** Since the argv is staticly allocated, any subsequent
593** calls will clobber the old argv.
594**
b3cbe40f
EA
595** Called By:
596** deliver
b3cbe40f
EA
597*/
598
599char **
600buildargv(tmplt, flags, host, user, from)
601 char **tmplt;
602 int flags;
603 char *host;
604 char *user;
605 char *from;
606{
607 register char *p;
608 register char *q;
609 static char *pv[MAXPV+1];
610 char **pvp;
611 char **mvp;
612 static char buf[512];
613 register char *bp;
614 char pbuf[30];
615
616 /*
617 ** Do initial argv setup.
618 ** Insert the mailer name. Notice that $x expansion is
619 ** NOT done on the mailer name. Then, if the mailer has
620 ** a picky -f flag, we insert it as appropriate. This
621 ** code does not check for 'pv' overflow; this places a
622 ** manifest lower limit of 4 for MAXPV.
623 */
624
625 pvp = pv;
626 bp = buf;
627
628 *pvp++ = tmplt[0];
629
630 /* insert -f or -r flag as appropriate */
631 if (flagset(M_FOPT|M_ROPT, flags) && FromFlag)
632 {
633 if (flagset(M_FOPT, flags))
634 *pvp++ = "-f";
635 else
636 *pvp++ = "-r";
637 *pvp++ = From.q_paddr;
638 }
639
640 /*
641 ** Build the rest of argv.
642 ** For each prototype parameter, the prototype is
643 ** scanned character at a time. If a dollar-sign is
644 ** found, 'q' is set to the appropriate expansion,
645 ** otherwise it is null. Then either the string
646 ** pointed to by q, or the original character, is
647 ** interpolated into the buffer. Buffer overflow is
648 ** checked.
649 */
650
651 for (mvp = tmplt; (p = *++mvp) != NULL; )
652 {
653 if (pvp >= &pv[MAXPV])
654 {
655 syserr("Too many parameters to %s", pv[0]);
656 return (NULL);
657 }
658 *pvp++ = bp;
659 for (; *p != '\0'; p++)
660 {
661 /* q will be the interpolated quantity */
662 q = NULL;
663 if (*p == '$')
664 {
665 switch (*++p)
666 {
667 case 'f': /* from person */
668 q = from;
669 break;
670
671 case 'u': /* user */
672 q = user;
673 break;
674
675 case 'h': /* host */
676 q = host;
677 break;
678
679 case 'c': /* hop count */
680 sprintf(pbuf, "%d", HopCount);
681 q = pbuf;
682 break;
683 }
684 }
685
686 /*
687 ** Interpolate q or output one character
688 ** Strip quote bits as we proceed.....
689 */
690
691 if (q != NULL)
692 {
693 while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0')
694 continue;
695 bp--;
b3cbe40f
EA
696 }
697 else if (bp < &buf[sizeof buf - 1])
698 *bp++ = *p;
699 }
700 *bp++ = '\0';
701 if (bp >= &buf[sizeof buf - 1])
702 return (NULL);
703 }
704 *pvp = NULL;
705
706# ifdef DEBUG
707 if (Debug)
708 {
709 printf("Interpolated argv is:\n");
710 for (mvp = pv; *mvp != NULL; mvp++)
711 printf("\t%s\n", *mvp);
712 }
713# endif DEBUG
714
715 return (pv);
716}
717\f/*
718** MAILFILE -- Send a message to a file.
719**
720** Parameters:
721** filename -- the name of the file to send to.
722**
723** Returns:
724** The exit code associated with the operation.
725**
726** Side Effects:
727** none.
728**
b3cbe40f
EA
729** Called By:
730** deliver
b3cbe40f
EA
731*/
732
733mailfile(filename)
734 char *filename;
735{
736 char buf[MAXLINE];
737 register FILE *f;
738 auto long tim;
739 extern char *ctime();
740
741 f = fopen(filename, "a");
742 if (f == NULL)
743 return (EX_CANTCREAT);
744
745 /* output the timestamp */
746 time(&tim);
747 fprintf(f, "From %s %s", From.q_paddr, ctime(&tim));
748 rewind(stdin);
749 while (fgets(buf, sizeof buf, stdin) != NULL)
750 {
751 fputs(buf, f);
752 if (ferror(f))
753 {
754 fclose(f);
755 return (EX_IOERR);
756 }
757 }
758 fputs("\n", f);
759 fclose(f);
760 return (EX_OK);
761}