xref: /original-bsd/usr.bin/mail/names.c (revision 088910ec)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)names.c	5.17 (Berkeley) 06/26/92";
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 *
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 *
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 *
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 *
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 *
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 *
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 			fprintf(fout, "From %s %s", myname, date);
237 			puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
238 			while ((c = getc(fo)) != EOF)
239 				(void) putc(c, fout);
240 			rewind(fo);
241 			(void) putc('\n', fout);
242 			(void) fflush(fout);
243 			if (ferror(fout))
244 				perror(tempEdit);
245 			(void) Fclose(fout);
246 		}
247 
248 		/*
249 		 * Now either copy "image" to the desired file
250 		 * or give it as the standard input to the desired
251 		 * program as appropriate.
252 		 */
253 
254 		if (ispipe) {
255 			int pid;
256 			char *shell;
257 
258 			/*
259 			 * XXX
260 			 * We can't really reuse the same image file,
261 			 * because multiple piped recipients will
262 			 * share the same lseek location and trample
263 			 * on one another.
264 			 */
265 			if ((shell = value("SHELL")) == NOSTR)
266 				shell = _PATH_CSHELL;
267 			pid = start_command(shell, sigmask(SIGHUP)|
268 					sigmask(SIGINT)|sigmask(SIGQUIT),
269 				image, -1, "-c", fname, NOSTR);
270 			if (pid < 0) {
271 				senderr++;
272 				goto cant;
273 			}
274 			free_child(pid);
275 		} else {
276 			int f;
277 			if ((fout = Fopen(fname, "a")) == NULL) {
278 				perror(fname);
279 				senderr++;
280 				goto cant;
281 			}
282 			if ((f = dup(image)) < 0) {
283 				perror("dup");
284 				fin = NULL;
285 			} else
286 				fin = Fdopen(f, "r");
287 			if (fin == NULL) {
288 				fprintf(stderr, "Can't reopen image\n");
289 				(void) Fclose(fout);
290 				senderr++;
291 				goto cant;
292 			}
293 			rewind(fin);
294 			while ((c = getc(fin)) != EOF)
295 				(void) putc(c, fout);
296 			if (ferror(fout))
297 				senderr++, perror(fname);
298 			(void) Fclose(fout);
299 			(void) Fclose(fin);
300 		}
301 cant:
302 		/*
303 		 * In days of old we removed the entry from the
304 		 * the list; now for sake of header expansion
305 		 * we leave it in and mark it as deleted.
306 		 */
307 		np->n_type |= GDEL;
308 		np = np->n_flink;
309 	}
310 	if (image >= 0) {
311 		(void) close(image);
312 		image = -1;
313 	}
314 	return(top);
315 }
316 
317 /*
318  * Determine if the passed address is a local "send to file" address.
319  * If any of the network metacharacters precedes any slashes, it can't
320  * be a filename.  We cheat with .'s to allow path names like ./...
321  */
322 int
323 isfileaddr(name)
324 	char *name;
325 {
326 	register char *cp;
327 
328 	if (*name == '+')
329 		return 1;
330 	for (cp = name; *cp; cp++) {
331 		if (*cp == '!' || *cp == '%' || *cp == '@')
332 			return 0;
333 		if (*cp == '/')
334 			return 1;
335 	}
336 	return 0;
337 }
338 
339 /*
340  * Map all of the aliased users in the invoker's mailrc
341  * file and insert them into the list.
342  * Changed after all these months of service to recursively
343  * expand names (2/14/80).
344  */
345 
346 struct name *
347 usermap(names)
348 	struct name *names;
349 {
350 	register struct name *new, *np, *cp;
351 	struct grouphead *gh;
352 	register int metoo;
353 
354 	new = NIL;
355 	np = names;
356 	metoo = (value("metoo") != NOSTR);
357 	while (np != NIL) {
358 		if (np->n_name[0] == '\\') {
359 			cp = np->n_flink;
360 			new = put(new, np);
361 			np = cp;
362 			continue;
363 		}
364 		gh = findgroup(np->n_name);
365 		cp = np->n_flink;
366 		if (gh != NOGRP)
367 			new = gexpand(new, gh, metoo, np->n_type);
368 		else
369 			new = put(new, np);
370 		np = cp;
371 	}
372 	return(new);
373 }
374 
375 /*
376  * Recursively expand a group name.  We limit the expansion to some
377  * fixed level to keep things from going haywire.
378  * Direct recursion is not expanded for convenience.
379  */
380 
381 struct name *
382 gexpand(nlist, gh, metoo, ntype)
383 	struct name *nlist;
384 	struct grouphead *gh;
385 	int metoo, ntype;
386 {
387 	struct group *gp;
388 	struct grouphead *ngh;
389 	struct name *np;
390 	static int depth;
391 	char *cp;
392 
393 	if (depth > MAXEXP) {
394 		printf("Expanding alias to depth larger than %d\n", MAXEXP);
395 		return(nlist);
396 	}
397 	depth++;
398 	for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
399 		cp = gp->ge_name;
400 		if (*cp == '\\')
401 			goto quote;
402 		if (strcmp(cp, gh->g_name) == 0)
403 			goto quote;
404 		if ((ngh = findgroup(cp)) != NOGRP) {
405 			nlist = gexpand(nlist, ngh, metoo, ntype);
406 			continue;
407 		}
408 quote:
409 		np = nalloc(cp, ntype);
410 		/*
411 		 * At this point should allow to expand
412 		 * to self if only person in group
413 		 */
414 		if (gp == gh->g_list && gp->ge_link == NOGE)
415 			goto skip;
416 		if (!metoo && strcmp(cp, myname) == 0)
417 			np->n_type |= GDEL;
418 skip:
419 		nlist = put(nlist, np);
420 	}
421 	depth--;
422 	return(nlist);
423 }
424 
425 /*
426  * Concatenate the two passed name lists, return the result.
427  */
428 struct name *
429 cat(n1, n2)
430 	struct name *n1, *n2;
431 {
432 	register struct name *tail;
433 
434 	if (n1 == NIL)
435 		return(n2);
436 	if (n2 == NIL)
437 		return(n1);
438 	tail = tailof(n1);
439 	tail->n_flink = n2;
440 	n2->n_blink = tail;
441 	return(n1);
442 }
443 
444 /*
445  * Unpack the name list onto a vector of strings.
446  * Return an error if the name list won't fit.
447  */
448 char **
449 unpack(np)
450 	struct name *np;
451 {
452 	register char **ap, **top;
453 	register struct name *n;
454 	int t, extra, metoo, verbose;
455 
456 	n = np;
457 	if ((t = count(n)) == 0)
458 		panic("No names to unpack");
459 	/*
460 	 * Compute the number of extra arguments we will need.
461 	 * We need at least two extra -- one for "mail" and one for
462 	 * the terminating 0 pointer.  Additional spots may be needed
463 	 * to pass along -f to the host mailer.
464 	 */
465 	extra = 2;
466 	extra++;
467 	metoo = value("metoo") != NOSTR;
468 	if (metoo)
469 		extra++;
470 	verbose = value("verbose") != NOSTR;
471 	if (verbose)
472 		extra++;
473 	top = (char **) salloc((t + extra) * sizeof *top);
474 	ap = top;
475 	*ap++ = "send-mail";
476 	*ap++ = "-i";
477 	if (metoo)
478 		*ap++ = "-m";
479 	if (verbose)
480 		*ap++ = "-v";
481 	for (; n != NIL; n = n->n_flink)
482 		if ((n->n_type & GDEL) == 0)
483 			*ap++ = n->n_name;
484 	*ap = NOSTR;
485 	return(top);
486 }
487 
488 /*
489  * Remove all of the duplicates from the passed name list by
490  * insertion sorting them, then checking for dups.
491  * Return the head of the new list.
492  */
493 struct name *
494 elide(names)
495 	struct name *names;
496 {
497 	register struct name *np, *t, *new;
498 	struct name *x;
499 
500 	if (names == NIL)
501 		return(NIL);
502 	new = names;
503 	np = names;
504 	np = np->n_flink;
505 	if (np != NIL)
506 		np->n_blink = NIL;
507 	new->n_flink = NIL;
508 	while (np != NIL) {
509 		t = new;
510 		while (strcasecmp(t->n_name, np->n_name) < 0) {
511 			if (t->n_flink == NIL)
512 				break;
513 			t = t->n_flink;
514 		}
515 
516 		/*
517 		 * If we ran out of t's, put the new entry after
518 		 * the current value of t.
519 		 */
520 
521 		if (strcasecmp(t->n_name, np->n_name) < 0) {
522 			t->n_flink = np;
523 			np->n_blink = t;
524 			t = np;
525 			np = np->n_flink;
526 			t->n_flink = NIL;
527 			continue;
528 		}
529 
530 		/*
531 		 * Otherwise, put the new entry in front of the
532 		 * current t.  If at the front of the list,
533 		 * the new guy becomes the new head of the list.
534 		 */
535 
536 		if (t == new) {
537 			t = np;
538 			np = np->n_flink;
539 			t->n_flink = new;
540 			new->n_blink = t;
541 			t->n_blink = NIL;
542 			new = t;
543 			continue;
544 		}
545 
546 		/*
547 		 * The normal case -- we are inserting into the
548 		 * middle of the list.
549 		 */
550 
551 		x = np;
552 		np = np->n_flink;
553 		x->n_flink = t;
554 		x->n_blink = t->n_blink;
555 		t->n_blink->n_flink = x;
556 		t->n_blink = x;
557 	}
558 
559 	/*
560 	 * Now the list headed up by new is sorted.
561 	 * Go through it and remove duplicates.
562 	 */
563 
564 	np = new;
565 	while (np != NIL) {
566 		t = np;
567 		while (t->n_flink != NIL &&
568 		       strcasecmp(np->n_name, t->n_flink->n_name) == 0)
569 			t = t->n_flink;
570 		if (t == np || t == NIL) {
571 			np = np->n_flink;
572 			continue;
573 		}
574 
575 		/*
576 		 * Now t points to the last entry with the same name
577 		 * as np.  Make np point beyond t.
578 		 */
579 
580 		np->n_flink = t->n_flink;
581 		if (t->n_flink != NIL)
582 			t->n_flink->n_blink = np;
583 		np = np->n_flink;
584 	}
585 	return(new);
586 }
587 
588 /*
589  * Put another node onto a list of names and return
590  * the list.
591  */
592 struct name *
593 put(list, node)
594 	struct name *list, *node;
595 {
596 	node->n_flink = list;
597 	node->n_blink = NIL;
598 	if (list != NIL)
599 		list->n_blink = node;
600 	return(node);
601 }
602 
603 /*
604  * Determine the number of undeleted elements in
605  * a name list and return it.
606  */
607 int
608 count(np)
609 	register struct name *np;
610 {
611 	register int c;
612 
613 	for (c = 0; np != NIL; np = np->n_flink)
614 		if ((np->n_type & GDEL) == 0)
615 			c++;
616 	return c;
617 }
618 
619 /*
620  * Delete the given name from a namelist.
621  */
622 struct name *
623 delname(np, name)
624 	register struct name *np;
625 	char name[];
626 {
627 	register struct name *p;
628 
629 	for (p = np; p != NIL; p = p->n_flink)
630 		if (strcasecmp(p->n_name, name) == 0) {
631 			if (p->n_blink == NIL) {
632 				if (p->n_flink != NIL)
633 					p->n_flink->n_blink = NIL;
634 				np = p->n_flink;
635 				continue;
636 			}
637 			if (p->n_flink == NIL) {
638 				if (p->n_blink != NIL)
639 					p->n_blink->n_flink = NIL;
640 				continue;
641 			}
642 			p->n_blink->n_flink = p->n_flink;
643 			p->n_flink->n_blink = p->n_blink;
644 		}
645 	return np;
646 }
647 
648 /*
649  * Pretty print a name list
650  * Uncomment it if you need it.
651  */
652 
653 /*
654 void
655 prettyprint(name)
656 	struct name *name;
657 {
658 	register struct name *np;
659 
660 	np = name;
661 	while (np != NIL) {
662 		fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
663 		np = np->n_flink;
664 	}
665 	fprintf(stderr, "\n");
666 }
667 */
668