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