GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mail/send.c Lines: 96 291 33.0 %
Date: 2017-11-07 Branches: 52 258 20.2 %

Line Branch Exec Source
1
/*	$OpenBSD: send.c,v 1.24 2015/01/20 16:59:07 millert Exp $	*/
2
/*	$NetBSD: send.c,v 1.6 1996/06/08 19:48:39 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
#include "rcv.h"
34
#include "extern.h"
35
36
static volatile sig_atomic_t sendsignal;	/* Interrupted by a signal? */
37
38
/*
39
 * Mail -- a mail program
40
 *
41
 * Mail to others.
42
 */
43
44
/*
45
 * Send message described by the passed pointer to the
46
 * passed output buffer.  Return -1 on error.
47
 * Adjust the status: field if need be.
48
 * If doign is given, suppress ignored header fields.
49
 * prefix is a string to prepend to each output line.
50
 */
51
int
52
sendmessage(struct message *mp, FILE *obuf, struct ignoretab *doign,
53
	    char *prefix)
54
{
55
	int count;
56
	FILE *ibuf;
57
	char line[LINESIZE];
58
	char visline[4 * LINESIZE - 3];
59
	int ishead, infld, ignoring = 0, dostat, firstline;
60
	char *cp, *cp2;
61
	int c = 0;
62
	int length;
63
	int prefixlen = 0;
64
	int rval;
65
	int dovis;
66
	struct sigaction act, saveint;
67
	sigset_t oset;
68
69
	sendsignal = 0;
70
	rval = -1;
71
	dovis = isatty(fileno(obuf));
72
	sigemptyset(&act.sa_mask);
73
	act.sa_flags = SA_RESTART;
74
	act.sa_handler = sendint;
75
	(void)sigaction(SIGINT, &act, &saveint);
76
	(void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
77
78
	/*
79
	 * Compute the prefix string, without trailing whitespace
80
	 */
81
	if (prefix != NULL) {
82
		cp2 = 0;
83
		for (cp = prefix; *cp; cp++)
84
			if (*cp != ' ' && *cp != '\t')
85
				cp2 = cp;
86
		prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
87
	}
88
	ibuf = setinput(mp);
89
	count = mp->m_size;
90
	ishead = 1;
91
	dostat = doign == 0 || !isign("status", doign);
92
	infld = 0;
93
	firstline = 1;
94
	/*
95
	 * Process headers first
96
	 */
97
	while (count > 0 && ishead) {
98
		if (fgets(line, sizeof(line), ibuf) == NULL)
99
			break;
100
		count -= length = strlen(line);
101
		if (firstline) {
102
			/*
103
			 * First line is the From line, so no headers
104
			 * there to worry about
105
			 */
106
			firstline = 0;
107
			ignoring = doign == ignoreall;
108
		} else if (line[0] == '\n') {
109
			/*
110
			 * If line is blank, we've reached end of
111
			 * headers, so force out status: field
112
			 * and note that we are no longer in header
113
			 * fields
114
			 */
115
			if (dostat) {
116
				if (statusput(mp, obuf, prefix) == -1)
117
					goto out;
118
				dostat = 0;
119
			}
120
			ishead = 0;
121
			ignoring = doign == ignoreall;
122
		} else if (infld && (line[0] == ' ' || line[0] == '\t')) {
123
			/*
124
			 * If this line is a continuation (via space or tab)
125
			 * of a previous header field, just echo it
126
			 * (unless the field should be ignored).
127
			 * In other words, nothing to do.
128
			 */
129
		} else {
130
			/*
131
			 * Pick up the header field if we have one.
132
			 */
133
			for (cp = line;
134
			    (c = (unsigned char)*cp++) && c != ':' && !isspace(c); )
135
				;
136
			cp2 = --cp;
137
			while (isspace((unsigned char)*cp++))
138
				;
139
			if (cp[-1] != ':') {
140
				/*
141
				 * Not a header line, force out status:
142
				 * This happens in uucp style mail where
143
				 * there are no headers at all.
144
				 */
145
				if (dostat) {
146
					if (statusput(mp, obuf, prefix) == -1)
147
						goto out;
148
					dostat = 0;
149
				}
150
				if (doign != ignoreall)
151
					/* add blank line */
152
					(void)putc('\n', obuf);
153
				ishead = 0;
154
				ignoring = 0;
155
			} else {
156
				/*
157
				 * If it is an ignored field and
158
				 * we care about such things, skip it.
159
				 */
160
				*cp2 = 0;	/* temporarily null terminate */
161
				if (doign && isign(line, doign))
162
					ignoring = 1;
163
				else if (strcasecmp(line, "status") == 0) {
164
					/*
165
					 * If the field is "status," go compute
166
					 * and print the real Status: field
167
					 */
168
					if (dostat) {
169
						if (statusput(mp, obuf, prefix) == -1)
170
							goto out;
171
						dostat = 0;
172
					}
173
					ignoring = 1;
174
				} else {
175
					ignoring = 0;
176
					*cp2 = c;	/* restore */
177
				}
178
				infld = 1;
179
			}
180
		}
181
		if (!ignoring) {
182
			/*
183
			 * Strip trailing whitespace from prefix
184
			 * if line is blank.
185
			 */
186
			if (prefix != NULL) {
187
				if (length > 1)
188
					fputs(prefix, obuf);
189
				else
190
					(void)fwrite(prefix, sizeof(*prefix),
191
							prefixlen, obuf);
192
			}
193
			if (dovis) {
194
				length = strvis(visline, line, VIS_SAFE|VIS_NOSLASH);
195
				(void)fwrite(visline, sizeof(*visline), length, obuf);
196
			} else
197
				(void)fwrite(line, sizeof(*line), length, obuf);
198
			if (ferror(obuf))
199
				goto out;
200
		}
201
		if (sendsignal == SIGINT)
202
			goto out;
203
	}
204
	/*
205
	 * Copy out message body
206
	 */
207
	if (doign == ignoreall)
208
		count--;		/* skip final blank line */
209
	while (count > 0) {
210
		if (fgets(line, sizeof(line), ibuf) == NULL) {
211
			c = 0;
212
			break;
213
		}
214
		count -= c = strlen(line);
215
		if (prefix != NULL) {
216
			/*
217
			 * Strip trailing whitespace from prefix
218
			 * if line is blank.
219
			 */
220
			if (c > 1)
221
				fputs(prefix, obuf);
222
			else
223
				(void)fwrite(prefix, sizeof(*prefix),
224
						prefixlen, obuf);
225
		}
226
		/*
227
		 * We can't read the record file (or inbox for recipient)
228
		 * properly with 'From ' lines in the message body (from
229
		 * forwarded messages or sentences starting with "From "),
230
		 * so we will prepend those lines with a '>'.
231
		 */
232
		if (strncmp(line, "From ", 5) == 0)
233
			(void)fwrite(">", 1, 1, obuf); /* '>' before 'From ' */
234
		if (dovis) {
235
			length = strvis(visline, line, VIS_SAFE|VIS_NOSLASH);
236
			(void)fwrite(visline, sizeof(*visline), length, obuf);
237
		} else
238
			(void)fwrite(line, sizeof(*line), c, obuf);
239
		if (ferror(obuf) || sendsignal == SIGINT)
240
			goto out;
241
	}
242
	if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
243
		/* no final blank line */
244
		if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
245
			goto out;
246
	rval = 0;
247
out:
248
	sendsignal = 0;
249
	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
250
	(void)sigaction(SIGINT, &saveint, NULL);
251
	return(rval);
252
}
253
254
/*
255
 * Output a reasonable looking status field.
256
 */
257
int
258
statusput(struct message *mp, FILE *obuf, char *prefix)
259
{
260
	char statout[3];
261
	char *cp = statout;
262
263
	if (mp->m_flag & MREAD)
264
		*cp++ = 'R';
265
	if ((mp->m_flag & MNEW) == 0)
266
		*cp++ = 'O';
267
	*cp = 0;
268
	if (statout[0]) {
269
		fprintf(obuf, "%sStatus: %s\n",
270
			prefix == NULL ? "" : prefix, statout);
271
		return(ferror(obuf) ? -1 : 0);
272
	}
273
	return(0);
274
}
275
276
/*
277
 * Interface between the argument list and the mail1 routine
278
 * which does all the dirty work.
279
 */
280
int
281
mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts,
282
     char *fromaddr, char *subject)
