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