names.c revision 126415
155714Skris/*
255714Skris * Copyright (c) 1980, 1993
355714Skris *	The Regents of the University of California.  All rights reserved.
455714Skris *
555714Skris * Redistribution and use in source and binary forms, with or without
655714Skris * modification, are permitted provided that the following conditions
755714Skris * are met:
8280297Sjkim * 1. Redistributions of source code must retain the above copyright
955714Skris *    notice, this list of conditions and the following disclaimer.
1055714Skris * 2. Redistributions in binary form must reproduce the above copyright
1155714Skris *    notice, this list of conditions and the following disclaimer in the
1255714Skris *    documentation and/or other materials provided with the distribution.
1355714Skris * 3. All advertising materials mentioning features or use of this software
1455714Skris *    must display the following acknowledgement:
15280297Sjkim *	This product includes software developed by the University of
1655714Skris *	California, Berkeley and its contributors.
1755714Skris * 4. Neither the name of the University nor the names of its contributors
1855714Skris *    may be used to endorse or promote products derived from this software
1955714Skris *    without specific prior written permission.
2055714Skris *
2155714Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22280297Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2555714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155714Skris * SUCH DAMAGE.
3255714Skris */
3355714Skris
3455714Skris#ifndef lint
3555714Skris#if 0
3655714Skrisstatic char sccsid[] = "@(#)names.c	8.1 (Berkeley) 6/6/93";
37280297Sjkim#endif
3855714Skris#endif /* not lint */
3955714Skris#include <sys/cdefs.h>
40280297Sjkim__FBSDID("$FreeBSD: head/usr.bin/mail/names.c 126415 2004-02-29 20:44:44Z mikeh $");
4155714Skris
4255714Skris/*
4355714Skris * Mail -- a mail program
4455714Skris *
4555714Skris * Handle name lists.
4655714Skris */
4755714Skris
4855714Skris#include "rcv.h"
4955714Skris#include <fcntl.h>
5055714Skris#include "extern.h"
5155714Skris
52280297Sjkim/*
5355714Skris * Allocate a single element of a name list,
5455714Skris * initialize its name field to the passed
5555714Skris * name and return it.
5655714Skris */
5755714Skrisstruct name *
5855714Skrisnalloc(str, ntype)
5955714Skris	char str[];
6055714Skris	int ntype;
6155714Skris{
6255714Skris	struct name *np;
6355714Skris
64238405Sjkim	np = (struct name *)salloc(sizeof(*np));
6555714Skris	np->n_flink = NULL;
66280297Sjkim	np->n_blink = NULL;
67280297Sjkim	np->n_type = ntype;
68194206Ssimon	np->n_name = savestr(str);
69280297Sjkim	return (np);
70280297Sjkim}
71280297Sjkim
72280297Sjkim/*
73280297Sjkim * Find the tail of a list and return it.
74194206Ssimon */
75280297Sjkimstruct name *
76280297Sjkimtailof(name)
7755714Skris	struct name *name;
78194206Ssimon{
79280297Sjkim	struct name *np;
80194206Ssimon
81280297Sjkim	np = name;
82280297Sjkim	if (np == NULL)
83280297Sjkim		return (NULL);
84280297Sjkim	while (np->n_flink != NULL)
85280297Sjkim		np = np->n_flink;
86194206Ssimon	return (np);
87280297Sjkim}
88280297Sjkim
8955714Skris/*
90238405Sjkim * Extract a list of names from a line,
91280297Sjkim * and make a list of names from it.
92280297Sjkim * Return the list or NULL if none found.
93280297Sjkim */
94280297Sjkimstruct name *
95280297Sjkimextract(line, ntype)
96280297Sjkim	char line[];
97280297Sjkim	int ntype;
98280297Sjkim{
99280297Sjkim	char *cp, *nbuf;
100238405Sjkim	struct name *top, *np, *t;
101238405Sjkim
102280297Sjkim	if (line == NULL || *line == '\0')
103280297Sjkim		return (NULL);
104280297Sjkim	if ((nbuf = malloc(strlen(line) + 1)) == NULL)
105280297Sjkim		err(1, "Out of memory");
106280297Sjkim	top = NULL;
107280297Sjkim	np = NULL;
108280297Sjkim	cp = line;
109280297Sjkim	while ((cp = yankword(cp, nbuf)) != NULL) {
110280297Sjkim		t = nalloc(nbuf, ntype);
111		if (top == NULL)
112			top = t;
113		else
114			np->n_flink = t;
115		t->n_blink = np;
116		np = t;
117	}
118	(void)free(nbuf);
119	return (top);
120}
121
122/*
123 * Turn a list of names into a string of the same names.
124 */
125char *
126detract(np, ntype)
127	struct name *np;
128	int ntype;
129{
130	int s, comma;
131	char *cp, *top;
132	struct name *p;
133
134	comma = ntype & GCOMMA;
135	if (np == NULL)
136		return (NULL);
137	ntype &= ~GCOMMA;
138	s = 0;
139	if (debug && comma)
140		fprintf(stderr, "detract asked to insert commas\n");
141	for (p = np; p != NULL; p = p->n_flink) {
142		if (ntype && (p->n_type & GMASK) != ntype)
143			continue;
144		s += strlen(p->n_name) + 1;
145		if (comma)
146			s++;
147	}
148	if (s == 0)
149		return (NULL);
150	s += 2;
151	top = salloc(s);
152	cp = top;
153	for (p = np; p != NULL; p = p->n_flink) {
154		if (ntype && (p->n_type & GMASK) != ntype)
155			continue;
156		cp += strlcpy(cp, p->n_name, strlen(p->n_name) + 1);
157		if (comma && p->n_flink != NULL)
158			*cp++ = ',';
159		*cp++ = ' ';
160	}
161	*--cp = '\0';
162	if (comma && *--cp == ',')
163		*cp = '\0';
164	return (top);
165}
166
167/*
168 * Grab a single word (liberal word)
169 * Throw away things between ()'s, and take anything between <>.
170 */
171char *
172yankword(ap, wbuf)
173	char *ap, wbuf[];
174{
175	char *cp, *cp2;
176
177	cp = ap;
178	for (;;) {
179		if (*cp == '\0')
180			return (NULL);
181		if (*cp == '(') {
182			int nesting = 0;
183
184			while (*cp != '\0') {
185				switch (*cp++) {
186				case '(':
187					nesting++;
188					break;
189				case ')':
190					--nesting;
191					break;
192				}
193				if (nesting <= 0)
194					break;
195			}
196		} else if (*cp == ' ' || *cp == '\t' || *cp == ',')
197			cp++;
198		else
199			break;
200	}
201	if (*cp ==  '<')
202		for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
203			;
204	else
205		for (cp2 = wbuf; *cp != '\0' && strchr(" \t,(", *cp) == NULL;
206		    *cp2++ = *cp++)
207			;
208	*cp2 = '\0';
209	return (cp);
210}
211
212/*
213 * Grab a single login name (liberal word)
214 * Throw away things between ()'s, take anything between <>,
215 * and look for words before metacharacters %, @, !.
216 */
217char *
218yanklogin(ap, wbuf)
219	char *ap, wbuf[];
220{
221	char *cp, *cp2, *cp_temp;
222	int n;
223
224	cp = ap;
225	for (;;) {
226		if (*cp == '\0')
227			return (NULL);
228		if (*cp == '(') {
229			int nesting = 0;
230
231			while (*cp != '\0') {
232				switch (*cp++) {
233				case '(':
234					nesting++;
235					break;
236				case ')':
237					--nesting;
238					break;
239				}
240				if (nesting <= 0)
241					break;
242			}
243		} else if (*cp == ' ' || *cp == '\t' || *cp == ',')
244			cp++;
245		else
246			break;
247	}
248
249	/*
250	 * Now, let's go forward till we meet the needed character,
251	 * and step one word back.
252	 */
253
254	/* First, remember current point. */
255	cp_temp = cp;
256	n = 0;
257
258	/*
259	 * Note that we look ahead in a cycle. This is safe, since
260	 * non-end of string is checked first.
261	 */
262	while(*cp != '\0' && strchr("@%!", *(cp + 1)) == NULL)
263		cp++;
264
265	/*
266	 * Now, start stepping back to the first non-word character,
267	 * while counting the number of symbols in a word.
268	 */
269	while(cp != cp_temp && strchr(" \t,<>", *(cp - 1)) == NULL) {
270		n++;
271		cp--;
272	}
273
274	/* Finally, grab the word forward. */
275	cp2 = wbuf;
276	while(n >= 0) {
277		*cp2++=*cp++;
278		n--;
279	}
280
281	*cp2 = '\0';
282	return (cp);
283}
284
285/*
286 * For each recipient in the passed name list with a /
287 * in the name, append the message to the end of the named file
288 * and remove him from the recipient list.
289 *
290 * Recipients whose name begins with | are piped through the given
291 * program and removed.
292 */
293struct name *
294outof(names, fo, hp)
295	struct name *names;
296	FILE *fo;
297	struct header *hp;
298{
299	int c, ispipe;
300	struct name *np, *top;
301	time_t now;
302	char *date, *fname;
303	FILE *fout, *fin;
304
305	top = names;
306	np = names;
307	(void)time(&now);
308	date = ctime(&now);
309	while (np != NULL) {
310		if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
311			np = np->n_flink;
312			continue;
313		}
314		ispipe = np->n_name[0] == '|';
315		if (ispipe)
316			fname = np->n_name+1;
317		else
318			fname = expand(np->n_name);
319
320		/*
321		 * See if we have copied the complete message out yet.
322		 * If not, do so.
323		 */
324
325		if (image < 0) {
326			int fd;
327			char tempname[PATHSIZE];
328
329			(void)snprintf(tempname, sizeof(tempname),
330			    "%s/mail.ReXXXXXXXXXX", tmpdir);
331			if ((fd = mkstemp(tempname)) == -1 ||
332			    (fout = Fdopen(fd, "a")) == NULL) {
333				warn("%s", tempname);
334				senderr++;
335				goto cant;
336			}
337			image = open(tempname, O_RDWR);
338			(void)rm(tempname);
339			if (image < 0) {
340				warn("%s", tempname);
341				senderr++;
342				(void)Fclose(fout);
343				goto cant;
344			}
345			(void)fcntl(image, F_SETFD, 1);
346			fprintf(fout, "From %s %s", myname, date);
347			puthead(hp, fout,
348			    GTO|GSUBJECT|GCC|GREPLYTO|GINREPLYTO|GNL);
349			while ((c = getc(fo)) != EOF)
350				(void)putc(c, fout);
351			rewind(fo);
352			fprintf(fout, "\n");
353			(void)fflush(fout);
354			if (ferror(fout)) {
355				warn("%s", tempname);
356				senderr++;
357				(void)Fclose(fout);
358				goto cant;
359			}
360			(void)Fclose(fout);
361		}
362
363		/*
364		 * Now either copy "image" to the desired file
365		 * or give it as the standard input to the desired
366		 * program as appropriate.
367		 */
368
369		if (ispipe) {
370			int pid;
371			char *sh;
372			sigset_t nset;
373
374			/*
375			 * XXX
376			 * We can't really reuse the same image file,
377			 * because multiple piped recipients will
378			 * share the same lseek location and trample
379			 * on one another.
380			 */
381			if ((sh = value("SHELL")) == NULL)
382				sh = _PATH_CSHELL;
383			(void)sigemptyset(&nset);
384			(void)sigaddset(&nset, SIGHUP);
385			(void)sigaddset(&nset, SIGINT);
386			(void)sigaddset(&nset, SIGQUIT);
387			pid = start_command(sh, &nset, image, -1, "-c", fname,
388			    NULL);
389			if (pid < 0) {
390				senderr++;
391				goto cant;
392			}
393			free_child(pid);
394		} else {
395			int f;
396			if ((fout = Fopen(fname, "a")) == NULL) {
397				warn("%s", fname);
398				senderr++;
399				goto cant;
400			}
401			if ((f = dup(image)) < 0) {
402				warn("dup");
403				fin = NULL;
404			} else
405				fin = Fdopen(f, "r");
406			if (fin == NULL) {
407				fprintf(stderr, "Can't reopen image\n");
408				(void)Fclose(fout);
409				senderr++;
410				goto cant;
411			}
412			rewind(fin);
413			while ((c = getc(fin)) != EOF)
414				(void)putc(c, fout);
415			if (ferror(fout)) {
416				warnx("%s", fname);
417				senderr++;
418				(void)Fclose(fout);
419				(void)Fclose(fin);
420				goto cant;
421			}
422			(void)Fclose(fout);
423			(void)Fclose(fin);
424		}
425cant:
426		/*
427		 * In days of old we removed the entry from the
428		 * the list; now for sake of header expansion
429		 * we leave it in and mark it as deleted.
430		 */
431		np->n_type |= GDEL;
432		np = np->n_flink;
433	}
434	if (image >= 0) {
435		(void)close(image);
436		image = -1;
437	}
438	return (top);
439}
440
441/*
442 * Determine if the passed address is a local "send to file" address.
443 * If any of the network metacharacters precedes any slashes, it can't
444 * be a filename.  We cheat with .'s to allow path names like ./...
445 */
446int
447isfileaddr(name)
448	char *name;
449{
450	char *cp;
451
452	if (*name == '+')
453		return (1);
454	for (cp = name; *cp != '\0'; cp++) {
455		if (*cp == '!' || *cp == '%' || *cp == '@')
456			return (0);
457		if (*cp == '/')
458			return (1);
459	}
460	return (0);
461}
462
463/*
464 * Map all of the aliased users in the invoker's mailrc
465 * file and insert them into the list.
466 * Changed after all these months of service to recursively
467 * expand names (2/14/80).
468 */
469
470struct name *
471usermap(names)
472	struct name *names;
473{
474	struct name *new, *np, *cp;
475	struct grouphead *gh;
476	int metoo;
477
478	new = NULL;
479	np = names;
480	metoo = (value("metoo") != NULL);
481	while (np != NULL) {
482		if (np->n_name[0] == '\\') {
483			cp = np->n_flink;
484			new = put(new, np);
485			np = cp;
486			continue;
487		}
488		gh = findgroup(np->n_name);
489		cp = np->n_flink;
490		if (gh != NULL)
491			new = gexpand(new, gh, metoo, np->n_type);
492		else
493			new = put(new, np);
494		np = cp;
495	}
496	return (new);
497}
498
499/*
500 * Recursively expand a group name.  We limit the expansion to some
501 * fixed level to keep things from going haywire.
502 * Direct recursion is not expanded for convenience.
503 */
504
505struct name *
506gexpand(nlist, gh, metoo, ntype)
507	struct name *nlist;
508	struct grouphead *gh;
509	int metoo, ntype;
510{
511	struct group *gp;
512	struct grouphead *ngh;
513	struct name *np;
514	static int depth;
515	char *cp;
516
517	if (depth > MAXEXP) {
518		printf("Expanding alias to depth larger than %d\n", MAXEXP);
519		return (nlist);
520	}
521	depth++;
522	for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
523		cp = gp->ge_name;
524		if (*cp == '\\')
525			goto quote;
526		if (strcmp(cp, gh->g_name) == 0)
527			goto quote;
528		if ((ngh = findgroup(cp)) != NULL) {
529			nlist = gexpand(nlist, ngh, metoo, ntype);
530			continue;
531		}
532quote:
533		np = nalloc(cp, ntype);
534		/*
535		 * At this point should allow to expand
536		 * to self if only person in group
537		 */
538		if (gp == gh->g_list && gp->ge_link == NULL)
539			goto skip;
540		if (!metoo && strcmp(cp, myname) == 0)
541			np->n_type |= GDEL;
542skip:
543		nlist = put(nlist, np);
544	}
545	depth--;
546	return (nlist);
547}
548
549/*
550 * Concatenate the two passed name lists, return the result.
551 */
552struct name *
553cat(n1, n2)
554	struct name *n1, *n2;
555{
556	struct name *tail;
557
558	if (n1 == NULL)
559		return (n2);
560	if (n2 == NULL)
561		return (n1);
562	tail = tailof(n1);
563	tail->n_flink = n2;
564	n2->n_blink = tail;
565	return (n1);
566}
567
568/*
569 * Unpack the name list onto a vector of strings.
570 * Return an error if the name list won't fit.
571 */
572char **
573unpack(np)
574	struct name *np;
575{
576	char **ap, **top;
577	struct name *n;
578	int t, extra, metoo, verbose;
579
580	n = np;
581	if ((t = count(n)) == 0)
582		errx(1, "No names to unpack");
583	/*
584	 * Compute the number of extra arguments we will need.
585	 * We need at least two extra -- one for "mail" and one for
586	 * the terminating 0 pointer.  Additional spots may be needed
587	 * to pass along -f to the host mailer.
588	 */
589	extra = 2;
590	extra++;
591	metoo = value("metoo") != NULL;
592	if (metoo)
593		extra++;
594	verbose = value("verbose") != NULL;
595	if (verbose)
596		extra++;
597	top = (char **)salloc((t + extra) * sizeof(*top));
598	ap = top;
599	*ap++ = "send-mail";
600	*ap++ = "-i";
601	if (metoo)
602		*ap++ = "-m";
603	if (verbose)
604		*ap++ = "-v";
605	for (; n != NULL; n = n->n_flink)
606		if ((n->n_type & GDEL) == 0)
607			*ap++ = n->n_name;
608	*ap = NULL;
609	return (top);
610}
611
612/*
613 * Remove all of the duplicates from the passed name list by
614 * insertion sorting them, then checking for dups.
615 * Return the head of the new list.
616 */
617struct name *
618elide(names)
619	struct name *names;
620{
621	struct name *np, *t, *new;
622	struct name *x;
623
624	if (names == NULL)
625		return (NULL);
626	new = names;
627	np = names;
628	np = np->n_flink;
629	if (np != NULL)
630		np->n_blink = NULL;
631	new->n_flink = NULL;
632	while (np != NULL) {
633		t = new;
634		while (strcasecmp(t->n_name, np->n_name) < 0) {
635			if (t->n_flink == NULL)
636				break;
637			t = t->n_flink;
638		}
639
640		/*
641		 * If we ran out of t's, put the new entry after
642		 * the current value of t.
643		 */
644
645		if (strcasecmp(t->n_name, np->n_name) < 0) {
646			t->n_flink = np;
647			np->n_blink = t;
648			t = np;
649			np = np->n_flink;
650			t->n_flink = NULL;
651			continue;
652		}
653
654		/*
655		 * Otherwise, put the new entry in front of the
656		 * current t.  If at the front of the list,
657		 * the new guy becomes the new head of the list.
658		 */
659
660		if (t == new) {
661			t = np;
662			np = np->n_flink;
663			t->n_flink = new;
664			new->n_blink = t;
665			t->n_blink = NULL;
666			new = t;
667			continue;
668		}
669
670		/*
671		 * The normal case -- we are inserting into the
672		 * middle of the list.
673		 */
674
675		x = np;
676		np = np->n_flink;
677		x->n_flink = t;
678		x->n_blink = t->n_blink;
679		t->n_blink->n_flink = x;
680		t->n_blink = x;
681	}
682
683	/*
684	 * Now the list headed up by new is sorted.
685	 * Go through it and remove duplicates.
686	 */
687
688	np = new;
689	while (np != NULL) {
690		t = np;
691		while (t->n_flink != NULL &&
692		    strcasecmp(np->n_name, t->n_flink->n_name) == 0)
693			t = t->n_flink;
694		if (t == np || t == NULL) {
695			np = np->n_flink;
696			continue;
697		}
698
699		/*
700		 * Now t points to the last entry with the same name
701		 * as np.  Make np point beyond t.
702		 */
703
704		np->n_flink = t->n_flink;
705		if (t->n_flink != NULL)
706			t->n_flink->n_blink = np;
707		np = np->n_flink;
708	}
709	return (new);
710}
711
712/*
713 * Put another node onto a list of names and return
714 * the list.
715 */
716struct name *
717put(list, node)
718	struct name *list, *node;
719{
720	node->n_flink = list;
721	node->n_blink = NULL;
722	if (list != NULL)
723		list->n_blink = node;
724	return (node);
725}
726
727/*
728 * Determine the number of undeleted elements in
729 * a name list and return it.
730 */
731int
732count(np)
733	struct name *np;
734{
735	int c;
736
737	for (c = 0; np != NULL; np = np->n_flink)
738		if ((np->n_type & GDEL) == 0)
739			c++;
740	return (c);
741}
742
743/*
744 * Delete the given name from a namelist.
745 */
746struct name *
747delname(np, name)
748	struct name *np;
749	char name[];
750{
751	struct name *p;
752
753	for (p = np; p != NULL; p = p->n_flink)
754		if (strcasecmp(p->n_name, name) == 0) {
755			if (p->n_blink == NULL) {
756				if (p->n_flink != NULL)
757					p->n_flink->n_blink = NULL;
758				np = p->n_flink;
759				continue;
760			}
761			if (p->n_flink == NULL) {
762				if (p->n_blink != NULL)
763					p->n_blink->n_flink = NULL;
764				continue;
765			}
766			p->n_blink->n_flink = p->n_flink;
767			p->n_flink->n_blink = p->n_blink;
768		}
769	return (np);
770}
771
772/*
773 * Pretty print a name list
774 * Uncomment it if you need it.
775 */
776
777/*
778void
779prettyprint(name)
780	struct name *name;
781{
782	struct name *np;
783
784	np = name;
785	while (np != NULL) {
786		fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
787		np = np->n_flink;
788	}
789	fprintf(stderr, "\n");
790}
791*/
792