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