GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mail/aux.c Lines: 13 243 5.3 %
Date: 2017-11-07 Branches: 10 236 4.2 %

Line Branch Exec Source
1
/*	$OpenBSD: aux.c,v 1.29 2015/10/16 17:56:07 mmcc Exp $	*/
2
/*	$NetBSD: aux.c,v 1.5 1997/05/13 06:15:52 mikel 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
#include "rcv.h"
34
#include <fcntl.h>
35
#include "extern.h"
36
37
/*
38
 * Mail -- a mail program
39
 *
40
 * Auxiliary functions.
41
 */
42
static char *save2str(char *, char *);
43
44
/*
45
 * Return a pointer to a dynamic copy of the argument.
46
 */
47
char *
48
savestr(char *str)
49
{
50
	char *new;
51
68
	int size = strlen(str) + 1;
52
53
34
	if ((new = salloc(size)) != NULL)
54
34
		(void)memcpy(new, str, size);
55
34
	return(new);
56
}
57
58
/*
59
 * Make a copy of new argument incorporating old one.
60
 */
61
static char *
62
save2str(char *str, char *old)
63
{
64
	char *new;
65
	int newsize = strlen(str) + 1;
66
	int oldsize = old ? strlen(old) + 1 : 0;
67
68
	if ((new = salloc(newsize + oldsize)) != NULL) {
69
		if (oldsize) {
70
			(void)memcpy(new, old, oldsize);
71
			new[oldsize - 1] = ' ';
72
		}
73
		(void)memcpy(new + oldsize, str, newsize);
74
	}
75
	return(new);
76
}
77
78
/*
79
 * Touch the named message by setting its MTOUCH flag.
80
 * Touched messages have the effect of not being sent
81
 * back to the system mailbox on exit.
82
 */
83
void
84
touch(struct message *mp)
85
{
86
87
	mp->m_flag |= MTOUCH;
88
	if ((mp->m_flag & MREAD) == 0)
89
		mp->m_flag |= MREAD|MSTATUS;
90
}
91
92
/*
93
 * Test to see if the passed file name is a directory.
94
 * Return true if it is.
95
 */
96
int
97
isdir(char *name)
98
{
99
	struct stat sbuf;
100
101
	if (stat(name, &sbuf) < 0)
102
		return(0);
103
	return(S_ISDIR(sbuf.st_mode));
104
}
105
106
/*
107
 * Count the number of arguments in the given string raw list.
108
 */
109
int
110
argcount(char **argv)
111
{
112
	char **ap;
113
114
	for (ap = argv; *ap++ != NULL;)
115
		;
116
	return(ap - argv - 1);
117
}
118
119
/*
120
 * Return the desired header line from the passed message
121
 * pointer (or NULL if the desired header field is not available).
122
 */
123
char *
124
hfield(char *field, struct message *mp)
125
{
126
	FILE *ibuf;
127
	char linebuf[LINESIZE];
128
	int lc;
129
	char *hfield;
130
	char *colon, *oldhfield = NULL;
131
132
	ibuf = setinput(mp);
133
	if ((lc = mp->m_lines - 1) < 0)
134
		return(NULL);
135
	if (readline(ibuf, linebuf, LINESIZE, NULL) < 0)
136
		return(NULL);
137
	while (lc > 0) {
138
		if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
139
			return(oldhfield);
140
		if ((hfield = ishfield(linebuf, colon, field)) != NULL)
141
			oldhfield = save2str(hfield, oldhfield);
142
	}
143
	return(oldhfield);
144
}
145
146
/*
147
 * Return the next header field found in the given message.
148
 * Return >= 0 if something found, < 0 elsewise.
149
 * "colon" is set to point to the colon in the header.
150
 * Must deal with \ continuations & other such fraud.
151
 */
152
int
153
gethfield(FILE *f, char *linebuf, int rem, char **colon)
154
{
155
	char line2[LINESIZE];
156
	char *cp, *cp2;
157
	int c;
158
159
	for (;;) {
160
		if (--rem < 0)
161
			return(-1);
162
		if ((c = readline(f, linebuf, LINESIZE, NULL)) <= 0)
163
			return(-1);
164
		for (cp = linebuf;
165
		    isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':';
166
		    cp++)
167
			;
168
		if (*cp != ':' || cp == linebuf)
169
			continue;
170
		/*
171
		 * I guess we got a headline.
172
		 * Handle wraparounding
173
		 */
174
		*colon = cp;
175
		cp = linebuf + c;
176
		for (;;) {
177
			while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
178
				;
179
			cp++;
180
			if (rem <= 0)
181
				break;
182
			ungetc(c = getc(f), f);
183
			if (c != ' ' && c != '\t')
184
				break;
185
			if ((c = readline(f, line2, LINESIZE, NULL)) < 0)
186
				break;
187
			rem--;
188
			for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
189
				;
190
			c -= cp2 - line2;
191
			if (cp + c >= linebuf + LINESIZE - 2)
192
				break;
193
			*cp++ = ' ';
194
			(void)memcpy(cp, cp2, c);
195
			cp += c;
196
		}
197
		*cp = 0;
198
		return(rem);
199
	}
200
	/* NOTREACHED */
201
}
202
203
/*
204
 * Check whether the passed line is a header line of
205
 * the desired breed.  Return the field body, or 0.
206
 */