283
{
284
4
	struct header head;
285
286
2
	head.h_to = to;
287
2
	head.h_from = fromaddr;
288
2
	head.h_subject = subject;
289
2
	head.h_cc = cc;
290
2
	head.h_bcc = bcc;
291
2
	head.h_smopts = smopts;
292
2
	mail1(&head, 0);
293
2
	return(0);
294
2
}
295
296
/*
297
 * Send mail to a bunch of user names.  The interface is through
298
 * the mail routine below.
299
 */
300
int
301
sendmail(void *v)
302
{
303
	char *str = v;
304
	struct header head;
305
306
	head.h_to = extract(str, GTO);
307
	head.h_from = NULL;
308
	head.h_subject = NULL;
309
	head.h_cc = NULL;
310
	head.h_bcc = NULL;
311
	head.h_smopts = NULL;
312
	mail1(&head, 0);
313
	return(0);
314
}
315
316
/*
317
 * Mail a message on standard input to the people indicated
318
 * in the passed header.  (Internal interface).
319
 */
320
void
321
mail1(struct header *hp, int printheaders)
322
{
323
	char *cp, *envfrom = NULL;
324
4
	char *argv[8];
325
2
	char **ap = argv;
326
	pid_t pid;
327
	struct name *to;
328
	FILE *mtf;
329
330
	/*
331
	 * Collect user's mail from standard input.
332
	 * Get the result as mtf.
333
	 */
334
2
	if ((mtf = collect(hp, printheaders)) == NULL)
335
		return;
336
2
	if (fsize(mtf) == 0) {
337
		if (value("skipempty") != NULL)
338
			goto out;
339
		if (hp->h_subject == NULL || *hp->h_subject == '\0')
340
			puts("No message, no subject; hope that's ok");
341
		else
342
			puts("Null message body; hope that's ok");
343
	}
344
	/*
345
	 * Now, take the user names from the combined
346
	 * to and cc lists and do all the alias
347
	 * processing.
348
	 */
349
2
	senderr = 0;
350
2
	to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
351
2
	if (to == NULL) {
352
		puts("No recipients specified");
353
		senderr++;
354
	}
355
	/*
356
	 * Look through the recipient list for names with /'s
357
	 * in them which we write to as files directly.
358
	 */
359
2
	to = outof(to, mtf, hp);
360
2
	if (senderr)
361
		savedeadletter(mtf);
362
2
	to = elide(to);
363
2
	if (count(to) == 0)
364
		goto out;
365
2
	fixhead(hp, to);
366
2
	if ((mtf = infix(hp, mtf)) == NULL) {
367
		fputs(". . . message lost, sorry.\n", stderr);
368
		return;
369
	}
370
2
	if ((cp = value("record")) != NULL)
371
		(void)savemail(expand(cp), mtf);
372
373
	/* Setup sendmail arguments. */
374
2
        *ap++ = "send-mail";
375
2
        *ap++ = "-i";
376
2
        *ap++ = "-t";
377
6
	cp = hp->h_from ? hp->h_from : value("from");
378
2
	if (cp != NULL) {
379
		envfrom = skin(cp);
380
		*ap++ = "-f";
381
		*ap++ = envfrom;
382
		if (envfrom == cp)
383
			envfrom = NULL;
384
	}
385
2
	if (value("metoo") != NULL)
386
                *ap++ = "-m";
387
2
	if (value("verbose") != NULL)
388
                *ap++ = "-v";
389
2
	*ap = NULL;
390
2
	if (debug) {
391
		fputs("Sendmail arguments:", stdout);
392
		for (ap = argv; *ap != NULL; ap++)
393
			printf(" \"%s\"", *ap);
394
		putchar('\n');
395
		goto out;
396
	}
397
	/*
398
	 * Fork, set up the temporary mail file as standard
399
	 * input for "mail", and exec with the user list we generated
400
	 * far above.
401
	 */
402
2
	pid = fork();
403
2
	if (pid == -1) {
404
		warn("fork");
405
		savedeadletter(mtf);
406
		goto out;
407
	}
408
2
	if (pid == 0) {
409
		sigset_t nset;
410
411
		sigemptyset(&nset);
412
		sigaddset(&nset, SIGHUP);
413
		sigaddset(&nset, SIGINT);
414
		sigaddset(&nset, SIGQUIT);
415
		sigaddset(&nset, SIGTSTP);
416
		sigaddset(&nset, SIGTTIN);
417
		sigaddset(&nset, SIGTTOU);
418
		prepare_child(&nset, fileno(mtf), -1);
419
		if ((cp = value("sendmail")) != NULL)
420
			cp = expand(cp);
421
		else
422
			cp = _PATH_SENDMAIL;
423
		execv(cp, argv);
424
		warn("%s", cp);
425
		_exit(1);
426
	}
427
2
	free(envfrom);
428
2
	if (value("verbose") != NULL)
429
		(void)wait_child(pid);
430
	else
431
2
		free_child(pid);
432
out:
433
2
	(void)Fclose(mtf);
434
4
}
435
436
/*
437
 * Fix the header by glopping all of the expanded names from
438
 * the distribution list into the appropriate fields.
439
 */
