2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 static char sccsid
[] = "@(#)send.c 8.1 (Berkeley) 6/6/93";
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
42 * Mail -- a mail program
48 * Send message described by the passed pointer to the
49 * passed output buffer. Return -1 on error.
50 * Adjust the status: field if need be.
51 * If doign is given, suppress ignored header fields.
52 * prefix is a string to prepend to each output line.
55 sendmessage(struct message
*mp
, FILE *obuf
, struct ignoretab
*doign
,
60 char *cp
, *cp2
, line
[LINESIZE
];
61 int ishead
, infld
, ignoring
, dostat
, firstline
;
62 int c
, length
, prefixlen
;
65 * Compute the prefix string, without trailing whitespace
69 for (cp
= prefix
; *cp
!= '\0'; cp
++)
70 if (*cp
!= ' ' && *cp
!= '\t')
72 prefixlen
= cp2
== NULL
? 0 : cp2
- prefix
+ 1;
77 dostat
= doign
== 0 || !isign("status", doign
);
81 * Process headers first
83 while (count
> 0 && ishead
) {
84 if (fgets(line
, sizeof(line
), ibuf
) == NULL
)
86 count
-= length
= strlen(line
);
89 * First line is the From line, so no headers
90 * there to worry about
93 ignoring
= doign
== ignoreall
;
94 } else if (line
[0] == '\n') {
96 * If line is blank, we've reached end of
97 * headers, so force out status: field
98 * and note that we are no longer in header
102 statusput(mp
, obuf
, prefix
);
106 ignoring
= doign
== ignoreall
;
107 } else if (infld
&& (line
[0] == ' ' || line
[0] == '\t')) {
109 * If this line is a continuation (via space or tab)
110 * of a previous header field, just echo it
111 * (unless the field should be ignored).
112 * In other words, nothing to do.
116 * Pick up the header field if we have one.
118 for (cp
= line
; (c
= *cp
++) != '\0' && c
!= ':' &&
119 !isspace((unsigned char)c
);)
122 while (isspace((unsigned char)*cp
++))
126 * Not a header line, force out status:
127 * This happens in uucp style mail where
128 * there are no headers at all.
131 statusput(mp
, obuf
, prefix
);
134 if (doign
!= ignoreall
)
136 (void)putc('\n', obuf
);
141 * If it is an ignored field and
142 * we care about such things, skip it.
144 *cp2
= '\0'; /* temporarily null terminate */
145 if (doign
&& isign(line
, doign
))
147 else if ((line
[0] == 's' || line
[0] == 'S') &&
148 strcasecmp(line
, "status") == 0) {
150 * If the field is "status," go compute
151 * and print the real Status: field
154 statusput(mp
, obuf
, prefix
);
160 *cp2
= c
; /* restore */
167 * Strip trailing whitespace from prefix
170 if (prefix
!= NULL
) {
174 (void)fwrite(prefix
, sizeof(*prefix
),
177 (void)fwrite(line
, sizeof(*line
), length
, obuf
);
183 * Copy out message body
185 if (doign
== ignoreall
)
186 count
--; /* skip final blank line */
189 if (fgets(line
, sizeof(line
), ibuf
) == NULL
) {
193 count
-= c
= strlen(line
);
195 * Strip trailing whitespace from prefix
201 (void)fwrite(prefix
, sizeof(*prefix
),
203 (void)fwrite(line
, sizeof(*line
), c
, obuf
);
209 c
= count
< LINESIZE
? count
: LINESIZE
;
210 if ((c
= fread(line
, sizeof(*line
), c
, ibuf
)) <= 0)
213 if (fwrite(line
, sizeof(*line
), c
, obuf
) != c
)
216 if (doign
== ignoreall
&& c
> 0 && line
[c
- 1] != '\n')
217 /* no final blank line */
218 if ((c
= getc(ibuf
)) != EOF
&& putc(c
, obuf
) == EOF
)
224 * Output a reasonable looking status field.
227 statusput(struct message
*mp
, FILE *obuf
, char *prefix
)
232 if (mp
->m_flag
& MREAD
)
234 if ((mp
->m_flag
& MNEW
) == 0)
237 if (statout
[0] != '\0')
238 fprintf(obuf
, "%sStatus: %s\n",
239 prefix
== NULL
? "" : prefix
, statout
);
243 * Interface between the argument list and the mail1 routine
244 * which does all the dirty work.
247 mail(struct name
*to
, struct name
*cc
, struct name
*bcc
, struct name
*smopts
,
248 char *subject
, char *replyto
)
253 head
.h_subject
= subject
;
256 head
.h_smopts
= smopts
;
257 head
.h_replyto
= replyto
;
258 head
.h_inreplyto
= NULL
;
265 * Send mail to a bunch of user names. The interface is through
266 * the mail routine below.
273 head
.h_to
= extract(str
, GTO
);
274 head
.h_subject
= NULL
;
277 head
.h_smopts
= NULL
;
278 head
.h_replyto
= value("REPLYTO");
279 head
.h_inreplyto
= NULL
;
285 * Mail a message on standard input to the people indicated
286 * in the passed header. (Internal interface).
289 mail1(struct header
*hp
, int printheaders
)
295 struct name
*to
, *nsto
;
299 * Collect user's mail from standard input.
300 * Get the result as mtf.
302 if ((mtf
= collect(hp
, printheaders
)) == NULL
)
304 if (value("interactive") != NULL
) {
305 if (value("askcc") != NULL
|| value("askbcc") != NULL
) {
306 if (value("askcc") != NULL
)
308 if (value("askbcc") != NULL
)
312 (void)fflush(stdout
);
315 if (fsize(mtf
) == 0) {
316 if (value("dontsendempty") != NULL
)
318 if (hp
->h_subject
== NULL
)
319 printf("No message, no subject; hope that's ok\n");
321 printf("Null message body; hope that's ok\n");
324 * Now, take the user names from the combined
325 * to and cc lists and do all the alias
329 to
= usermap(cat(hp
->h_bcc
, cat(hp
->h_to
, hp
->h_cc
)));
331 printf("No recipients specified\n");
335 * Look through the recipient list for names with /'s
336 * in them which we write to as files directly.
338 to
= outof(to
, mtf
, hp
);
344 if (value("recordrecip") != NULL
) {
346 * Before fixing the header, save old To:.
347 * We do this because elide above has sorted To: list, and
348 * we would like to save message in a file named by the first
349 * recipient the user has entered, not the one being the first
350 * after sorting happened.
352 if ((nsto
= malloc(sizeof(struct name
))) == NULL
)
353 err(1, "Out of memory");
354 bcopy(hp
->h_to
, nsto
, sizeof(struct name
));
357 if ((mtf
= infix(hp
, mtf
)) == NULL
) {
358 fprintf(stderr
, ". . . message lost, sorry.\n");
361 namelist
= unpack(cat(hp
->h_smopts
, to
));
365 printf("Sendmail arguments:");
366 for (t
= namelist
; *t
!= NULL
; t
++)
367 printf(" \"%s\"", *t
);
371 if (value("recordrecip") != NULL
) {
373 * Extract first recipient username from saved To: and use it
376 if ((nbuf
= malloc(strlen(detract(nsto
, 0)) + 1)) == NULL
)
377 err(1, "Out of memory");
378 if ((cp
= yanklogin(detract(nsto
, 0), nbuf
)) != NULL
)
379 (void)savemail(expand(nbuf
), mtf
);
382 } else if ((cp
= value("record")) != NULL
)
383 (void)savemail(expand(cp
), mtf
);
385 * Fork, set up the temporary mail file as standard
386 * input for "mail", and exec with the user list we generated
397 (void)sigemptyset(&nset
);
398 (void)sigaddset(&nset
, SIGHUP
);
399 (void)sigaddset(&nset
, SIGINT
);
400 (void)sigaddset(&nset
, SIGQUIT
);
401 (void)sigaddset(&nset
, SIGTSTP
);
402 (void)sigaddset(&nset
, SIGTTIN
);
403 (void)sigaddset(&nset
, SIGTTOU
);
404 prepare_child(&nset
, fileno(mtf
), -1);
405 if ((cp
= value("sendmail")) != NULL
)
413 if (value("verbose") != NULL
)
414 (void)wait_child(pid
);
422 * Fix the header by glopping all of the expanded names from
423 * the distribution list into the appropriate fields.
426 fixhead(struct header
*hp
, struct name
*tolist
)
433 for (np
= tolist
; np
!= NULL
; np
= np
->n_flink
) {
434 /* Don't copy deleted addresses to the header */
435 if (np
->n_type
& GDEL
)
437 if ((np
->n_type
& GMASK
) == GTO
)
439 cat(hp
->h_to
, nalloc(np
->n_name
, np
->n_type
));
440 else if ((np
->n_type
& GMASK
) == GCC
)
442 cat(hp
->h_cc
, nalloc(np
->n_name
, np
->n_type
));
443 else if ((np
->n_type
& GMASK
) == GBCC
)
445 cat(hp
->h_bcc
, nalloc(np
->n_name
, np
->n_type
));
450 * Prepend a header in front of the collected stuff
451 * and return the new file.
454 infix(struct header
*hp
, FILE *fi
)
458 char tempname
[PATHSIZE
];
460 (void)snprintf(tempname
, sizeof(tempname
),
461 "%s/mail.RsXXXXXXXXXX", tmpdir
);
462 if ((fd
= mkstemp(tempname
)) == -1 ||
463 (nfo
= Fdopen(fd
, "w")) == NULL
) {
464 warn("%s", tempname
);
467 if ((nfi
= Fopen(tempname
, "r")) == NULL
) {
468 warn("%s", tempname
);
474 (void)puthead(hp
, nfo
,
475 GTO
|GSUBJECT
|GCC
|GBCC
|GREPLYTO
|GINREPLYTO
|GNL
|GCOMMA
);
488 warn("%s", tempname
);
501 * Dump the to, subject, cc header on the
502 * passed file buffer.
505 puthead(struct header
*hp
, FILE *fo
, int w
)
510 if (hp
->h_to
!= NULL
&& w
& GTO
)
511 fmt("To:", hp
->h_to
, fo
, w
&GCOMMA
), gotcha
++;
512 if (hp
->h_subject
!= NULL
&& w
& GSUBJECT
)
513 fprintf(fo
, "Subject: %s\n", hp
->h_subject
), gotcha
++;
514 if (hp
->h_cc
!= NULL
&& w
& GCC
)
515 fmt("Cc:", hp
->h_cc
, fo
, w
&GCOMMA
), gotcha
++;
516 if (hp
->h_bcc
!= NULL
&& w
& GBCC
)
517 fmt("Bcc:", hp
->h_bcc
, fo
, w
&GCOMMA
), gotcha
++;
518 if (hp
->h_replyto
!= NULL
&& w
& GREPLYTO
)
519 fprintf(fo
, "Reply-To: %s\n", hp
->h_replyto
), gotcha
++;
520 if (hp
->h_inreplyto
!= NULL
&& w
& GINREPLYTO
)
521 fprintf(fo
, "In-Reply-To: <%s>\n", hp
->h_inreplyto
), gotcha
++;
522 if (gotcha
&& w
& GNL
)
523 (void)putc('\n', fo
);
528 * Format the given header line to not exceed 72 characters.
531 fmt(const char *str
, struct name
*np
, FILE *fo
, int comma
)
535 comma
= comma
? 1 : 0;
539 for (; np
!= NULL
; np
= np
->n_flink
) {
540 if (np
->n_flink
== NULL
)
542 len
= strlen(np
->n_name
);
543 col
++; /* for the space */
544 if (col
+ len
+ comma
> 72 && col
> 4) {
549 fputs(np
->n_name
, fo
);
558 * Save the outgoing mail on the passed file.
563 savemail(char name
[], FILE *fi
)
570 if ((fo
= Fopen(name
, "a")) == NULL
) {
575 fprintf(fo
, "From %s %s", myname
, ctime(&now
));
576 while ((i
= fread(buf
, 1, sizeof(buf
), fi
)) > 0)
577 (void)fwrite(buf
, 1, i
, fo
);