1: /*
2: * Copyright (c) 1980 Regents of the University of California.
3: * All rights reserved. The Berkeley software License Agreement
4: * specifies the terms and conditions for redistribution.
5: */
6:
7: #ifndef lint
8: static char *sccsid = "@(#)names.c 5.3 (Berkeley) 11/10/85";
9: #endif not lint
10:
11: /*
12: * Mail -- a mail program
13: *
14: * Handle name lists.
15: */
16:
17: #include "rcv.h"
18:
19: /*
20: * Allocate a single element of a name list,
21: * initialize its name field to the passed
22: * name and return it.
23: */
24:
25: struct name *
26: nalloc(str)
27: char str[];
28: {
29: register struct name *np;
30:
31: np = (struct name *) salloc(sizeof *np);
32: np->n_flink = NIL;
33: np->n_blink = NIL;
34: np->n_type = -1;
35: np->n_name = savestr(str);
36: return(np);
37: }
38:
39: /*
40: * Find the tail of a list and return it.
41: */
42:
43: struct name *
44: tailof(name)
45: struct name *name;
46: {
47: register struct name *np;
48:
49: np = name;
50: if (np == NIL)
51: return(NIL);
52: while (np->n_flink != NIL)
53: np = np->n_flink;
54: return(np);
55: }
56:
57: /*
58: * Extract a list of names from a line,
59: * and make a list of names from it.
60: * Return the list or NIL if none found.
61: */
62:
63: name *
64: extract(line, ntype)
65: char line[];
66: {
67: register char *cp;
68: register struct name *top, *np, *t;
69: char nbuf[BUFSIZ], abuf[BUFSIZ];
70:
71: if (line == NOSTR || strlen(line) == 0)
72: return(NIL);
73: top = NIL;
74: np = NIL;
75: cp = line;
76: while ((cp = yankword(cp, nbuf)) != NOSTR) {
77: if (np != NIL && equal(nbuf, "at")) {
78: strcpy(abuf, nbuf);
79: if ((cp = yankword(cp, nbuf)) == NOSTR) {
80: strcpy(nbuf, abuf);
81: goto normal;
82: }
83: strcpy(abuf, np->n_name);
84: stradd(abuf, '@');
85: strcat(abuf, nbuf);
86: np->n_name = savestr(abuf);
87: continue;
88: }
89: :
90: t = nalloc(nbuf);
91: t->n_type = ntype;
92: if (top == NIL)
93: top = t;
94: else
95: np->n_flink = t;
96: t->n_blink = np;
97: np = t;
98: }
99: return(top);
100: }
101:
102: /*
103: * Turn a list of names into a string of the same names.
104: */
105:
106: char *
107: detract(np, ntype)
108: register struct name *np;
109: {
110: register int s;
111: register char *cp, *top;
112: register struct name *p;
113: register int comma;
114:
115: comma = ntype & GCOMMA;
116: if (np == NIL)
117: return(NOSTR);
118: ntype &= ~GCOMMA;
119: s = 0;
120: if (debug && comma)
121: fprintf(stderr, "detract asked to insert commas\n");
122: for (p = np; p != NIL; p = p->n_flink) {
123: if (ntype && (p->n_type & GMASK) != ntype)
124: continue;
125: s += strlen(p->n_name) + 1;
126: if (comma)
127: s++;
128: }
129: if (s == 0)
130: return(NOSTR);
131: s += 2;
132: top = salloc(s);
133: cp = top;
134: for (p = np; p != NIL; p = p->n_flink) {
135: if (ntype && (p->n_type & GMASK) != ntype)
136: continue;
137: cp = copy(p->n_name, cp);
138: if (comma && p->n_flink != NIL)
139: *cp++ = ',';
140: *cp++ = ' ';
141: }
142: *--cp = 0;
143: if (comma && *--cp == ',')
144: *cp = 0;
145: return(top);
146: }
147:
148: /*
149: * Grab a single word (liberal word)
150: * Throw away things between ()'s.
151: */
152:
153: char *
154: yankword(ap, wbuf)
155: char *ap, wbuf[];
156: {
157: register char *cp, *cp2;
158:
159: cp = ap;
160: do {
161: while (*cp && any(*cp, " \t,"))
162: cp++;
163: if (*cp == '(') {
164: register int nesting = 0;
165:
166: while (*cp != '\0') {
167: switch (*cp++) {
168: case '(':
169: nesting++;
170: break;
171: case ')':
172: --nesting;
173: break;
174: }
175: if (nesting <= 0)
176: break;
177: }
178: }
179: if (*cp == '\0')
180: return(NOSTR);
181: } while (any(*cp, " \t,("));
182: for (cp2 = wbuf; *cp && !any(*cp, " \t,("); *cp2++ = *cp++)
183: ;
184: *cp2 = '\0';
185: return(cp);
186: }
187:
188: /*
189: * Verify that all the users in the list of names are
190: * legitimate. Bitch about and delink those who aren't.
191: */
192:
193: struct name *
194: verify(names)
195: struct name *names;
196: {
197: register struct name *np, *top, *t, *x;
198: register char *cp;
199:
200: #ifdef SENDMAIL
201: return(names);
202: #else
203: top = names;
204: np = names;
205: while (np != NIL) {
206: if (np->n_type & GDEL) {
207: np = np->n_flink;
208: continue;
209: }
210: for (cp = "!:@^"; *cp; cp++)
211: if (any(*cp, np->n_name))
212: break;
213: if (*cp != 0) {
214: np = np->n_flink;
215: continue;
216: }
217: cp = np->n_name;
218: while (*cp == '\\')
219: cp++;
220: if (equal(cp, "msgs") ||
221: getuserid(cp) != -1) {
222: np = np->n_flink;
223: continue;
224: }
225: fprintf(stderr, "Can't send to %s\n", np->n_name);
226: senderr++;
227: if (np == top) {
228: top = np->n_flink;
229: if (top != NIL)
230: top->n_blink = NIL;
231: np = top;
232: continue;
233: }
234: x = np->n_blink;
235: t = np->n_flink;
236: x->n_flink = t;
237: if (t != NIL)
238: t->n_blink = x;
239: np = t;
240: }
241: return(top);
242: #endif
243: }
244:
245: /*
246: * For each recipient in the passed name list with a /
247: * in the name, append the message to the end of the named file
248: * and remove him from the recipient list.
249: *
250: * Recipients whose name begins with | are piped through the given
251: * program and removed.
252: */
253:
254: struct name *
255: outof(names, fo, hp)
256: struct name *names;
257: FILE *fo;
258: struct header *hp;
259: {
260: register int c;
261: register struct name *np, *top, *t, *x;
262: long now;
263: char *date, *fname, *shell, *ctime();
264: FILE *fout, *fin;
265: int ispipe, s, pid;
266: extern char tempEdit[];
267:
268: top = names;
269: np = names;
270: time(&now);
271: date = ctime(&now);
272: while (np != NIL) {
273: if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
274: np = np->n_flink;
275: continue;
276: }
277: ispipe = np->n_name[0] == '|';
278: if (ispipe)
279: fname = np->n_name+1;
280: else
281: fname = expand(np->n_name);
282:
283: /*
284: * See if we have copied the complete message out yet.
285: * If not, do so.
286: */
287:
288: if (image < 0) {
289: if ((fout = fopen(tempEdit, "a")) == NULL) {
290: perror(tempEdit);
291: senderr++;
292: goto cant;
293: }
294: image = open(tempEdit, 2);
295: unlink(tempEdit);
296: if (image < 0) {
297: perror(tempEdit);
298: senderr++;
299: goto cant;
300: }
301: else {
302: rewind(fo);
303: fprintf(fout, "From %s %s", myname, date);
304: puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
305: while ((c = getc(fo)) != EOF)
306: putc(c, fout);
307: rewind(fo);
308: putc('\n', fout);
309: fflush(fout);
310: if (ferror(fout))
311: perror(tempEdit);
312: fclose(fout);
313: }
314: }
315:
316: /*
317: * Now either copy "image" to the desired file
318: * or give it as the standard input to the desired
319: * program as appropriate.
320: */
321:
322: if (ispipe) {
323: wait(&s);
324: switch (pid = fork()) {
325: case 0:
326: sigchild();
327: sigsys(SIGHUP, SIG_IGN);
328: sigsys(SIGINT, SIG_IGN);
329: sigsys(SIGQUIT, SIG_IGN);
330: close(0);
331: dup(image);
332: close(image);
333: if ((shell = value("SHELL")) == NOSTR)
334: shell = SHELL;
335: execl(shell, shell, "-c", fname, 0);
336: perror(shell);
337: exit(1);
338: break;
339:
340: case -1:
341: perror("fork");
342: senderr++;
343: goto cant;
344: }
345: }
346: else {
347: if ((fout = fopen(fname, "a")) == NULL) {
348: perror(fname);
349: senderr++;
350: goto cant;
351: }
352: fin = Fdopen(image, "r");
353: if (fin == NULL) {
354: fprintf(stderr, "Can't reopen image\n");
355: fclose(fout);
356: senderr++;
357: goto cant;
358: }
359: rewind(fin);
360: while ((c = getc(fin)) != EOF)
361: putc(c, fout);
362: if (ferror(fout))
363: senderr++, perror(fname);
364: fclose(fout);
365: fclose(fin);
366: }
367:
368: cant:
369:
370: /*
371: * In days of old we removed the entry from the
372: * the list; now for sake of header expansion
373: * we leave it in and mark it as deleted.
374: */
375:
376: #ifdef CRAZYWOW
377: if (np == top) {
378: top = np->n_flink;
379: if (top != NIL)
380: top->n_blink = NIL;
381: np = top;
382: continue;
383: }
384: x = np->n_blink;
385: t = np->n_flink;
386: x->n_flink = t;
387: if (t != NIL)
388: t->n_blink = x;
389: np = t;
390: #endif
391:
392: np->n_type |= GDEL;
393: np = np->n_flink;
394: }
395: if (image >= 0) {
396: close(image);
397: image = -1;
398: }
399: return(top);
400: }
401:
402: /*
403: * Determine if the passed address is a local "send to file" address.
404: * If any of the network metacharacters precedes any slashes, it can't
405: * be a filename. We cheat with .'s to allow path names like ./...
406: */
407: isfileaddr(name)
408: char *name;
409: {
410: register char *cp;
411: extern char *metanet;
412:
413: if (any('@', name))
414: return(0);
415: if (*name == '+')
416: return(1);
417: for (cp = name; *cp; cp++) {
418: if (*cp == '.')
419: continue;
420: if (any(*cp, metanet))
421: return(0);
422: if (*cp == '/')
423: return(1);
424: }
425: return(0);
426: }
427:
428: /*
429: * Map all of the aliased users in the invoker's mailrc
430: * file and insert them into the list.
431: * Changed after all these months of service to recursively
432: * expand names (2/14/80).
433: */
434:
435: struct name *
436: usermap(names)
437: struct name *names;
438: {
439: register struct name *new, *np, *cp;
440: struct name *getto;
441: struct grouphead *gh;
442: register int metoo;
443:
444: new = NIL;
445: np = names;
446: getto = NIL;
447: metoo = (value("metoo") != NOSTR);
448: while (np != NIL) {
449: if (np->n_name[0] == '\\') {
450: cp = np->n_flink;
451: new = put(new, np);
452: np = cp;
453: continue;
454: }
455: gh = findgroup(np->n_name);
456: cp = np->n_flink;
457: if (gh != NOGRP)
458: new = gexpand(new, gh, metoo, np->n_type);
459: else
460: new = put(new, np);
461: np = cp;
462: }
463: return(new);
464: }
465:
466: /*
467: * Recursively expand a group name. We limit the expansion to some
468: * fixed level to keep things from going haywire.
469: * Direct recursion is not expanded for convenience.
470: */
471:
472: struct name *
473: gexpand(nlist, gh, metoo, ntype)
474: struct name *nlist;
475: struct grouphead *gh;
476: {
477: struct group *gp;
478: struct grouphead *ngh;
479: struct name *np;
480: static int depth;
481: char *cp;
482:
483: if (depth > MAXEXP) {
484: printf("Expanding alias to depth larger than %d\n", MAXEXP);
485: return(nlist);
486: }
487: depth++;
488: for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
489: cp = gp->ge_name;
490: if (*cp == '\\')
491: goto quote;
492: if (strcmp(cp, gh->g_name) == 0)
493: goto quote;
494: if ((ngh = findgroup(cp)) != NOGRP) {
495: nlist = gexpand(nlist, ngh, metoo, ntype);
496: continue;
497: }
498: quote:
499: np = nalloc(cp);
500: np->n_type = ntype;
501: /*
502: * At this point should allow to expand
503: * to self if only person in group
504: */
505: if (gp == gh->g_list && gp->ge_link == NOGE)
506: goto skip;
507: if (!metoo && strcmp(cp, myname) == 0)
508: np->n_type |= GDEL;
509: skip:
510: nlist = put(nlist, np);
511: }
512: depth--;
513: return(nlist);
514: }
515:
516:
517:
518: /*
519: * Compute the length of the passed name list and
520: * return it.
521: */
522:
523: lengthof(name)
524: struct name *name;
525: {
526: register struct name *np;
527: register int c;
528:
529: for (c = 0, np = name; np != NIL; c++, np = np->n_flink)
530: ;
531: return(c);
532: }
533:
534: /*
535: * Concatenate the two passed name lists, return the result.
536: */
537:
538: struct name *
539: cat(n1, n2)
540: struct name *n1, *n2;
541: {
542: register struct name *tail;
543:
544: if (n1 == NIL)
545: return(n2);
546: if (n2 == NIL)
547: return(n1);
548: tail = tailof(n1);
549: tail->n_flink = n2;
550: n2->n_blink = tail;
551: return(n1);
552: }
553:
554: /*
555: * Unpack the name list onto a vector of strings.
556: * Return an error if the name list won't fit.
557: */
558:
559: char **
560: unpack(np)
561: struct name *np;
562: {
563: register char **ap, **top;
564: register struct name *n;
565: char *cp;
566: char hbuf[10];
567: int t, extra, metoo, verbose;
568:
569: n = np;
570: if ((t = lengthof(n)) == 0)
571: panic("No names to unpack");
572:
573: /*
574: * Compute the number of extra arguments we will need.
575: * We need at least two extra -- one for "mail" and one for
576: * the terminating 0 pointer. Additional spots may be needed
577: * to pass along -r and -f to the host mailer.
578: */
579:
580: extra = 2;
581: if (rflag != NOSTR)
582: extra += 2;
583: #ifdef SENDMAIL
584: extra++;
585: metoo = value("metoo") != NOSTR;
586: if (metoo)
587: extra++;
588: verbose = value("verbose") != NOSTR;
589: if (verbose)
590: extra++;
591: #endif SENDMAIL
592: if (hflag)
593: extra += 2;
594: top = (char **) salloc((t + extra) * sizeof cp);
595: ap = top;
596: *ap++ = "send-mail";
597: if (rflag != NOSTR) {
598: *ap++ = "-r";
599: *ap++ = rflag;
600: }
601: #ifdef SENDMAIL
602: *ap++ = "-i";
603: if (metoo)
604: *ap++ = "-m";
605: if (verbose)
606: *ap++ = "-v";
607: #endif SENDMAIL
608: if (hflag) {
609: *ap++ = "-h";
610: sprintf(hbuf, "%d", hflag);
611: *ap++ = savestr(hbuf);
612: }
613: while (n != NIL) {
614: if (n->n_type & GDEL) {
615: n = n->n_flink;
616: continue;
617: }
618: *ap++ = n->n_name;
619: n = n->n_flink;
620: }
621: *ap = NOSTR;
622: return(top);
623: }
624:
625: /*
626: * See if the user named himself as a destination
627: * for outgoing mail. If so, set the global flag
628: * selfsent so that we avoid removing his mailbox.
629: */
630:
631: mechk(names)
632: struct name *names;
633: {
634: register struct name *np;
635:
636: for (np = names; np != NIL; np = np->n_flink)
637: if ((np->n_type & GDEL) == 0 && equal(np->n_name, myname)) {
638: selfsent++;
639: return;
640: }
641: }
642:
643: /*
644: * Remove all of the duplicates from the passed name list by
645: * insertion sorting them, then checking for dups.
646: * Return the head of the new list.
647: */
648:
649: struct name *
650: elide(names)
651: struct name *names;
652: {
653: register struct name *np, *t, *new;
654: struct name *x;
655:
656: if (names == NIL)
657: return(NIL);
658: new = names;
659: np = names;
660: np = np->n_flink;
661: if (np != NIL)
662: np->n_blink = NIL;
663: new->n_flink = NIL;
664: while (np != NIL) {
665: t = new;
666: while (nstrcmp(t->n_name, np->n_name) < 0) {
667: if (t->n_flink == NIL)
668: break;
669: t = t->n_flink;
670: }
671:
672: /*
673: * If we ran out of t's, put the new entry after
674: * the current value of t.
675: */
676:
677: if (nstrcmp(t->n_name, np->n_name) < 0) {
678: t->n_flink = np;
679: np->n_blink = t;
680: t = np;
681: np = np->n_flink;
682: t->n_flink = NIL;
683: continue;
684: }
685:
686: /*
687: * Otherwise, put the new entry in front of the
688: * current t. If at the front of the list,
689: * the new guy becomes the new head of the list.
690: */
691:
692: if (t == new) {
693: t = np;
694: np = np->n_flink;
695: t->n_flink = new;
696: new->n_blink = t;
697: t->n_blink = NIL;
698: new = t;
699: continue;
700: }
701:
702: /*
703: * The normal case -- we are inserting into the
704: * middle of the list.
705: */
706:
707: x = np;
708: np = np->n_flink;
709: x->n_flink = t;
710: x->n_blink = t->n_blink;
711: t->n_blink->n_flink = x;
712: t->n_blink = x;
713: }
714:
715: /*
716: * Now the list headed up by new is sorted.
717: * Go through it and remove duplicates.
718: */
719:
720: np = new;
721: while (np != NIL) {
722: t = np;
723: while (t->n_flink!=NIL &&
724: icequal(np->n_name,t->n_flink->n_name))
725: t = t->n_flink;
726: if (t == np || t == NIL) {
727: np = np->n_flink;
728: continue;
729: }
730:
731: /*
732: * Now t points to the last entry with the same name
733: * as np. Make np point beyond t.
734: */
735:
736: np->n_flink = t->n_flink;
737: if (t->n_flink != NIL)
738: t->n_flink->n_blink = np;
739: np = np->n_flink;
740: }
741: return(new);
742: }
743:
744: /*
745: * Version of strcmp which ignores case differences.
746: */
747:
748: nstrcmp(s1, s2)
749: register char *s1, *s2;
750: {
751: register int c1, c2;
752:
753: do {
754: c1 = *s1++;
755: c2 = *s2++;
756: } while (c1 && c1 == c2);
757: return(c1 - c2);
758: }
759:
760: /*
761: * Put another node onto a list of names and return
762: * the list.
763: */
764:
765: struct name *
766: put(list, node)
767: struct name *list, *node;
768: {
769: node->n_flink = list;
770: node->n_blink = NIL;
771: if (list != NIL)
772: list->n_blink = node;
773: return(node);
774: }
775:
776: /*
777: * Determine the number of elements in
778: * a name list and return it.
779: */
780:
781: count(np)
782: register struct name *np;
783: {
784: register int c = 0;
785:
786: while (np != NIL) {
787: c++;
788: np = np->n_flink;
789: }
790: return(c);
791: }
792:
793: cmpdomain(name, dname)
794: register char *name, *dname;
795: {
796: char buf[BUFSIZ];
797:
798: strcpy(buf, dname);
799: buf[strlen(name)] = '\0';
800: return(icequal(name, buf));
801: }
802:
803: /*
804: * Delete the given name from a namelist, using the passed
805: * function to compare the names.
806: */
807: struct name *
808: delname(np, name, cmpfun)
809: register struct name *np;
810: char name[];
811: int (* cmpfun)();
812: {
813: register struct name *p;
814:
815: for (p = np; p != NIL; p = p->n_flink)
816: if ((* cmpfun)(p->n_name, name)) {
817: if (p->n_blink == NIL) {
818: if (p->n_flink != NIL)
819: p->n_flink->n_blink = NIL;
820: np = p->n_flink;
821: continue;
822: }
823: if (p->n_flink == NIL) {
824: if (p->n_blink != NIL)
825: p->n_blink->n_flink = NIL;
826: continue;
827: }
828: p->n_blink->n_flink = p->n_flink;
829: p->n_flink->n_blink = p->n_blink;
830: }
831: return(np);
832: }
833:
834: /*
835: * Call the given routine on each element of the name
836: * list, replacing said value if need be.
837: */
838:
839: mapf(np, from)
840: register struct name *np;
841: char *from;
842: {
843: register struct name *p;
844:
845: for (p = np; p != NIL; p = p->n_flink)
846: p->n_name = netmap(p->n_name, from);
847: }
848:
849: /*
850: * Pretty print a name list
851: * Uncomment it if you need it.
852: */
853:
854: prettyprint(name)
855: struct name *name;
856: {
857: register struct name *np;
858:
859: np = name;
860: while (np != NIL) {
861: fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
862: np = np->n_flink;
863: }
864: fprintf(stderr, "\n");
865: }