Annotation of src/usr.bin/mail/send.c, Revision 1.19
1.19 ! martynas 1: /* $OpenBSD: send.c,v 1.18 2007/03/20 21:01:08 millert Exp $ */
1.2 deraadt 2: /* $NetBSD: send.c,v 1.6 1996/06/08 19:48:39 christos Exp $ */
3:
1.1 deraadt 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.
1.17 millert 16: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 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: #ifndef lint
1.2 deraadt 34: #if 0
1.14 millert 35: static const char sccsid[] = "@(#)send.c 8.1 (Berkeley) 6/6/93";
1.2 deraadt 36: #else
1.19 ! martynas 37: static const char rcsid[] = "$OpenBSD: send.c,v 1.18 2007/03/20 21:01:08 millert Exp $";
1.2 deraadt 38: #endif
1.1 deraadt 39: #endif /* not lint */
40:
41: #include "rcv.h"
42: #include "extern.h"
43:
1.15 millert 44: static volatile sig_atomic_t sendsignal; /* Interrupted by a signal? */
45:
1.1 deraadt 46: /*
47: * Mail -- a mail program
48: *
49: * Mail to others.
50: */
51:
52: /*
53: * Send message described by the passed pointer to the
54: * passed output buffer. Return -1 on error.
55: * Adjust the status: field if need be.
56: * If doign is given, suppress ignored header fields.
57: * prefix is a string to prepend to each output line.
58: */
59: int
1.14 millert 60: sendmessage(struct message *mp, FILE *obuf, struct ignoretab *doign,
61: char *prefix)
1.1 deraadt 62: {
1.7 millert 63: int count;
1.9 millert 64: FILE *ibuf;
1.1 deraadt 65: char line[LINESIZE];
1.16 pjanzen 66: char visline[4 * LINESIZE - 3];
1.2 deraadt 67: int ishead, infld, ignoring = 0, dostat, firstline;
1.9 millert 68: char *cp, *cp2;
69: int c = 0;
1.1 deraadt 70: int length;
1.2 deraadt 71: int prefixlen = 0;
1.15 millert 72: int rval;
1.16 pjanzen 73: int dovis;
1.15 millert 74: struct sigaction act, saveint;
75: sigset_t oset;
76:
77: sendsignal = 0;
78: rval = -1;
1.16 pjanzen 79: dovis = isatty(fileno(obuf));
1.15 millert 80: sigemptyset(&act.sa_mask);
81: act.sa_flags = SA_RESTART;
82: act.sa_handler = sendint;
83: (void)sigaction(SIGINT, &act, &saveint);
84: (void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
1.1 deraadt 85:
86: /*
87: * Compute the prefix string, without trailing whitespace
88: */
1.5 millert 89: if (prefix != NULL) {
1.1 deraadt 90: cp2 = 0;
91: for (cp = prefix; *cp; cp++)
92: if (*cp != ' ' && *cp != '\t')
93: cp2 = cp;
94: prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
95: }
96: ibuf = setinput(mp);
97: count = mp->m_size;
98: ishead = 1;
99: dostat = doign == 0 || !isign("status", doign);
100: infld = 0;
101: firstline = 1;
102: /*
103: * Process headers first
104: */
105: while (count > 0 && ishead) {
1.3 millert 106: if (fgets(line, sizeof(line), ibuf) == NULL)
1.1 deraadt 107: break;
108: count -= length = strlen(line);
109: if (firstline) {
1.3 millert 110: /*
1.1 deraadt 111: * First line is the From line, so no headers
112: * there to worry about
113: */
114: firstline = 0;
115: ignoring = doign == ignoreall;
116: } else if (line[0] == '\n') {
117: /*
118: * If line is blank, we've reached end of
119: * headers, so force out status: field
120: * and note that we are no longer in header
121: * fields
122: */
123: if (dostat) {
1.15 millert 124: if (statusput(mp, obuf, prefix) == -1)
125: goto out;
1.1 deraadt 126: dostat = 0;
127: }
128: ishead = 0;
129: ignoring = doign == ignoreall;
130: } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
131: /*
132: * If this line is a continuation (via space or tab)
133: * of a previous header field, just echo it
134: * (unless the field should be ignored).
135: * In other words, nothing to do.
136: */
137: } else {
138: /*
139: * Pick up the header field if we have one.
140: */
141: for (cp = line; (c = *cp++) && c != ':' && !isspace(c);)
142: ;
143: cp2 = --cp;
144: while (isspace(*cp++))
145: ;
146: if (cp[-1] != ':') {
147: /*
148: * Not a header line, force out status:
149: * This happens in uucp style mail where
150: * there are no headers at all.
151: */
152: if (dostat) {
1.15 millert 153: if (statusput(mp, obuf, prefix) == -1)
154: goto out;
1.1 deraadt 155: dostat = 0;
156: }
157: if (doign != ignoreall)
158: /* add blank line */
1.4 millert 159: (void)putc('\n', obuf);
1.1 deraadt 160: ishead = 0;
161: ignoring = 0;
162: } else {
163: /*
164: * If it is an ignored field and
165: * we care about such things, skip it.
166: */
167: *cp2 = 0; /* temporarily null terminate */
168: if (doign && isign(line, doign))
169: ignoring = 1;
1.19 ! martynas 170: else if (strcasecmp(line, "status") == 0) {
1.1 deraadt 171: /*
172: * If the field is "status," go compute
173: * and print the real Status: field
174: */
175: if (dostat) {
1.15 millert 176: if (statusput(mp, obuf, prefix) == -1)
177: goto out;
1.1 deraadt 178: dostat = 0;
179: }
180: ignoring = 1;
181: } else {
182: ignoring = 0;
183: *cp2 = c; /* restore */
184: }
185: infld = 1;
186: }
187: }
188: if (!ignoring) {
189: /*
190: * Strip trailing whitespace from prefix
191: * if line is blank.
192: */
1.13 millert 193: if (prefix != NULL) {
1.1 deraadt 194: if (length > 1)
195: fputs(prefix, obuf);
196: else
1.4 millert 197: (void)fwrite(prefix, sizeof(*prefix),
1.1 deraadt 198: prefixlen, obuf);
1.13 millert 199: }
1.16 pjanzen 200: if (dovis) {
201: length = strvis(visline, line, VIS_SAFE|VIS_NOSLASH);
202: (void)fwrite(visline, sizeof(*visline), length, obuf);
203: } else
204: (void)fwrite(line, sizeof(*line), length, obuf);
1.1 deraadt 205: if (ferror(obuf))
1.15 millert 206: goto out;
1.1 deraadt 207: }
1.15 millert 208: if (sendsignal == SIGINT)
209: goto out;
1.1 deraadt 210: }
211: /*
212: * Copy out message body
213: */
214: if (doign == ignoreall)
215: count--; /* skip final blank line */
1.13 millert 216: while (count > 0) {
217: if (fgets(line, sizeof(line), ibuf) == NULL) {
218: c = 0;
219: break;
220: }
221: count -= c = strlen(line);
222: if (prefix != NULL) {
1.1 deraadt 223: /*
224: * Strip trailing whitespace from prefix
225: * if line is blank.
226: */
227: if (c > 1)
228: fputs(prefix, obuf);
229: else
1.4 millert 230: (void)fwrite(prefix, sizeof(*prefix),
1.1 deraadt 231: prefixlen, obuf);
232: }
1.13 millert 233: /*
234: * We can't read the record file (or inbox for recipient)
235: * properly with 'From ' lines in the message body (from
236: * forwarded messages or sentences starting with "From "),
237: * so we will prepend those lines with a '>'.
238: */
239: if (strncmp(line, "From ", 5) == 0)
240: (void)fwrite(">", 1, 1, obuf); /* '>' before 'From ' */
1.16 pjanzen 241: if (dovis) {
242: length = strvis(visline, line, VIS_SAFE|VIS_NOSLASH);
243: (void)fwrite(visline, sizeof(*visline), length, obuf);
244: } else
245: (void)fwrite(line, sizeof(*line), c, obuf);
1.15 millert 246: if (ferror(obuf) || sendsignal == SIGINT)
247: goto out;
1.13 millert 248: }
1.1 deraadt 249: if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
250: /* no final blank line */
251: if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
1.15 millert 252: goto out;
253: rval = 0;
254: out:
255: sendsignal = 0;
256: (void)sigprocmask(SIG_SETMASK, &oset, NULL);
257: (void)sigaction(SIGINT, &saveint, NULL);
258: return(rval);
1.1 deraadt 259: }
260:
261: /*
262: * Output a reasonable looking status field.
263: */
1.15 millert 264: int
1.14 millert 265: statusput(struct message *mp, FILE *obuf, char *prefix)
1.1 deraadt 266: {
267: char statout[3];
1.9 millert 268: char *cp = statout;
1.1 deraadt 269:
270: if (mp->m_flag & MREAD)
271: *cp++ = 'R';
272: if ((mp->m_flag & MNEW) == 0)
273: *cp++ = 'O';
274: *cp = 0;
1.15 millert 275: if (statout[0]) {
1.1 deraadt 276: fprintf(obuf, "%sStatus: %s\n",
1.5 millert 277: prefix == NULL ? "" : prefix, statout);
1.15 millert 278: return(ferror(obuf) ? -1 : 0);
279: }
280: return(0);
1.1 deraadt 281: }
282:
283: /*
284: * Interface between the argument list and the mail1 routine
285: * which does all the dirty work.
286: */
287: int
1.14 millert 288: mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts,
289: char *subject)
1.1 deraadt 290: {
291: struct header head;
292:
293: head.h_to = to;
294: head.h_subject = subject;
295: head.h_cc = cc;
296: head.h_bcc = bcc;
297: head.h_smopts = smopts;
298: mail1(&head, 0);
299: return(0);
300: }
301:
302:
303: /*
304: * Send mail to a bunch of user names. The interface is through
305: * the mail routine below.
306: */
307: int
1.14 millert 308: sendmail(void *v)
1.1 deraadt 309: {
1.2 deraadt 310: char *str = v;
1.1 deraadt 311: struct header head;
312:
313: head.h_to = extract(str, GTO);
1.5 millert 314: head.h_subject = NULL;
1.14 millert 315: head.h_cc = NULL;
316: head.h_bcc = NULL;
317: head.h_smopts = NULL;
1.1 deraadt 318: mail1(&head, 0);
319: return(0);
320: }
321:
322: /*
323: * Mail a message on standard input to the people indicated
324: * in the passed header. (Internal interface).
325: */
326: void
1.14 millert 327: mail1(struct header *hp, int printheaders)
1.1 deraadt 328: {
329: char *cp;
1.14 millert 330: pid_t pid;
1.1 deraadt 331: char **namelist;
332: struct name *to;
333: FILE *mtf;
334:
335: /*
336: * Collect user's mail from standard input.
337: * Get the result as mtf.
338: */
339: if ((mtf = collect(hp, printheaders)) == NULL)
340: return;
1.13 millert 341: if (fsize(mtf) == 0) {
1.5 millert 342: if (hp->h_subject == NULL)
1.3 millert 343: puts("No message, no subject; hope that's ok");
1.1 deraadt 344: else
1.3 millert 345: puts("Null message body; hope that's ok");
1.13 millert 346: }
1.1 deraadt 347: /*
348: * Now, take the user names from the combined
349: * to and cc lists and do all the alias
350: * processing.
351: */
352: senderr = 0;
353: to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
1.14 millert 354: if (to == NULL) {
1.3 millert 355: puts("No recipients specified");
1.1 deraadt 356: senderr++;
357: }
358: /*
359: * Look through the recipient list for names with /'s
360: * in them which we write to as files directly.
361: */
362: to = outof(to, mtf, hp);
363: if (senderr)
364: savedeadletter(mtf);
365: to = elide(to);
366: if (count(to) == 0)
367: goto out;
368: fixhead(hp, to);
369: if ((mtf = infix(hp, mtf)) == NULL) {
1.3 millert 370: fputs(". . . message lost, sorry.\n", stderr);
1.1 deraadt 371: return;
372: }
1.12 mickey 373: namelist = unpack(hp->h_smopts, to);
1.1 deraadt 374: if (debug) {
375: char **t;
376:
1.3 millert 377: fputs("Sendmail arguments:", stdout);
1.5 millert 378: for (t = namelist; *t != NULL; t++)
1.1 deraadt 379: printf(" \"%s\"", *t);
1.3 millert 380: putchar('\n');
1.1 deraadt 381: goto out;
382: }
1.5 millert 383: if ((cp = value("record")) != NULL)
1.4 millert 384: (void)savemail(expand(cp), mtf);
1.1 deraadt 385: /*
386: * Fork, set up the temporary mail file as standard
387: * input for "mail", and exec with the user list we generated
388: * far above.
389: */
390: pid = fork();
391: if (pid == -1) {
1.3 millert 392: warn("fork");
1.1 deraadt 393: savedeadletter(mtf);
394: goto out;
395: }
396: if (pid == 0) {
1.2 deraadt 397: sigset_t nset;
1.6 millert 398:
1.2 deraadt 399: sigemptyset(&nset);
400: sigaddset(&nset, SIGHUP);
401: sigaddset(&nset, SIGINT);
402: sigaddset(&nset, SIGQUIT);
403: sigaddset(&nset, SIGTSTP);
404: sigaddset(&nset, SIGTTIN);
405: sigaddset(&nset, SIGTTOU);
406: prepare_child(&nset, fileno(mtf), -1);
1.5 millert 407: if ((cp = value("sendmail")) != NULL)
1.1 deraadt 408: cp = expand(cp);
409: else
410: cp = _PATH_SENDMAIL;
411: execv(cp, namelist);
1.11 millert 412: warn("%s", cp);
1.1 deraadt 413: _exit(1);
414: }
1.5 millert 415: if (value("verbose") != NULL)
1.4 millert 416: (void)wait_child(pid);
1.1 deraadt 417: else
418: free_child(pid);
419: out:
1.3 millert 420: (void)Fclose(mtf);
1.1 deraadt 421: }
422:
423: /*
424: * Fix the header by glopping all of the expanded names from
425: * the distribution list into the appropriate fields.
426: */
427: void
1.14 millert 428: fixhead(struct header *hp, struct name *tolist)
1.1 deraadt 429: {
1.9 millert 430: struct name *np;
1.1 deraadt 431:
1.14 millert 432: hp->h_to = NULL;
433: hp->h_cc = NULL;
434: hp->h_bcc = NULL;
435: for (np = tolist; np != NULL; np = np->n_flink)
1.1 deraadt 436: if ((np->n_type & GMASK) == GTO)
437: hp->h_to =
438: cat(hp->h_to, nalloc(np->n_name, np->n_type));
439: else if ((np->n_type & GMASK) == GCC)
440: hp->h_cc =
441: cat(hp->h_cc, nalloc(np->n_name, np->n_type));
442: else if ((np->n_type & GMASK) == GBCC)
443: hp->h_bcc =
444: cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
445: }
446:
447: /*
448: * Prepend a header in front of the collected stuff
449: * and return the new file.
450: */
451: FILE *
1.14 millert 452: infix(struct header *hp, FILE *fi)
1.1 deraadt 453: {
1.9 millert 454: FILE *nfo, *nfi;
1.8 millert 455: int c, fd;
456: char tempname[PATHSIZE];
1.1 deraadt 457:
1.8 millert 458: (void)snprintf(tempname, sizeof(tempname),
459: "%s/mail.RsXXXXXXXXXX", tmpdir);
460: if ((fd = mkstemp(tempname)) == -1 ||
461: (nfo = Fdopen(fd, "w")) == NULL) {
1.11 millert 462: warn("%s", tempname);
1.1 deraadt 463: return(fi);
464: }
1.8 millert 465: if ((nfi = Fopen(tempname, "r")) == NULL) {
1.11 millert 466: warn("%s", tempname);
1.3 millert 467: (void)Fclose(nfo);
1.8 millert 468: (void)rm(tempname);
1.1 deraadt 469: return(fi);
470: }
1.8 millert 471: (void)rm(tempname);
1.4 millert 472: (void)puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
1.1 deraadt 473: c = getc(fi);
474: while (c != EOF) {
1.4 millert 475: (void)putc(c, nfo);
1.1 deraadt 476: c = getc(fi);
477: }
478: if (ferror(fi)) {
1.3 millert 479: warn("read");
1.1 deraadt 480: rewind(fi);
481: return(fi);
482: }
1.4 millert 483: (void)fflush(nfo);
1.1 deraadt 484: if (ferror(nfo)) {
1.11 millert 485: warn("%s", tempname);
1.3 millert 486: (void)Fclose(nfo);
487: (void)Fclose(nfi);
1.1 deraadt 488: rewind(fi);
489: return(fi);
490: }
1.3 millert 491: (void)Fclose(nfo);
492: (void)Fclose(fi);
1.1 deraadt 493: rewind(nfi);
494: return(nfi);
495: }
496:
497: /*
498: * Dump the to, subject, cc header on the
499: * passed file buffer.
500: */
501: int
1.14 millert 502: puthead(struct header *hp, FILE *fo, int w)
1.1 deraadt 503: {
1.9 millert 504: int gotcha;
1.1 deraadt 505:
506: gotcha = 0;
1.14 millert 507: if (hp->h_to != NULL && w & GTO)
1.1 deraadt 508: fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
1.5 millert 509: if (hp->h_subject != NULL && w & GSUBJECT)
1.1 deraadt 510: fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
1.14 millert 511: if (hp->h_cc != NULL && w & GCC)
1.1 deraadt 512: fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
1.14 millert 513: if (hp->h_bcc != NULL && w & GBCC)
1.1 deraadt 514: fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
515: if (gotcha && w & GNL)
1.4 millert 516: (void)putc('\n', fo);
1.1 deraadt 517: return(0);
518: }
519:
520: /*
521: * Format the given header line to not exceed 72 characters.
522: */
523: void
1.14 millert 524: fmt(char *str, struct name *np, FILE *fo, int comma)
1.1 deraadt 525: {
1.9 millert 526: int col, len;
1.1 deraadt 527:
528: comma = comma ? 1 : 0;
529: col = strlen(str);
530: if (col)
531: fputs(str, fo);
1.14 millert 532: for (; np != NULL; np = np->n_flink) {
533: if (np->n_flink == NULL)
1.1 deraadt 534: comma = 0;
535: len = strlen(np->n_name);
536: col++; /* for the space */
537: if (col + len + comma > 72 && col > 4) {
538: fputs("\n ", fo);
539: col = 4;
540: } else
541: putc(' ', fo);
542: fputs(np->n_name, fo);
543: if (comma)
544: putc(',', fo);
545: col += len + comma;
546: }
547: putc('\n', fo);
548: }
549:
550: /*
551: * Save the outgoing mail on the passed file.
552: */
553: /*ARGSUSED*/
554: int
1.14 millert 555: savemail(char *name, FILE *fi)
1.1 deraadt 556: {
1.9 millert 557: FILE *fo;
1.1 deraadt 558: char buf[BUFSIZ];
1.2 deraadt 559: time_t now;
1.18 millert 560: mode_t m;
1.1 deraadt 561:
1.18 millert 562: m = umask(077);
563: fo = Fopen(name, "a");
564: (void)umask(m);
565: if (fo == NULL) {
1.11 millert 566: warn("%s", name);
1.3 millert 567: return(-1);
1.1 deraadt 568: }
1.4 millert 569: (void)time(&now);
1.1 deraadt 570: fprintf(fo, "From %s %s", myname, ctime(&now));
1.13 millert 571: while (fgets(buf, sizeof(buf), fi) == buf) {
572: /*
573: * We can't read the record file (or inbox for recipient)
574: * in the message body (from forwarded messages or sentences
575: * starting with "From "), so we will prepend those lines with
576: * a '>'.
577: */
578: if (strncmp(buf, "From ", 5) == 0)
579: (void)fwrite(">", 1, 1, fo); /* '>' before 'From ' */
580: (void)fwrite(buf, 1, strlen(buf), fo);
581: }
1.4 millert 582: (void)putc('\n', fo);
583: (void)fflush(fo);
1.1 deraadt 584: if (ferror(fo))
1.11 millert 585: warn("%s", name);
1.3 millert 586: (void)Fclose(fo);
1.1 deraadt 587: rewind(fi);
1.3 millert 588: return(0);
1.15 millert 589: }
590:
591: /*ARGSUSED*/
592: void
593: sendint(int s)
594: {
595:
596: sendsignal = s;
1.1 deraadt 597: }