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