cleanup to simplify configuration & fix minor bugs
[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
cb590f52 9static char SccsId[] = "@(#)deliver.c 1.5 %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;
84 Error = 0;
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 }
185 pid = fork();
186 if (pid < 0)
187 {
188 syserr("Cannot fork");
189 if (editfcn != NULL)
190 {
191 close(pvect[0]);
192 close(pvect[1]);
193 }
194 return (-1);
195 }
196 else if (pid == 0)
197 {
198 /* child -- set up input & exec mailer */
199 signal(SIGINT, SIG_IGN);
200 if (editfcn != NULL)
201 {
202 close(0);
203 if (dup(pvect[0]) < 0)
204 {
205 syserr("Cannot dup to zero!");
206 exit(EX_OSERR);
207 }
208 close(pvect[0]);
209 close(pvect[1]);
210 }
211 if (!flagset(M_RESTR, m->m_flags))
212 setuid(getuid());
213# ifdef LOG
214 initlog(NULL, 0, LOG_CLOSE);
215# endif LOG
216 endpwent();
217 execv(m->m_mailer, pvp);
218 /* syserr fails because log is closed */
219 /* syserr("Cannot exec %s", m->m_mailer); */
220 exit(EX_UNAVAIL);
221 }
222
223 /* arrange to write out header message if error */
224 if (editfcn != NULL)
225 {
226 close(pvect[0]);
227 signal(SIGPIPE, pipesig);
228 mfile = fdopen(pvect[1], "w");
229 (*editfcn)(mfile);
230 fclose(mfile);
231 }
232
233 /*
234 ** Wait for child to die and report status.
235 ** We should never get fatal errors (e.g., segmentation
236 ** violation), so we report those specially. For other
237 ** errors, we choose a status message (into statmsg),
238 ** and if it represents an error, we print it.
239 */
240
241 while ((i = wait(&st)) > 0 && i != pid)
242 continue;
243 if (i < 0)
244 {
245 syserr("wait");
246 return (-1);
247 }
248 if ((st & 0377) != 0)
249 {
250 syserr("%s: stat %o", pvp[0], st);
251 ExitStat = EX_UNAVAIL;
252 return (-1);
253 }
254 i = (st >> 8) & 0377;
255 giveresponse(i, FALSE, m);
256 return (i);
257}
258\f/*
259** GIVERESPONSE -- Interpret an error response from a mailer
260**
261** Parameters:
262** stat -- the status code from the mailer (high byte
263** only; core dumps must have been taken care of
264** already).
265** force -- if set, force an error message output, even
266** if the mailer seems to like to print its own
267** messages.
268** m -- the mailer descriptor for this mailer.
269**
270** Returns:
271** none.
272**
273** Side Effects:
274** Error may be set.
275** ExitStat may be set.
276**
b3cbe40f
EA
277** Called By:
278** deliver
b3cbe40f
EA
279*/
280
281giveresponse(stat, force, m)
282 int stat;
283 int force;
284 register struct mailer *m;
285{
286 register char *statmsg;
287 extern char *SysExMsg[];
288 register int i;
289 extern int N_SysEx;
290
291 i = stat - EX__BASE;
292 if (i < 0 || i > N_SysEx)
293 statmsg = NULL;
294 else
295 statmsg = SysExMsg[i];
296 if (stat == 0)
297 statmsg = "ok";
298 else
299 {
300 Error++;
301 if (statmsg == NULL && m->m_badstat != 0)
302 {
303 stat = m->m_badstat;
304 i = stat - EX__BASE;
305# ifdef DEBUG
306 if (i < 0 || i >= N_SysEx)
307 syserr("Bad m_badstat %d", stat);
308 else
309# endif DEBUG
310 statmsg = SysExMsg[i];
311 }
312 if (statmsg == NULL)
313 usrerr("unknown mailer response %d", stat);
314 else if (force || !flagset(M_QUIET, m->m_flags))
315 usrerr("%s", statmsg);
316 }
317
318 /*
319 ** Final cleanup.
320 ** Log a record of the transaction. Compute the new
321 ** ExitStat -- if we already had an error, stick with
322 ** that.
323 */
324
325# ifdef LOG
326 if (statmsg == NULL)
327 logmsg(LOG_INFO, "%s->%s: error %d", From.q_paddr, To, stat);
328 else
329 logmsg(LOG_INFO, "%s->%s: %s", From.q_paddr, To, statmsg);
330# endif LOG
cb590f52 331 setstat(stat);
b3cbe40f
EA
332 return (stat);
333}
334\f/*
335** PUTHEADER -- insert the From header into some mail
336**
337** For mailers such as 'msgs' that want the header inserted
338** into the mail, this edit filter inserts the From line and
339** then passes the rest of the message through.
340**
341** Parameters:
342** fp -- the file pointer for the output.
343**
344** Returns:
345** none
346**
347** Side Effects:
348** Puts a "From" line in UNIX format, and then
349** outputs the rest of the message.
350**
b3cbe40f
EA
351** Called By:
352** deliver
b3cbe40f
EA
353*/
354
355putheader(fp)
356 register FILE *fp;
357{
358 char buf[MAXLINE + 1];
359 long tim;
360 extern char *ctime();
361
362 time(&tim);
363 fprintf(fp, "From %s %s", From.q_paddr, ctime(&tim));
364 while (fgets(buf, sizeof buf, stdin) != NULL && !ferror(fp))
365 fputs(buf, fp);
366 if (ferror(fp))
367 {
368 syserr("putheader: write error");
369 setstat(EX_IOERR);
370 }
371}
372\f/*
373** PIPESIG -- Handle broken pipe signals
374**
375** This just logs an error.
376**
377** Parameters:
378** none
379**
380** Returns:
381** none
382**
383** Side Effects:
384** logs an error message.
b3cbe40f
EA
385*/
386
387pipesig()
388{
389 syserr("Broken pipe");
390}
391\f/*
392** SENDTO -- Designate a send list.
393**
394** The parameter is a comma-separated list of people to send to.
395** This routine arranges to send to all of them.
396**
397** Parameters:
398** list -- the send list.
399** copyf -- the copy flag; passed to parse.
400**
401** Returns:
402** none
403**
404** Side Effects:
405** none.
406**
b3cbe40f
EA
407** Called By:
408** main
409** alias
b3cbe40f
EA
410*/
411
412sendto(list, copyf)
413 char *list;
414 int copyf;
415{
416 register char *p;
417 register char *q;
418 register char c;
419 addrq *a;
420 extern addrq *parse();
421 bool more;
422
423 /* more keeps track of what the previous delimiter was */
424 more = TRUE;
425 for (p = list; more; )
426 {
427 /* find the end of this address */
428 q = p;
429 while ((c = *p++) != '\0' && c != ',' && c != '\n')
430 continue;
431 more = c != '\0';
432 *--p = '\0';
433 if (more)
434 p++;
435
436 /* parse the address */
437 if ((a = parse(q, (addrq *) NULL, copyf)) == NULL)
438 continue;
439
440 /* arrange to send to this person */
441 recipient(a, &SendQ);
442 }
443 To = NULL;
444}
445\f/*
446** RECIPIENT -- Designate a message recipient
447**
448** Saves the named person for future mailing.
449**
450** Designates a person as a recipient. This routine
451** does the initial parsing, and checks to see if
452** this person has already received the mail.
453** It also supresses local network names and turns them into
454** local names.
455**
456** Parameters:
457** a -- the (preparsed) address header for the recipient.
458** targetq -- the queue to add the name to.
459**
460** Returns:
461** none.
462**
463** Side Effects:
464** none.
465**
b3cbe40f
EA
466** Called By:
467** sendto
468** main
b3cbe40f
EA
469*/
470
471recipient(a, targetq)
472 register addrq *a;
473 addrq *targetq;
474{
475 register addrq *q;
476 register struct mailer *m;
477 register char **pvp;
478 extern char *xalloc();
479 extern bool forward();
480 extern int errno;
481 extern bool sameaddr();
482
483 To = a->q_paddr;
484 m = a->q_mailer;
485 errno = 0;
486# ifdef DEBUG
487 if (Debug)
488 printf("recipient(%s)\n", To);
489# endif DEBUG
490
491 /*
492 ** Don't go to the net if already on the target host.
493 ** This is important on the berkeley network, since
494 ** it get confused if we ask to send to ourselves.
495 ** For nets like the ARPANET, we probably will have
496 ** the local list set to NULL to simplify testing.
497 ** The canonical representation of the name is also set
498 ** to be just the local name so the duplicate letter
499 ** suppression algorithm will work.
500 */
501
502 if ((pvp = m->m_local) != NULL)
503 {
504 while (*pvp != NULL)
505 {
506 if (strcmp(*pvp++, a->q_host) == 0)
507 {
508 a->q_mailer = m = &Mailer[0];
509 break;
510 }
511 }
512 }
513
514 /*
515 ** Look up this person in the recipient list. If they
516 ** are there already, return, otherwise continue.
517 */
518
519 if (!ForceMail)
520 {
521 for (q = &SendQ; (q = nxtinq(q)) != NULL; )
522 if (sameaddr(q, a, FALSE))
523 {
524# ifdef DEBUG
525 if (Debug)
526 printf("(%s in SendQ)\n", a->q_paddr);
527# endif DEBUG
528 return;
529 }
530 for (q = &AliasQ; (q = nxtinq(q)) != NULL; )
531 if (sameaddr(q, a, FALSE))
532 {
533# ifdef DEBUG
534 if (Debug)
535 printf("(%s in AliasQ)\n", a->q_paddr);
536# endif DEBUG
537 return;
538 }
539 }
540
541 /*
542 ** See if the user wants hir mail forwarded.
543 ** `Forward' must do the forwarding recursively.
544 */
545
546 if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a))
547 return;
548
549 /*
550 ** Put the user onto the target queue.
551 */
552
553 if (targetq != NULL)
554 {
555 putonq(a, targetq);
556 }
557
558 return;
559}
560\f/*
561** BUILDARGV -- Build an argument vector for a mail server.
562**
563** Using a template defined in config.c, an argv is built.
564** The format of the template is already a vector. The
565** items of this vector are copied, unless a dollar sign
566** is encountered. In this case, the next character
567** specifies something else to copy in. These can be
568** $f The from address.
569** $h The host.
570** $u The user.
571** $c The hop count.
572** The vector is built in a local buffer. A pointer to
573** the static argv is returned.
574**
575** Parameters:
576** tmplt -- a template for an argument vector.
577** flags -- the flags for this server.
578** host -- the host name to send to.
579** user -- the user name to send to.
580** from -- the person this mail is from.
581**
582** Returns:
583** A pointer to an argv.
584**
585** Side Effects:
586** none
587**
588** WARNING:
589** Since the argv is staticly allocated, any subsequent
590** calls will clobber the old argv.
591**
b3cbe40f
EA
592** Called By:
593** deliver
b3cbe40f
EA
594*/
595
596char **
597buildargv(tmplt, flags, host, user, from)
598 char **tmplt;
599 int flags;
600 char *host;
601 char *user;
602 char *from;
603{
604 register char *p;
605 register char *q;
606 static char *pv[MAXPV+1];
607 char **pvp;
608 char **mvp;
609 static char buf[512];
610 register char *bp;
611 char pbuf[30];
612
613 /*
614 ** Do initial argv setup.
615 ** Insert the mailer name. Notice that $x expansion is
616 ** NOT done on the mailer name. Then, if the mailer has
617 ** a picky -f flag, we insert it as appropriate. This
618 ** code does not check for 'pv' overflow; this places a
619 ** manifest lower limit of 4 for MAXPV.
620 */
621
622 pvp = pv;
623 bp = buf;
624
625 *pvp++ = tmplt[0];
626
627 /* insert -f or -r flag as appropriate */
628 if (flagset(M_FOPT|M_ROPT, flags) && FromFlag)
629 {
630 if (flagset(M_FOPT, flags))
631 *pvp++ = "-f";
632 else
633 *pvp++ = "-r";
634 *pvp++ = From.q_paddr;
635 }
636
637 /*
638 ** Build the rest of argv.
639 ** For each prototype parameter, the prototype is
640 ** scanned character at a time. If a dollar-sign is
641 ** found, 'q' is set to the appropriate expansion,
642 ** otherwise it is null. Then either the string
643 ** pointed to by q, or the original character, is
644 ** interpolated into the buffer. Buffer overflow is
645 ** checked.
646 */
647
648 for (mvp = tmplt; (p = *++mvp) != NULL; )
649 {
650 if (pvp >= &pv[MAXPV])
651 {
652 syserr("Too many parameters to %s", pv[0]);
653 return (NULL);
654 }
655 *pvp++ = bp;
656 for (; *p != '\0'; p++)
657 {
658 /* q will be the interpolated quantity */
659 q = NULL;
660 if (*p == '$')
661 {
662 switch (*++p)
663 {
664 case 'f': /* from person */
665 q = from;
666 break;
667
668 case 'u': /* user */
669 q = user;
670 break;
671
672 case 'h': /* host */
673 q = host;
674 break;
675
676 case 'c': /* hop count */
677 sprintf(pbuf, "%d", HopCount);
678 q = pbuf;
679 break;
680 }
681 }
682
683 /*
684 ** Interpolate q or output one character
685 ** Strip quote bits as we proceed.....
686 */
687
688 if (q != NULL)
689 {
690 while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0')
691 continue;
692 bp--;
b3cbe40f
EA
693 }
694 else if (bp < &buf[sizeof buf - 1])
695 *bp++ = *p;
696 }
697 *bp++ = '\0';
698 if (bp >= &buf[sizeof buf - 1])
699 return (NULL);
700 }
701 *pvp = NULL;
702
703# ifdef DEBUG
704 if (Debug)
705 {
706 printf("Interpolated argv is:\n");
707 for (mvp = pv; *mvp != NULL; mvp++)
708 printf("\t%s\n", *mvp);
709 }
710# endif DEBUG
711
712 return (pv);
713}
714\f/*
715** MAILFILE -- Send a message to a file.
716**
717** Parameters:
718** filename -- the name of the file to send to.
719**
720** Returns:
721** The exit code associated with the operation.
722**
723** Side Effects:
724** none.
725**
b3cbe40f
EA
726** Called By:
727** deliver
b3cbe40f
EA
728*/
729
730mailfile(filename)
731 char *filename;
732{
733 char buf[MAXLINE];
734 register FILE *f;
735 auto long tim;
736 extern char *ctime();
737
738 f = fopen(filename, "a");
739 if (f == NULL)
740 return (EX_CANTCREAT);
741
742 /* output the timestamp */
743 time(&tim);
744 fprintf(f, "From %s %s", From.q_paddr, ctime(&tim));
745 rewind(stdin);
746 while (fgets(buf, sizeof buf, stdin) != NULL)
747 {
748 fputs(buf, f);
749 if (ferror(f))
750 {
751 fclose(f);
752 return (EX_IOERR);
753 }
754 }
755 fputs("\n", f);
756 fclose(f);
757 return (EX_OK);
758}