GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mail/names.c Lines: 43 257 16.7 %
Date: 2017-11-07 Branches: 20 226 8.8 %

Line Branch Exec Source
1
/*	$OpenBSD: names.c,v 1.23 2015/10/16 17:56:07 mmcc Exp $	*/
2
/*	$NetBSD: names.c,v 1.5 1996/06/08 19:48:32 christos Exp $	*/
3
4
/*
5
 * Copyright (c) 1980, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
/*
34
 * Mail -- a mail program
35
 *
36
 * Handle name lists.
37
 */
38
39
#include "rcv.h"
40
#include <fcntl.h>
41
#include "extern.h"
42
43
/*
44
 * Allocate a single element of a name list,
45
 * initialize its name field to the passed
46
 * name and return it.
47
 */
48
struct name *
49
nalloc(char *str, int ntype)
50
{
51
	struct name *np;
52
53
8
	np = (struct name *)salloc(sizeof(*np));
54
4
	np->n_flink = NULL;
55
4
	np->n_blink = NULL;
56
4
	np->n_type = ntype;
57
4
	np->n_name = savestr(str);
58
4
	return(np);
59
}
60
61
/*
62
 * Find the tail of a list and return it.
63
 */
64
struct name *
65
tailof(struct name *name)
66
{
67
	struct name *np;
68
69
	np = name;
70
	if (np == NULL)
71
		return(NULL);
72
	while (np->n_flink != NULL)
73
		np = np->n_flink;
74
	return(np);
75
}
76
77
/*
78
 * Extract a list of names from a line,
79
 * and make a list of names from it.
80
 * Return the list or NULL if none found.
81
 */
82
struct name *
83
extract(char *line, int ntype)
84
{
85
	char *cp;
86
	struct name *top, *np, *t;
87
	char *nbuf;
88
89
	if (line == NULL || *line == '\0')
90
		return(NULL);
91
	if ((nbuf = malloc(strlen(line) + 1)) == NULL)
92
		err(1, "malloc");
93
	top = NULL;
94
	np = NULL;
95
	cp = line;
96
	while ((cp = yankword(cp, nbuf)) != NULL) {
97
		t = nalloc(nbuf, ntype);
98
		if (top == NULL)
99
			top = t;
100
		else
101
			np->n_flink = t;
102
		t->n_blink = np;
103
		np = t;
104
	}
105
	(void)free(nbuf);
106
	return(top);
107
}
108
109
/*
110
 * Turn a list of names into a string of the same names.
111
 */
112
char *
113
detract(struct name *np, int ntype)
114
{
115
	int s, comma;
116
	char *cp, *top;
117
	struct name *p;
118
119
	comma = ntype & GCOMMA;
120
	if (np == NULL)
121
		return(NULL);
122
	ntype &= ~GCOMMA;
123
	s = 0;
124
	if (debug && comma)
125
		fputs("detract asked to insert commas\n", stderr);
126
	for (p = np; p != NULL; p = p->n_flink) {
127
		if (ntype && (p->n_type & GMASK) != ntype)
128
			continue;
129
		s += strlen(p->n_name) + 1;
130
		if (comma)
131
			s++;
132
	}
133
	if (s == 0)
134
		return(NULL);
135
	s += 2;
136
	top = salloc(s);
137
	cp = top;
138
	for (p = np; p != NULL; p = p->n_flink) {
139
		if (ntype && (p->n_type & GMASK) != ntype)
140
			continue;
141
		cp = copy(p->n_name, cp);
142
		if (comma && p->n_flink != NULL)
143
			*cp++ = ',';
144
		*cp++ = ' ';
145
	}
146
	*--cp = 0;
147
	if (comma && *--cp == ',')
148
		*cp = 0;
149
	return(top);
150
}
151
152
/*
153
 * Grab a single word (liberal word)
154
 * Throw away things between ()'s, and take anything between <>.
155
 */
