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