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