156
char *
157
yankword(char *ap, char *wbuf)
158
{
159
	char *cp, *cp2;
160
161
	cp = ap;
162
	for (;;) {
163
		if (*cp == '\0')
164
			return(NULL);
165
		if (*cp == '(') {
166
			int nesting = 0;
167
168
			while (*cp != '\0') {
169
				switch (*cp++) {
170
				case '(':
171
					nesting++;
172
					break;
173
				case ')':
174
					--nesting;
175
					break;
176
				}
177
				if (nesting <= 0)
178
					break;
179
			}
180
		} else if (*cp == ' ' || *cp == '\t' || *cp == ',')
181
			cp++;
182
		else
183
			break;
184
	}
185
	if (*cp ==  '<')
186
		for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
187
			;
188
	else
189
		for (cp2 = wbuf; *cp && !strchr(" \t,(", *cp); *cp2++ = *cp++)
190
			;
191
	*cp2 = '\0';
192
	return(cp);
193
}
194
195
/*
196
 * For each recipient in the passed name list with a /
197
 * in the name, append the message to the end of the named file
198
 * and remove him from the recipient list.
199
 *
200
 * Recipients whose name begins with | are piped through the given
201
 * program and removed.
202
 */
203
struct name *
204
outof(struct name *names, FILE *fo, struct header *hp)
205
{
206
	int c, ispipe;
207
	struct name *np, *top;
208
4
	time_t now;
209
	char *date, *fname;
210
	FILE *fout, *fin;
211
212
2
	if (value("expandaddr") == NULL)
213
2
		return(names);
214
215
	top = names;
216
	np = names;
217
	(void)time(&now);
218
	date = ctime(&now);
219
	while (np != NULL) {
220
		if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
221
			np = np->n_flink;
222
			continue;
223
		}
224
		ispipe = np->n_name[0] == '|';
225
		if (ispipe)
226
			fname = np->n_name+1;
227
		else
228
			fname = expand(np->n_name);
229
230
		/*
231
		 * See if we have copied the complete message out yet.
232
		 * If not, do so.
233
		 */
234
		if (image < 0) {
235
			int fd;
236
			char tempname[PATHSIZE];
237
238
			(void)snprintf(tempname, sizeof(tempname),
239
			    "%s/mail.ReXXXXXXXXXX", tmpdir);
240
			if ((fd = mkstemp(tempname)) == -1 ||
241
			    (fout = Fdopen(fd, "a")) == NULL) {
242
				warn("%s", tempname);
243
				senderr++;
244
				goto cant;
245
			}
246
			image = open(tempname, O_RDWR | O_CLOEXEC);
247
			(void)rm(tempname);
248
			if (image < 0) {
249
				warn("%s", tempname);
250
				senderr++;
251
				(void)Fclose(fout);
252
				goto cant;
253
			}
254
			fprintf(fout, "From %s %s", myname, date);
255
			puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
256
			while ((c = getc(fo)) != EOF)
257
				(void)putc(c, fout);
258
			rewind(fo);
259
			(void)putc('\n', fout);
260
			(void)fflush(fout);
261
			if (ferror(fout))
262
				warn("%s", tempname);
263
			(void)Fclose(fout);
264
		}
265
266
		/*
267
		 * Now either copy "image" to the desired file
268
		 * or give it as the standard input to the desired
269
		 * program as appropriate.
270
		 */
271
		if (ispipe) {
272
			pid_t pid;
273
			char *shell;
274
			sigset_t nset;
275
276
			/*
277
			 * XXX
278
			 * We can't really reuse the same image file,
279
			 * because multiple piped recipients will
280
			 * share the same lseek location and trample
281
			 * on one another.
282
			 */
283
			shell = value("SHELL");
284
			sigemptyset(&nset);
285
			sigaddset(&nset, SIGHUP);
286
			sigaddset(&nset, SIGINT);
287
			sigaddset(&nset, SIGQUIT);
288
			pid = start_command(shell, &nset,
289
				image, -1, "-c", fname, NULL);
290
			if (pid < 0) {
291
				senderr++;
292
				goto cant;
293
			}
294
			free_child(pid);
295
		} else {
296
			int f;
297
			if ((fout = Fopen(fname, "a")) == NULL) {
298
				warn("%s", fname);
299
				senderr++;
300
				goto cant;
301
			}
302
			if ((f = dup(image)) < 0) {
303
				warn("dup");
304
				fin = NULL;
305
			} else
306
				fin = Fdopen(f, "r");
307
			if (fin == NULL) {
308
				fputs("Can't reopen image\n", stderr);
309
				(void)Fclose(fout);
310
				senderr++;
311
				goto cant;
312
			}
313
			rewind(fin);
314
			while ((c = getc(fin)) != EOF)
315
				(void)putc(c, fout);
316
			if (ferror(fout)) {
317
				senderr++;
318
				warn("%s", fname);
319
			}
320
			(void)Fclose(fout);
321
			(void)Fclose(fin);
322
		}
323
cant:
324
		/*
325
		 * In days of old we removed the entry from the
326
		 * the list; now for sake of header expansion
327
		 * we leave it in and mark it as deleted.
328
		 */
329
		np->n_type |= GDEL;
330
		np = np->n_flink;
331
	}
