xref: /original-bsd/usr.bin/mail/names.c (revision 18f6d767)
1 #ifndef lint
2 static char sccsid[] = "@(#)names.c	2.9 (Berkeley) 08/11/83";
3 #endif
4 
5 /*
6  * Mail -- a mail program
7  *
8  * Handle name lists.
9  */
10 
11 #include "rcv.h"
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 SENDMAIL
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 				sigchild();
310 				sigsys(SIGHUP, SIG_IGN);
311 				sigsys(SIGINT, SIG_IGN);
312 				sigsys(SIGQUIT, SIG_IGN);
313 				close(0);
314 				dup(image);
315 				close(image);
316 				if ((shell = value("SHELL")) == NOSTR)
317 					shell = SHELL;
318 				execl(shell, shell, "-c", fname, 0);
319 				perror(shell);
320 				exit(1);
321 				break;
322 
323 			case -1:
324 				perror("fork");
325 				senderr++;
326 				goto cant;
327 			}
328 		}
329 		else {
330 			if ((fout = fopen(fname, "a")) == NULL) {
331 				perror(fname);
332 				senderr++;
333 				goto cant;
334 			}
335 			fin = Fdopen(image, "r");
336 			if (fin == NULL) {
337 				fprintf(stderr, "Can't reopen image\n");
338 				fclose(fout);
339 				senderr++;
340 				goto cant;
341 			}
342 			rewind(fin);
343 			while ((c = getc(fin)) != EOF)
344 				putc(c, fout);
345 			if (ferror(fout))
346 				senderr++, perror(fname);
347 			fclose(fout);
348 			fclose(fin);
349 		}
350 
351 cant:
352 
353 		/*
354 		 * In days of old we removed the entry from the
355 		 * the list; now for sake of header expansion
356 		 * we leave it in and mark it as deleted.
357 		 */
358 
359 #ifdef CRAZYWOW
360 		if (np == top) {
361 			top = np->n_flink;
362 			if (top != NIL)
363 				top->n_blink = NIL;
364 			np = top;
365 			continue;
366 		}
367 		x = np->n_blink;
368 		t = np->n_flink;
369 		x->n_flink = t;
370 		if (t != NIL)
371 			t->n_blink = x;
372 		np = t;
373 #endif
374 
375 		np->n_type |= GDEL;
376 		np = np->n_flink;
377 	}
378 	if (image >= 0) {
379 		close(image);
380 		image = -1;
381 	}
382 	return(top);
383 }
384 
385 /*
386  * Determine if the passed address is a local "send to file" address.
387  * If any of the network metacharacters precedes any slashes, it can't
388  * be a filename.  We cheat with .'s to allow path names like ./...
389  */
390 isfileaddr(name)
391 	char *name;
392 {
393 	register char *cp;
394 	extern char *metanet;
395 
396 	if (any('@', name))
397 		return(0);
398 	if (*name == '+')
399 		return(1);
400 	for (cp = name; *cp; cp++) {
401 		if (*cp == '.')
402 			continue;
403 		if (any(*cp, metanet))
404 			return(0);
405 		if (*cp == '/')
406 			return(1);
407 	}
408 	return(0);
409 }
410 
411 /*
412  * Map all of the aliased users in the invoker's mailrc
413  * file and insert them into the list.
414  * Changed after all these months of service to recursively
415  * expand names (2/14/80).
416  */
417 
418 struct name *
419 usermap(names)
420 	struct name *names;
421 {
422 	register struct name *new, *np, *cp;
423 	struct name *getto;
424 	struct grouphead *gh;
425 	register int metoo;
426 
427 	new = NIL;
428 	np = names;
429 	getto = NIL;
430 	metoo = (value("metoo") != NOSTR);
431 	while (np != NIL) {
432 		if (np->n_name[0] == '\\') {
433 			cp = np->n_flink;
434 			new = put(new, np);
435 			np = cp;
436 			continue;
437 		}
438 		gh = findgroup(np->n_name);
439 		cp = np->n_flink;
440 		if (gh != NOGRP)
441 			new = gexpand(new, gh, metoo, np->n_type);
442 		else
443 			new = put(new, np);
444 		np = cp;
445 	}
446 	return(new);
447 }
448 
449 /*
450  * Recursively expand a group name.  We limit the expansion to some
451  * fixed level to keep things from going haywire.
452  * Direct recursion is not expanded for convenience.
453  */
454 
455 struct name *
456 gexpand(nlist, gh, metoo, ntype)
457 	struct name *nlist;
458 	struct grouphead *gh;
459 {
460 	struct group *gp;
461 	struct grouphead *ngh;
462 	struct name *np;
463 	static int depth;
464 	char *cp;
465 
466 	if (depth > MAXEXP) {
467 		printf("Expanding alias to depth larger than %d\n", MAXEXP);
468 		return(nlist);
469 	}
470 	depth++;
471 	for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
472 		cp = gp->ge_name;
473 		if (*cp == '\\')
474 			goto quote;
475 		if (strcmp(cp, gh->g_name) == 0)
476 			goto quote;
477 		if ((ngh = findgroup(cp)) != NOGRP) {
478 			nlist = gexpand(nlist, ngh, metoo, ntype);
479 			continue;
480 		}
481 quote:
482 		np = nalloc(cp);
483 		np->n_type = ntype;
484 		/*
485 		 * At this point should allow to expand
486 		 * to self if only person in group
487 		 */
488 		if (gp == gh->g_list && gp->ge_link == NOGE)
489 			goto skip;
490 		if (!metoo && strcmp(cp, myname) == 0)
491 			np->n_type |= GDEL;
492 skip:
493 		nlist = put(nlist, np);
494 	}
495 	depth--;
496 	return(nlist);
497 }
498 
499 
500 
501 /*
502  * Compute the length of the passed name list and
503  * return it.
504  */
505 
506 lengthof(name)
507 	struct name *name;
508 {
509 	register struct name *np;
510 	register int c;
511 
512 	for (c = 0, np = name; np != NIL; c++, np = np->n_flink)
513 		;
514 	return(c);
515 }
516 
517 /*
518  * Concatenate the two passed name lists, return the result.
519  */
520 
521 struct name *
522 cat(n1, n2)
523 	struct name *n1, *n2;
524 {
525 	register struct name *tail;
526 
527 	if (n1 == NIL)
528 		return(n2);
529 	if (n2 == NIL)
530 		return(n1);
531 	tail = tailof(n1);
532 	tail->n_flink = n2;
533 	n2->n_blink = tail;
534 	return(n1);
535 }
536 
537 /*
538  * Unpack the name list onto a vector of strings.
539  * Return an error if the name list won't fit.
540  */
541 
542 char **
543 unpack(np)
544 	struct name *np;
545 {
546 	register char **ap, **top;
547 	register struct name *n;
548 	char *cp;
549 	char hbuf[10];
550 	int t, extra, metoo, verbose;
551 
552 	n = np;
553 	if ((t = lengthof(n)) == 0)
554 		panic("No names to unpack");
555 
556 	/*
557 	 * Compute the number of extra arguments we will need.
558 	 * We need at least two extra -- one for "mail" and one for
559 	 * the terminating 0 pointer.  Additional spots may be needed
560 	 * to pass along -r and -f to the host mailer.
561 	 */
562 
563 	extra = 2;
564 	if (rflag != NOSTR)
565 		extra += 2;
566 #ifdef SENDMAIL
567 	extra++;
568 	metoo = value("metoo") != NOSTR;
569 	if (metoo)
570 		extra++;
571 	verbose = value("verbose") != NOSTR;
572 	if (verbose)
573 		extra++;
574 #endif SENDMAIL
575 	if (hflag)
576 		extra += 2;
577 	top = (char **) salloc((t + extra) * sizeof cp);
578 	ap = top;
579 	*ap++ = "send-mail";
580 	if (rflag != NOSTR) {
581 		*ap++ = "-r";
582 		*ap++ = rflag;
583 	}
584 #ifdef SENDMAIL
585 	*ap++ = "-i";
586 	if (metoo)
587 		*ap++ = "-m";
588 	if (verbose)
589 		*ap++ = "-v";
590 #endif SENDMAIL
591 	if (hflag) {
592 		*ap++ = "-h";
593 		sprintf(hbuf, "%d", hflag);
594 		*ap++ = savestr(hbuf);
595 	}
596 	while (n != NIL) {
597 		if (n->n_type & GDEL) {
598 			n = n->n_flink;
599 			continue;
600 		}
601 		*ap++ = n->n_name;
602 		n = n->n_flink;
603 	}
604 	*ap = NOSTR;
605 	return(top);
606 }
607 
608 /*
609  * See if the user named himself as a destination
610  * for outgoing mail.  If so, set the global flag
611  * selfsent so that we avoid removing his mailbox.
612  */
613 
614 mechk(names)
615 	struct name *names;
616 {
617 	register struct name *np;
618 
619 	for (np = names; np != NIL; np = np->n_flink)
620 		if ((np->n_type & GDEL) == 0 && equal(np->n_name, myname)) {
621 			selfsent++;
622 			return;
623 		}
624 }
625 
626 /*
627  * Remove all of the duplicates from the passed name list by
628  * insertion sorting them, then checking for dups.
629  * Return the head of the new list.
630  */
631 
632 struct name *
633 elide(names)
634 	struct name *names;
635 {
636 	register struct name *np, *t, *new;
637 	struct name *x;
638 
639 	if (names == NIL)
640 		return(NIL);
641 	new = names;
642 	np = names;
643 	np = np->n_flink;
644 	if (np != NIL)
645 		np->n_blink = NIL;
646 	new->n_flink = NIL;
647 	while (np != NIL) {
648 		t = new;
649 		while (nstrcmp(t->n_name, np->n_name) < 0) {
650 			if (t->n_flink == NIL)
651 				break;
652 			t = t->n_flink;
653 		}
654 
655 		/*
656 		 * If we ran out of t's, put the new entry after
657 		 * the current value of t.
658 		 */
659 
660 		if (nstrcmp(t->n_name, np->n_name) < 0) {
661 			t->n_flink = np;
662 			np->n_blink = t;
663 			t = np;
664 			np = np->n_flink;
665 			t->n_flink = NIL;
666 			continue;
667 		}
668 
669 		/*
670 		 * Otherwise, put the new entry in front of the
671 		 * current t.  If at the front of the list,
672 		 * the new guy becomes the new head of the list.
673 		 */
674 
675 		if (t == new) {
676 			t = np;
677 			np = np->n_flink;
678 			t->n_flink = new;
679 			new->n_blink = t;
680 			t->n_blink = NIL;
681 			new = t;
682 			continue;
683 		}
684 
685 		/*
686 		 * The normal case -- we are inserting into the
687 		 * middle of the list.
688 		 */
689 
690 		x = np;
691 		np = np->n_flink;
692 		x->n_flink = t;
693 		x->n_blink = t->n_blink;
694 		t->n_blink->n_flink = x;
695 		t->n_blink = x;
696 	}
697 
698 	/*
699 	 * Now the list headed up by new is sorted.
700 	 * Go through it and remove duplicates.
701 	 */
702 
703 	np = new;
704 	while (np != NIL) {
705 		t = np;
706 		while (t->n_flink!=NIL &&
707 		    icequal(np->n_name,t->n_flink->n_name))
708 			t = t->n_flink;
709 		if (t == np || t == NIL) {
710 			np = np->n_flink;
711 			continue;
712 		}
713 
714 		/*
715 		 * Now t points to the last entry with the same name
716 		 * as np.  Make np point beyond t.
717 		 */
718 
719 		np->n_flink = t->n_flink;
720 		if (t->n_flink != NIL)
721 			t->n_flink->n_blink = np;
722 		np = np->n_flink;
723 	}
724 	return(new);
725 }
726 
727 /*
728  * Version of strcmp which ignores case differences.
729  */
730 
731 nstrcmp(s1, s2)
732 	register char *s1, *s2;
733 {
734 	register int c1, c2;
735 
736 	do {
737 		c1 = *s1++;
738 		c2 = *s2++;
739 	} while (c1 && c1 == c2);
740 	return(c1 - c2);
741 }
742 
743 /*
744  * Put another node onto a list of names and return
745  * the list.
746  */
747 
748 struct name *
749 put(list, node)
750 	struct name *list, *node;
751 {
752 	node->n_flink = list;
753 	node->n_blink = NIL;
754 	if (list != NIL)
755 		list->n_blink = node;
756 	return(node);
757 }
758 
759 /*
760  * Determine the number of elements in
761  * a name list and return it.
762  */
763 
764 count(np)
765 	register struct name *np;
766 {
767 	register int c = 0;
768 
769 	while (np != NIL) {
770 		c++;
771 		np = np->n_flink;
772 	}
773 	return(c);
774 }
775 
776 cmpdomain(name, dname)
777 	register char *name, *dname;
778 {
779 	char buf[BUFSIZ];
780 
781 	strcpy(buf, dname);
782 	buf[strlen(name)] = '\0';
783 	return(icequal(name, buf));
784 }
785 
786 /*
787  * Delete the given name from a namelist, using the passed
788  * function to compare the names.
789  */
790 struct name *
791 delname(np, name, cmpfun)
792 	register struct name *np;
793 	char name[];
794 	int (* cmpfun)();
795 {
796 	register struct name *p;
797 
798 	for (p = np; p != NIL; p = p->n_flink)
799 		if ((* cmpfun)(p->n_name, name)) {
800 			if (p->n_blink == NIL) {
801 				if (p->n_flink != NIL)
802 					p->n_flink->n_blink = NIL;
803 				np = p->n_flink;
804 				continue;
805 			}
806 			if (p->n_flink == NIL) {
807 				if (p->n_blink != NIL)
808 					p->n_blink->n_flink = NIL;
809 				continue;
810 			}
811 			p->n_blink->n_flink = p->n_flink;
812 			p->n_flink->n_blink = p->n_blink;
813 		}
814 	return(np);
815 }
816 
817 /*
818  * Call the given routine on each element of the name
819  * list, replacing said value if need be.
820  */
821 
822 mapf(np, from)
823 	register struct name *np;
824 	char *from;
825 {
826 	register struct name *p;
827 
828 	for (p = np; p != NIL; p = p->n_flink)
829 		p->n_name = netmap(p->n_name, from);
830 }
831 
832 /*
833  * Pretty print a name list
834  * Uncomment it if you need it.
835  */
836 
837 prettyprint(name)
838 	struct name *name;
839 {
840 	register struct name *np;
841 
842 	np = name;
843 	while (np != NIL) {
844 		fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
845 		np = np->n_flink;
846 	}
847 	fprintf(stderr, "\n");
848 }
849