207
208
char*
209
ishfield(char *linebuf, char *colon, char *field)
210
{
211
	char *cp = colon;
212
213
	*cp = 0;
214
	if (strcasecmp(linebuf, field) != 0) {
215
		*cp = ':';
216
		return(0);
217
	}
218
	*cp = ':';
219
	for (cp++; *cp == ' ' || *cp == '\t'; cp++)
220
		;
221
	return(cp);
222
}
223
224
/*
225
 * Copy a string, lowercasing it as we go.  ``dsize'' should be
226
 * the real size (not len) of the dest string (guarantee NUL term).
227
 */
228
size_t
229
istrlcpy(char *dst, const char *src, size_t dsize)
230
{
231
	char *d = dst;
232
	const char *s = src;
233
	size_t n = dsize;
234
235
	/* Copy as many bytes as will fit */
236

42
	if (n != 0 && --n != 0) {
237
		do {
238
142
			if ((*d++ = tolower((unsigned char)*s++)) == 0)
239
				break;
240
128
		} while (--n != 0);
241
	}
242
243
	/* Not enough room in dst, add NUL and traverse rest of src */
244
14
	if (n == 0) {
245
		if (dsize != 0)
246
			*d = '\0';		/* NUL-terminate dst */
247
		while (*s++)
248
			;
249
	}
250
251
14
	return(s - src - 1);	/* count does not include NUL */
252
}
253
254
/*
255
 * The following code deals with input stacking to do source
256
 * commands.  All but the current file pointer are saved on
257
 * the stack.
258
 */
259
static	int	ssp;			/* Top of file stack */
260
struct sstack {
261
	FILE	*s_file;		/* File we were in. */
262
	int	s_cond;			/* Saved state of conditionals */
263
	int	s_loading;		/* Loading .mailrc, etc. */
264
} sstack[OPEN_MAX];
265
266
/*
267
 * Pushdown current input file and switch to a new one.
268
 * Set the global flag "sourcing" so that others will realize
269
 * that they are no longer reading from a tty (in all probability).
270
 */
271
int
272
source(void *v)
273
{
274
	char **arglist = v;
275
	FILE *fi;
276
	char *cp;
277
278
	if ((cp = expand(*arglist)) == NULL)
279
		return(1);
280
	if ((fi = Fopen(cp, "r")) == NULL) {
281
		warn("%s", cp);
282
		return(1);
283
	}
284
	if (ssp >= OPEN_MAX - 1) {
285
		puts("Too much \"sourcing\" going on.");
286
		(void)Fclose(fi);
287
		return(1);
288
	}
289
	sstack[ssp].s_file = input;
290
	sstack[ssp].s_cond = cond;
291
	sstack[ssp].s_loading = loading;
292
	ssp++;
293
	loading = 0;
294
	cond = CANY;
295
	input = fi;
296
	sourcing++;
297
	return(0);
298
}
299
300
/*
301
 * Pop the current input back to the previous level.
302
 * Update the "sourcing" flag as appropriate.
303
 */
304
int
305
unstack(void)
306
{
307
308
	if (ssp <= 0) {
309
		puts("\"Source\" stack over-pop.");
310
		sourcing = 0;
311
		return(1);
312
	}
313
	(void)Fclose(input);
314
	if (cond != CANY)
315
		puts("Unmatched \"if\"");
316
	ssp--;
317
	cond = sstack[ssp].s_cond;
318
	loading = sstack[ssp].s_loading;
319
	input = sstack[ssp].s_file;
320
	if (ssp == 0)
321
		sourcing = loading;
322
	return(0);
323
}
324
325
/*
326
 * Touch the indicated file.
327
 * This is nifty for the shell.
328
 */
