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