xref: /original-bsd/usr.bin/mail/names.c (revision 2d15e5d1)
1 #
2 
3 /*
4  * Mail -- a mail program
5  *
6  * Handle name lists.
7  */
8 
9 #include "rcv.h"
10 
11 static char *SccsId = "@(#)names.c	2.4 09/16/81";
12 
13 /*
14  * Allocate a single element of a name list,
15  * initialize its name field to the passed
16  * name and return it.
17  */
18 
19 struct name *
20 nalloc(str)
21 	char str[];
22 {
23 	register struct name *np;
24 
25 	np = (struct name *) salloc(sizeof *np);
26 	np->n_flink = NIL;
27 	np->n_blink = NIL;
28 	np->n_type = -1;
29 	np->n_name = savestr(str);
30 	return(np);
31 }
32 
33 /*
34  * Find the tail of a list and return it.
35  */
36 
37 struct name *
38 tailof(name)
39 	struct name *name;
40 {
41 	register struct name *np;
42 
43 	np = name;
44 	if (np == NIL)
45 		return(NIL);
46 	while (np->n_flink != NIL)
47 		np = np->n_flink;
48 	return(np);
49 }
50 
51 /*
52  * Extract a list of names from a line,
53  * and make a list of names from it.
54  * Return the list or NIL if none found.
55  */
56 
57 struct name *
58 extract(line, ntype)
59 	char line[];
60 {
61 	register char *cp;
62 	register struct name *top, *np, *t;
63 	char nbuf[BUFSIZ], abuf[BUFSIZ];
64 
65 	if (line == NOSTR || strlen(line) == 0)
66 		return(NIL);
67 	top = NIL;
68 	np = NIL;
69 	cp = line;
70 	while ((cp = yankword(cp, nbuf)) != NOSTR) {
71 		if (np != NIL && equal(nbuf, "at")) {
72 			strcpy(abuf, nbuf);
73 			if ((cp = yankword(cp, nbuf)) == NOSTR) {
74 				strcpy(nbuf, abuf);
75 				goto normal;
76 			}
77 			strcpy(abuf, np->n_name);
78 			stradd(abuf, '@');
79 			strcat(abuf, nbuf);
80 			np->n_name = savestr(abuf);
81 			continue;
82 		}
83 normal:
84 		t = nalloc(nbuf);
85 		t->n_type = ntype;
86 		if (top == NIL)
87 			top = t;
88 		else
89 			np->n_flink = t;
90 		t->n_blink = np;
91 		np = t;
92 	}
93 	return(top);
94 }
95 
96 /*
97  * Turn a list of names into a string of the same names.
98  */
99 
100 char *
101 detract(np, ntype)
102 	register struct name *np;
103 {
104 	register int s;
105 	register char *cp, *top;
106 	register struct name *p;
107 	register int comma;
108 
109 	comma = ntype & GCOMMA;
110 	if (np == NIL)
111 		return(NOSTR);
112 	ntype &= ~GCOMMA;
113 	s = 0;
114 	if (debug && comma)
115 		fprintf(stderr, "detract asked to insert commas\n");
116 	for (p = np; p != NIL; p = p->n_flink) {
117 		if (ntype && (p->n_type & GMASK) != ntype)
118 			continue;
119 		s += strlen(p->n_name) + 1;
120 		if (comma)
121 			s++;
122 	}
123 	if (s == 0)
124 		return(NOSTR);
125 	s += 2;
126 	top = salloc(s);
127 	cp = top;
128 	for (p = np; p != NIL; p = p->n_flink) {
129 		if (ntype && (p->n_type & GMASK) != ntype)
130 			continue;
131 		cp = copy(p->n_name, cp);
132 		if (comma && p->n_flink != NIL)
133 			*cp++ = ',';
134 		*cp++ = ' ';
135 	}
136 	*--cp = 0;
137 	if (comma && *--cp == ',')
138 		*cp = 0;
139 	return(top);
140 }
141 
142 /*
143  * Grab a single word (liberal word)
144  * Throw away things between ()'s.
145  */
146 
147 char *
148 yankword(ap, wbuf)
149 	char *ap, wbuf[];
150 {
151 	register char *cp, *cp2;
152 
153 	do {
154 		for (cp = ap; *cp && any(*cp, " \t,"); cp++)
155 			;
156 		if (*cp == '(') {
157 			while (*cp && *cp != ')')
158 				cp++;
159 			if (*cp)
160 				cp++;
161 		}
162 		if (*cp == '\0')
163 			return(NOSTR);
164 	} while (any(*cp, " \t,("));
165 	for (cp2 = wbuf; *cp && !any(*cp, " \t,("); *cp2++ = *cp++)
166 		;
167 	*cp2 = '\0';
168 	return(cp);
169 }
170 
171 /*
172  * Verify that all the users in the list of names are
173  * legitimate.  Bitch about and delink those who aren't.
174  */
175 
176 struct name *
177 verify(names)
178 	struct name *names;
179 {
180 	register struct name *np, *top, *t, *x;
181 	register char *cp;
182 
183 #ifdef DELIVERMAIL
184 	return(names);
185 #else
186 	top = names;
187 	np = names;
188 	while (np != NIL) {
189 		if (np->n_type & GDEL) {
190 			np = np->n_flink;
191 			continue;
192 		}
193 		for (cp = "!:@^"; *cp; cp++)
194 			if (any(*cp, np->n_name))
195 				break;
196 		if (*cp != 0) {
197 			np = np->n_flink;
198 			continue;
199 		}
200 		cp = np->n_name;
201 		while (*cp == '\\')
202 			cp++;
203 		if (equal(cp, "msgs") ||
204 		    getuserid(cp) != -1) {
205 			np = np->n_flink;
206 			continue;
207 		}
208 		fprintf(stderr, "Can't send to %s\n", np->n_name);
209 		senderr++;
210 		if (np == top) {
211 			top = np->n_flink;
212 			if (top != NIL)
213 				top->n_blink = NIL;
214 			np = top;
215 			continue;
216 		}
217 		x = np->n_blink;
218 		t = np->n_flink;
219 		x->n_flink = t;
220 		if (t != NIL)
221 			t->n_blink = x;
222 		np = t;
223 	}
224 	return(top);
225 #endif
226 }
227 
228 /*
229  * For each recipient in the passed name list with a /
230  * in the name, append the message to the end of the named file
231  * and remove him from the recipient list.
232  *
233  * Recipients whose name begins with | are piped through the given
234  * program and removed.
235  */
236 
237 struct name *
238 outof(names, fo, hp)
239 	struct name *names;
240 	FILE *fo;
241 	struct header *hp;
242 {
243 	register int c;
244 	register struct name *np, *top, *t, *x;
245 	long now;
246 	char *date, *fname, *shell, *ctime();
247 	FILE *fout, *fin;
248 	int ispipe, s, pid;
249 	extern char tempEdit[];
250 
251 	top = names;
252 	np = names;
253 	time(&now);
254 	date = ctime(&now);
255 	while (np != NIL) {
256 		if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
257 			np = np->n_flink;
258 			continue;
259 		}
260 		ispipe = np->n_name[0] == '|';
261 		if (ispipe)
262 			fname = np->n_name+1;
263 		else
264 			fname = expand(np->n_name);
265 
266 		/*
267 		 * See if we have copied the complete message out yet.
268 		 * If not, do so.
269 		 */
270 
271 		if (image < 0) {
272 			if ((fout = fopen(tempEdit, "a")) == NULL) {
273 				perror(tempEdit);
274 				senderr++;
275 				goto cant;
276 			}
277 			image = open(tempEdit, 2);
278 			unlink(tempEdit);
279 			if (image < 0) {
280 				perror(tempEdit);
281 				senderr++;
282 				goto cant;
283 			}
284 			else {
285 				rewind(fo);
286 				fprintf(fout, "From %s %s", myname, date);
287 				puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
288 				while ((c = getc(fo)) != EOF)
289 					putc(c, fout);
290 				rewind(fo);
291 				putc('\n', fout);
292 				fflush(fout);
293 				if (ferror(fout))
294 					perror(tempEdit);
295 				fclose(fout);
296 			}
297 		}
298 
299 		/*
300 		 * Now either copy "image" to the desired file
301 		 * or give it as the standard input to the desired
302 		 * program as appropriate.
303 		 */
304 
305 		if (ispipe) {
306 			wait(&s);
307 			switch (pid = fork()) {
308 			case 0:
309 				sigsys(SIGHUP, SIG_IGN);
310 				sigsys(SIGINT, SIG_IGN);
311 				sigsys(SIGQUIT, SIG_IGN);
312 				close(0);
313 				dup(image);
314 				close(image);
315 				if ((shell = value("SHELL")) == NOSTR)
316 					shell = SHELL;
317 				execl(shell, shell, "-c", fname, 0);
318 				perror(shell);
319 				exit(1);
320 				break;
321 
322 			case -1:
323 				perror("fork");
324 				senderr++;
325 				goto cant;
326 			}
327 		}
328 		else {
329 			if ((fout = fopen(fname, "a")) == NULL) {
330 				perror(fname);
331 				senderr++;
332 				goto cant;
333 			}
334 			fin = Fdopen(image, "r");
335 			if (fin == NULL) {
336 				fprintf(stderr, "Can't reopen image\n");
337 				fclose(fout);
338 				senderr++;
339 				goto cant;
340 			}
341 			rewind(fin);
342 			while ((c = getc(fin)) != EOF)
343 				putc(c, fout);
344 			if (ferror(fout))
345 				senderr++, perror(fname);
346 			fclose(fout);
347 			fclose(fin);
348 		}
349 
350 cant:
351 
352 		/*
353 		 * In days of old we removed the entry from the
354 		 * the list; now for sake of header expansion
355 		 * we leave it in and mark it as deleted.
356 		 */
357 
358 #ifdef CRAZYWOW
359 		if (np == top) {
360 			top = np->n_flink;
361 			if (top != NIL)
362 				top->n_blink = NIL;
363 			np = top;
364 			continue;
365 		}
366 		x = np->n_blink;
367 		t = np->n_flink;
368 		x->n_flink = t;
369 		if (t != NIL)
370 			t->n_blink = x;
371 		np = t;
372 #endif
373 
374 		np->n_type |= GDEL;
375 		np = np->n_flink;
376 	}
377 	if (image >= 0) {
378 		close(image);
379 		image = -1;
380 	}
381 	return(top);
382 }
383 
384 /*
385  * Determine if the passed address is a local "send to file" address.
386  * If any of the network metacharacters precedes any slashes, it can't
387  * be a filename.  We cheat with .'s to allow path names like ./...
388  */
389 isfileaddr(name)
390 	char *name;
391 {
392 	register char *cp;
393 	extern char *metanet;
394 
395 	if (any('@', name))
396 		return(0);
397 	for (cp = name; *cp; cp++) {
398 		if (*cp == '.')
399 			continue;
400 		if (any(*cp, metanet))
401 			return(0);
402 		if (*cp == '/')
403 			return(1);
404 	}
405 	return(0);
406 }
407 
408 /*
409  * Map all of the aliased users in the invoker's mailrc
410  * file and insert them into the list.
411  * Changed after all these months of service to recursively
412  * expand names (2/14/80).
413  */
414 
415 struct name *
416 usermap(names)
417 	struct name *names;
418 {
419 	register struct name *new, *np, *cp;
420 	struct name *getto;
421 	struct grouphead *gh;
422 	register int metoo;
423 
424 	new = NIL;
425 	np = names;
426 	getto = NIL;
427 	metoo = (value("metoo") != NOSTR);
428 	while (np != NIL) {
429 		if (np->n_name[0] == '\\') {
430 			cp = np->n_flink;
431 			new = put(new, np);
432 			np = cp;
433 			continue;
434 		}
435 		gh = findgroup(np->n_name);
436 		cp = np->n_flink;
437 		if (gh != NOGRP)
438 			new = gexpand(new, gh, metoo, np->n_type);
439 		else
440 			new = put(new, np);
441 		np = cp;
442 	}
443 	return(new);
444 }
445 
446 /*
447  * Recursively expand a group name.  We limit the expansion to some
448  * fixed level to keep things from going haywire.
449  * Direct recursion is not expanded for convenience.
450  */
451 
452 struct name *
453 gexpand(nlist, gh, metoo, ntype)
454 	struct name *nlist;
455 	struct grouphead *gh;
456 {
457 	struct group *gp;
458 	struct grouphead *ngh;
459 	struct name *np;
460 	static int depth;
461 	char *cp;
462 
463 	if (depth > MAXEXP) {
464 		printf("Expanding alias to depth larger than %d\n", MAXEXP);
465 		return(nlist);
466 	}
467 	depth++;
468 	for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
469 		cp = gp->ge_name;
470 		if (*cp == '\\')
471 			goto quote;
472 		if (strcmp(cp, gh->g_name) == 0)
473 			goto quote;
474 		if ((ngh = findgroup(cp)) != NOGRP) {
475 			nlist = gexpand(nlist, ngh, metoo, ntype);
476 			continue;
477 		}
478 quote:
479 		np = nalloc(cp);
480 		np->n_type = ntype;
481 		/*
482 		 * At this point should allow to expand
483 		 * to self if only person in group
484 		 */
485 		if (gp == gh->g_list && gp->ge_link == NOGE)
486 			goto skip;
487 		if (!metoo && strcmp(cp, myname) == 0)
488 			np->n_type |= GDEL;
489 skip:
490 		nlist = put(nlist, np);
491 	}
492 	depth--;
493 	return(nlist);
494 }
495 
496 
497 
498 /*
499  * Compute the length of the passed name list and
500  * return it.
501  */
502 
503 lengthof(name)
504 	struct name *name;
505 {
506 	register struct name *np;
507 	register int c;
508 
509 	for (c = 0, np = name; np != NIL; c++, np = np->n_flink)
510 		;
511 	return(c);
512 }
513 
514 /*
515  * Concatenate the two passed name lists, return the result.
516  */
517 
518 struct name *
519 cat(n1, n2)
520 	struct name *n1, *n2;
521 {
522 	register struct name *tail;
523 
524 	if (n1 == NIL)
525 		return(n2);
526 	if (n2 == NIL)
527 		return(n1);
528 	tail = tailof(n1);
529 	tail->n_flink = n2;
530 	n2->n_blink = tail;
531 	return(n1);
532 }
533 
534 /*
535  * Unpack the name list onto a vector of strings.
536  * Return an error if the name list won't fit.
537  */
538 
539 char **
540 unpack(np)
541 	struct name *np;
542 {
543 	register char **ap, **top;
544 	register struct name *n;
545 	char *cp;
546 	char hbuf[10];
547 	int t, extra, metoo;
548 
549 	n = np;
550 	if ((t = lengthof(n)) == 0)
551 		panic("No names to unpack");
552 
553 	/*
554 	 * Compute the number of extra arguments we will need.
555 	 * We need at least two extra -- one for "mail" and one for
556 	 * the terminating 0 pointer.  Additional spots may be needed
557 	 * to pass along -r and -f to the host mailer.
558 	 */
559 
560 	extra = 2;
561 	if (rflag != NOSTR)
562 		extra += 2;
563 #ifdef DELIVERMAIL
564 	extra++;
565 	metoo = value("metoo") != NOSTR;
566 	if (metoo)
567 		extra++;
568 #endif DELIVERMAIL
569 	if (hflag)
570 		extra += 2;
571 	top = (char **) salloc((t + extra) * sizeof cp);
572 	ap = top;
573 	*ap++ = "send-mail";
574 	if (rflag != NOSTR) {
575 		*ap++ = "-r";
576 		*ap++ = rflag;
577 	}
578 #ifdef DELIVERMAIL
579 	*ap++ = "-i";
580 	if (metoo)
581 		*ap++ = "-m";
582 #endif DELIVERMAIL
583 	if (hflag) {
584 		*ap++ = "-h";
585 		sprintf(hbuf, "%d", hflag);
586 		*ap++ = savestr(hbuf);
587 	}
588 	while (n != NIL) {
589 		if (n->n_type & GDEL) {
590 			n = n->n_flink;
591 			continue;
592 		}
593 		*ap++ = n->n_name;
594 		n = n->n_flink;
595 	}
596 	*ap = NOSTR;
597 	return(top);
598 }
599 
600 /*
601  * See if the user named himself as a destination
602  * for outgoing mail.  If so, set the global flag
603  * selfsent so that we avoid removing his mailbox.
604  */
605 
606 mechk(names)
607 	struct name *names;
608 {
609 	register struct name *np;
610 
611 	for (np = names; np != NIL; np = np->n_flink)
612 		if ((np->n_type & GDEL) == 0 && equal(np->n_name, myname)) {
613 			selfsent++;
614 			return;
615 		}
616 }
617 
618 /*
619  * Remove all of the duplicates from the passed name list by
620  * insertion sorting them, then checking for dups.
621  * Return the head of the new list.
622  */
623 
624 struct name *
625 elide(names)
626 	struct name *names;
627 {
628 	register struct name *np, *t, *new;
629 	struct name *x;
630 
631 	if (names == NIL)
632 		return(NIL);
633 	new = names;
634 	np = names;
635 	np = np->n_flink;
636 	if (np != NIL)
637 		np->n_blink = NIL;
638 	new->n_flink = NIL;
639 	while (np != NIL) {
640 		t = new;
641 		while (nstrcmp(t->n_name, np->n_name) < 0) {
642 			if (t->n_flink == NIL)
643 				break;
644 			t = t->n_flink;
645 		}
646 
647 		/*
648 		 * If we ran out of t's, put the new entry after
649 		 * the current value of t.
650 		 */
651 
652 		if (nstrcmp(t->n_name, np->n_name) < 0) {
653 			t->n_flink = np;
654 			np->n_blink = t;
655 			t = np;
656 			np = np->n_flink;
657 			t->n_flink = NIL;
658 			continue;
659 		}
660 
661 		/*
662 		 * Otherwise, put the new entry in front of the
663 		 * current t.  If at the front of the list,
664 		 * the new guy becomes the new head of the list.
665 		 */
666 
667 		if (t == new) {
668 			t = np;
669 			np = np->n_flink;
670 			t->n_flink = new;
671 			new->n_blink = t;
672 			t->n_blink = NIL;
673 			new = t;
674 			continue;
675 		}
676 
677 		/*
678 		 * The normal case -- we are inserting into the
679 		 * middle of the list.
680 		 */
681 
682 		x = np;
683 		np = np->n_flink;
684 		x->n_flink = t;
685 		x->n_blink = t->n_blink;
686 		t->n_blink->n_flink = x;
687 		t->n_blink = x;
688 	}
689 
690 	/*
691 	 * Now the list headed up by new is sorted.
692 	 * Go through it and remove duplicates.
693 	 */
694 
695 	np = new;
696 	while (np != NIL) {
697 		t = np;
698 		while (t->n_flink!=NIL &&
699 		    icequal(np->n_name,t->n_flink->n_name))
700 			t = t->n_flink;
701 		if (t == np || t == NIL) {
702 			np = np->n_flink;
703 			continue;
704 		}
705 
706 		/*
707 		 * Now t points to the last entry with the same name
708 		 * as np.  Make np point beyond t.
709 		 */
710 
711 		np->n_flink = t->n_flink;
712 		if (t->n_flink != NIL)
713 			t->n_flink->n_blink = np;
714 		np = np->n_flink;
715 	}
716 	return(new);
717 }
718 
719 /*
720  * Version of strcmp which ignores case differences.
721  */
722 
723 nstrcmp(s1, s2)
724 	register char *s1, *s2;
725 {
726 	register int c1, c2;
727 
728 	do {
729 		c1 = *s1++;
730 		c2 = *s2++;
731 	} while (c1 && c1 == c2);
732 	return(c1 - c2);
733 }
734 
735 /*
736  * Put another node onto a list of names and return
737  * the list.
738  */
739 
740 struct name *
741 put(list, node)
742 	struct name *list, *node;
743 {
744 	node->n_flink = list;
745 	node->n_blink = NIL;
746 	if (list != NIL)
747 		list->n_blink = node;
748 	return(node);
749 }
750 
751 /*
752  * Determine the number of elements in
753  * a name list and return it.
754  */
755 
756 count(np)
757 	register struct name *np;
758 {
759 	register int c = 0;
760 
761 	while (np != NIL) {
762 		c++;
763 		np = np->n_flink;
764 	}
765 	return(c);
766 }
767 
768 /*
769  * Delete the given name from a namelist.
770  */
771 struct name *
772 delname(np, name)
773 	register struct name *np;
774 	char name[];
775 {
776 	register struct name *p;
777 
778 	for (p = np; p != NIL; p = p->n_flink)
779 		if (icequal(p->n_name, name)) {
780 			if (p->n_blink == NIL) {
781 				if (p->n_flink != NIL)
782 					p->n_flink->n_blink = NIL;
783 				np = p->n_flink;
784 				continue;
785 			}
786 			if (p->n_flink == NIL) {
787 				if (p->n_blink != NIL)
788 					p->n_blink->n_flink = NIL;
789 				continue;
790 			}
791 			p->n_blink->n_flink = p->n_flink;
792 			p->n_flink->n_blink = p->n_blink;
793 		}
794 	return(np);
795 }
796 
797 /*
798  * Call the given routine on each element of the name
799  * list, replacing said value if need be.
800  */
801 
802 mapf(np, from)
803 	register struct name *np;
804 	char *from;
805 {
806 	register struct name *p;
807 
808 	for (p = np; p != NIL; p = p->n_flink)
809 		p->n_name = netmap(p->n_name, from);
810 }
811 
812 /*
813  * Pretty print a name list
814  * Uncomment it if you need it.
815  */
816 
817 prettyprint(name)
818 	struct name *name;
819 {
820 	register struct name *np;
821 
822 	np = name;
823 	while (np != NIL) {
824 		fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
825 		np = np->n_flink;
826 	}
827 	fprintf(stderr, "\n");
828 }
829