cleaned up comments
[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
f895a45d 9static char SccsId[] = "@(#)deliver.c 1.4 %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
138# ifdef BADMAIL
139 /*
140 ** If the mailer doesn't return the proper
141 ** exit statuses, check here to see if the
142 ** user exists so that we can give a pretty
143 ** error message.
144 */
145
146 if (m == &Mailer[0])
147 {
148 if (getpwnam(user) == NULL)
149 {
150 giveresponse(EX_NOUSER, TRUE, m);
151 return (EX_NOUSER);
152 }
153 }
154# endif BADMAIL
155
156 /*
157 ** If the mailer wants a From line, insert a new editfcn.
158 */
159
160 if (flagset(M_HDR, m->m_flags) && editfcn == NULL)
161 editfcn = putheader;
162
163 /*
164 ** Call the mailer.
165 ** The argument vector gets built, pipes through 'editfcn'
166 ** are created as necessary, and we fork & exec as
167 ** appropriate. In the parent, we call 'editfcn'.
168 */
169
170 pvp = buildargv(m->m_argv, m->m_flags, host, user, From.q_paddr);
171 if (pvp == NULL)
172 {
173 usrerr("name too long");
174 return (-1);
175 }
176 rewind(stdin);
177
178 /* create a pipe if we will need one */
179 if (editfcn != NULL && pipe(pvect) < 0)
180 {
181 syserr("pipe");
182 return (-1);
183 }
184 pid = fork();
185 if (pid < 0)
186 {
187 syserr("Cannot fork");
188 if (editfcn != NULL)
189 {
190 close(pvect[0]);
191 close(pvect[1]);
192 }
193 return (-1);
194 }
195 else if (pid == 0)
196 {
197 /* child -- set up input & exec mailer */
198 signal(SIGINT, SIG_IGN);
199 if (editfcn != NULL)
200 {
201 close(0);
202 if (dup(pvect[0]) < 0)
203 {
204 syserr("Cannot dup to zero!");
205 exit(EX_OSERR);
206 }
207 close(pvect[0]);
208 close(pvect[1]);
209 }
210 if (!flagset(M_RESTR, m->m_flags))
211 setuid(getuid());
212# ifdef LOG
213 initlog(NULL, 0, LOG_CLOSE);
214# endif LOG
215 endpwent();
216 execv(m->m_mailer, pvp);
217 /* syserr fails because log is closed */
218 /* syserr("Cannot exec %s", m->m_mailer); */
219 exit(EX_UNAVAIL);
220 }
221
222 /* arrange to write out header message if error */
223 if (editfcn != NULL)
224 {
225 close(pvect[0]);
226 signal(SIGPIPE, pipesig);
227 mfile = fdopen(pvect[1], "w");
228 (*editfcn)(mfile);
229 fclose(mfile);
230 }
231
232 /*
233 ** Wait for child to die and report status.
234 ** We should never get fatal errors (e.g., segmentation
235 ** violation), so we report those specially. For other
236 ** errors, we choose a status message (into statmsg),
237 ** and if it represents an error, we print it.
238 */
239
240 while ((i = wait(&st)) > 0 && i != pid)
241 continue;
242 if (i < 0)
243 {
244 syserr("wait");
245 return (-1);
246 }
247 if ((st & 0377) != 0)
248 {
249 syserr("%s: stat %o", pvp[0], st);
250 ExitStat = EX_UNAVAIL;
251 return (-1);
252 }
253 i = (st >> 8) & 0377;
254 giveresponse(i, FALSE, m);
255 return (i);
256}
257\f/*
258** GIVERESPONSE -- Interpret an error response from a mailer
259**
260** Parameters:
261** stat -- the status code from the mailer (high byte
262** only; core dumps must have been taken care of
263** already).
264** force -- if set, force an error message output, even
265** if the mailer seems to like to print its own
266** messages.
267** m -- the mailer descriptor for this mailer.
268**
269** Returns:
270** none.
271**
272** Side Effects:
273** Error may be set.
274** ExitStat may be set.
275**
b3cbe40f
EA
276** Called By:
277** deliver
b3cbe40f
EA
278*/
279
280giveresponse(stat, force, m)
281 int stat;
282 int force;
283 register struct mailer *m;
284{
285 register char *statmsg;
286 extern char *SysExMsg[];
287 register int i;
288 extern int N_SysEx;
289
290 i = stat - EX__BASE;
291 if (i < 0 || i > N_SysEx)
292 statmsg = NULL;
293 else
294 statmsg = SysExMsg[i];
295 if (stat == 0)
296 statmsg = "ok";
297 else
298 {
299 Error++;
300 if (statmsg == NULL && m->m_badstat != 0)
301 {
302 stat = m->m_badstat;
303 i = stat - EX__BASE;
304# ifdef DEBUG
305 if (i < 0 || i >= N_SysEx)
306 syserr("Bad m_badstat %d", stat);
307 else
308# endif DEBUG
309 statmsg = SysExMsg[i];
310 }
311 if (statmsg == NULL)
312 usrerr("unknown mailer response %d", stat);
313 else if (force || !flagset(M_QUIET, m->m_flags))
314 usrerr("%s", statmsg);
315 }
316
317 /*
318 ** Final cleanup.
319 ** Log a record of the transaction. Compute the new
320 ** ExitStat -- if we already had an error, stick with
321 ** that.
322 */
323
324# ifdef LOG
325 if (statmsg == NULL)
326 logmsg(LOG_INFO, "%s->%s: error %d", From.q_paddr, To, stat);
327 else
328 logmsg(LOG_INFO, "%s->%s: %s", From.q_paddr, To, statmsg);
329# endif LOG
330 if (ExitStat == EX_OK)
331 ExitStat = stat;
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}