440
void
441
fixhead(struct header *hp, struct name *tolist)
442
{
443
	struct name *np;
444
445
4
	hp->h_to = NULL;
446
2
	hp->h_cc = NULL;
447
2
	hp->h_bcc = NULL;
448
8
	for (np = tolist; np != NULL; np = np->n_flink)
449
2
		if ((np->n_type & GMASK) == GTO)
450
2
			hp->h_to =
451
2
				cat(hp->h_to, nalloc(np->n_name, np->n_type));
452
		else if ((np->n_type & GMASK) == GCC)
453
			hp->h_cc =
454
				cat(hp->h_cc, nalloc(np->n_name, np->n_type));
455
		else if ((np->n_type & GMASK) == GBCC)
456
			hp->h_bcc =
457
				cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
458
2
}
459
460
/*
461
 * Prepend a header in front of the collected stuff
462
 * and return the new file.
463
 */
464
FILE *
465
infix(struct header *hp, FILE *fi)
466
{
467
	FILE *nfo, *nfi;
468
	int c, fd;
469
4
	char tempname[PATHSIZE];
470
471
4
	(void)snprintf(tempname, sizeof(tempname),
472
2
	    "%s/mail.RsXXXXXXXXXX", tmpdir);
473

4
	if ((fd = mkstemp(tempname)) == -1 ||
474
2
	    (nfo = Fdopen(fd, "w")) == NULL) {
475
		warn("%s", tempname);
476
		return(fi);
477
	}
478
2
	if ((nfi = Fopen(tempname, "r")) == NULL) {
479
		warn("%s", tempname);
480
		(void)Fclose(nfo);
481
		(void)rm(tempname);
482
		return(fi);
483
	}
484
2
	(void)rm(tempname);
485
2
	(void)puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
486

8
	c = getc(fi);
487
1065784
	while (c != EOF) {
488
1065780
		(void)putc(c, nfo);
489

2131560
		c = getc(fi);
490
	}
491

4
	if (ferror(fi)) {
492
		warn("read");
493
		rewind(fi);
494
		return(fi);
495
	}
496
2
	(void)fflush(nfo);
497

4
	if (ferror(nfo)) {
498
		warn("%s", tempname);
499
		(void)Fclose(nfo);
500
		(void)Fclose(nfi);
501
		rewind(fi);
502
		return(fi);
503
	}
504
2
	(void)Fclose(nfo);
505
2
	(void)Fclose(fi);
506
2
	rewind(nfi);
507
2
	return(nfi);
508
2
}
509
510
/*
511
 * Dump the to, subject, cc header on the
512
 * passed file buffer.
513
 */
