one-true-awk: Avoid a NULL dereference.
[freebsd-src.git] / usr.bin / mail / send.c
blob5be740565d73b581bc5e14e41f69956bba2e62f1
1 /*
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
30 #ifndef lint
31 #if 0
32 static char sccsid[] = "@(#)send.c 8.1 (Berkeley) 6/6/93";
33 #endif
34 #endif /* not lint */
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
38 #include "rcv.h"
39 #include "extern.h"
42 * Mail -- a mail program
44 * Mail to others.
48 * Send message described by the passed pointer to the
49 * passed output buffer. Return -1 on error.
50 * Adjust the status: field if need be.
51 * If doign is given, suppress ignored header fields.
52 * prefix is a string to prepend to each output line.
54 int
55 sendmessage(struct message *mp, FILE *obuf, struct ignoretab *doign,
56 char *prefix)
58 long count;
59 FILE *ibuf;
60 char *cp, *cp2, line[LINESIZE];
61 int ishead, infld, ignoring, dostat, firstline;
62 int c, length, prefixlen;
65 * Compute the prefix string, without trailing whitespace
67 if (prefix != NULL) {
68 cp2 = 0;
69 for (cp = prefix; *cp != '\0'; cp++)
70 if (*cp != ' ' && *cp != '\t')
71 cp2 = cp;
72 prefixlen = cp2 == NULL ? 0 : cp2 - prefix + 1;
74 ibuf = setinput(mp);
75 count = mp->m_size;
76 ishead = 1;
77 dostat = doign == 0 || !isign("status", doign);
78 infld = 0;
79 firstline = 1;
81 * Process headers first
83 while (count > 0 && ishead) {
84 if (fgets(line, sizeof(line), ibuf) == NULL)
85 break;
86 count -= length = strlen(line);
87 if (firstline) {
89 * First line is the From line, so no headers
90 * there to worry about
92 firstline = 0;
93 ignoring = doign == ignoreall;
94 } else if (line[0] == '\n') {
96 * If line is blank, we've reached end of
97 * headers, so force out status: field
98 * and note that we are no longer in header
99 * fields
101 if (dostat) {
102 statusput(mp, obuf, prefix);
103 dostat = 0;
105 ishead = 0;
106 ignoring = doign == ignoreall;
107 } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
109 * If this line is a continuation (via space or tab)
110 * of a previous header field, just echo it
111 * (unless the field should be ignored).
112 * In other words, nothing to do.
114 } else {
116 * Pick up the header field if we have one.
118 for (cp = line; (c = *cp++) != '\0' && c != ':' &&
119 !isspace((unsigned char)c);)
121 cp2 = --cp;
122 while (isspace((unsigned char)*cp++))
124 if (cp[-1] != ':') {
126 * Not a header line, force out status:
127 * This happens in uucp style mail where
128 * there are no headers at all.
130 if (dostat) {
131 statusput(mp, obuf, prefix);
132 dostat = 0;
134 if (doign != ignoreall)
135 /* add blank line */
136 (void)putc('\n', obuf);
137 ishead = 0;
138 ignoring = 0;
139 } else {
141 * If it is an ignored field and
142 * we care about such things, skip it.
144 *cp2 = '\0'; /* temporarily null terminate */
145 if (doign && isign(line, doign))
146 ignoring = 1;
147 else if ((line[0] == 's' || line[0] == 'S') &&
148 strcasecmp(line, "status") == 0) {
150 * If the field is "status," go compute
151 * and print the real Status: field
153 if (dostat) {
154 statusput(mp, obuf, prefix);
155 dostat = 0;
157 ignoring = 1;
158 } else {
159 ignoring = 0;
160 *cp2 = c; /* restore */
162 infld = 1;
165 if (!ignoring) {
167 * Strip trailing whitespace from prefix
168 * if line is blank.
170 if (prefix != NULL) {
171 if (length > 1)
172 fputs(prefix, obuf);
173 else
174 (void)fwrite(prefix, sizeof(*prefix),
175 prefixlen, obuf);
177 (void)fwrite(line, sizeof(*line), length, obuf);
178 if (ferror(obuf))
179 return (-1);
183 * Copy out message body
185 if (doign == ignoreall)
186 count--; /* skip final blank line */
187 if (prefix != NULL)
188 while (count > 0) {
189 if (fgets(line, sizeof(line), ibuf) == NULL) {
190 c = 0;
191 break;
193 count -= c = strlen(line);
195 * Strip trailing whitespace from prefix
196 * if line is blank.
198 if (c > 1)
199 fputs(prefix, obuf);
200 else
201 (void)fwrite(prefix, sizeof(*prefix),
202 prefixlen, obuf);
203 (void)fwrite(line, sizeof(*line), c, obuf);
204 if (ferror(obuf))
205 return (-1);
207 else
208 while (count > 0) {
209 c = count < LINESIZE ? count : LINESIZE;
210 if ((c = fread(line, sizeof(*line), c, ibuf)) <= 0)
211 break;
212 count -= c;
213 if (fwrite(line, sizeof(*line), c, obuf) != c)
214 return (-1);
216 if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
217 /* no final blank line */
218 if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
219 return (-1);
220 return (0);
224 * Output a reasonable looking status field.
226 void
227 statusput(struct message *mp, FILE *obuf, char *prefix)
229 char statout[3];
230 char *cp = statout;
232 if (mp->m_flag & MREAD)
233 *cp++ = 'R';
234 if ((mp->m_flag & MNEW) == 0)
235 *cp++ = 'O';
236 *cp = '\0';
237 if (statout[0] != '\0')
238 fprintf(obuf, "%sStatus: %s\n",
239 prefix == NULL ? "" : prefix, statout);
243 * Interface between the argument list and the mail1 routine
244 * which does all the dirty work.
247 mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts,
248 char *subject, char *replyto)
250 struct header head;
252 head.h_to = to;
253 head.h_subject = subject;
254 head.h_cc = cc;
255 head.h_bcc = bcc;
256 head.h_smopts = smopts;
257 head.h_replyto = replyto;
258 head.h_inreplyto = NULL;
259 mail1(&head, 0);
260 return (0);
265 * Send mail to a bunch of user names. The interface is through
266 * the mail routine below.
269 sendmail(char *str)
271 struct header head;
273 head.h_to = extract(str, GTO);
274 head.h_subject = NULL;
275 head.h_cc = NULL;
276 head.h_bcc = NULL;
277 head.h_smopts = NULL;
278 head.h_replyto = value("REPLYTO");
279 head.h_inreplyto = NULL;
280 mail1(&head, 0);
281 return (0);
285 * Mail a message on standard input to the people indicated
286 * in the passed header. (Internal interface).
288 void
289 mail1(struct header *hp, int printheaders)
291 char *cp;
292 char *nbuf;
293 int pid;
294 char **namelist;
295 struct name *to, *nsto;
296 FILE *mtf;
299 * Collect user's mail from standard input.
300 * Get the result as mtf.
302 if ((mtf = collect(hp, printheaders)) == NULL)
303 return;
304 if (value("interactive") != NULL) {
305 if (value("askcc") != NULL || value("askbcc") != NULL) {
306 if (value("askcc") != NULL)
307 grabh(hp, GCC);
308 if (value("askbcc") != NULL)
309 grabh(hp, GBCC);
310 } else {
311 printf("EOT\n");
312 (void)fflush(stdout);
315 if (fsize(mtf) == 0) {
316 if (value("dontsendempty") != NULL)
317 goto out;
318 if (hp->h_subject == NULL)
319 printf("No message, no subject; hope that's ok\n");
320 else
321 printf("Null message body; hope that's ok\n");
324 * Now, take the user names from the combined
325 * to and cc lists and do all the alias
326 * processing.
328 senderr = 0;
329 to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
330 if (to == NULL) {
331 printf("No recipients specified\n");
332 senderr++;
335 * Look through the recipient list for names with /'s
336 * in them which we write to as files directly.
338 to = outof(to, mtf, hp);
339 if (senderr)
340 savedeadletter(mtf);
341 to = elide(to);
342 if (count(to) == 0)
343 goto out;
344 if (value("recordrecip") != NULL) {
346 * Before fixing the header, save old To:.
347 * We do this because elide above has sorted To: list, and
348 * we would like to save message in a file named by the first
349 * recipient the user has entered, not the one being the first
350 * after sorting happened.
352 if ((nsto = malloc(sizeof(struct name))) == NULL)
353 err(1, "Out of memory");
354 bcopy(hp->h_to, nsto, sizeof(struct name));
356 fixhead(hp, to);
357 if ((mtf = infix(hp, mtf)) == NULL) {
358 fprintf(stderr, ". . . message lost, sorry.\n");
359 return;
361 namelist = unpack(cat(hp->h_smopts, to));
362 if (debug) {
363 char **t;
365 printf("Sendmail arguments:");
366 for (t = namelist; *t != NULL; t++)
367 printf(" \"%s\"", *t);
368 printf("\n");
369 goto out;
371 if (value("recordrecip") != NULL) {
373 * Extract first recipient username from saved To: and use it
374 * as a filename.
376 if ((nbuf = malloc(strlen(detract(nsto, 0)) + 1)) == NULL)
377 err(1, "Out of memory");
378 if ((cp = yanklogin(detract(nsto, 0), nbuf)) != NULL)
379 (void)savemail(expand(nbuf), mtf);
380 free(nbuf);
381 free(nsto);
382 } else if ((cp = value("record")) != NULL)
383 (void)savemail(expand(cp), mtf);
385 * Fork, set up the temporary mail file as standard
386 * input for "mail", and exec with the user list we generated
387 * far above.
389 pid = fork();
390 if (pid == -1) {
391 warn("fork");
392 savedeadletter(mtf);
393 goto out;
395 if (pid == 0) {
396 sigset_t nset;
397 (void)sigemptyset(&nset);
398 (void)sigaddset(&nset, SIGHUP);
399 (void)sigaddset(&nset, SIGINT);
400 (void)sigaddset(&nset, SIGQUIT);
401 (void)sigaddset(&nset, SIGTSTP);
402 (void)sigaddset(&nset, SIGTTIN);
403 (void)sigaddset(&nset, SIGTTOU);
404 prepare_child(&nset, fileno(mtf), -1);
405 if ((cp = value("sendmail")) != NULL)
406 cp = expand(cp);
407 else
408 cp = _PATH_SENDMAIL;
409 execv(cp, namelist);
410 warn("%s", cp);
411 _exit(1);
413 if (value("verbose") != NULL)
414 (void)wait_child(pid);
415 else
416 free_child(pid);
417 out:
418 (void)Fclose(mtf);
422 * Fix the header by glopping all of the expanded names from
423 * the distribution list into the appropriate fields.
425 void
426 fixhead(struct header *hp, struct name *tolist)
428 struct name *np;
430 hp->h_to = NULL;
431 hp->h_cc = NULL;
432 hp->h_bcc = NULL;
433 for (np = tolist; np != NULL; np = np->n_flink) {
434 /* Don't copy deleted addresses to the header */
435 if (np->n_type & GDEL)
436 continue;
437 if ((np->n_type & GMASK) == GTO)
438 hp->h_to =
439 cat(hp->h_to, nalloc(np->n_name, np->n_type));
440 else if ((np->n_type & GMASK) == GCC)
441 hp->h_cc =
442 cat(hp->h_cc, nalloc(np->n_name, np->n_type));
443 else if ((np->n_type & GMASK) == GBCC)
444 hp->h_bcc =
445 cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
450 * Prepend a header in front of the collected stuff
451 * and return the new file.
453 FILE *
454 infix(struct header *hp, FILE *fi)
456 FILE *nfo, *nfi;
457 int c, fd;
458 char tempname[PATHSIZE];
460 (void)snprintf(tempname, sizeof(tempname),
461 "%s/mail.RsXXXXXXXXXX", tmpdir);
462 if ((fd = mkstemp(tempname)) == -1 ||
463 (nfo = Fdopen(fd, "w")) == NULL) {
464 warn("%s", tempname);
465 return (fi);
467 if ((nfi = Fopen(tempname, "r")) == NULL) {
468 warn("%s", tempname);
469 (void)Fclose(nfo);
470 (void)rm(tempname);
471 return (fi);
473 (void)rm(tempname);
474 (void)puthead(hp, nfo,
475 GTO|GSUBJECT|GCC|GBCC|GREPLYTO|GINREPLYTO|GNL|GCOMMA);
476 c = getc(fi);
477 while (c != EOF) {
478 (void)putc(c, nfo);
479 c = getc(fi);
481 if (ferror(fi)) {
482 warnx("read");
483 rewind(fi);
484 return (fi);
486 (void)fflush(nfo);
487 if (ferror(nfo)) {
488 warn("%s", tempname);
489 (void)Fclose(nfo);
490 (void)Fclose(nfi);
491 rewind(fi);
492 return (fi);
494 (void)Fclose(nfo);
495 (void)Fclose(fi);
496 rewind(nfi);
497 return (nfi);
501 * Dump the to, subject, cc header on the
502 * passed file buffer.
505 puthead(struct header *hp, FILE *fo, int w)
507 int gotcha;
509 gotcha = 0;
510 if (hp->h_to != NULL && w & GTO)
511 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
512 if (hp->h_subject != NULL && w & GSUBJECT)
513 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
514 if (hp->h_cc != NULL && w & GCC)
515 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
516 if (hp->h_bcc != NULL && w & GBCC)
517 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
518 if (hp->h_replyto != NULL && w & GREPLYTO)
519 fprintf(fo, "Reply-To: %s\n", hp->h_replyto), gotcha++;
520 if (hp->h_inreplyto != NULL && w & GINREPLYTO)
521 fprintf(fo, "In-Reply-To: <%s>\n", hp->h_inreplyto), gotcha++;
522 if (gotcha && w & GNL)
523 (void)putc('\n', fo);
524 return (0);
528 * Format the given header line to not exceed 72 characters.
530 void
531 fmt(const char *str, struct name *np, FILE *fo, int comma)
533 int col, len;
535 comma = comma ? 1 : 0;
536 col = strlen(str);
537 if (col)
538 fputs(str, fo);
539 for (; np != NULL; np = np->n_flink) {
540 if (np->n_flink == NULL)
541 comma = 0;
542 len = strlen(np->n_name);
543 col++; /* for the space */
544 if (col + len + comma > 72 && col > 4) {
545 fprintf(fo, "\n ");
546 col = 4;
547 } else
548 fprintf(fo, " ");
549 fputs(np->n_name, fo);
550 if (comma)
551 fprintf(fo, ",");
552 col += len + comma;
554 fprintf(fo, "\n");
558 * Save the outgoing mail on the passed file.
561 /*ARGSUSED*/
563 savemail(char name[], FILE *fi)
565 FILE *fo;
566 char buf[BUFSIZ];
567 int i;
568 time_t now;
570 if ((fo = Fopen(name, "a")) == NULL) {
571 warn("%s", name);
572 return (-1);
574 (void)time(&now);
575 fprintf(fo, "From %s %s", myname, ctime(&now));
576 while ((i = fread(buf, 1, sizeof(buf), fi)) > 0)
577 (void)fwrite(buf, 1, i, fo);
578 fprintf(fo, "\n");
579 (void)fflush(fo);
580 if (ferror(fo))
581 warn("%s", name);
582 (void)Fclose(fo);
583 rewind(fi);
584 return (0);