332
	if (image >= 0) {
333
		(void)close(image);
334
		image = -1;
335
	}
336
	return(top);
337
2
}
338
339
/*
340
 * Determine if the passed address is a local "send to file" address.
341
 * If any of the network metacharacters precedes any slashes, it can't
342
 * be a filename.  We cheat with .'s to allow path names like ./...
343
 */
344
int
345
isfileaddr(char *name)
346
{
347
	char *cp;
348
349
	if (*name == '+')
350
		return(1);
351
	for (cp = name; *cp; cp++) {
352
		if (*cp == '!' || *cp == '%' || *cp == '@')
353
			return(0);
354
		if (*cp == '/')
355
			return(1);
356
	}
357
	return(0);
358
}
359
360
/*
361
 * Map all of the aliased users in the invoker's mailrc
362
 * file and insert them into the list.
363
 * Changed after all these months of service to recursively
364
 * expand names (2/14/80).
365
 */
366
struct name *
367
usermap(struct name *names)
368
{
369
	struct name *new, *np, *cp;
370
	struct grouphead *gh;
371
	int metoo;
372
373
	new = NULL;
374
	np = names;
375
4
	metoo = (value("metoo") != NULL);
376
8
	while (np != NULL) {
377
2
		if (np->n_name[0] == '\\') {
378
			cp = np->n_flink;
379
			new = put(new, np);
380
			np = cp;
381
			continue;
382
		}
383
2
		gh = findgroup(np->n_name);
384
2
		cp = np->n_flink;
385
2
		if (gh != NULL)
386
			new = gexpand(new, gh, metoo, np->n_type);
387
		else
388
2
			new = put(new, np);
389
		np = cp;
390
	}
391
2
	return(new);
392
}
393
394
/*
395
 * Recursively expand a group name.  We limit the expansion to some
396
 * fixed level to keep things from going haywire.
397
 * Direct recursion is not expanded for convenience.
398
 */
399
struct name *
400
gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
401
{
402
	struct group *gp;
403
	struct grouphead *ngh;
404
	struct name *np;
405
	static int depth;
406
	char *cp;
407
408
	if (depth > MAXEXP) {
409
		printf("Expanding alias to depth larger than %d\n", MAXEXP);
410
		return(nlist);
411
	}
412
	depth++;
413
	for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
414
		cp = gp->ge_name;
415
		if (*cp == '\\')
416
			goto quote;
417
		if (strcmp(cp, gh->g_name) == 0)
418
			goto quote;
419
		if ((ngh = findgroup(cp)) != NULL) {
420
			nlist = gexpand(nlist, ngh, metoo, ntype);
421
			continue;
422
		}
423
quote:
424
		np = nalloc(cp, ntype);
425
		/*
426
		 * At this point should allow to expand
427
		 * to self if only person in group
428
		 */
429
		if (gp == gh->g_list && gp->ge_link == NULL)
430
			goto skip;
431
		if (!metoo && strcmp(cp, myname) == 0)
432
			np->n_type |= GDEL;
433
skip:
434
		nlist = put(nlist, np);
435
	}
436
	depth--;
437
	return(nlist);
438
}
439
440
/*
441
 * Concatenate the two passed name lists, return the result.
442
 */
443
struct name *
444
cat(struct name *n1, struct name *n2)
445
{
446
	struct name *tail;
447
448
16
	if (n1 == NULL)
449
6
		return(n2);
450
2
	if (n2 == NULL)
451
2
		return(n1);
452
	tail = tailof(n1);
453
	tail->n_flink = n2;
454
	n2->n_blink = tail;
455
	return(n1);
456
8
}
457
458
/*
459
 * Remove all of the duplicates from the passed name list by
460
 * insertion sorting them, then checking for dups.
461
 * Return the head of the new list.
462
 */
