Annotation of src/usr.bin/mail/names.c, Revision 1.15
1.15 ! millert 1: /* $OpenBSD: names.c,v 1.14 2001/11/21 15:26:39 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
1.14 millert 39: static const char sccsid[] = "@(#)names.c 8.1 (Berkeley) 6/6/93";
1.2 deraadt 40: #else
1.15 ! millert 41: static const char rcsid[] = "$OpenBSD: names.c,v 1.14 2001/11/21 15:26:39 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 *
1.14 millert 61: nalloc(char *str, int ntype)
1.1 deraadt 62: {
1.9 millert 63: struct name *np;
1.1 deraadt 64:
1.5 millert 65: np = (struct name *)salloc(sizeof(*np));
1.14 millert 66: np->n_flink = NULL;
67: np->n_blink = NULL;
1.1 deraadt 68: np->n_type = ntype;
69: np->n_name = savestr(str);
70: return(np);
71: }
72:
73: /*
74: * Find the tail of a list and return it.
75: */
76: struct name *
1.14 millert 77: tailof(struct name *name)
1.1 deraadt 78: {
1.9 millert 79: struct name *np;
1.1 deraadt 80:
81: np = name;
1.14 millert 82: if (np == NULL)
83: return(NULL);
84: while (np->n_flink != NULL)
1.1 deraadt 85: np = np->n_flink;
86: return(np);
87: }
88:
89: /*
90: * Extract a list of names from a line,
91: * and make a list of names from it.
1.14 millert 92: * Return the list or NULL if none found.
1.1 deraadt 93: */
94: struct name *
1.14 millert 95: extract(char *line, int ntype)
1.1 deraadt 96: {
1.9 millert 97: char *cp;
98: struct name *top, *np, *t;
1.8 millert 99: char *nbuf;
1.1 deraadt 100:
1.6 millert 101: if (line == NULL || *line == '\0')
1.14 millert 102: return(NULL);
1.8 millert 103: if ((nbuf = (char *)malloc(strlen(line) + 1)) == NULL)
1.9 millert 104: errx(1, "Out of memory");
1.14 millert 105: top = NULL;
106: np = NULL;
1.1 deraadt 107: cp = line;
1.6 millert 108: while ((cp = yankword(cp, nbuf)) != NULL) {
1.1 deraadt 109: t = nalloc(nbuf, ntype);
1.14 millert 110: if (top == NULL)
1.1 deraadt 111: top = t;
112: else
113: np->n_flink = t;
114: t->n_blink = np;
115: np = t;
116: }
1.8 millert 117: (void)free(nbuf);
1.4 millert 118: return(top);
1.1 deraadt 119: }
120:
121: /*
122: * Turn a list of names into a string of the same names.
123: */
124: char *
1.14 millert 125: detract(struct name *np, int ntype)
1.1 deraadt 126: {
1.9 millert 127: int s, comma;
128: char *cp, *top;
129: struct name *p;
1.1 deraadt 130:
131: comma = ntype & GCOMMA;
1.14 millert 132: if (np == NULL)
1.6 millert 133: return(NULL);
1.1 deraadt 134: ntype &= ~GCOMMA;
135: s = 0;
136: if (debug && comma)
1.4 millert 137: fputs("detract asked to insert commas\n", stderr);
1.14 millert 138: for (p = np; p != NULL; p = p->n_flink) {
1.1 deraadt 139: if (ntype && (p->n_type & GMASK) != ntype)
140: continue;
141: s += strlen(p->n_name) + 1;
142: if (comma)
143: s++;
144: }
145: if (s == 0)
1.6 millert 146: return(NULL);
1.1 deraadt 147: s += 2;
148: top = salloc(s);
149: cp = top;
1.14 millert 150: for (p = np; p != NULL; p = p->n_flink) {
1.1 deraadt 151: if (ntype && (p->n_type & GMASK) != ntype)
152: continue;
153: cp = copy(p->n_name, cp);
1.14 millert 154: if (comma && p->n_flink != NULL)
1.1 deraadt 155: *cp++ = ',';
156: *cp++ = ' ';
157: }
158: *--cp = 0;
159: if (comma && *--cp == ',')
160: *cp = 0;
161: return(top);
162: }
163:
164: /*
165: * Grab a single word (liberal word)
166: * Throw away things between ()'s, and take anything between <>.
167: */
168: char *
1.14 millert 169: yankword(char *ap, char *wbuf)
1.1 deraadt 170: {
1.9 millert 171: char *cp, *cp2;
1.1 deraadt 172:
173: cp = ap;
174: for (;;) {
175: if (*cp == '\0')
1.6 millert 176: return(NULL);
1.1 deraadt 177: if (*cp == '(') {
1.9 millert 178: int nesting = 0;
1.1 deraadt 179:
180: while (*cp != '\0') {
181: switch (*cp++) {
182: case '(':
183: nesting++;
184: break;
185: case ')':
186: --nesting;
187: break;
188: }
189: if (nesting <= 0)
190: break;
191: }
192: } else if (*cp == ' ' || *cp == '\t' || *cp == ',')
193: cp++;
194: else
195: break;
196: }
197: if (*cp == '<')
198: for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
199: ;
200: else
1.3 millert 201: for (cp2 = wbuf; *cp && !strchr(" \t,(", *cp); *cp2++ = *cp++)
1.1 deraadt 202: ;
203: *cp2 = '\0';
1.4 millert 204: return(cp);
1.1 deraadt 205: }
206:
207: /*
208: * For each recipient in the passed name list with a /
209: * in the name, append the message to the end of the named file
210: * and remove him from the recipient list.
211: *
212: * Recipients whose name begins with | are piped through the given
213: * program and removed.
214: */
215: struct name *
1.14 millert 216: outof(struct name *names, FILE *fo, struct header *hp)
1.1 deraadt 217: {
1.9 millert 218: int c, ispipe;
219: struct name *np, *top;
1.2 deraadt 220: time_t now;
221: char *date, *fname;
1.1 deraadt 222: FILE *fout, *fin;
223:
224: top = names;
225: np = names;
1.5 millert 226: (void)time(&now);
1.1 deraadt 227: date = ctime(&now);
1.14 millert 228: while (np != NULL) {
1.1 deraadt 229: if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
230: np = np->n_flink;
231: continue;
232: }
233: ispipe = np->n_name[0] == '|';
234: if (ispipe)
235: fname = np->n_name+1;
236: else
237: fname = expand(np->n_name);
238:
239: /*
240: * See if we have copied the complete message out yet.
241: * If not, do so.
242: */
243: if (image < 0) {
1.7 millert 244: int fd;
245: char tempname[PATHSIZE];
246:
247: (void)snprintf(tempname, sizeof(tempname),
248: "%s/mail.ReXXXXXXXXXX", tmpdir);
249: if ((fd = mkstemp(tempname)) == -1 ||
250: (fout = Fdopen(fd, "a")) == NULL) {
1.11 millert 251: warn("%s", tempname);
1.1 deraadt 252: senderr++;
253: goto cant;
254: }
1.7 millert 255: image = open(tempname, O_RDWR);
256: (void)rm(tempname);
1.1 deraadt 257: if (image < 0) {
1.11 millert 258: warn("%s", tempname);
1.1 deraadt 259: senderr++;
1.4 millert 260: (void)Fclose(fout);
1.1 deraadt 261: goto cant;
262: }
1.5 millert 263: (void)fcntl(image, F_SETFD, 1);
1.1 deraadt 264: fprintf(fout, "From %s %s", myname, date);
265: puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
266: while ((c = getc(fo)) != EOF)
1.5 millert 267: (void)putc(c, fout);
1.1 deraadt 268: rewind(fo);
1.5 millert 269: (void)putc('\n', fout);
270: (void)fflush(fout);
1.1 deraadt 271: if (ferror(fout))
1.11 millert 272: warn("%s", tempname);
1.4 millert 273: (void)Fclose(fout);
1.1 deraadt 274: }
275:
276: /*
277: * Now either copy "image" to the desired file
278: * or give it as the standard input to the desired
279: * program as appropriate.
280: */
281: if (ispipe) {
1.14 millert 282: pid_t pid;
1.1 deraadt 283: char *shell;
1.2 deraadt 284: sigset_t nset;
1.1 deraadt 285:
286: /*
287: * XXX
288: * We can't really reuse the same image file,
289: * because multiple piped recipients will
290: * share the same lseek location and trample
291: * on one another.
292: */
1.13 millert 293: shell = value("SHELL");
1.2 deraadt 294: sigemptyset(&nset);
295: sigaddset(&nset, SIGHUP);
296: sigaddset(&nset, SIGINT);
297: sigaddset(&nset, SIGQUIT);
298: pid = start_command(shell, &nset,
1.6 millert 299: image, -1, "-c", fname, NULL);
1.1 deraadt 300: if (pid < 0) {
301: senderr++;
302: goto cant;
303: }
304: free_child(pid);
305: } else {
306: int f;
307: if ((fout = Fopen(fname, "a")) == NULL) {
1.11 millert 308: warn("%s", fname);
1.1 deraadt 309: senderr++;
310: goto cant;
311: }
312: if ((f = dup(image)) < 0) {
1.4 millert 313: warn("dup");
1.1 deraadt 314: fin = NULL;
315: } else
316: fin = Fdopen(f, "r");
317: if (fin == NULL) {
1.4 millert 318: fputs("Can't reopen image\n", stderr);
319: (void)Fclose(fout);
1.1 deraadt 320: senderr++;
321: goto cant;
322: }
323: rewind(fin);
324: while ((c = getc(fin)) != EOF)
1.5 millert 325: (void)putc(c, fout);
1.4 millert 326: if (ferror(fout)) {
327: senderr++;
1.11 millert 328: warn("%s", fname);
1.4 millert 329: }
330: (void)Fclose(fout);
331: (void)Fclose(fin);
1.1 deraadt 332: }
333: cant:
334: /*
335: * In days of old we removed the entry from the
336: * the list; now for sake of header expansion
337: * we leave it in and mark it as deleted.
338: */
339: np->n_type |= GDEL;
340: np = np->n_flink;
341: }
342: if (image >= 0) {
1.4 millert 343: (void)close(image);
1.1 deraadt 344: image = -1;
345: }
346: return(top);
347: }
348:
349: /*
350: * Determine if the passed address is a local "send to file" address.
351: * If any of the network metacharacters precedes any slashes, it can't
352: * be a filename. We cheat with .'s to allow path names like ./...
353: */
354: int
1.14 millert 355: isfileaddr(char *name)
1.1 deraadt 356: {
1.9 millert 357: char *cp;
1.1 deraadt 358:
359: if (*name == '+')
1.4 millert 360: return(1);
1.1 deraadt 361: for (cp = name; *cp; cp++) {
362: if (*cp == '!' || *cp == '%' || *cp == '@')
1.4 millert 363: return(0);
1.1 deraadt 364: if (*cp == '/')
1.4 millert 365: return(1);
1.1 deraadt 366: }
1.4 millert 367: return(0);
1.1 deraadt 368: }
369:
370: /*
371: * Map all of the aliased users in the invoker's mailrc
372: * file and insert them into the list.
373: * Changed after all these months of service to recursively
374: * expand names (2/14/80).
375: */
376: struct name *
1.14 millert 377: usermap(struct name *names)
1.1 deraadt 378: {
1.9 millert 379: struct name *new, *np, *cp;
1.1 deraadt 380: struct grouphead *gh;
1.9 millert 381: int metoo;
1.1 deraadt 382:
1.14 millert 383: new = NULL;
1.1 deraadt 384: np = names;
1.6 millert 385: metoo = (value("metoo") != NULL);
1.14 millert 386: while (np != NULL) {
1.1 deraadt 387: if (np->n_name[0] == '\\') {
388: cp = np->n_flink;
389: new = put(new, np);
390: np = cp;
391: continue;
392: }
393: gh = findgroup(np->n_name);
394: cp = np->n_flink;
1.14 millert 395: if (gh != NULL)
1.1 deraadt 396: new = gexpand(new, gh, metoo, np->n_type);
397: else
398: new = put(new, np);
399: np = cp;
400: }
401: return(new);
402: }
403:
404: /*
405: * Recursively expand a group name. We limit the expansion to some
406: * fixed level to keep things from going haywire.
407: * Direct recursion is not expanded for convenience.
408: */
409: struct name *
1.14 millert 410: gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
1.1 deraadt 411: {
412: struct group *gp;
413: struct grouphead *ngh;
414: struct name *np;
415: static int depth;
416: char *cp;
417:
418: if (depth > MAXEXP) {
419: printf("Expanding alias to depth larger than %d\n", MAXEXP);
420: return(nlist);
421: }
422: depth++;
1.14 millert 423: for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
1.1 deraadt 424: cp = gp->ge_name;
425: if (*cp == '\\')
426: goto quote;
427: if (strcmp(cp, gh->g_name) == 0)
428: goto quote;
1.14 millert 429: if ((ngh = findgroup(cp)) != NULL) {
1.1 deraadt 430: nlist = gexpand(nlist, ngh, metoo, ntype);
431: continue;
432: }
433: quote:
434: np = nalloc(cp, ntype);
435: /*
436: * At this point should allow to expand
437: * to self if only person in group
438: */
1.14 millert 439: if (gp == gh->g_list && gp->ge_link == NULL)
1.1 deraadt 440: goto skip;
441: if (!metoo && strcmp(cp, myname) == 0)
442: np->n_type |= GDEL;
443: skip:
444: nlist = put(nlist, np);
445: }
446: depth--;
447: return(nlist);
448: }
449:
450: /*
451: * Concatenate the two passed name lists, return the result.
452: */
453: struct name *
1.14 millert 454: cat(struct name *n1, struct name *n2)
1.1 deraadt 455: {
1.9 millert 456: struct name *tail;
1.1 deraadt 457:
1.14 millert 458: if (n1 == NULL)
1.1 deraadt 459: return(n2);
1.14 millert 460: if (n2 == NULL)
1.1 deraadt 461: return(n1);
462: tail = tailof(n1);
463: tail->n_flink = n2;
464: n2->n_blink = tail;
465: return(n1);
466: }
467:
468: /*
469: * Unpack the name list onto a vector of strings.
470: * Return an error if the name list won't fit.
471: */
472: char **
1.15 ! millert 473: unpack(struct name *sm, struct name *np)
1.1 deraadt 474: {
1.9 millert 475: char **ap, **top;
1.1 deraadt 476: int t, extra, metoo, verbose;
477:
1.12 mickey 478: if ((t = count(np)) == 0)
1.9 millert 479: errx(1, "No names to unpack");
1.12 mickey 480: t += count(sm);
481:
1.1 deraadt 482: /*
483: * Compute the number of extra arguments we will need.
1.10 millert 484: * We need at least four extra -- one for "send-mail", one for the
485: * "-i" flag, one for the "--" to signal end of command line
486: * arguments, and one for the terminating 0 pointer.
1.1 deraadt 487: */
1.10 millert 488: extra = 4;
1.6 millert 489: metoo = value("metoo") != NULL;
1.1 deraadt 490: if (metoo)
491: extra++;
1.6 millert 492: verbose = value("verbose") != NULL;
1.1 deraadt 493: if (verbose)
494: extra++;
1.5 millert 495: top = (char **)salloc((t + extra) * sizeof(*top));
1.1 deraadt 496: ap = top;
497: *ap++ = "send-mail";
498: *ap++ = "-i";
499: if (metoo)
500: *ap++ = "-m";
501: if (verbose)
502: *ap++ = "-v";
1.14 millert 503: for (; sm != NULL; sm = sm->n_flink)
1.12 mickey 504: if ((sm->n_type & GDEL) == 0)
505: *ap++ = sm->n_name;
1.10 millert 506: *ap++ = "--";
1.14 millert 507: for (; np != NULL; np = np->n_flink)
1.12 mickey 508: if ((np->n_type & GDEL) == 0)
509: *ap++ = np->n_name;
1.6 millert 510: *ap = NULL;
1.1 deraadt 511: return(top);
512: }
513:
514: /*
515: * Remove all of the duplicates from the passed name list by
516: * insertion sorting them, then checking for dups.
517: * Return the head of the new list.
518: */
519: struct name *
1.14 millert 520: elide(struct name *names)
1.1 deraadt 521: {
1.9 millert 522: struct name *np, *t, *new;
1.1 deraadt 523: struct name *x;
524:
1.14 millert 525: if (names == NULL)
526: return(NULL);
1.1 deraadt 527: new = names;
528: np = names;
529: np = np->n_flink;
1.14 millert 530: if (np != NULL)
531: np->n_blink = NULL;
532: new->n_flink = NULL;
533: while (np != NULL) {
1.1 deraadt 534: t = new;
535: while (strcasecmp(t->n_name, np->n_name) < 0) {
1.14 millert 536: if (t->n_flink == NULL)
1.1 deraadt 537: break;
538: t = t->n_flink;
539: }
540:
541: /*
542: * If we ran out of t's, put the new entry after
543: * the current value of t.
544: */
545: if (strcasecmp(t->n_name, np->n_name) < 0) {
546: t->n_flink = np;
547: np->n_blink = t;
548: t = np;
549: np = np->n_flink;
1.14 millert 550: t->n_flink = NULL;
1.1 deraadt 551: continue;
552: }
553:
554: /*
555: * Otherwise, put the new entry in front of the
556: * current t. If at the front of the list,
557: * the new guy becomes the new head of the list.
558: */
559: if (t == new) {
560: t = np;
561: np = np->n_flink;
562: t->n_flink = new;
563: new->n_blink = t;
1.14 millert 564: t->n_blink = NULL;
1.1 deraadt 565: new = t;
566: continue;
567: }
568:
569: /*
570: * The normal case -- we are inserting into the
571: * middle of the list.
572: */
573: x = np;
574: np = np->n_flink;
575: x->n_flink = t;
576: x->n_blink = t->n_blink;
577: t->n_blink->n_flink = x;
578: t->n_blink = x;
579: }
580:
581: /*
582: * Now the list headed up by new is sorted.
583: * Go through it and remove duplicates.
584: */
585: np = new;
1.14 millert 586: while (np != NULL) {
1.1 deraadt 587: t = np;
1.14 millert 588: while (t->n_flink != NULL &&
1.1 deraadt 589: strcasecmp(np->n_name, t->n_flink->n_name) == 0)
590: t = t->n_flink;
1.14 millert 591: if (t == np || t == NULL) {
1.1 deraadt 592: np = np->n_flink;
593: continue;
594: }
595:
596: /*
597: * Now t points to the last entry with the same name
598: * as np. Make np point beyond t.
599: */
600: np->n_flink = t->n_flink;
1.14 millert 601: if (t->n_flink != NULL)
1.1 deraadt 602: t->n_flink->n_blink = np;
603: np = np->n_flink;
604: }
605: return(new);
606: }
607:
608: /*
609: * Put another node onto a list of names and return
610: * the list.
611: */
612: struct name *
1.14 millert 613: put(struct name *list, struct name *node)
1.1 deraadt 614: {
615: node->n_flink = list;
1.14 millert 616: node->n_blink = NULL;
617: if (list != NULL)
1.1 deraadt 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: int
1.14 millert 627: count(struct name *np)
1.1 deraadt 628: {
1.9 millert 629: int c;
1.1 deraadt 630:
1.14 millert 631: for (c = 0; np != NULL; np = np->n_flink)
1.1 deraadt 632: if ((np->n_type & GDEL) == 0)
633: c++;
1.4 millert 634: return(c);
1.1 deraadt 635: }
636:
637: /*
638: * Delete the given name from a namelist.
639: */
640: struct name *
1.14 millert 641: delname(struct name *np, char *name)
1.1 deraadt 642: {
1.9 millert 643: struct name *p;
1.1 deraadt 644:
1.14 millert 645: for (p = np; p != NULL; p = p->n_flink)
1.13 millert 646: if ((strcasecmp(p->n_name, name) == 0) ||
647: (value("allnet") &&
648: strncasecmp(p->n_name, name, strlen(name)) == 0 &&
649: *(p->n_name+strlen(name)) == '@')) {
1.14 millert 650: if (p->n_blink == NULL) {
651: if (p->n_flink != NULL)
652: p->n_flink->n_blink = NULL;
1.1 deraadt 653: np = p->n_flink;
654: continue;
655: }
1.14 millert 656: if (p->n_flink == NULL) {
657: if (p->n_blink != NULL)
658: p->n_blink->n_flink = NULL;
1.1 deraadt 659: continue;
660: }
661: p->n_blink->n_flink = p->n_flink;
662: p->n_flink->n_blink = p->n_blink;
663: }
1.4 millert 664: return(np);
1.1 deraadt 665: }
666:
667: /*
668: * Pretty print a name list
669: * Uncomment it if you need it.
670: */
1.14 millert 671: #if 0
1.1 deraadt 672: void
673: prettyprint(name)
674: struct name *name;
675: {
1.9 millert 676: struct name *np;
1.1 deraadt 677:
678: np = name;
1.14 millert 679: while (np != NULL) {
1.1 deraadt 680: fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
681: np = np->n_flink;
682: }
1.4 millert 683: putc('\n', stderr);
1.1 deraadt 684: }
1.14 millert 685: #endif