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