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