|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)spost.c 1.6 (Berkeley) 11/2/85";
3: #endif
4:
5: /* spost.c - feed messages to sendmail */
6: /*
7: * (This is a simpler, faster, replacement for "post" for use when "sendmail"
8: * is the transport system)
9: */
10:
11: #include <ctype.h>
12: #include <stdio.h>
13: #include <sys/file.h>
14: #include "../h/mh.h"
15: #include "../h/addrsbr.h"
16: #include "../h/aliasbr.h"
17: #include "../h/dropsbr.h"
18: #include "../zotnet/tws.h"
19:
20: extern char *getfullname(), *getusr();
21: extern int errno;
22: extern int sys_nerr;
23: extern char *sys_errlist[];
24:
25: #define uptolow(c) (isupper (c) ? tolower (c) : c)
26:
27: #define SENDMAIL "/usr/sbin/sendmail"
28: #define MAX_SM_FIELD 1476 /* < largest hdr field sendmail will accept */
29: #define FCCS 10 /* max number of fccs allowed */
30:
31: struct swit switches[] = {
32: #define FILTSW 0
33: "filter filterfile", 0,
34: #define NFILTSW 1
35: "nofilter", 0,
36:
37: #define FRMTSW 2
38: "format", 0,
39: #define NFRMTSW 3
40: "noformat", 0,
41:
42: #define REMVSW 4
43: "remove", 0,
44: #define NREMVSW 5
45: "noremove", 0,
46:
47: #define VERBSW 6
48: "verbose", 0,
49: #define NVERBSW 7
50: "noverbose", 0,
51:
52: #define WATCSW 8
53: "watch", 0,
54: #define NWATCSW 9
55: "nowatch", 0,
56:
57: #define HELPSW 10
58: "help", 4,
59:
60: #define DEBUGSW 11
61: "debug", -5,
62:
63: #define DISTSW 12
64: "dist", -4, /* interface from dist */
65:
66: #define BACKSW 13
67: "backup", 0,
68: #define NBACKSW 14
69: "nobackup", 0,
70:
71: #define CHKSW 15
72: "check", -5, /* interface from whom */
73: #define NCHKSW 16
74: "nocheck", -7, /* interface from whom */
75: #define WHOMSW 17
76: "whom", -4, /* interface from whom */
77:
78: #define PUSHSW 18 /* fork to sendmail then exit */
79: "push", -4,
80: #define NPUSHSW 19 /* exec sendmail */
81: "nopush", -6,
82:
83: #define ALIASW 20
84: "alias aliasfile", 0,
85: #define NALIASW 21
86: "noalias", 0,
87:
88: #define WIDTHSW 22
89: "width columns", 0,
90:
91: #define LIBSW 23
92: "library directory", -7,
93:
94: #define ANNOSW 24
95: "idanno number", -6,
96:
97: NULL, NULL
98: };
99:
100: struct headers {
101: char *value;
102: unsigned int flags;
103: #define HNOP 0x0000 /* just used to keep .set around */
104: #define HBAD 0x0001 /* bad header - don't let it through */
105: #define HADR 0x0002 /* header has an address field */
106: #define HSUB 0x0004 /* Subject: header */
107: #define HTRY 0x0008 /* try to send to addrs on header */
108: #define HBCC 0x0010 /* don't output this header */
109: #define HMNG 0x0020 /* mung this header */
110: #define HNGR 0x0040 /* no groups allowed in this header */
111: #define HFCC 0x0080 /* FCC: type header */
112: #define HNIL 0x0100 /* okay for this header not to have addrs */
113: #define HIGN 0x0200 /* ignore this header */
114: unsigned int set;
115: #define MFRM 0x0001 /* we've seen a From: */
116: #define MDAT 0x0002 /* we've seen a Date: */
117: #define MRFM 0x0004 /* we've seen a Resent-From: */
118: #define MVIS 0x0008 /* we've seen sighted addrs */
119: #define MINV 0x0010 /* we've seen blind addrs */
120: #define MRDT 0x0020 /* we've seen a Resent-Date: */
121: };
122:
123: static struct headers NHeaders[] = {
124: "Return-Path", HBAD, NULL,
125: "Received", HBAD, NULL,
126: "Reply-To", HADR | HNGR, NULL,
127: "From", HADR | HNGR, MFRM,
128: "Sender", HADR | HBAD, NULL,
129: "Date", HNOP, MDAT,
130: "Subject", HSUB, NULL,
131: "To", HADR | HTRY, MVIS,
132: "cc", HADR | HTRY, MVIS,
133: "Bcc", HADR | HTRY | HBCC | HNIL, MINV,
134: "Message-Id", HBAD, NULL,
135: "Fcc", HFCC, NULL,
136: NULL
137: };
138:
139: static struct headers RHeaders[] = {
140: "Resent-Reply-To", HADR | HNGR, NULL,
141: "Resent-From", HADR | HNGR, MRFM,
142: "Resent-Sender", HADR | HBAD, NULL,
143: "Resent-Date", HNOP, MRDT,
144: "Resent-Subject", HSUB, NULL,
145: "Resent-To", HADR | HTRY, MVIS,
146: "Resent-cc", HADR | HTRY, MVIS,
147: "Resent-Bcc", HADR | HTRY | HBCC, MINV,
148: "Resent-Message-Id", HBAD, NULL,
149: "Resent-Fcc", HFCC, NULL,
150: "Reply-To", HADR, NULL,
151: "Fcc", HIGN, NULL,
152: NULL
153: };
154:
155: static short fccind = 0; /* index into fccfold[] */
156: static int badmsg = 0; /* message has bad semantics */
157: static int verbose = 0; /* spell it out */
158: static int debug = 0; /* debugging post */
159: static int rmflg = 1; /* remove temporary file when done */
160: static int watch = 0; /* watch the delivery process */
161: static int backflg = 0; /* rename input file as *.bak when done */
162: static int whomflg = 0; /* if just checking addresses */
163: static int pushflg = 0; /* if going to fork to sendmail */
164: static int aliasflg = -1; /* if going to process aliases */
165: static int outputlinelen = 72;
166: static unsigned msgflags = 0; /* what we've seen */
167:
168: static enum {
169: normal, resent
170: } msgstate = normal;
171:
172: static char tmpfil[] = "/tmp/pstXXXXXX";
173: static char from[BUFSIZ]; /* my network address */
174: static char signature[BUFSIZ]; /* my signature */
175: static char *filter = NULL; /* the filter for BCC'ing */
176: static char *subject = NULL; /* the subject field for BCC'ing */
177: static char *fccfold[FCCS]; /* foldernames for FCC'ing */
178: static struct headers *hdrtab; /* table for the message we're doing */
179: static FILE *out; /* output (temp) file */
180:
181: static int get_header(), putone();
182: static void putfmt(), start_headers(), finish_headers(), putadr(),
183: insert_fcc(), file(), fcc(), die();
184:
185: /* ARGSUSED */
186: main(argc, argv)
187: int argc;
188: char *argv[];
189: {
190: int state, i, pid, compnum;
191: char *cp, *msg = NULL, **argp = argv + 1,
192: *sargv[16], buf[BUFSIZ], name[NAMESZ], *arguments[MAXARGS];
193: FILE *in;
194:
195: invo_name = r1bindex(argv[0], '/');
196: mts_init(invo_name);
197: if ((cp = m_find(invo_name)) != NULL) {
198: argp = copyip(brkstring(cp, " ", "\n"), arguments);
199: (void) copyip(argv + 1, argp);
200: argp = arguments;
201: }
202: while (cp = *argp++) {
203: if (*cp == '-')
204: switch (smatch(++cp, switches)) {
205: case AMBIGSW:
206: ambigsw(cp, switches);
207: done(1);
208: case UNKWNSW:
209: adios(NULLCP, "-%s unknown", cp);
210: case HELPSW:
211: (void) sprintf(buf, "%s [switches] file",
212: invo_name);
213: help(buf, switches);
214: done(1);
215:
216: case DEBUGSW:
217: debug++;
218: continue;
219:
220: case DISTSW:
221: msgstate = resent;
222: continue;
223:
224: case WHOMSW:
225: whomflg++;
226: continue;
227:
228: case FILTSW:
229: if (!(filter = *argp++) || *filter == '-')
230: adios(NULLCP, "missing argument to %s",
231: argp[-2]);
232: continue;
233: case NFILTSW:
234: filter = NULL;
235: continue;
236:
237: case REMVSW:
238: rmflg++;
239: continue;
240: case NREMVSW:
241: rmflg = 0;
242: continue;
243:
244: case BACKSW:
245: backflg++;
246: continue;
247: case NBACKSW:
248: backflg = 0;
249: continue;
250:
251: case VERBSW:
252: verbose++;
253: continue;
254: case NVERBSW:
255: verbose = 0;
256: continue;
257:
258: case WATCSW:
259: watch++;
260: continue;
261: case NWATCSW:
262: watch = 0;
263: continue;
264:
265: case PUSHSW:
266: pushflg++;
267: continue;
268: case NPUSHSW:
269: pushflg = 0;
270: continue;
271:
272: case ALIASW:
273: if (!(cp = *argp++) || *cp == '-')
274: adios(NULLCP, "missing argument to %s",
275: argp[-2]);
276: if (aliasflg < 0)
277: /* load default aka's */
278: (void) alias(AliasFile);
279: aliasflg = 1;
280: if ((state = alias(cp)) != AK_OK)
281: adios(NULLCP,
282: "aliasing error in file %s - %s",
283: cp, akerror(state));
284: continue;
285: case NALIASW:
286: aliasflg = 0;
287: continue;
288:
289: case WIDTHSW:
290: if (!(cp = *argp++) || *cp == '-')
291: adios(NULLCP, "missing argument to %s",
292: argp[-2]);
293: outputlinelen = atoi(cp);
294: if (outputlinelen <= 10)
295: outputlinelen = 72;
296: continue;
297:
298: case LIBSW:
299: case ANNOSW:
300: /* -library & -idanno switch ignored */
301: if (!(cp = *argp++) || *cp == '-')
302: adios(NULLCP, "missing argument to %s",
303: argp[-2]);
304: continue;
305: }
306: if (msg)
307: adios(NULLCP, "only one message at a time!");
308: else
309: msg = cp;
310: }
311:
312: if (aliasflg < 0)
313: alias(AliasFile); /* load default aka's */
314:
315: if (!msg)
316: adios(NULLCP, "usage: %s [switches] file", invo_name);
317:
318: if ((in = fopen(msg, "r")) == NULL)
319: adios(msg, "unable to open");
320:
321: start_headers();
322: if (debug) {
323: verbose++;
324: out = stdout;
325: } else {
326: (void) mktemp(tmpfil);
327: if ((out = fopen(tmpfil, "w")) == NULL)
328: adios(tmpfil, "unable to create");
329: (void) chmod(tmpfil, 0600);
330: }
331:
332: hdrtab = (msgstate == normal) ? NHeaders : RHeaders;
333:
334: for (compnum = 1, state = FLD;;) {
335: switch (state = m_getfld(state, name, buf, sizeof buf, in)) {
336: case FLD:
337: compnum++;
338: putfmt(name, buf, out);
339: continue;
340:
341: case FLDPLUS:
342: compnum++;
343: cp = add(buf, cp);
344: while (state == FLDPLUS) {
345: state = m_getfld(state, name, buf, sizeof buf, in);
346: cp = add(buf, cp);
347: }
348: putfmt(name, cp, out);
349: free(cp);
350: continue;
351:
352: case BODY:
353: finish_headers(out);
354: fprintf(out, "\n%s", buf);
355: if (whomflg == 0)
356: while (state == BODY) {
357: state = m_getfld(state, name, buf,
358: sizeof buf, in);
359: fputs(buf, out);
360: }
361: break;
362:
363: case FILEEOF:
364: finish_headers(out);
365: break;
366:
367: case LENERR:
368: case FMTERR:
369: adios(NULLCP, "message format error in component #%d",
370: compnum);
371:
372: default:
373: adios(NULLCP, "getfld() returned %d", state);
374: }
375: break;
376: }
377:
378: (void) fclose(in);
379: if (backflg && !whomflg) {
380: (void) strcpy(buf, m_backup(msg));
381: if (rename(msg, buf) == NOTOK)
382: advise(buf, "unable to rename %s to", msg);
383: }
384: if (debug) {
385: done(0);
386: } else
387: (void) fclose(out);
388:
389: file(tmpfil);
390:
391: /*
392: * re-open the temp file, unlink it and exec sendmail, giving it the
393: * msg temp file as std in.
394: */
395: if (freopen(tmpfil, "r", stdin) == NULL)
396: adios(tmpfil, "can't reopen for sendmail");
397: if (rmflg)
398: (void) unlink(tmpfil);
399:
400: argp = sargv;
401: *argp++ = "send-mail";
402: *argp++ = "-m"; /* send to me too */
403: *argp++ = "-t"; /* read msg for recipients */
404: *argp++ = "-i"; /* don't stop on "." */
405: if (whomflg)
406: *argp++ = "-bv";
407: if (watch || verbose)
408: *argp++ = "-v";
409: *argp = NULL;
410:
411: if (pushflg && !(watch || verbose)) {
412: /* Insure sendmail exists if using the push flag */
413: errno = 0;
414: if (access(SENDMAIL, X_OK) < 0) {
415: adios(SENDMAIL, errno > 0 && errno < sys_nerr ?
416: sys_errlist[errno] : "unknown error");
417: }
418:
419: /* fork to a child to run sendmail */
420: for (i = 0; (pid = vfork()) == NOTOK && i < 5; i++)
421: sleep(5);
422: switch (pid) {
423: case NOTOK:
424: fprintf(verbose ? stdout : stderr,
425: "%s: can't fork to %s\n", invo_name, SENDMAIL);
426: exit(-1);
427: case OK:
428: /* we're the child .. */
429: break;
430: default:
431: exit(0);
432: }
433: }
434: execv(SENDMAIL, sargv);
435: adios(SENDMAIL, "can't exec");
436: }
437:
438: /* DRAFT GENERATION */
439: static void
440: putfmt(name, str, out)
441: char *name, *str;
442: FILE *out;
443: {
444: int count, grp, i, keep;
445: char *cp, *pp, *qp, namep[BUFSIZ];
446: struct mailname *mp, *np;
447: struct headers *hdr;
448:
449: while (*str == ' ' || *str == '\t')
450: str++;
451:
452: if ((i = get_header(name, hdrtab)) == NOTOK) {
453: fprintf(out, "%s: %s", name, str);
454: return;
455: }
456: hdr = &hdrtab[i];
457: if (hdr->flags & HIGN)
458: return;
459: if (hdr->flags & HBAD) {
460: advise(NULLCP, "illegal header line -- %s:", name);
461: badmsg++;
462: return;
463: }
464: msgflags |= hdr->set;
465:
466: if (hdr->flags & HSUB)
467: subject = subject ? add(str, add("\t", subject)) : getcpy(str);
468:
469: if (hdr->flags & HFCC) {
470: if (cp = rindex(str, '\n'))
471: *cp = NULL;
472: for (cp = pp = str; cp = index(pp, ','); pp = cp) {
473: *cp++ = NULL;
474: insert_fcc(hdr, pp);
475: }
476: insert_fcc(hdr, pp);
477: return;
478: }
479: if (*str != '\n' && *str != '\0')
480: if (aliasflg && hdr->flags & HTRY) {
481: /*
482: * this header contains address(es) that we have to
483: * do alias expansion on. Because of the saved state
484: * in getname we have to put all the addresses into a
485: * list. We then let putadr munch on that list,
486: * possibly expanding aliases.
487: */
488: register struct mailname *f = 0;
489: register struct mailname *mp = 0;
490:
491: while (cp = getname(str)) {
492: mp = getm(cp, NULLCP, 0, AD_HOST, NULLCP);
493: if (f == 0) {
494: f = mp;
495: mp->m_next = mp;
496: } else {
497: mp->m_next = f->m_next;
498: f->m_next = mp;
499: f = mp;
500: }
501: }
502: /*
503: * This error only seems to occur when a
504: * message contains an address that is
505: * incorrectly split to a continuation line
506: * (e.g. the continuation line is missing the
507: * required white space).
508: */
509: if (f == 0 || mp == 0) {
510: advise(NULLCP, "bogus address -- \"%s\"", str);
511: badmsg++;
512: return;
513: }
514: f = mp->m_next;
515: mp->m_next = 0;
516: putadr(name, f, hdr->flags & HBCC);
517: } else {
518: fprintf(out, "%s: %s", name, str);
519: }
520: }
521:
522: static void
523: start_headers()
524: {
525: char *cp;
526: char sigbuf[BUFSIZ];
527:
528: (void) strcpy(from, getusr());
529:
530: if ((cp = getfullname()) && *cp) {
531: (void) strcpy(sigbuf, cp);
532: (void) sprintf(signature, "%s <%s>", sigbuf, from);
533: } else
534: (void) sprintf(signature, "%s", from);
535: }
536:
537: static char **bcc_list;
538: static int bcc_list_size;
539: static int bcc_list_next;
540:
541: static void
542: add_bcc(name)
543: char *name;
544: {
545: if (bcc_list_next >= bcc_list_size) {
546: if (! bcc_list) {
547: bcc_list_size = 32;
548: bcc_list = (char **)malloc(32 * sizeof(*bcc_list));
549: } else {
550: bcc_list_size <<= 1;
551: bcc_list = (char **)realloc(bcc_list,
552: bcc_list_size * sizeof(*bcc_list));
553: }
554: }
555: bcc_list[bcc_list_next++] = getcpy(name);
556: }
557:
558: static void
559: put_bcc_list(name)
560: char *name;
561: {
562: register int i, linepos, namelen;
563:
564: if (! bcc_list_next)
565: return;
566:
567: fprintf(out, "%s: ", name);
568: namelen = strlen(name) + 2;
569: linepos = namelen;
570: for (i = 0; i < bcc_list_next; ++i) {
571: if (linepos > MAX_SM_FIELD) {
572: fprintf(out, "\n%s: ", name);
573: linepos = namelen;
574: }
575: linepos = putone(bcc_list[i], linepos, namelen);
576: }
577: putc('\n', out);
578: }
579:
580: static void
581: finish_headers(out)
582: FILE *out;
583: {
584: switch (msgstate) {
585: case normal:
586: put_bcc_list("bcc");
587: if (!(msgflags & MDAT))
588: fprintf(out, "Date: %s\n", dtimenow());
589: if (msgflags & MFRM)
590: fprintf(out, "Sender: %s\n", from);
591: else
592: fprintf(out, "From: %s\n", signature);
593: break;
594:
595: case resent:
596: put_bcc_list("resent-bcc");
597: if (!(msgflags & MRDT))
598: fprintf(out, "Resent-Date: %s\n", dtimenow());
599: if (msgflags & MRFM)
600: fprintf(out, "Resent-Sender: %s\n", from);
601: else
602: fprintf(out, "Resent-From: %s\n", signature);
603: break;
604: }
605:
606: if (badmsg)
607: adios(NULLCP, "re-format message and try again");
608: }
609:
610: static int
611: get_header(header, table)
612: char *header;
613: struct headers *table;
614: {
615: struct headers *h;
616:
617: for (h = table; h->value; h++)
618: if (uleq(header, h->value))
619: return (h - table);
620:
621: return NOTOK;
622: }
623:
624: /*
625: * output the address list for header "name". The address list is a linked
626: * list of mailname structs. "nl" points to the head of the list. Alias
627: * substitution should be done on nl.
628: */
629: static void
630: putadr(name, nl, isbcc)
631: char *name;
632: struct mailname *nl;
633: int isbcc;
634: {
635: register struct mailname *mp, *mp2;
636: register int linepos;
637: register char *cp;
638: int namelen;
639:
640: fprintf(out, "%s: ", name);
641: namelen = strlen(name) + 2;
642: linepos = namelen;
643:
644: for (mp = nl; mp;) {
645: if (linepos > MAX_SM_FIELD) {
646: fprintf(out, "\n%s: ", name);
647: linepos = namelen;
648: }
649: if (mp->m_nohost && (cp = akvalue(mp->m_mbox)) != mp->m_mbox) {
650: /*
651: * an alias - if 'invisible' or we're doing
652: * bcc's, expand it. Otherwise just add it's
653: * names to the bcc list.
654: */
655: if (!isbcc && akvisible()) {
656: char tmpname[NAMESZ];
657:
658: sprintf(tmpname, "(%s)", mp->m_mbox);
659: linepos = putone(tmpname, linepos, namelen);
660: }
661: while (cp = getname(cp)) {
662: if (linepos > MAX_SM_FIELD) {
663: fprintf(out, "\n%s: ", name);
664: linepos = namelen;
665: }
666: mp2 = getm(cp, NULLCP, 0, AD_HOST, NULLCP);
667: if (!isbcc && akvisible())
668: add_bcc(mp2->m_text);
669: else
670: linepos = putone(mp2->m_text, linepos,
671: namelen);
672: mnfree(mp2);
673: }
674: } else
675: /* not a local name - use what the user typed */
676: linepos = putone(mp->m_text, linepos, namelen);
677: mp2 = mp;
678: mp = mp->m_next;
679: mnfree(mp2);
680: }
681: putc('\n', out);
682: }
683:
684: static int
685: putone(adr, pos, indent)
686: register char *adr;
687: register int pos;
688: int indent;
689: {
690: register int len;
691: static int linepos;
692:
693: len = strlen(adr);
694: if (pos == indent)
695: linepos = pos;
696: else if (linepos + len > outputlinelen) {
697: fprintf(out, ",\n%*s", indent, "");
698: linepos = indent;
699: pos += indent + 2;
700: } else {
701: fputs(", ", out);
702: linepos += 2;
703: pos += 2;
704: }
705: fputs(adr, out);
706: linepos += len;
707: return (pos + len);
708: }
709:
710: static void
711: insert_fcc(hdr, pp)
712: struct headers *hdr;
713: char *pp;
714: {
715: char *cp;
716:
717: for (cp = pp; isspace(*cp); cp++)
718: continue;
719: for (pp += strlen(pp) - 1; pp > cp && isspace(*pp); pp--)
720: continue;
721: if (pp >= cp)
722: *++pp = NULL;
723: if (*cp == NULL)
724: return;
725:
726: if (fccind >= FCCS)
727: adios(NULLCP, "too many %ss", hdr->value);
728: fccfold[fccind++] = getcpy(cp);
729: }
730:
731: static void
732: file(path)
733: char *path;
734: {
735: int i;
736:
737: if (fccind == 0)
738: return;
739:
740: for (i = 0; i < fccind; i++)
741: if (whomflg)
742: printf("Fcc: %s\n", fccfold[i]);
743: else
744: fcc(path, fccfold[i]);
745: }
746:
747: static void
748: fcc(file, folder)
749: char *file, *folder;
750: {
751: int i, child_id, status;
752: char fold[BUFSIZ];
753:
754: if (verbose)
755: printf("%sFcc: %s\n", msgstate == resent ? "Resent-" : "",
756: folder);
757: (void) fflush(stdout);
758:
759: for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
760: sleep(5);
761: switch (child_id) {
762: case NOTOK:
763: if (!verbose)
764: fprintf(stderr, " %sFcc %s: ",
765: msgstate == resent ? "Resent-" : "", folder);
766: fprintf(verbose ? stdout : stderr, "no forks, so not ok\n");
767: break;
768:
769: case OK:
770: (void) sprintf(fold, "%s%s",
771: *folder == '+' || *folder == '@' ? "" : "+", folder);
772: execlp(fileproc, r1bindex(fileproc, '/'),
773: "-link", "-file", file, fold, NULL);
774: _exit(-1);
775:
776: default:
777: if (status = pidwait(child_id)) {
778: if (!verbose)
779: fprintf(stderr, " %sFcc %s: ",
780: msgstate == resent ? "Resent-" : "", folder);
781: fprintf(verbose ? stdout : stderr,
782: " errored (0%o)\n", status);
783: }
784: }
785:
786: (void) fflush(stdout);
787: }
788:
789: /* VARARGS2 */
790: static void
791: die(what, fmt, a, b, c, d)
792: char *what, *fmt, *a, *b, *c, *d;
793: {
794: adios(what, fmt, a, b, c, d);
795: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.