|
|
1.1 root 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:
9: static char SccsId[] = "@(#)deliver.c 1.11 10/27/80";
10:
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: ** Called By:
45: ** main
46: ** savemail
47: **
48: ** Files:
49: ** standard input -- must be opened to the message to
50: ** deliver.
51: */
52:
53: deliver(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: Errors = 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: /*
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.
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: }
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: # ifdef VFORK
186: pid = vfork();
187: # else
188: pid = fork();
189: # endif
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 */
203: /* make diagnostic output be standard output */
204: close(2);
205: dup(1);
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!");
213: _exit(EX_OSERR);
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
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: */
235: endpwent();
236: # endif
237: execv(m->m_mailer, pvp);
238: /* syserr fails because log is closed */
239: /* syserr("Cannot exec %s", m->m_mailer); */
240: _exit(EX_UNAVAILABLE);
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);
271: ExitStat = EX_UNAVAILABLE;
272: return (-1);
273: }
274: i = (st >> 8) & 0377;
275: giveresponse(i, FALSE, m);
276: return (i);
277: }
278: /*
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:
294: ** Errors may be incremented.
295: ** ExitStat may be set.
296: **
297: ** Called By:
298: ** deliver
299: */
300:
301: giveresponse(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;
310: extern long MsgSize;
311: char buf[30];
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: {
322: Errors++;
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:
347: if (statmsg == NULL)
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);
355: # endif LOG
356: setstat(stat);
357: return (stat);
358: }
359: /*
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: **
376: ** Called By:
377: ** deliver
378: */
379:
380: putheader(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: /*
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.
410: */
411:
412: pipesig()
413: {
414: syserr("Broken pipe");
415: signal(SIGPIPE, SIG_IGN);
416: }
417: /*
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: **
433: ** Called By:
434: ** main
435: ** alias
436: */
437:
438: sendto(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: /*
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: **
492: ** Called By:
493: ** sendto
494: ** main
495: */
496:
497: recipient(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:
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: /*
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: **
595: ** Called By:
596: ** deliver
597: */
598:
599: char **
600: buildargv(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--;
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: /*
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: **
729: ** Called By:
730: ** deliver
731: */
732:
733: mailfile(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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.