names.c revision 1590
1205147Sedwin/*
2205147Sedwin * Copyright (c) 1980, 1993
3205147Sedwin *	The Regents of the University of California.  All rights reserved.
4205147Sedwin *
5205147Sedwin * Redistribution and use in source and binary forms, with or without
6205147Sedwin * modification, are permitted provided that the following conditions
7205147Sedwin * are met:
8205147Sedwin * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char sccsid[] = "@(#)names.c	8.1 (Berkeley) 6/6/93";
36#endif /* not lint */
37
38/*
39 * Mail -- a mail program
40 *
41 * Handle name lists.
42 */
43
44#include "rcv.h"
45#include <fcntl.h>
46#include "extern.h"
47
48/*
49 * Allocate a single element of a name list,
50 * initialize its name field to the passed
51 * name and return it.
52 */
53struct name *
54nalloc(str, ntype)
55	char str[];
56	int ntype;
57{
58	register struct name *np;
59
60	np = (struct name *) salloc(sizeof *np);
61	np->n_flink = NIL;
62	np->n_blink = NIL;
63	np->n_type = ntype;
64	np->n_name = savestr(str);
65	return(np);
66}
67
68/*
69 * Find the tail of a list and return it.
70 */
71struct name *
72tailof(name)
73	struct name *name;
74{
75	register struct name *np;
76
77	np = name;
78	if (np == NIL)
79		return(NIL);
80	while (np->n_flink != NIL)
81		np = np->n_flink;
82	return(np);
83}
84
85/*
86 * Extract a list of names from a line,
87 * and make a list of names from it.
88 * Return the list or NIL if none found.
89 */
90struct name *
91extract(line, ntype)
92	char line[];
93	int ntype;
94{
95	register char *cp;
96	register struct name *top, *np, *t;
97	char nbuf[BUFSIZ];
98
99	if (line == NOSTR || *line == '\0')
100		return NIL;
101	top = NIL;
102	np = NIL;
103	cp = line;
104	while ((cp = yankword(cp, nbuf)) != NOSTR) {
105		t = nalloc(nbuf, ntype);
106		if (top == NIL)
107			top = t;
108		else
109			np->n_flink = t;
110		t->n_blink = np;
111		np = t;
112	}
113	return top;
114}
115
116/*
117 * Turn a list of names into a string of the same names.
118 */
119char *
120detract(np, ntype)
121	register struct name *np;
122	int ntype;
123{
124	register int s;
125	register char *cp, *top;
126	register struct name *p;
127	register int comma;
128
129	comma = ntype & GCOMMA;
130	if (np == NIL)
131		return(NOSTR);
132	ntype &= ~GCOMMA;
133	s = 0;
134	if (debug && comma)
135		fprintf(stderr, "detract asked to insert commas\n");
136	for (p = np; p != NIL; p = p->n_flink) {
137		if (ntype && (p->n_type & GMASK) != ntype)
138			continue;
139		s += strlen(p->n_name) + 1;
140		if (comma)
141			s++;
142	}
143	if (s == 0)
144		return(NOSTR);
145	s += 2;
146	top = salloc(s);
147	cp = top;
148	for (p = np; p != NIL; p = p->n_flink) {
149		if (ntype && (p->n_type & GMASK) != ntype)
150			continue;
151		cp = copy(p->n_name, cp);
152		if (comma && p->n_flink != NIL)
153			*cp++ = ',';
154		*cp++ = ' ';
155	}
156	*--cp = 0;
157	if (comma && *--cp == ',')
158		*cp = 0;
159	return(top);
160}
161
162/*
163 * Grab a single word (liberal word)
164 * Throw away things between ()'s, and take anything between <>.
165 */
166char *
167yankword(ap, wbuf)
168	char *ap, wbuf[];
169{
170	register char *cp, *cp2;
171
172	cp = ap;
173	for (;;) {
174		if (*cp == '\0')
175			return NOSTR;
176		if (*cp == '(') {
177			register int nesting = 0;
178
179			while (*cp != '\0') {
180				switch (*cp++) {
181				case '(':
182					nesting++;
183					break;
184				case ')':
185					--nesting;
186					break;
187				}
188				if (nesting <= 0)
189					break;
190			}
191		} else if (*cp == ' ' || *cp == '\t' || *cp == ',')
192			cp++;
193		else
194			break;
195	}
196	if (*cp ==  '<')
197		for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
198			;
199	else
200		for (cp2 = wbuf; *cp && !index(" \t,(", *cp); *cp2++ = *cp++)
201			;
202	*cp2 = '\0';
203	return cp;
204}
205
206/*
207 * For each recipient in the passed name list with a /
208 * in the name, append the message to the end of the named file
209 * and remove him from the recipient list.
210 *
211 * Recipients whose name begins with | are piped through the given
212 * program and removed.
213 */
214struct name *
215outof(names, fo, hp)
216	struct name *names;
217	FILE *fo;
218	struct header *hp;
219{
220	register int c;
221	register struct name *np, *top;
222	time_t now, time();
223	char *date, *fname, *ctime();
224	FILE *fout, *fin;
225	int ispipe;
226	extern char tempEdit[];
227
228	top = names;
229	np = names;
230	(void) time(&now);
231	date = ctime(&now);
232	while (np != NIL) {
233		if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
234			np = np->n_flink;
235			continue;
236		}
237		ispipe = np->n_name[0] == '|';
238		if (ispipe)
239			fname = np->n_name+1;
240		else
241			fname = expand(np->n_name);
242
243		/*
244		 * See if we have copied the complete message out yet.
245		 * If not, do so.
246		 */
247
248		if (image < 0) {
249			if ((fout = Fopen(tempEdit, "a")) == NULL) {
250				perror(tempEdit);
251				senderr++;
252				goto cant;
253			}
254			image = open(tempEdit, 2);
255			(void) unlink(tempEdit);
256			if (image < 0) {
257				perror(tempEdit);
258				senderr++;
259				(void) Fclose(fout);
260				goto cant;
261			}
262			(void) fcntl(image, F_SETFD, 1);
263			fprintf(fout, "From %s %s", myname, date);
264			puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
265			while ((c = getc(fo)) != EOF)
266				(void) putc(c, fout);
267			rewind(fo);
268			(void) putc('\n', fout);
269			(void) fflush(fout);
270			if (ferror(fout))
271				perror(tempEdit);
272			(void) Fclose(fout);
273		}
274
275		/*
276		 * Now either copy "image" to the desired file
277		 * or give it as the standard input to the desired
278		 * program as appropriate.
279		 */
280
281		if (ispipe) {
282			int pid;
283			char *shell;
284
285			/*
286			 * XXX
287			 * We can't really reuse the same image file,
288			 * because multiple piped recipients will
289			 * share the same lseek location and trample
290			 * on one another.
291			 */
292			if ((shell = value("SHELL")) == NOSTR)
293				shell = _PATH_CSHELL;
294			pid = start_command(shell, sigmask(SIGHUP)|
295					sigmask(SIGINT)|sigmask(SIGQUIT),
296				image, -1, "-c", fname, NOSTR);
297			if (pid < 0) {
298				senderr++;
299				goto cant;
300			}
301			free_child(pid);
302		} else {
303			int f;
304			if ((fout = Fopen(fname, "a")) == NULL) {
305				perror(fname);
306				senderr++;
307				goto cant;
308			}
309			if ((f = dup(image)) < 0) {
310				perror("dup");
311				fin = NULL;
312			} else
313				fin = Fdopen(f, "r");
314			if (fin == NULL) {
315				fprintf(stderr, "Can't reopen image\n");
316				(void) Fclose(fout);
317				senderr++;
318				goto cant;
319			}
320			rewind(fin);
321			while ((c = getc(fin)) != EOF)
322				(void) putc(c, fout);
323			if (ferror(fout))
324				senderr++, perror(fname);
325			(void) Fclose(fout);
326			(void) Fclose(fin);
327		}
328cant:
329		/*
330		 * In days of old we removed the entry from the
331		 * the list; now for sake of header expansion
332		 * we leave it in and mark it as deleted.
333		 */
334		np->n_type |= GDEL;
335		np = np->n_flink;
336	}
337	if (image >= 0) {
338		(void) close(image);
339		image = -1;
340	}
341	return(top);
342}
343
344/*
345 * Determine if the passed address is a local "send to file" address.
346 * If any of the network metacharacters precedes any slashes, it can't
347 * be a filename.  We cheat with .'s to allow path names like ./...
348 */
349int
350isfileaddr(name)
351	char *name;
352{
353	register char *cp;
354
355	if (*name == '+')
356		return 1;
357	for (cp = name; *cp; cp++) {
358		if (*cp == '!' || *cp == '%' || *cp == '@')
359			return 0;
360		if (*cp == '/')
361			return 1;
362	}
363	return 0;
364}
365
366/*
367 * Map all of the aliased users in the invoker's mailrc
368 * file and insert them into the list.
369 * Changed after all these months of service to recursively
370 * expand names (2/14/80).
371 */
372
373struct name *
374usermap(names)
375	struct name *names;
376{
377	register struct name *new, *np, *cp;
378	struct grouphead *gh;
379	register int metoo;
380
381	new = NIL;
382	np = names;
383	metoo = (value("metoo") != NOSTR);
384	while (np != NIL) {
385		if (np->n_name[0] == '\\') {
386			cp = np->n_flink;
387			new = put(new, np);
388			np = cp;
389			continue;
390		}
391		gh = findgroup(np->n_name);
392		cp = np->n_flink;
393		if (gh != NOGRP)
394			new = gexpand(new, gh, metoo, np->n_type);
395		else
396			new = put(new, np);
397		np = cp;
398	}
399	return(new);
400}
401
402/*
403 * Recursively expand a group name.  We limit the expansion to some
404 * fixed level to keep things from going haywire.
405 * Direct recursion is not expanded for convenience.
406 */
407
408struct name *
409gexpand(nlist, gh, metoo, ntype)
410	struct name *nlist;
411	struct grouphead *gh;
412	int metoo, ntype;
413{
414	struct group *gp;
415	struct grouphead *ngh;
416	struct name *np;
417	static int depth;
418	char *cp;
419
420	if (depth > MAXEXP) {
421		printf("Expanding alias to depth larger than %d\n", MAXEXP);
422		return(nlist);
423	}
424	depth++;
425	for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
426		cp = gp->ge_name;
427		if (*cp == '\\')
428			goto quote;
429		if (strcmp(cp, gh->g_name) == 0)
430			goto quote;
431		if ((ngh = findgroup(cp)) != NOGRP) {
432			nlist = gexpand(nlist, ngh, metoo, ntype);
433			continue;
434		}
435quote:
436		np = nalloc(cp, ntype);
437		/*
438		 * At this point should allow to expand
439		 * to self if only person in group
440		 */
441		if (gp == gh->g_list && gp->ge_link == NOGE)
442			goto skip;
443		if (!metoo && strcmp(cp, myname) == 0)
444			np->n_type |= GDEL;
445skip:
446		nlist = put(nlist, np);
447	}
448	depth--;
449	return(nlist);
450}
451
452/*
453 * Concatenate the two passed name lists, return the result.
454 */
455struct name *
456cat(n1, n2)
457	struct name *n1, *n2;
458{
459	register struct name *tail;
460
461	if (n1 == NIL)
462		return(n2);
463	if (n2 == NIL)
464		return(n1);
465	tail = tailof(n1);
466	tail->n_flink = n2;
467	n2->n_blink = tail;
468	return(n1);
469}
470
471/*
472 * Unpack the name list onto a vector of strings.
473 * Return an error if the name list won't fit.
474 */
475char **
476unpack(np)
477	struct name *np;
478{
479	register char **ap, **top;
480	register struct name *n;
481	int t, extra, metoo, verbose;
482
483	n = np;
484	if ((t = count(n)) == 0)
485		panic("No names to unpack");
486	/*
487	 * Compute the number of extra arguments we will need.
488	 * We need at least two extra -- one for "mail" and one for
489	 * the terminating 0 pointer.  Additional spots may be needed
490	 * to pass along -f to the host mailer.
491	 */
492	extra = 2;
493	extra++;
494	metoo = value("metoo") != NOSTR;
495	if (metoo)
496		extra++;
497	verbose = value("verbose") != NOSTR;
498	if (verbose)
499		extra++;
500	top = (char **) salloc((t + extra) * sizeof *top);
501	ap = top;
502	*ap++ = "send-mail";
503	*ap++ = "-i";
504	if (metoo)
505		*ap++ = "-m";
506	if (verbose)
507		*ap++ = "-v";
508	for (; n != NIL; n = n->n_flink)
509		if ((n->n_type & GDEL) == 0)
510			*ap++ = n->n_name;
511	*ap = NOSTR;
512	return(top);
513}
514
515/*
516 * Remove all of the duplicates from the passed name list by
517 * insertion sorting them, then checking for dups.
518 * Return the head of the new list.
519 */
520struct name *
521elide(names)
522	struct name *names;
523{
524	register struct name *np, *t, *new;
525	struct name *x;
526
527	if (names == NIL)
528		return(NIL);
529	new = names;
530	np = names;
531	np = np->n_flink;
532	if (np != NIL)
533		np->n_blink = NIL;
534	new->n_flink = NIL;
535	while (np != NIL) {
536		t = new;
537		while (strcasecmp(t->n_name, np->n_name) < 0) {
538			if (t->n_flink == NIL)
539				break;
540			t = t->n_flink;
541		}
542
543		/*
544		 * If we ran out of t's, put the new entry after
545		 * the current value of t.
546		 */
547
548		if (strcasecmp(t->n_name, np->n_name) < 0) {
549			t->n_flink = np;
550			np->n_blink = t;
551			t = np;
552			np = np->n_flink;
553			t->n_flink = NIL;
554			continue;
555		}
556
557		/*
558		 * Otherwise, put the new entry in front of the
559		 * current t.  If at the front of the list,
560		 * the new guy becomes the new head of the list.
561		 */
562
563		if (t == new) {
564			t = np;
565			np = np->n_flink;
566			t->n_flink = new;
567			new->n_blink = t;
568			t->n_blink = NIL;
569			new = t;
570			continue;
571		}
572
573		/*
574		 * The normal case -- we are inserting into the
575		 * middle of the list.
576		 */
577
578		x = np;
579		np = np->n_flink;
580		x->n_flink = t;
581		x->n_blink = t->n_blink;
582		t->n_blink->n_flink = x;
583		t->n_blink = x;
584	}
585
586	/*
587	 * Now the list headed up by new is sorted.
588	 * Go through it and remove duplicates.
589	 */
590
591	np = new;
592	while (np != NIL) {
593		t = np;
594		while (t->n_flink != NIL &&
595		       strcasecmp(np->n_name, t->n_flink->n_name) == 0)
596			t = t->n_flink;
597		if (t == np || t == NIL) {
598			np = np->n_flink;
599			continue;
600		}
601
602		/*
603		 * Now t points to the last entry with the same name
604		 * as np.  Make np point beyond t.
605		 */
606
607		np->n_flink = t->n_flink;
608		if (t->n_flink != NIL)
609			t->n_flink->n_blink = np;
610		np = np->n_flink;
611	}
612	return(new);
613}
614
615/*
616 * Put another node onto a list of names and return
617 * the list.
618 */
619struct name *
620put(list, node)
621	struct name *list, *node;
622{
623	node->n_flink = list;
624	node->n_blink = NIL;
625	if (list != NIL)
626		list->n_blink = node;
627	return(node);
628}
629
630/*
631 * Determine the number of undeleted elements in
632 * a name list and return it.
633 */
634int
635count(np)
636	register struct name *np;
637{
638	register int c;
639
640	for (c = 0; np != NIL; np = np->n_flink)
641		if ((np->n_type & GDEL) == 0)
642			c++;
643	return c;
644}
645
646/*
647 * Delete the given name from a namelist.
648 */
649struct name *
650delname(np, name)
651	register struct name *np;
652	char name[];
653{
654	register struct name *p;
655
656	for (p = np; p != NIL; p = p->n_flink)
657		if (strcasecmp(p->n_name, name) == 0) {
658			if (p->n_blink == NIL) {
659				if (p->n_flink != NIL)
660					p->n_flink->n_blink = NIL;
661				np = p->n_flink;
662				continue;
663			}
664			if (p->n_flink == NIL) {
665				if (p->n_blink != NIL)
666					p->n_blink->n_flink = NIL;
667				continue;
668			}
669			p->n_blink->n_flink = p->n_flink;
670			p->n_flink->n_blink = p->n_blink;
671		}
672	return np;
673}
674
675/*
676 * Pretty print a name list
677 * Uncomment it if you need it.
678 */
679
680/*
681void
682prettyprint(name)
683	struct name *name;
684{
685	register struct name *np;
686
687	np = name;
688	while (np != NIL) {
689		fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
690		np = np->n_flink;
691	}
692	fprintf(stderr, "\n");
693}
694*/
695