Annotation of src/usr.bin/mail/send.c, Revision 1.25
1.25 ! millert 1: /* $OpenBSD: send.c,v 1.24 2015/01/20 16:59:07 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: #include "rcv.h"
34: #include "extern.h"
35:
1.15 millert 36: static volatile sig_atomic_t sendsignal; /* Interrupted by a signal? */
37:
1.1 deraadt 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
1.14 millert 52: sendmessage(struct message *mp, FILE *obuf, struct ignoretab *doign,
53: char *prefix)
1.1 deraadt 54: {
1.7 millert 55: int count;
1.9 millert 56: FILE *ibuf;
1.1 deraadt 57: char line[LINESIZE];
1.16 pjanzen 58: char visline[4 * LINESIZE - 3];
1.2 deraadt 59: int ishead, infld, ignoring = 0, dostat, firstline;
1.9 millert 60: char *cp, *cp2;
61: int c = 0;
1.1 deraadt 62: int length;
1.2 deraadt 63: int prefixlen = 0;
1.15 millert 64: int rval;
1.16 pjanzen 65: int dovis;
1.15 millert 66: struct sigaction act, saveint;
67: sigset_t oset;
68:
69: sendsignal = 0;
70: rval = -1;
1.16 pjanzen 71: dovis = isatty(fileno(obuf));
1.15 millert 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);
1.1 deraadt 77:
78: /*
79: * Compute the prefix string, without trailing whitespace
80: */
1.5 millert 81: if (prefix != NULL) {
1.1 deraadt 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) {
1.3 millert 98: if (fgets(line, sizeof(line), ibuf) == NULL)
1.1 deraadt 99: break;
100: count -= length = strlen(line);
101: if (firstline) {
1.3 millert 102: /*
1.1 deraadt 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) {
1.15 millert 116: if (statusput(mp, obuf, prefix) == -1)
117: goto out;
1.1 deraadt 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: */
1.23 okan 133: for (cp = line;
134: (c = (unsigned char)*cp++) && c != ':' && !isspace(c); )
1.1 deraadt 135: ;
136: cp2 = --cp;
1.23 okan 137: while (isspace((unsigned char)*cp++))
1.1 deraadt 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) {
1.15 millert 146: if (statusput(mp, obuf, prefix) == -1)
147: goto out;
1.1 deraadt 148: dostat = 0;
149: }
150: if (doign != ignoreall)
151: /* add blank line */
1.4 millert 152: (void)putc('\n', obuf);
1.1 deraadt 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;
1.19 martynas 163: else if (strcasecmp(line, "status") == 0) {
1.1 deraadt 164: /*
165: * If the field is "status," go compute
166: * and print the real Status: field
167: */
168: if (dostat) {
1.15 millert 169: if (statusput(mp, obuf, prefix) == -1)
170: goto out;
1.1 deraadt 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: */
1.13 millert 186: if (prefix != NULL) {
1.1 deraadt 187: if (length > 1)
188: fputs(prefix, obuf);
189: else
1.4 millert 190: (void)fwrite(prefix, sizeof(*prefix),
1.1 deraadt 191: prefixlen, obuf);
1.13 millert 192: }
1.16 pjanzen 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);
1.1 deraadt 198: if (ferror(obuf))
1.15 millert 199: goto out;
1.1 deraadt 200: }
1.15 millert 201: if (sendsignal == SIGINT)
202: goto out;
1.1 deraadt 203: }
204: /*
205: * Copy out message body
206: */
207: if (doign == ignoreall)
208: count--; /* skip final blank line */
1.13 millert 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) {
1.1 deraadt 216: /*
217: * Strip trailing whitespace from prefix
218: * if line is blank.
219: */
220: if (c > 1)
221: fputs(prefix, obuf);
222: else
1.4 millert 223: (void)fwrite(prefix, sizeof(*prefix),
1.1 deraadt 224: prefixlen, obuf);
225: }
1.13 millert 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 ' */
1.16 pjanzen 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);
1.15 millert 239: if (ferror(obuf) || sendsignal == SIGINT)
240: goto out;
1.13 millert 241: }
1.1 deraadt 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)
1.15 millert 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);
1.1 deraadt 252: }
253:
254: /*
255: * Output a reasonable looking status field.
256: */
1.15 millert 257: int
1.14 millert 258: statusput(struct message *mp, FILE *obuf, char *prefix)
1.1 deraadt 259: {
260: char statout[3];
1.9 millert 261: char *cp = statout;
1.1 deraadt 262:
263: if (mp->m_flag & MREAD)
264: *cp++ = 'R';
265: if ((mp->m_flag & MNEW) == 0)
266: *cp++ = 'O';
267: *cp = 0;
1.15 millert 268: if (statout[0]) {
1.1 deraadt 269: fprintf(obuf, "%sStatus: %s\n",
1.5 millert 270: prefix == NULL ? "" : prefix, statout);
1.15 millert 271: return(ferror(obuf) ? -1 : 0);
272: }
273: return(0);
1.1 deraadt 274: }
275:
276: /*
277: * Interface between the argument list and the mail1 routine
278: * which does all the dirty work.
279: */
280: int
1.14 millert 281: mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts,
1.24 millert 282: char *fromaddr, char *subject)
1.1 deraadt 283: {
284: struct header head;
285:
286: head.h_to = to;
1.24 millert 287: head.h_from = fromaddr;
1.1 deraadt 288: head.h_subject = subject;
289: head.h_cc = cc;
290: head.h_bcc = bcc;
291: head.h_smopts = smopts;
292: mail1(&head, 0);
293: return(0);
294: }
295:
296: /*
297: * Send mail to a bunch of user names. The interface is through
298: * the mail routine below.
299: */
300: int
1.14 millert 301: sendmail(void *v)
1.1 deraadt 302: {
1.2 deraadt 303: char *str = v;
1.1 deraadt 304: struct header head;
305:
306: head.h_to = extract(str, GTO);
1.24 millert 307: head.h_from = NULL;
1.5 millert 308: head.h_subject = NULL;
1.14 millert 309: head.h_cc = NULL;
310: head.h_bcc = NULL;
311: head.h_smopts = NULL;
1.1 deraadt 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
1.14 millert 321: mail1(struct header *hp, int printheaders)
1.1 deraadt 322: {
1.24 millert 323: char *cp, *envfrom = NULL;
324: char *argv[8];
325: char **ap = argv;
1.14 millert 326: pid_t pid;
1.1 deraadt 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: if ((mtf = collect(hp, printheaders)) == NULL)
335: return;
1.13 millert 336: if (fsize(mtf) == 0) {
1.20 martynas 337: if (value("skipempty") != NULL)
338: goto out;
1.21 martynas 339: if (hp->h_subject == NULL || *hp->h_subject == '\0')
1.3 millert 340: puts("No message, no subject; hope that's ok");
1.1 deraadt 341: else
1.3 millert 342: puts("Null message body; hope that's ok");
1.13 millert 343: }
1.1 deraadt 344: /*
345: * Now, take the user names from the combined
346: * to and cc lists and do all the alias
347: * processing.
348: */
349: senderr = 0;
350: to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
1.14 millert 351: if (to == NULL) {
1.3 millert 352: puts("No recipients specified");
1.1 deraadt 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: to = outof(to, mtf, hp);
360: if (senderr)
361: savedeadletter(mtf);
362: to = elide(to);
363: if (count(to) == 0)
364: goto out;
365: fixhead(hp, to);
366: if ((mtf = infix(hp, mtf)) == NULL) {
1.3 millert 367: fputs(". . . message lost, sorry.\n", stderr);
1.1 deraadt 368: return;
369: }
1.24 millert 370: if ((cp = value("record")) != NULL)
371: (void)savemail(expand(cp), mtf);
372:
373: /* Setup sendmail arguments. */
1.25 ! millert 374: *ap++ = "sendmail";
1.24 millert 375: *ap++ = "-i";
376: *ap++ = "-t";
377: cp = hp->h_from ? hp->h_from : value("from");
378: if (cp != NULL) {
379: envfrom = skin(cp);
380: *ap++ = "-f";
381: *ap++ = envfrom;
382: if (envfrom == cp)
383: envfrom = NULL;
384: }
385: if (value("metoo") != NULL)
386: *ap++ = "-m";
387: if (value("verbose") != NULL)
388: *ap++ = "-v";
389: *ap = NULL;
1.1 deraadt 390: if (debug) {
1.3 millert 391: fputs("Sendmail arguments:", stdout);
1.24 millert 392: for (ap = argv; *ap != NULL; ap++)
393: printf(" \"%s\"", *ap);
1.3 millert 394: putchar('\n');
1.1 deraadt 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: pid = fork();
403: if (pid == -1) {
1.3 millert 404: warn("fork");
1.1 deraadt 405: savedeadletter(mtf);
406: goto out;
407: }
408: if (pid == 0) {
1.2 deraadt 409: sigset_t nset;
1.6 millert 410:
1.2 deraadt 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);
1.5 millert 419: if ((cp = value("sendmail")) != NULL)
1.1 deraadt 420: cp = expand(cp);
421: else
422: cp = _PATH_SENDMAIL;
1.24 millert 423: execv(cp, argv);
1.11 millert 424: warn("%s", cp);
1.1 deraadt 425: _exit(1);
426: }
1.24 millert 427: free(envfrom);
1.5 millert 428: if (value("verbose") != NULL)
1.4 millert 429: (void)wait_child(pid);
1.1 deraadt 430: else
431: free_child(pid);
432: out:
1.3 millert 433: (void)Fclose(mtf);
1.1 deraadt 434: }
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
1.14 millert 441: fixhead(struct header *hp, struct name *tolist)
1.1 deraadt 442: {
1.9 millert 443: struct name *np;
1.1 deraadt 444:
1.14 millert 445: hp->h_to = NULL;
446: hp->h_cc = NULL;
447: hp->h_bcc = NULL;
448: for (np = tolist; np != NULL; np = np->n_flink)
1.1 deraadt 449: if ((np->n_type & GMASK) == GTO)
450: hp->h_to =
451: 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: }
459:
460: /*
461: * Prepend a header in front of the collected stuff
462: * and return the new file.
463: */
464: FILE *
1.14 millert 465: infix(struct header *hp, FILE *fi)
1.1 deraadt 466: {
1.9 millert 467: FILE *nfo, *nfi;
1.8 millert 468: int c, fd;
469: char tempname[PATHSIZE];
1.1 deraadt 470:
1.8 millert 471: (void)snprintf(tempname, sizeof(tempname),
472: "%s/mail.RsXXXXXXXXXX", tmpdir);
473: if ((fd = mkstemp(tempname)) == -1 ||
474: (nfo = Fdopen(fd, "w")) == NULL) {
1.11 millert 475: warn("%s", tempname);
1.1 deraadt 476: return(fi);
477: }
1.8 millert 478: if ((nfi = Fopen(tempname, "r")) == NULL) {
1.11 millert 479: warn("%s", tempname);
1.3 millert 480: (void)Fclose(nfo);
1.8 millert 481: (void)rm(tempname);
1.1 deraadt 482: return(fi);
483: }
1.8 millert 484: (void)rm(tempname);
1.4 millert 485: (void)puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
1.1 deraadt 486: c = getc(fi);
487: while (c != EOF) {
1.4 millert 488: (void)putc(c, nfo);
1.1 deraadt 489: c = getc(fi);
490: }
491: if (ferror(fi)) {
1.3 millert 492: warn("read");
1.1 deraadt 493: rewind(fi);
494: return(fi);
495: }
1.4 millert 496: (void)fflush(nfo);
1.1 deraadt 497: if (ferror(nfo)) {
1.11 millert 498: warn("%s", tempname);
1.3 millert 499: (void)Fclose(nfo);
500: (void)Fclose(nfi);
1.1 deraadt 501: rewind(fi);
502: return(fi);
503: }
1.3 millert 504: (void)Fclose(nfo);
505: (void)Fclose(fi);
1.1 deraadt 506: rewind(nfi);
507: return(nfi);
508: }
509:
510: /*
511: * Dump the to, subject, cc header on the
512: * passed file buffer.
513: */
514: int
1.14 millert 515: puthead(struct header *hp, FILE *fo, int w)
1.1 deraadt 516: {
1.9 millert 517: int gotcha;
1.24 millert 518: char *from;
1.1 deraadt 519:
520: gotcha = 0;
1.24 millert 521: from = hp->h_from ? hp->h_from : value("from");
522: if (from != NULL)
523: fprintf(fo, "From: %s\n", from), gotcha++;
1.14 millert 524: if (hp->h_to != NULL && w & GTO)
1.1 deraadt 525: fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
1.5 millert 526: if (hp->h_subject != NULL && w & GSUBJECT)
1.1 deraadt 527: fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
1.14 millert 528: if (hp->h_cc != NULL && w & GCC)
1.1 deraadt 529: fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
1.14 millert 530: if (hp->h_bcc != NULL && w & GBCC)
1.1 deraadt 531: fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
532: if (gotcha && w & GNL)
1.4 millert 533: (void)putc('\n', fo);
1.1 deraadt 534: return(0);
535: }
536:
537: /*
538: * Format the given header line to not exceed 72 characters.
539: */
540: void
1.14 millert 541: fmt(char *str, struct name *np, FILE *fo, int comma)
1.1 deraadt 542: {
1.9 millert 543: int col, len;
1.1 deraadt 544:
545: comma = comma ? 1 : 0;
546: col = strlen(str);
547: if (col)
548: fputs(str, fo);
1.14 millert 549: for (; np != NULL; np = np->n_flink) {
550: if (np->n_flink == NULL)
1.1 deraadt 551: comma = 0;
552: len = strlen(np->n_name);
553: col++; /* for the space */
554: if (col + len + comma > 72 && col > 4) {
555: fputs("\n ", fo);
556: col = 4;
557: } else
558: putc(' ', fo);
559: fputs(np->n_name, fo);
560: if (comma)
561: putc(',', fo);
562: col += len + comma;
563: }
564: putc('\n', fo);
565: }
566:
567: /*
568: * Save the outgoing mail on the passed file.
569: */
570: /*ARGSUSED*/
571: int
1.14 millert 572: savemail(char *name, FILE *fi)
1.1 deraadt 573: {
1.9 millert 574: FILE *fo;
1.1 deraadt 575: char buf[BUFSIZ];
1.2 deraadt 576: time_t now;
1.18 millert 577: mode_t m;
1.1 deraadt 578:
1.18 millert 579: m = umask(077);
580: fo = Fopen(name, "a");
581: (void)umask(m);
582: if (fo == NULL) {
1.11 millert 583: warn("%s", name);
1.3 millert 584: return(-1);
1.1 deraadt 585: }
1.4 millert 586: (void)time(&now);
1.1 deraadt 587: fprintf(fo, "From %s %s", myname, ctime(&now));
1.13 millert 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: }
1.4 millert 599: (void)putc('\n', fo);
600: (void)fflush(fo);
1.1 deraadt 601: if (ferror(fo))
1.11 millert 602: warn("%s", name);
1.3 millert 603: (void)Fclose(fo);
1.1 deraadt 604: rewind(fi);
1.3 millert 605: return(0);
1.15 millert 606: }
607:
608: /*ARGSUSED*/
609: void
610: sendint(int s)
611: {
612:
613: sendsignal = s;
1.1 deraadt 614: }