xref: /original-bsd/usr.bin/mail/names.c (revision 01e9891b)
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.5 02/26/82";
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 	if (*name == '+')
398 		return(1);
399 	for (cp = name; *cp; cp++) {
400 		if (*cp == '.')
401 			continue;
402 		if (any(*cp, metanet))
403 			return(0);
404 		if (*cp == '/')
405 			return(1);
406 	}
407 	return(0);
408 }
409 
410 /*
411  * Map all of the aliased users in the invoker's mailrc
412  * file and insert them into the list.
413  * Changed after all these months of service to recursively
414  * expand names (2/14/80).
415  */
416 
417 struct name *
418 usermap(names)
419 	struct name *names;
420 {
421 	register struct name *new, *np, *cp;
422 	struct name *getto;
423 	struct grouphead *gh;
424 	register int metoo;
425 
426 	new = NIL;
427 	np = names;
428 	getto = NIL;
429 	metoo = (value("metoo") != NOSTR);
430 	while (np != NIL) {
431 		if (np->n_name[0] == '\\') {
432 			cp = np->n_flink;
433 			new = put(new, np);
434 			np = cp;
435 			continue;
436 		}
437 		gh = findgroup(np->n_name);
438 		cp = np->n_flink;
439 		if (gh != NOGRP)
440 			new = gexpand(new, gh, metoo, np->n_type);
441 		else
442 			new = put(new, np);
443 		np = cp;
444 	}
445 	return(new);
446 }
447 
448 /*
449  * Recursively expand a group name.  We limit the expansion to some
450  * fixed level to keep things from going haywire.
451  * Direct recursion is not expanded for convenience.
452  */
453 
454 struct name *
455 gexpand(nlist, gh, metoo, ntype)
456 	struct name *nlist;
457 	struct grouphead *gh;
458 {
459 	struct group *gp;
460 	struct grouphead *ngh;
461 	struct name *np;
462 	static int depth;
463 	char *cp;
464 
465 	if (depth > MAXEXP) {
466 		printf("Expanding alias to depth larger than %d\n", MAXEXP);
467 		return(nlist);
468 	}
469 	depth++;
470 	for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
471 		cp = gp->ge_name;
472 		if (*cp == '\\')
473 			goto quote;
474 		if (strcmp(cp, gh->g_name) == 0)
475 			goto quote;
476 		if ((ngh = findgroup(cp)) != NOGRP) {
477 			nlist = gexpand(nlist, ngh, metoo, ntype);
478 			continue;
479 		}
480 quote:
481 		np = nalloc(cp);
482 		np->n_type = ntype;
483 		/*
484 		 * At this point should allow to expand
485 		 * to self if only person in group
486 		 */
487 		if (gp == gh->g_list && gp->ge_link == NOGE)
488 			goto skip;
489 		if (!metoo && strcmp(cp, myname) == 0)
490 			np->n_type |= GDEL;
491 skip:
492 		nlist = put(nlist, np);
493 	}
494 	depth--;
495 	return(nlist);
496 }
497 
498 
499 
500 /*
501  * Compute the length of the passed name list and
502  * return it.
503  */
504 
505 lengthof(name)
506 	struct name *name;
507 {
508 	register struct name *np;
509 	register int c;
510 
511 	for (c = 0, np = name; np != NIL; c++, np = np->n_flink)
512 		;
513 	return(c);
514 }
515 
516 /*
517  * Concatenate the two passed name lists, return the result.
518  */
519 
520 struct name *
521 cat(n1, n2)
522 	struct name *n1, *n2;
523 {
524 	register struct name *tail;
525 
526 	if (n1 == NIL)
527 		return(n2);
528 	if (n2 == NIL)
529 		return(n1);
530 	tail = tailof(n1);
531 	tail->n_flink = n2;
532 	n2->n_blink = tail;
533 	return(n1);
534 }
535 
536 /*
537  * Unpack the name list onto a vector of strings.
538  * Return an error if the name list won't fit.
539  */
540 
541 char **
542 unpack(np)
543 	struct name *np;
544 {
545 	register char **ap, **top;
546 	register struct name *n;
547 	char *cp;
548 	char hbuf[10];
549 	int t, extra, metoo;
550 
551 	n = np;
552 	if ((t = lengthof(n)) == 0)
553 		panic("No names to unpack");
554 
555 	/*
556 	 * Compute the number of extra arguments we will need.
557 	 * We need at least two extra -- one for "mail" and one for
558 	 * the terminating 0 pointer.  Additional spots may be needed
559 	 * to pass along -r and -f to the host mailer.
560 	 */
561 
562 	extra = 2;
563 	if (rflag != NOSTR)
564 		extra += 2;
565 #ifdef DELIVERMAIL
566 	extra++;
567 	metoo = value("metoo") != NOSTR;
568 	if (metoo)
569 		extra++;
570 #endif DELIVERMAIL
571 	if (hflag)
572 		extra += 2;
573 	top = (char **) salloc((t + extra) * sizeof cp);
574 	ap = top;
575 	*ap++ = "send-mail";
576 	if (rflag != NOSTR) {
577 		*ap++ = "-r";
578 		*ap++ = rflag;
579 	}
580 #ifdef DELIVERMAIL
581 	*ap++ = "-i";
582 	if (metoo)
583 		*ap++ = "-m";
584 #endif DELIVERMAIL
585 	if (hflag) {
586 		*ap++ = "-h";
587 		sprintf(hbuf, "%d", hflag);
588 		*ap++ = savestr(hbuf);
589 	}
590 	while (n != NIL) {
591 		if (n->n_type & GDEL) {
592 			n = n->n_flink;
593 			continue;
594 		}
595 		*ap++ = n->n_name;
596 		n = n->n_flink;
597 	}
598 	*ap = NOSTR;
599 	return(top);
600 }
601 
602 /*
603  * See if the user named himself as a destination
604  * for outgoing mail.  If so, set the global flag
605  * selfsent so that we avoid removing his mailbox.
606  */
607 
608 mechk(names)
609 	struct name *names;
610 {
611 	register struct name *np;
612 
613 	for (np = names; np != NIL; np = np->n_flink)
614 		if ((np->n_type & GDEL) == 0 && equal(np->n_name, myname)) {
615 			selfsent++;
616 			return;
617 		}
618 }
619 
620 /*
621  * Remove all of the duplicates from the passed name list by
622  * insertion sorting them, then checking for dups.
623  * Return the head of the new list.
624  */
625 
626 struct name *
627 elide(names)
628 	struct name *names;
629 {
630 	register struct name *np, *t, *new;
631 	struct name *x;
632 
633 	if (names == NIL)
634 		return(NIL);
635 	new = names;
636 	np = names;
637 	np = np->n_flink;
638 	if (np != NIL)
639 		np->n_blink = NIL;
640 	new->n_flink = NIL;
641 	while (np != NIL) {
642 		t = new;
643 		while (nstrcmp(t->n_name, np->n_name) < 0) {
644 			if (t->n_flink == NIL)
645 				break;
646 			t = t->n_flink;
647 		}
648 
649 		/*
650 		 * If we ran out of t's, put the new entry after
651 		 * the current value of t.
652 		 */
653 
654 		if (nstrcmp(t->n_name, np->n_name) < 0) {
655 			t->n_flink = np;
656 			np->n_blink = t;
657 			t = np;
658 			np = np->n_flink;
659 			t->n_flink = NIL;
660 			continue;
661 		}
662 
663 		/*
664 		 * Otherwise, put the new entry in front of the
665 		 * current t.  If at the front of the list,
666 		 * the new guy becomes the new head of the list.
667 		 */
668 
669 		if (t == new) {
670 			t = np;
671 			np = np->n_flink;
672 			t->n_flink = new;
673 			new->n_blink = t;
674 			t->n_blink = NIL;
675 			new = t;
676 			continue;
677 		}
678 
679 		/*
680 		 * The normal case -- we are inserting into the
681 		 * middle of the list.
682 		 */
683 
684 		x = np;
685 		np = np->n_flink;
686 		x->n_flink = t;
687 		x->n_blink = t->n_blink;
688 		t->n_blink->n_flink = x;
689 		t->n_blink = x;
690 	}
691 
692 	/*
693 	 * Now the list headed up by new is sorted.
694 	 * Go through it and remove duplicates.
695 	 */
696 
697 	np = new;
698 	while (np != NIL) {
699 		t = np;
700 		while (t->n_flink!=NIL &&
701 		    icequal(np->n_name,t->n_flink->n_name))
702 			t = t->n_flink;
703 		if (t == np || t == NIL) {
704 			np = np->n_flink;
705 			continue;
706 		}
707 
708 		/*
709 		 * Now t points to the last entry with the same name
710 		 * as np.  Make np point beyond t.
711 		 */
712 
713 		np->n_flink = t->n_flink;
714 		if (t->n_flink != NIL)
715 			t->n_flink->n_blink = np;
716 		np = np->n_flink;
717 	}
718 	return(new);
719 }
720 
721 /*
722  * Version of strcmp which ignores case differences.
723  */
724 
725 nstrcmp(s1, s2)
726 	register char *s1, *s2;
727 {
728 	register int c1, c2;
729 
730 	do {
731 		c1 = *s1++;
732 		c2 = *s2++;
733 	} while (c1 && c1 == c2);
734 	return(c1 - c2);
735 }
736 
737 /*
738  * Put another node onto a list of names and return
739  * the list.
740  */
741 
742 struct name *
743 put(list, node)
744 	struct name *list, *node;
745 {
746 	node->n_flink = list;
747 	node->n_blink = NIL;
748 	if (list != NIL)
749 		list->n_blink = node;
750 	return(node);
751 }
752 
753 /*
754  * Determine the number of elements in
755  * a name list and return it.
756  */
757 
758 count(np)
759 	register struct name *np;
760 {
761 	register int c = 0;
762 
763 	while (np != NIL) {
764 		c++;
765 		np = np->n_flink;
766 	}
767 	return(c);
768 }
769 
770 /*
771  * Delete the given name from a namelist.
772  */
773 struct name *
774 delname(np, name)
775 	register struct name *np;
776 	char name[];
777 {
778 	register struct name *p;
779 
780 	for (p = np; p != NIL; p = p->n_flink)
781 		if (icequal(p->n_name, name)) {
782 			if (p->n_blink == NIL) {
783 				if (p->n_flink != NIL)
784 					p->n_flink->n_blink = NIL;
785 				np = p->n_flink;
786 				continue;
787 			}
788 			if (p->n_flink == NIL) {
789 				if (p->n_blink != NIL)
790 					p->n_blink->n_flink = NIL;
791 				continue;
792 			}
793 			p->n_blink->n_flink = p->n_flink;
794 			p->n_flink->n_blink = p->n_blink;
795 		}
796 	return(np);
797 }
798 
799 /*
800  * Call the given routine on each element of the name
801  * list, replacing said value if need be.
802  */
803 
804 mapf(np, from)
805 	register struct name *np;
806 	char *from;
807 {
808 	register struct name *p;
809 
810 	for (p = np; p != NIL; p = p->n_flink)
811 		p->n_name = netmap(p->n_name, from);
812 }
813 
814 /*
815  * Pretty print a name list
816  * Uncomment it if you need it.
817  */
818 
819 prettyprint(name)
820 	struct name *name;
821 {
822 	register struct name *np;
823 
824 	np = name;
825 	while (np != NIL) {
826 		fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
827 		np = np->n_flink;
828 	}
829 	fprintf(stderr, "\n");
830 }
831