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