329
void
330
alter(char *name)
331
{
332
	struct timespec ts[2];
333
334
	clock_gettime(CLOCK_REALTIME, &ts[0]);
335
	ts[0].tv_sec++;
336
	ts[1].tv_nsec = UTIME_OMIT;
337
	(void)utimensat(AT_FDCWD, name, ts, 0);
338
}
339
340
/*
341
 * Examine the passed line buffer and
342
 * return true if it is all blanks and tabs.
343
 */
344
int
345
blankline(char *linebuf)
346
{
347
	char *cp;
348
349
	for (cp = linebuf; *cp; cp++)
350
		if (*cp != ' ' && *cp != '\t')
351
			return(0);
352
	return(1);
353
}
354
355
/*
356
 * Get sender's name from this message.  If the message has
357
 * a bunch of arpanet stuff in it, we may have to skin the name
358
 * before returning it.
359
 */
360
char *
361
nameof(struct message *mp, int reptype)
362
{
363
	char *cp, *cp2;
364
365
	cp = skin(name1(mp, reptype));
366
	if (reptype != 0 || charcount(cp, '!') < 2)
367
		return(cp);
368
	cp2 = strrchr(cp, '!');
369
	cp2--;
370
	while (cp2 > cp && *cp2 != '!')
371
		cp2--;
372
	if (*cp2 == '!')
373
		return(cp2 + 1);
374
	return(cp);
375
}
376
377
/*
378
 * Start of a "comment".
379
 * Ignore it.
380
 */
381
char *
382
skip_comment(char *cp)
383
{
384
	int nesting = 1;
385
386
	for (; nesting > 0 && *cp; cp++) {
387
		switch (*cp) {
388
		case '\\':
389
			if (cp[1])
390
				cp++;
391
			break;
392
		case '(':
393
			nesting++;
394
			break;
395
		case ')':
396
			nesting--;
397
			break;
398
		}
399
	}
400
	return(cp);
401
}
402
403
/*
404
 * Skin an arpa net address according to the RFC 822 interpretation
405
 * of "host-phrase."
406
 */
407
char *
408
skin(char *name)
409
{
410
	char *nbuf, *bufend, *cp, *cp2;
411
	int c, gotlt, lastsp;
412
413
	if (name == NULL)
414
		return(NULL);
415
	if (strchr(name, '(') == NULL && strchr(name, '<') == NULL
416
	    && strchr(name, ' ') == NULL)
417
		return(name);
418
419
	/* We assume that length(input) <= length(output) */
420
	if ((nbuf = malloc(strlen(name) + 1)) == NULL)
421
		err(1, "malloc");
422
	gotlt = 0;
423
	lastsp = 0;
424
	bufend = nbuf;
425
	for (cp = name, cp2 = bufend; (c = (unsigned char)*cp++) != '\0'; ) {
426
		switch (c) {
427
		case '(':
428
			cp = skip_comment(cp);
429
			lastsp = 0;
430
			break;
431
432
		case '"':
433
			/*
434
			 * Start of a "quoted-string".
435
			 * Copy it in its entirety.
436
			 */
437
			while ((c = (unsigned char)*cp) != '\0') {
438
				cp++;
439
				if (c == '"')
440
					break;
441
				if (c != '\\')
442
					*cp2++ = c;
443
				else if ((c = (unsigned char)*cp) != '\0') {
444
					*cp2++ = c;
445
					cp++;
446
				}
447
			}
448
			lastsp = 0;
449
			break;
450
451
		case ' ':
452
			if (strncmp(cp, "at ", 3) == 0)
453
				cp += 3, *cp2++ = '@';
454
			else
455
			if (strncmp(cp, "@ ", 2) == 0)
456
				cp += 2, *cp2++ = '@';
457
			else
458
				lastsp = 1;
459
			break;
460
461
		case '<':
462
			cp2 = bufend;
463
			gotlt++;
464
			lastsp = 0;
465
			break;
466
467
		case '>':
468
			if (gotlt) {
469
				gotlt = 0;
470
				while ((c = (unsigned char)*cp) && c != ',') {
471
					cp++;
472
					if (c == '(')
473
						cp = skip_comment(cp);
474
					else if (c == '"')
475
						while ((c = (unsigned char)*cp) != '\0') {
476
							cp++;
477
							if (c == '"')
478
								break;
479
							if (c == '\\' && *cp)
480
								cp++;
481
						}
482
				}
483
				lastsp = 0;
484
				break;
485
			}
486
			/* Fall into . . . */
487
488
		default:
489
			if (lastsp) {
490
				lastsp = 0;
491
				*cp2++ = ' ';
492
			}
493
			*cp2++ = c;
494
			if (c == ',' && *cp == ' ' && !gotlt) {
495
				*cp2++ = ' ';
496
				while (*++cp == ' ')
497
					;
498
				lastsp = 0;
499
				bufend = cp2;
500
			}
501
		}
502
	}
503
	*cp2 = 0;
504
505
	if ((cp = realloc(nbuf, strlen(nbuf) + 1)) != NULL)
506
		nbuf = cp;
507
	return(nbuf);
508
}
509
510
/*
511
 * Fetch the sender's name from the passed message.
512
 * Reptype can be
513
 *	0 -- get sender's name for display purposes
514
 *	1 -- get sender's name for reply
515
 *	2 -- get sender's name for Reply
516
 */