463
struct name *
464
elide(struct name *names)
465
{
466
	struct name *np, *t, *new;
467
	struct name *x;
468
469
4
	if (names == NULL)
470
		return(NULL);
471
	new = names;
472
	np = names;
473
2
	np = np->n_flink;
474
2
	if (np != NULL)
475
		np->n_blink = NULL;
476
2
	new->n_flink = NULL;
477
4
	while (np != NULL) {
478
		t = new;
479
		while (strcasecmp(t->n_name, np->n_name) < 0) {
480
			if (t->n_flink == NULL)
481
				break;
482
			t = t->n_flink;
483
		}
484
485
		/*
486
		 * If we ran out of t's, put the new entry after
487
		 * the current value of t.
488
		 */
489
		if (strcasecmp(t->n_name, np->n_name) < 0) {
490
			t->n_flink = np;
491
			np->n_blink = t;
492
			t = np;
493
			np = np->n_flink;
494
			t->n_flink = NULL;
495
			continue;
496
		}
497
498
		/*
499
		 * Otherwise, put the new entry in front of the
500
		 * current t.  If at the front of the list,
501
		 * the new guy becomes the new head of the list.
502
		 */
503
		if (t == new) {
504
			t = np;
505
			np = np->n_flink;
506
			t->n_flink = new;
507
			new->n_blink = t;
508
			t->n_blink = NULL;
509
			new = t;
510
			continue;
511
		}
512
513
		/*
514
		 * The normal case -- we are inserting into the
515
		 * middle of the list.
516
		 */
517
		x = np;
518
		np = np->n_flink;
519
		x->n_flink = t;
520
		x->n_blink = t->n_blink;
521
		t->n_blink->n_flink = x;
522
		t->n_blink = x;
523
	}
524
525
	/*
526
	 * Now the list headed up by new is sorted.
527
	 * Go through it and remove duplicates.
528
	 */
529
	np = new;
530
6
	while (np != NULL) {
531
		t = np;
532

6
		while (t->n_flink != NULL &&
533
		       strcasecmp(np->n_name, t->n_flink->n_name) == 0)
534
			t = t->n_flink;
535
2
		if (t == np || t == NULL) {
536
2
			np = np->n_flink;
537
2
			continue;
538
		}
539
540
		/*
541
		 * Now t points to the last entry with the same name
542
		 * as np.  Make np point beyond t.
543
		 */
544
		np->n_flink = t->n_flink;
545
		if (t->n_flink != NULL)
546
			t->n_flink->n_blink = np;
547
		np = np->n_flink;
548
	}
549
2
	return(new);
550
2
}
551
552
/*
553
 * Put another node onto a list of names and return
554
 * the list.
555
 */
556
struct name *
557
put(struct name *list, struct name *node)
558
{
559
4
	node->n_flink = list;
560
2
	node->n_blink = NULL;
561
2
	if (list != NULL)
562
		list->n_blink = node;
563
2
	return(node);
564
}
565
566
/*
567
 * Determine the number of undeleted elements in
568
 * a name list and return it.
569
 */
570
int
571
count(struct name *np)
572
{
573
	int c;
574
575
10
	for (c = 0; np != NULL; np = np->n_flink)
576
2
		if ((np->n_type & GDEL) == 0)
577
2
			c++;
578
2
	return(c);
579
}
580
581
/*
582
 * Delete the given name from a namelist.
583
 */
584
struct name *
585
delname(struct name *np, char *name)
586
{
587
	struct name *p;
588
589
	for (p = np; p != NULL; p = p->n_flink)
590
		if ((strcasecmp(p->n_name, name) == 0) ||
591
		    (value("allnet") &&
592
		    strncasecmp(p->n_name, name, strlen(name)) == 0 &&
593
		    *(p->n_name+strlen(name)) == '@')) {
594
			if (p->n_blink == NULL) {
595
				if (p->n_flink != NULL)
596
					p->n_flink->n_blink = NULL;
597
				np = p->n_flink;
598
				continue;
599
			}
600
			if (p->n_flink == NULL) {
601
				if (p->n_blink != NULL)
602
					p->n_blink->n_flink = NULL;
603
				continue;
604
			}
605
			p->n_blink->n_flink = p->n_flink;
606
			p->n_flink->n_blink = p->n_blink;
607
		}
608
	return(np);
609
}
610
611
/*
612
 * Pretty print a name list
613
 * Uncomment it if you need it.
614
 */
615
#if 0
616
void
617
prettyprint(struct name *name)
618
{
619
	struct name *np;
620
621
	np = name;
622
	while (np != NULL) {
623
		fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
624
		np = np->n_flink;
625
	}
626
	putc('\n', stderr);
627
}
628
#endif