1: /*
2: * Copyright (c) 1980 Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. All advertising materials mentioning features or use of this software
14: * must display the following acknowledgement:
15: * This product includes software developed by the University of
16: * California, Berkeley and its contributors.
17: * 4. Neither the name of the University nor the names of its contributors
18: * may be used to endorse or promote products derived from this software
19: * without specific prior written permission.
20: *
21: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31: * SUCH DAMAGE.
32: */
33:
34: #if !defined(lint) && defined(DOSCCS)
35: static char sccsid[] = "@(#)names.c 5.16 (Berkeley) 6/25/90";
36: #endif
37:
38: /*
39: * Mail -- a mail program
40: *
41: * Handle name lists.
42: */
43:
44: #include "rcv.h"
45:
46: /*
47: * Allocate a single element of a name list,
48: * initialize its name field to the passed
49: * name and return it.
50: */
51: struct name *
52: nalloc(str, ntype)
53: char str[];
54: {
55: register struct name *np;
56:
57: np = (struct name *) salloc(sizeof *np);
58: np->n_flink = NIL;
59: np->n_blink = NIL;
60: np->n_type = ntype;
61: np->n_name = savestr(str);
62: return(np);
63: }
64:
65: /*
66: * Find the tail of a list and return it.
67: */
68: struct name *
69: tailof(name)
70: struct name *name;
71: {
72: register struct name *np;
73:
74: np = name;
75: if (np == NIL)
76: return(NIL);
77: while (np->n_flink != NIL)
78: np = np->n_flink;
79: return(np);
80: }
81:
82: /*
83: * Extract a list of names from a line,
84: * and make a list of names from it.
85: * Return the list or NIL if none found.
86: */
87: name *
88: extract(line, ntype)
89: char line[];
90: {
91: register char *cp;
92: register struct name *top, *np, *t;
93: char nbuf[BUFSIZ];
94:
95: if (line == NOSTR || *line == '\0')
96: return NIL;
97: top = NIL;
98: np = NIL;
99: cp = line;
100: while ((cp = yankword(cp, nbuf)) != NOSTR) {
101: t = nalloc(nbuf, ntype);
102: if (top == NIL)
103: top = t;
104: else
105: np->n_flink = t;
106: t->n_blink = np;
107: np = t;
108: }
109: return top;
110: }
111:
112: /*
113: * Turn a list of names into a string of the same names.
114: */
115: char *
116: detract(np, ntype)
117: register struct name *np;
118: {
119: register int s;
120: register char *cp, *top;
121: register struct name *p;
122: register int comma;
123:
124: comma = ntype & GCOMMA;
125: if (np == NIL)
126: return(NOSTR);
127: ntype &= ~GCOMMA;
128: s = 0;
129: if (debug && comma)
130: fprintf(stderr, "detract asked to insert commas\n");
131: for (p = np; p != NIL; p = p->n_flink) {
132: if (ntype && (p->n_type & GMASK) != ntype)
133: continue;
134: s += strlen(p->n_name) + 1;
135: if (comma)
136: s++;
137: }
138: if (s == 0)
139: return(NOSTR);
140: s += 2;
141: top = salloc(s);
142: cp = top;
143: for (p = np; p != NIL; p = p->n_flink) {
144: if (ntype && (p->n_type & GMASK) != ntype)
145: continue;
146: cp = copy(p->n_name, cp);
147: if (comma && p->n_flink != NIL)
148: *cp++ = ',';
149: *cp++ = ' ';
150: }
151: *--cp = 0;
152: if (comma && *--cp == ',')
153: *cp = 0;
154: return(top);
155: }
156:
157: /*
158: * Grab a single word (liberal word)
159: * Throw away things between ()'s, and take anything between <>.
160: */
161: char *
162: yankword(ap, wbuf)
163: char *ap, wbuf[];
164: {
165: register char *cp, *cp2;
166:
167: cp = ap;
168: for (;;) {
169: if (*cp == '\0')
170: return NOSTR;
171: if (*cp == '(') {
172: register int nesting = 0;
173:
174: while (*cp != '\0') {
175: switch (*cp++) {
176: case '(':
177: nesting++;
178: break;
179: case ')':
180: --nesting;
181: break;
182: }
183: if (nesting <= 0)
184: break;
185: }
186: } else if (*cp == ' ' || *cp == '\t' || *cp == ',')
187: cp++;
188: else
189: break;
190: }
191: if (*cp == '<')
192: for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
193: ;
194: else
195: for (cp2 = wbuf; *cp && !index(" \t,(", *cp); *cp2++ = *cp++)
196: ;
197: *cp2 = '\0';
198: return cp;
199: }
200:
201: /*
202: * For each recipient in the passed name list with a /
203: * in the name, append the message to the end of the named file
204: * and remove him from the recipient list.
205: *
206: * Recipients whose name begins with | are piped through the given
207: * program and removed.
208: */
209: struct name *
210: outof(names, fo, hp)
211: struct name *names;
212: FILE *fo;
213: struct header *hp;
214: {
215: register int c;
216: register struct name *np, *top;
217: time_t now, time();
218: char *date, *fname, *ctime();
219: FILE *fout, *fin;
220: int ispipe;
221: extern char tempEdit[];
222:
223: top = names;
224: np = names;
225: (void) time(&now);
226: date = ctime(&now);
227: while (np != NIL) {
228: if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
229: np = np->n_flink;
230: continue;
231: }
232: ispipe = np->n_name[0] == '|';
233: if (ispipe)
234: fname = np->n_name+1;
235: else
236: fname = expand(np->n_name);
237:
238: /*
239: * See if we have copied the complete message out yet.
240: * If not, do so.
241: */
242:
243: if (image < 0) {
244: if ((fout = Fopen(tempEdit, "a")) == NULL) {
245: perror(tempEdit);
246: senderr++;
247: goto cant;
248: }
249: image = open(tempEdit, 2);
250: (void) unlink(tempEdit);
251: if (image < 0) {
252: perror(tempEdit);
253: senderr++;
254: (void) Fclose(fout);
255: goto cant;
256: }
257: fprintf(fout, "From %s %s", myname, date);
258: puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
259: while ((c = getc(fo)) != EOF)
260: (void) putc(c, fout);
261: rewind(fo);
262: (void) putc('\n', fout);
263: (void) fflush(fout);
264: if (ferror(fout))
265: perror(tempEdit);
266: (void) Fclose(fout);
267: }
268:
269: /*
270: * Now either copy "image" to the desired file
271: * or give it as the standard input to the desired
272: * program as appropriate.
273: */
274:
275: if (ispipe) {
276: int pid;
277: char *shell;
278:
279: /*
280: * XXX
281: * We can't really reuse the same image file,
282: * because multiple piped recipients will
283: * share the same lseek location and trample
284: * on one another.
285: */
286: if ((shell = value("SHELL")) == NOSTR)
287: shell = _PATH_CSHELL;
288: pid = start_command(shell, sigmask(SIGHUP)|
289: sigmask(SIGINT)|sigmask(SIGQUIT),
290: image, -1, "-c", fname, NOSTR);
291: if (pid < 0) {
292: senderr++;
293: goto cant;
294: }
295: free_child(pid);
296: } else {
297: int f;
298: if ((fout = Fopen(fname, "a")) == NULL) {
299: perror(fname);
300: senderr++;
301: goto cant;
302: }
303: if ((f = dup(image)) < 0) {
304: perror("dup");
305: fin = NULL;
306: } else
307: fin = Fdopen(f, "r");
308: if (fin == NULL) {
309: fprintf(stderr, "Can't reopen image\n");
310: (void) Fclose(fout);
311: senderr++;
312: goto cant;
313: }
314: rewind(fin);
315: while ((c = getc(fin)) != EOF)
316: (void) putc(c, fout);
317: if (ferror(fout))
318: senderr++, perror(fname);
319: (void) Fclose(fout);
320: (void) Fclose(fin);
321: }
322: cant:
323: /*
324: * In days of old we removed the entry from the
325: * the list; now for sake of header expansion
326: * we leave it in and mark it as deleted.
327: */
328: np->n_type |= GDEL;
329: np = np->n_flink;
330: }
331: if (image >= 0) {
332: (void) close(image);
333: image = -1;
334: }
335: return(top);
336: }
337:
338: /*
339: * Determine if the passed address is a local "send to file" address.
340: * If any of the network metacharacters precedes any slashes, it can't
341: * be a filename. We cheat with .'s to allow path names like ./...
342: */
343: isfileaddr(name)
344: char *name;
345: {
346: register char *cp;
347:
348: if (*name == '+')
349: return 1;
350: for (cp = name; *cp; cp++) {
351: if (*cp == '!' || *cp == '%' || *cp == '@')
352: return 0;
353: if (*cp == '/')
354: return 1;
355: }
356: return 0;
357: }
358:
359: /*
360: * Map all of the aliased users in the invoker's mailrc
361: * file and insert them into the list.
362: * Changed after all these months of service to recursively
363: * expand names (2/14/80).
364: */
365:
366: struct name *
367: usermap(names)
368: struct name *names;
369: {
370: register struct name *new, *np, *cp;
371: struct grouphead *gh;
372: register int metoo;
373:
374: new = NIL;
375: np = names;
376: metoo = (value("metoo") != NOSTR);
377: while (np != NIL) {
378: if (np->n_name[0] == '\\') {
379: cp = np->n_flink;
380: new = put(new, np);
381: np = cp;
382: continue;
383: }
384: gh = findgroup(np->n_name);
385: cp = np->n_flink;
386: if (gh != NOGRP)
387: new = gexpand(new, gh, metoo, np->n_type);
388: else
389: new = put(new, np);
390: np = cp;
391: }
392: return(new);
393: }
394:
395: /*
396: * Recursively expand a group name. We limit the expansion to some
397: * fixed level to keep things from going haywire.
398: * Direct recursion is not expanded for convenience.
399: */
400:
401: struct name *
402: gexpand(nlist, gh, metoo, ntype)
403: struct name *nlist;
404: struct grouphead *gh;
405: {
406: struct group *gp;
407: struct grouphead *ngh;
408: struct name *np;
409: static int depth;
410: char *cp;
411:
412: if (depth > MAXEXP) {
413: printf("Expanding alias to depth larger than %d\n", MAXEXP);
414: return(nlist);
415: }
416: depth++;
417: for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
418: cp = gp->ge_name;
419: if (*cp == '\\')
420: goto quote;
421: if (strcmp(cp, gh->g_name) == 0)
422: goto quote;
423: if ((ngh = findgroup(cp)) != NOGRP) {
424: nlist = gexpand(nlist, ngh, metoo, ntype);
425: continue;
426: }
427: quote:
428: np = nalloc(cp, ntype);
429: /*
430: * At this point should allow to expand
431: * to self if only person in group
432: */
433: if (gp == gh->g_list && gp->ge_link == NOGE)
434: goto skip;
435: if (!metoo && strcmp(cp, myname) == 0)
436: np->n_type |= GDEL;
437: skip:
438: nlist = put(nlist, np);
439: }
440: depth--;
441: return(nlist);
442: }
443:
444: /*
445: * Concatenate the two passed name lists, return the result.
446: */
447: struct name *
448: cat(n1, n2)
449: struct name *n1, *n2;
450: {
451: register struct name *tail;
452:
453: if (n1 == NIL)
454: return(n2);
455: if (n2 == NIL)
456: return(n1);
457: tail = tailof(n1);
458: tail->n_flink = n2;
459: n2->n_blink = tail;
460: return(n1);
461: }
462:
463: /*
464: * Unpack the name list onto a vector of strings.
465: * Return an error if the name list won't fit.
466: */
467: char **
468: unpack(np)
469: struct name *np;
470: {
471: register char **ap, **top;
472: register struct name *n;
473: int t, extra, metoo, verbose;
474:
475: n = np;
476: if ((t = count(n)) == 0)
477: panic("No names to unpack");
478: /*
479: * Compute the number of extra arguments we will need.
480: * We need at least two extra -- one for "mail" and one for
481: * the terminating 0 pointer. Additional spots may be needed
482: * to pass along -f to the host mailer.
483: */
484: extra = 2;
485: extra++;
486: metoo = value("metoo") != NOSTR;
487: if (metoo)
488: extra++;
489: verbose = value("verbose") != NOSTR;
490: if (verbose)
491: extra++;
492: top = (char **) salloc((t + extra) * sizeof *top);
493: ap = top;
494: *ap++ = "send-mail";
495: *ap++ = "-i";
496: if (metoo)
497: *ap++ = "-m";
498: if (verbose)
499: *ap++ = "-v";
500: for (; n != NIL; n = n->n_flink)
501: if ((n->n_type & GDEL) == 0)
502: *ap++ = n->n_name;
503: *ap = NOSTR;
504: return(top);
505: }
506:
507: /*
508: * Remove all of the duplicates from the passed name list by
509: * insertion sorting them, then checking for dups.
510: * Return the head of the new list.
511: */
512: struct name *
513: elide(names)
514: struct name *names;
515: {
516: register struct name *np, *t, *new;
517: struct name *x;
518:
519: if (names == NIL)
520: return(NIL);
521: new = names;
522: np = names;
523: np = np->n_flink;
524: if (np != NIL)
525: np->n_blink = NIL;
526: new->n_flink = NIL;
527: while (np != NIL) {
528: t = new;
529: while (strcasecmp(t->n_name, np->n_name) < 0) {
530: if (t->n_flink == NIL)
531: break;
532: t = t->n_flink;
533: }
534:
535: /*
536: * If we ran out of t's, put the new entry after
537: * the current value of t.
538: */
539:
540: if (strcasecmp(t->n_name, np->n_name) < 0) {
541: t->n_flink = np;
542: np->n_blink = t;
543: t = np;
544: np = np->n_flink;
545: t->n_flink = NIL;
546: continue;
547: }
548:
549: /*
550: * Otherwise, put the new entry in front of the
551: * current t. If at the front of the list,
552: * the new guy becomes the new head of the list.
553: */
554:
555: if (t == new) {
556: t = np;
557: np = np->n_flink;
558: t->n_flink = new;
559: new->n_blink = t;
560: t->n_blink = NIL;
561: new = t;
562: continue;
563: }
564:
565: /*
566: * The normal case -- we are inserting into the
567: * middle of the list.
568: */
569:
570: x = np;
571: np = np->n_flink;
572: x->n_flink = t;
573: x->n_blink = t->n_blink;
574: t->n_blink->n_flink = x;
575: t->n_blink = x;
576: }
577:
578: /*
579: * Now the list headed up by new is sorted.
580: * Go through it and remove duplicates.
581: */
582:
583: np = new;
584: while (np != NIL) {
585: t = np;
586: while (t->n_flink != NIL &&
587: strcasecmp(np->n_name, t->n_flink->n_name) == 0)
588: t = t->n_flink;
589: if (t == np || t == NIL) {
590: np = np->n_flink;
591: continue;
592: }
593:
594: /*
595: * Now t points to the last entry with the same name
596: * as np. Make np point beyond t.
597: */
598:
599: np->n_flink = t->n_flink;
600: if (t->n_flink != NIL)
601: t->n_flink->n_blink = np;
602: np = np->n_flink;
603: }
604: return(new);
605: }
606:
607: /*
608: * Put another node onto a list of names and return
609: * the list.
610: */
611: struct name *
612: put(list, node)
613: struct name *list, *node;
614: {
615: node->n_flink = list;
616: node->n_blink = NIL;
617: if (list != NIL)
618: list->n_blink = node;
619: return(node);
620: }
621:
622: /*
623: * Determine the number of undeleted elements in
624: * a name list and return it.
625: */
626: count(np)
627: register struct name *np;
628: {
629: register int c;
630:
631: for (c = 0; np != NIL; np = np->n_flink)
632: if ((np->n_type & GDEL) == 0)
633: c++;
634: return c;
635: }
636:
637: /*
638: * Delete the given name from a namelist.
639: */
640: struct name *
641: delname(np, name)
642: register struct name *np;
643: char name[];
644: {
645: register struct name *p;
646:
647: for (p = np; p != NIL; p = p->n_flink)
648: if (strcasecmp(p->n_name, name) == 0) {
649: if (p->n_blink == NIL) {
650: if (p->n_flink != NIL)
651: p->n_flink->n_blink = NIL;
652: np = p->n_flink;
653: continue;
654: }
655: if (p->n_flink == NIL) {
656: if (p->n_blink != NIL)
657: p->n_blink->n_flink = NIL;
658: continue;
659: }
660: p->n_blink->n_flink = p->n_flink;
661: p->n_flink->n_blink = p->n_blink;
662: }
663: return np;
664: }
665:
666: /*
667: * Pretty print a name list
668: * Uncomment it if you need it.
669: */
670:
671: /*
672: prettyprint(name)
673: struct name *name;
674: {
675: register struct name *np;
676:
677: np = name;
678: while (np != NIL) {
679: fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
680: np = np->n_flink;
681: }
682: fprintf(stderr, "\n");
683: }
684: */