No empty .Rs/.Re
[netbsd-mini2440.git] / usr.bin / mail / send.c
blob3e42bf09c84e3bb0adbd6fd7dcd96d0bcb0855ec
1 /* $NetBSD: send.c,v 1.35 2009/04/12 22:47:39 he Exp $ */
3 /*
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)send.c 8.1 (Berkeley) 6/6/93";
36 #else
37 __RCSID("$NetBSD: send.c,v 1.35 2009/04/12 22:47:39 he Exp $");
38 #endif
39 #endif /* not lint */
41 #include <assert.h>
43 #include "rcv.h"
44 #include "extern.h"
45 #ifdef MIME_SUPPORT
46 #include "mime.h"
47 #endif
48 #include "sig.h"
51 * Mail -- a mail program
53 * Mail to others.
57 * compare twwo name structures. Support for get_smopts.
59 static int
60 namecmp(struct name *np1, struct name *np2)
62 for (/*EMPTY*/; np1 && np2; np1 = np1->n_flink, np2 = np2->n_flink) {
63 if (strcmp(np1->n_name, np2->n_name) != 0)
64 return 1;
66 return np1 || np2;
70 * Get the sendmail options from the smopts list.
72 static struct name *
73 get_smopts(struct name *to)
75 struct smopts_s *sp;
76 struct name *smargs, *np;
77 smargs = NULL;
78 for (np = to; np; np = np->n_flink) {
79 if ((sp = findsmopts(np->n_name, 0)) == NULL)
80 continue;
81 if (smargs == NULL)
82 smargs = sp->s_smopts;
83 else if (namecmp(smargs, sp->s_smopts) != 0)
84 return NULL;
86 if (smargs &&
87 smargs->n_flink == NULL &&
88 (smargs->n_name == NULL || smargs->n_name[0] == '\0'))
89 return NULL;
90 return smargs;
94 * Output a reasonable looking status field.
96 static void
97 statusput(struct message *mp, FILE *obuf, const char *prefix)
99 char statout[3];
100 char *cp = statout;
102 if (mp->m_flag & MREAD)
103 *cp++ = 'R';
104 if ((mp->m_flag & MNEW) == 0)
105 *cp++ = 'O';
106 *cp = 0;
107 if (statout[0])
108 (void)fprintf(obuf, "%sStatus: %s\n",
109 prefix == NULL ? "" : prefix, statout);
113 * Send message described by the passed pointer to the
114 * passed output buffer. Return -1 on error.
115 * Adjust the status: field if need be.
116 * If doign is given, suppress ignored header fields.
117 * prefix is a string to prepend to each output line.
119 PUBLIC int
120 sendmessage(struct message *mp, FILE *obuf, struct ignoretab *doign,
121 const char *prefix, struct mime_info *mip)
123 off_t len;
124 FILE *ibuf;
125 char line[LINESIZE];
126 int isheadflag, infld, ignoring, dostat, firstline;
127 char *cp;
128 size_t prefixlen;
129 size_t linelen;
130 int rval;
132 ignoring = 0;
133 prefixlen = 0;
134 rval = -1;
137 * Compute the prefix string, without trailing whitespace
139 if (prefix != NULL) {
140 const char *dp, *dp2 = NULL;
141 for (dp = prefix; *dp; dp++)
142 if (!is_WSP(*dp))
143 dp2 = dp;
144 prefixlen = dp2 == 0 ? 0 : dp2 - prefix + 1;
146 ibuf = setinput(mp);
147 len = mp->m_size;
148 isheadflag = 1;
149 dostat = doign == 0 || !isign("status", doign);
150 infld = 0;
151 firstline = 1;
153 sig_check();
155 * Process headers first
157 #ifdef MIME_SUPPORT
158 if (mip)
159 obuf = mime_decode_header(mip);
160 #endif
161 while (len > 0 && isheadflag) {
162 sig_check();
163 if (fgets(line, (int)sizeof(line), ibuf) == NULL)
164 break;
165 len -= linelen = strlen(line);
166 if (firstline) {
168 * First line is the From line, so no headers
169 * there to worry about
171 firstline = 0;
172 ignoring = doign == ignoreall || doign == bouncetab;
173 #ifdef MIME_SUPPORT
174 /* XXX - ignore multipart boundary lines! */
175 if (line[0] == '-' && line[1] == '-')
176 ignoring = 1;
177 #endif
178 } else if (line[0] == '\n') {
180 * If line is blank, we've reached end of
181 * headers, so force out status: field
182 * and note that we are no longer in header
183 * fields
185 if (dostat) {
186 statusput(mp, obuf, prefix);
187 dostat = 0;
189 isheadflag = 0;
190 ignoring = doign == ignoreall;
191 } else if (infld && is_WSP(line[0])) {
193 * If this line is a continuation (via space or tab)
194 * of a previous header field, just echo it
195 * (unless the field should be ignored).
196 * In other words, nothing to do.
198 } else {
200 * Pick up the header field if we have one.
202 char *save_cp;
203 for (cp = line; *cp && *cp != ':' && !is_WSP(*cp); cp++)
204 continue;
205 save_cp = cp;
206 cp = skip_WSP(cp);
207 if (*cp++ != ':') {
209 * Not a header line, force out status:
210 * This happens in uucp style mail where
211 * there are no headers at all.
213 if (dostat) {
214 statusput(mp, obuf, prefix);
215 dostat = 0;
217 if (doign != ignoreall)
218 /* add blank line */
219 (void)putc('\n', obuf);
220 isheadflag = 0;
221 ignoring = 0;
222 } else {
224 * If it is an ignored field and
225 * we care about such things, skip it.
227 int save_c;
228 save_c = *save_cp;
229 *save_cp = 0; /* temporarily null terminate */
230 if (doign && isign(line, doign))
231 ignoring = 1;
232 else if ((line[0] == 's' || line[0] == 'S') &&
233 strcasecmp(line, "status") == 0) {
235 * If the field is "status," go compute
236 * and print the real Status: field
238 if (dostat) {
239 statusput(mp, obuf, prefix);
240 dostat = 0;
242 ignoring = 1;
243 } else {
244 ignoring = 0;
246 infld = 1;
247 *save_cp = save_c; /* restore the line */
250 if (!ignoring) {
252 * Strip trailing whitespace from prefix
253 * if line is blank.
255 if (prefix != NULL) {
256 if (linelen > 1)
257 (void)fputs(prefix, obuf);
258 else
259 (void)fwrite(prefix, sizeof(*prefix),
260 prefixlen, obuf);
262 (void)fwrite(line, sizeof(*line), linelen, obuf);
263 if (ferror(obuf))
264 goto out;
267 sig_check();
270 * Copy out message body
272 #ifdef MIME_SUPPORT
273 if (mip) {
274 obuf = mime_decode_body(mip);
275 sig_check();
276 if (obuf == NULL) { /* XXX - early out */
277 rval = 0;
278 goto out;
281 #endif
282 if (doign == ignoreall)
283 len--; /* skip final blank line */
285 linelen = 0; /* needed for in case len == 0 */
286 if (prefix != NULL) {
287 while (len > 0) {
288 sig_check();
289 if (fgets(line, (int)sizeof(line), ibuf) == NULL) {
290 linelen = 0;
291 break;
293 len -= linelen = strlen(line);
295 * Strip trailing whitespace from prefix
296 * if line is blank.
298 if (linelen > 1)
299 (void)fputs(prefix, obuf);
300 else
301 (void)fwrite(prefix, sizeof(*prefix),
302 prefixlen, obuf);
303 (void)fwrite(line, sizeof(*line), linelen, obuf);
304 if (ferror(obuf))
305 goto out;
308 else {
309 while (len > 0) {
310 sig_check();
311 linelen = (size_t)len < sizeof(line) ? (size_t)len : sizeof(line);
312 if ((linelen = fread(line, sizeof(*line), linelen, ibuf)) == 0) {
313 break;
315 len -= linelen;
316 if (fwrite(line, sizeof(*line), linelen, obuf) != linelen)
317 goto out;
320 sig_check();
321 if (doign == ignoreall && linelen > 0 && line[linelen - 1] != '\n') {
322 int c;
324 /* no final blank line */
325 if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
326 goto out;
328 rval = 0;
329 out:
330 sig_check();
331 return rval;
335 * Fix the header by glopping all of the expanded names from
336 * the distribution list into the appropriate fields.
338 static void
339 fixhead(struct header *hp, struct name *tolist)
341 struct name *np;
343 hp->h_to = NULL;
344 hp->h_cc = NULL;
345 hp->h_bcc = NULL;
346 for (np = tolist; np != NULL; np = np->n_flink) {
347 if (np->n_type & GDEL)
348 continue; /* Don't copy deleted addresses to the header */
349 if ((np->n_type & GMASK) == GTO)
350 hp->h_to =
351 cat(hp->h_to, nalloc(np->n_name, np->n_type));
352 else if ((np->n_type & GMASK) == GCC)
353 hp->h_cc =
354 cat(hp->h_cc, nalloc(np->n_name, np->n_type));
355 else if ((np->n_type & GMASK) == GBCC)
356 hp->h_bcc =
357 cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
362 * Format the given header line to not exceed 72 characters.
364 static void
365 fmt(const char *str, struct name *np, FILE *fo, size_t comma)
367 size_t col;
368 size_t len;
370 comma = comma ? 1 : 0;
371 col = strlen(str);
372 if (col)
373 (void)fputs(str, fo);
374 for (/*EMPTY*/; np != NULL; np = np->n_flink) {
375 if (np->n_flink == NULL)
376 comma = 0;
377 len = strlen(np->n_name);
378 col++; /* for the space */
379 if (col + len + comma > 72 && col > 4) {
380 (void)fputs("\n ", fo);
381 col = 4;
382 } else
383 (void)putc(' ', fo);
384 (void)fputs(np->n_name, fo);
385 if (comma)
386 (void)putc(',', fo);
387 col += len + comma;
389 (void)putc('\n', fo);
393 * Formatting for extra_header lines: try to wrap them before 72
394 * characters.
396 * XXX - should this allow for quoting?
398 static void
399 fmt2(const char *str, FILE *fo)
401 const char *p;
402 size_t col;
404 col = 0;
405 p = strchr(str, ':');
406 assert(p != NULL); /* this is a coding error */
408 while (str <= p) { /* output the header name */
409 (void)putc(*str, fo);
410 str++;
411 col++;
413 assert(is_WSP(*str)); /* this is a coding error */
415 (void)putc(' ', fo); /* output a single space after the ':' */
416 str = skip_WSP(str);
418 while (*str != '\0') {
419 if (p <= str) {
420 p = strpbrk(str, " \t");
421 if (p == NULL)
422 p = str + strlen(str);
423 if (col + (p - str) > 72 && col > 4) {
424 (void)fputs("\n ", fo);
425 col = 4;
426 str = skip_WSP(str);
427 continue;
430 (void)putc(*str, fo);
431 str++;
432 col++;
434 (void)putc('\n', fo);
438 * Dump the to, subject, cc header on the
439 * passed file buffer.
441 PUBLIC int
442 puthead(struct header *hp, FILE *fo, int w)
444 struct name *np;
445 int gotcha;
447 gotcha = 0;
448 if (hp->h_to != NULL && w & GTO)
449 fmt("To:", hp->h_to, fo, w & GCOMMA), gotcha++;
450 if (hp->h_subject != NULL && w & GSUBJECT)
451 (void)fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
452 if (hp->h_cc != NULL && w & GCC)
453 fmt("Cc:", hp->h_cc, fo, w & GCOMMA), gotcha++;
454 if (hp->h_bcc != NULL && w & GBCC)
455 fmt("Bcc:", hp->h_bcc, fo, w & GCOMMA), gotcha++;
456 if (hp->h_in_reply_to != NULL && w & GMISC)
457 (void)fprintf(fo, "In-Reply-To: %s\n", hp->h_in_reply_to), gotcha++;
458 if (hp->h_references != NULL && w & GMISC)
459 fmt("References:", hp->h_references, fo, w & GCOMMA), gotcha++;
460 if (hp->h_extra != NULL && w & GMISC) {
461 for (np = hp->h_extra; np != NULL; np = np->n_flink)
462 fmt2(np->n_name, fo);
463 gotcha++;
465 if (hp->h_smopts != NULL && w & GSMOPTS)
466 (void)fprintf(fo, "(sendmail options: %s)\n", detract(hp->h_smopts, GSMOPTS)), gotcha++;
467 #ifdef MIME_SUPPORT
468 if (w & GMIME && (hp->h_attach || value(ENAME_MIME_ENCODE_MSG)))
469 mime_putheader(fo, hp), gotcha++;
470 #endif
471 if (gotcha && w & GNL)
472 (void)putc('\n', fo);
473 return 0;
477 * Prepend a header in front of the collected stuff
478 * and return the new file.
480 static FILE *
481 infix(struct header *hp, FILE *fi)
483 FILE *nfo, *nfi;
484 int c, fd;
485 char tempname[PATHSIZE];
487 (void)snprintf(tempname, sizeof(tempname),
488 "%s/mail.RsXXXXXXXXXX", tmpdir);
489 if ((fd = mkstemp(tempname)) == -1 ||
490 (nfo = Fdopen(fd, "w")) == NULL) {
491 if (fd != -1)
492 (void)close(fd);
493 warn("%s", tempname);
494 return fi;
496 if ((nfi = Fopen(tempname, "r")) == NULL) {
497 warn("%s", tempname);
498 (void)Fclose(nfo);
499 (void)rm(tempname);
500 return fi;
502 (void)rm(tempname);
503 (void)puthead(hp, nfo,
504 GTO | GSUBJECT | GCC | GBCC | GMISC | GNL | GCOMMA
505 #ifdef MIME_SUPPORT
506 | GMIME
507 #endif
510 c = getc(fi);
511 while (c != EOF) {
512 (void)putc(c, nfo);
513 c = getc(fi);
515 if (ferror(fi)) {
516 warn("read");
517 rewind(fi);
518 return fi;
520 (void)fflush(nfo);
521 if (ferror(nfo)) {
522 warn("%s", tempname);
523 (void)Fclose(nfo);
524 (void)Fclose(nfi);
525 rewind(fi);
526 return fi;
528 (void)Fclose(nfo);
529 (void)Fclose(fi);
530 rewind(nfi);
531 return nfi;
535 * Save the outgoing mail on the passed file.
537 * Take care not to save a valid headline or the mbox file will be
538 * broken. Prefix valid headlines with '>'.
540 * Note: most servers prefix any line starting with "From " in the
541 * body of the message. We are more restrictive to avoid header/body
542 * issues.
544 /*ARGSUSED*/
545 static int
546 savemail(const char name[], FILE *fi)
548 FILE *fo;
549 time_t now;
550 mode_t m;
551 char *line;
552 size_t linelen;
553 int afterblank;
555 m = umask(077);
556 fo = Fopen(name, "a");
557 (void)umask(m);
558 if (fo == NULL) {
559 warn("%s", name);
560 return -1;
562 (void)time(&now);
563 (void)fprintf(fo, "From %s %s", myname, ctime(&now));
564 afterblank = 0;
565 while ((line = fgetln(fi, &linelen)) != NULL) {
566 char c, *cp;
567 cp = line + linelen - 1;
568 if (afterblank &&
569 linelen > sizeof("From . Aaa Aaa O0 00:00 0000") &&
570 line[0] == 'F' &&
571 line[1] == 'r' &&
572 line[2] == 'o' &&
573 line[3] == 'm' &&
574 line[4] == ' ' &&
575 *cp == '\n') {
576 c = *cp;
577 *cp = '\0';
578 if (ishead(line))
579 (void)fputc('>', fo);
580 *cp = c;
582 (void)fwrite(line, 1, linelen, fo);
583 afterblank = linelen == 1 && line[0] == '\n';
585 (void)putc('\n', fo);
586 (void)fflush(fo);
587 if (ferror(fo))
588 warn("%s", name);
589 (void)Fclose(fo);
590 rewind(fi);
591 return 0;
595 * Mail a message that is already prepared in a file.
597 PUBLIC void
598 mail2(FILE *mtf, const char **namelist)
600 int pid;
601 const char *cp;
603 if (debug) {
604 const char **t;
606 (void)printf("Sendmail arguments:");
607 for (t = namelist; *t != NULL; t++)
608 (void)printf(" \"%s\"", *t);
609 (void)printf("\n");
610 return;
612 if ((cp = value(ENAME_RECORD)) != NULL)
613 (void)savemail(expand(cp), mtf);
615 * Fork, set up the temporary mail file as standard
616 * input for "mail", and exec with the user list we generated
617 * far above.
619 pid = fork();
620 switch (pid) {
621 case -1:
622 warn("fork");
623 savedeadletter(mtf);
624 return;
625 case 0:
627 sigset_t nset;
628 (void)sigemptyset(&nset);
629 (void)sigaddset(&nset, SIGHUP);
630 (void)sigaddset(&nset, SIGINT);
631 (void)sigaddset(&nset, SIGQUIT);
632 (void)sigaddset(&nset, SIGTSTP);
633 (void)sigaddset(&nset, SIGTTIN);
634 (void)sigaddset(&nset, SIGTTOU);
635 prepare_child(&nset, fileno(mtf), -1);
636 if ((cp = value(ENAME_SENDMAIL)) != NULL)
637 cp = expand(cp);
638 else
639 cp = _PATH_SENDMAIL;
640 (void)execv(cp, (char *const *)__UNCONST(namelist));
641 warn("%s", cp);
642 _exit(1);
643 break; /* Appease GCC */
645 default:
646 if (value(ENAME_VERBOSE) != NULL)
647 (void)wait_child(pid);
648 else
649 free_child(pid);
650 break;
654 static struct name *
655 ncopy(struct name *np)
657 struct name *rv;
658 struct name *lp;
659 struct name *tp;
661 lp = NULL; /* XXX gcc -Wuninitialized sh3 */
662 rv = NULL;
663 for (/*EMPTY*/; np; np = np->n_flink) {
664 tp = nalloc(np->n_name, np->n_type);
665 if (rv == NULL)
666 rv = tp;
667 else {
668 lp->n_flink = tp;
669 tp->n_blink = lp;
671 lp = tp;
673 return rv;
677 * Mail a message on standard input to the people indicated
678 * in the passed header. (Internal interface).
680 PUBLIC void
681 mail1(struct header *hp, int printheaders)
683 const char **namelist;
684 struct name *to;
685 FILE *mtf;
688 * Collect user's mail from standard input.
689 * Get the result as mtf.
691 if ((mtf = collect(hp, printheaders)) == NULL)
692 return;
695 * Grab any extra header lines. Do this after collect() so
696 * that we can add header lines while collecting.
698 hp->h_extra = ncopy(extra_headers);
700 if (value(ENAME_INTERACTIVE) != NULL) {
701 if (value(ENAME_ASKCC) != NULL || value(ENAME_ASKBCC) != NULL) {
702 if (value(ENAME_ASKCC) != NULL)
703 (void)grabh(hp, GCC);
704 if (value(ENAME_ASKBCC) != NULL)
705 (void)grabh(hp, GBCC);
706 } else {
707 (void)printf("EOT\n");
708 (void)fflush(stdout);
711 if (fsize(mtf) == 0) {
712 if (value(ENAME_DONTSENDEMPTY) != NULL)
713 goto out;
714 if (hp->h_subject == NULL)
715 (void)printf("No message, no subject; hope that's ok\n");
716 else
717 (void)printf("Null message body; hope that's ok\n");
720 * Now, take the user names from the combined
721 * to and cc lists and do all the alias
722 * processing.
724 senderr = 0;
725 to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
726 if (to == NULL) {
727 (void)printf("No recipients specified\n");
728 senderr++;
730 #ifdef MIME_SUPPORT
732 * If there are attachments, repackage the mail as a
733 * multi-part MIME message.
735 if (hp->h_attach || value(ENAME_MIME_ENCODE_MSG))
736 mtf = mime_encode(mtf, hp);
737 #endif
739 * Look through the recipient list for names with /'s
740 * in them which we write to as files directly.
742 to = outof(to, mtf, hp);
743 if (senderr)
744 savedeadletter(mtf);
745 to = elide(to);
746 if (count(to) == 0)
747 goto out;
748 fixhead(hp, to);
749 if ((mtf = infix(hp, mtf)) == NULL) {
750 (void)fprintf(stderr, ". . . message lost, sorry.\n");
751 return;
753 if (hp->h_smopts == NULL) {
754 hp->h_smopts = get_smopts(to);
755 if (hp->h_smopts != NULL &&
756 hp->h_smopts->n_name[0] != '\0' &&
757 value(ENAME_SMOPTS_VERIFY) != NULL)
758 if (grabh(hp, GSMOPTS)) {
759 (void)printf("mail aborted!\n");
760 savedeadletter(mtf);
761 goto out;
764 namelist = unpack(cat(hp->h_smopts, to));
765 mail2(mtf, namelist);
766 out:
767 (void)Fclose(mtf);
771 * Interface between the argument list and the mail1 routine
772 * which does all the dirty work.
774 PUBLIC int
775 mail(struct name *to, struct name *cc, struct name *bcc,
776 struct name *smopts, char *subject, struct attachment *attach)
778 struct header head;
780 /* ensure that all header fields are initially NULL */
781 (void)memset(&head, 0, sizeof(head));
783 head.h_to = to;
784 head.h_subject = subject;
785 head.h_cc = cc;
786 head.h_bcc = bcc;
787 head.h_smopts = smopts;
788 #ifdef MIME_SUPPORT
789 head.h_attach = attach;
790 #endif
791 mail1(&head, 0);
792 return 0;
796 * Send mail to a bunch of user names. The interface is through
797 * the mail1 routine above.
799 PUBLIC int
800 sendmail(void *v)
802 char *str = v;
803 struct header head;
805 /* ensure that all header fields are initially NULL */
806 (void)memset(&head, 0, sizeof(head));
808 head.h_to = extract(str, GTO);
810 mail1(&head, 0);
811 return 0;