517
char *
518
name1(struct message *mp, int reptype)
519
{
520
	char namebuf[LINESIZE];
521
	char linebuf[LINESIZE];
522
	char *cp, *cp2;
523
	FILE *ibuf;
524
	int first = 1;
525
526
	if ((cp = hfield("from", mp)) != NULL)
527
		return(cp);
528
	if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
529
		return(cp);
530
	ibuf = setinput(mp);
531
	namebuf[0] = '\0';
532
	if (readline(ibuf, linebuf, LINESIZE, NULL) < 0)
533
		return(savestr(namebuf));
534
newname:
535
	for (cp = linebuf; *cp && *cp != ' '; cp++)
536
		;
537
	for (; *cp == ' ' || *cp == '\t'; cp++)
538
		;
539
	for (cp2 = &namebuf[strlen(namebuf)];
540
	     *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
541
		*cp2++ = *cp++;
542
	*cp2 = '\0';
543
	if (readline(ibuf, linebuf, LINESIZE, NULL) < 0)
544
		return(savestr(namebuf));
545
	if ((cp = strchr(linebuf, 'F')) == NULL)
546
		return(savestr(namebuf));
547
	if (strncmp(cp, "From", 4) != 0)
548
		return(savestr(namebuf));
549
	while ((cp = strchr(cp, 'r')) != NULL) {
550
		if (strncmp(cp, "remote", 6) == 0) {
551
			if ((cp = strchr(cp, 'f')) == NULL)
552
				break;
553
			if (strncmp(cp, "from", 4) != 0)
554
				break;
555
			if ((cp = strchr(cp, ' ')) == NULL)
556
				break;
557
			cp++;
558
			if (first) {
559
				cp2 = namebuf;
560
				first = 0;
561
			} else
562
				cp2 = strrchr(namebuf, '!') + 1;
563
			strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1);
564
			strlcat(namebuf, "!", sizeof(namebuf));
565
			goto newname;
566
		}
567
		cp++;
568
	}
569
	return(savestr(namebuf));
570
}
571
572
/*
573
 * Count the occurances of c in str
574
 */
575
int
576
charcount(char *str, int c)
577
{
578
	char *cp;
579
	int i;
580
581
	for (i = 0, cp = str; *cp; cp++)
582
		if (*cp == c)
583
			i++;
584
	return(i);
585
}
586
587
/*
588
 * Copy s1 to s2, return pointer to null in s2.
589
 */
590
char *
591
copy(char *s1, char *s2)
592
{
593
594
	while ((*s2++ = *s1++) != '\0')
595
		;
596
	return(s2 - 1);
597
}
598
599
/*
600
 * See if the given header field is supposed to be ignored.
601
 */
602
int
603
isign(char *field, struct ignoretab ignore[2])
604
{
605
	char realfld[LINESIZE];
606
607
	if (ignore == ignoreall)
608
		return(1);
609
	/*
610
	 * Lower-case the string, so that "Status" and "status"
611
	 * will hash to the same place.
612
	 */
613
	istrlcpy(realfld, field, sizeof(realfld));
614
	if (ignore[1].i_count > 0)
615
		return(!member(realfld, ignore + 1));
616
	else
617
		return(member(realfld, ignore));
618
}
619
620
int
621
member(char *realfield, struct ignoretab *table)
622
{
623
	struct ignore *igp;
624
625
46
	for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
626

2
		if (*igp->i_field == *realfield &&
627
		    equal(igp->i_field, realfield))
628
			return(1);
629
14
	return(0);
630
14
}
631
632
void
633
clearnew(void)
634
{
635
	struct message *mp;
636
637
	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
638
		if (mp->m_flag & MNEW) {
639
			mp->m_flag &= ~MNEW;
640
			mp->m_flag |= MSTATUS;
641
		}
642
	}
643
}