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