514
int
515
puthead(struct header *hp, FILE *fo, int w)
516
{
517
	int gotcha;
518
	char *from;
519
520
	gotcha = 0;
521
8
	from = hp->h_from ? hp->h_from : value("from");
522
2
	if (from != NULL)
523
		fprintf(fo, "From: %s\n", from), gotcha++;
524

4
	if (hp->h_to != NULL && w & GTO)
525
2
		fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
526

4
	if (hp->h_subject != NULL && w & GSUBJECT)
527
2
		fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
528

2
	if (hp->h_cc != NULL && w & GCC)
529
		fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
530

2
	if (hp->h_bcc != NULL && w & GBCC)
531
		fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
532

4
	if (gotcha && w & GNL)
533
4
		(void)putc('\n', fo);
534
2
	return(0);
535
}
536
537
/*
538
 * Format the given header line to not exceed 72 characters.
539
 */
540
void
541
fmt(char *str, struct name *np, FILE *fo, int comma)
542
{
543
	int col, len;
544
545
4
	comma = comma ? 1 : 0;
546
2
	col = strlen(str);
547
2
	if (col)
548
2
		fputs(str, fo);
549
6
	for (; np != NULL; np = np->n_flink) {
550
2
		if (np->n_flink == NULL)
551
2
			comma = 0;
552
2
		len = strlen(np->n_name);
553
2
		col++;		/* for the space */
554
2
		if (col + len + comma > 72 && col > 4) {
555
			fputs("\n    ", fo);
556
			col = 4;
557
		} else
558
4
			putc(' ', fo);
559
2
		fputs(np->n_name, fo);
560
2
		if (comma)
561
			putc(',', fo);
562
2
		col += len + comma;
563
	}
564
4
	putc('\n', fo);
565
2
}
566
567
/*
568
 * Save the outgoing mail on the passed file.
569
 */
