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