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