570
/*ARGSUSED*/
571
int
572
savemail(char *name, FILE *fi)
573
{
574
	FILE *fo;
575
	char buf[BUFSIZ];
576
	time_t now;
577
	mode_t m;
578
579
	m = umask(077);
580
	fo = Fopen(name, "a");
581
	(void)umask(m);
582
	if (fo == NULL) {
583
		warn("%s", name);
584
		return(-1);
585
	}
586
	(void)time(&now);
587
	fprintf(fo, "From %s %s", myname, ctime(&now));
588
	while (fgets(buf, sizeof(buf), fi) == buf) {
589
		/*
590
		 * We can't read the record file (or inbox for recipient)
591
		 * in the message body (from forwarded messages or sentences
592
		 * starting with "From "), so we will prepend those lines with
593
		 * a '>'.
594
		 */
595
		if (strncmp(buf, "From ", 5) == 0)
596
			(void)fwrite(">", 1, 1, fo);   /* '>' before 'From ' */
597
		(void)fwrite(buf, 1, strlen(buf), fo);
598
	}
599
	(void)putc('\n', fo);
600
	(void)fflush(fo);
601
	if (ferror(fo))
602
		warn("%s", name);
603
	(void)Fclose(fo);
604
	rewind(fi);
605
	return(0);
606
}
607
608
/*ARGSUSED*/
609
void
610
sendint(int s)
611
{
612
613
	sendsignal = s;
614
}