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