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