]>
git.cameronkatri.com Git - apple_cmds.git/blob - mail_cmds/mail/send.c
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 * 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.
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
36 static char sccsid
[] = "@(#)send.c 8.1 (Berkeley) 6/6/93";
38 static const char rcsid
[] =
39 "$FreeBSD: src/usr.bin/mail/send.c,v 1.14 2004/02/29 20:44:44 mikeh Exp $";
42 #include <sys/cdefs.h>
47 #include <libiosexec.h>
50 * Mail -- a mail program
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.
63 sendmessage(mp
, obuf
, doign
, prefix
)
66 struct ignoretab
*doign
;
71 char *cp
, *cp2
, line
[LINESIZE
];
72 int ishead
, infld
, ignoring
= 0, dostat
, firstline
;
73 int c
= 0, length
, prefixlen
= 0;
76 * Compute the prefix string, without trailing whitespace
80 for (cp
= prefix
; *cp
!= '\0'; cp
++)
81 if (*cp
!= ' ' && *cp
!= '\t')
83 prefixlen
= cp2
== NULL
? 0 : cp2
- prefix
+ 1;
88 dostat
= doign
== 0 || !isign("status", doign
);
92 * Process headers first
94 while (count
> 0 && ishead
) {
95 if (fgets(line
, sizeof(line
), ibuf
) == NULL
)
97 count
-= length
= strlen(line
);
100 * First line is the From line, so no headers
101 * there to worry about
104 ignoring
= doign
== ignoreall
;
105 } else if (line
[0] == '\n') {
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
113 statusput(mp
, obuf
, prefix
);
117 ignoring
= doign
== ignoreall
;
118 } else if (infld
&& (line
[0] == ' ' || line
[0] == '\t')) {
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.
127 * Pick up the header field if we have one.
129 for (cp
= line
; (c
= *cp
++) != '\0' && c
!= ':' &&
130 !isspace((unsigned char)c
);)
133 while (isspace((unsigned char)*cp
++))
137 * Not a header line, force out status:
138 * This happens in uucp style mail where
139 * there are no headers at all.
142 statusput(mp
, obuf
, prefix
);
145 if (doign
!= ignoreall
)
147 (void)putc('\n', obuf
);
152 * If it is an ignored field and
153 * we care about such things, skip it.
155 *cp2
= '\0'; /* temporarily null terminate */
156 if (doign
&& isign(line
, doign
))
158 else if ((line
[0] == 's' || line
[0] == 'S') &&
159 strcasecmp(line
, "status") == 0) {
161 * If the field is "status," go compute
162 * and print the real Status: field
165 statusput(mp
, obuf
, prefix
);
171 *cp2
= c
; /* restore */
178 * Strip trailing whitespace from prefix
181 if (prefix
!= NULL
) {
185 (void)fwrite(prefix
, sizeof(*prefix
),
188 (void)fwrite(line
, sizeof(*line
), length
, obuf
);
194 * Copy out message body
196 if (doign
== ignoreall
)
197 count
--; /* skip final blank line */
200 if (fgets(line
, sizeof(line
), ibuf
) == NULL
) {
204 count
-= c
= strlen(line
);
206 * Strip trailing whitespace from prefix
212 (void)fwrite(prefix
, sizeof(*prefix
),
214 (void)fwrite(line
, sizeof(*line
), c
, obuf
);
220 c
= count
< LINESIZE
? count
: LINESIZE
;
221 if ((c
= fread(line
, sizeof(*line
), c
, ibuf
)) <= 0)
224 if (fwrite(line
, sizeof(*line
), c
, obuf
) != c
)
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
)
235 * Output a reasonable looking status field.
238 statusput(mp
, obuf
, prefix
)
246 if (mp
->m_flag
& MREAD
)
248 if ((mp
->m_flag
& MNEW
) == 0)
251 if (statout
[0] != '\0')
252 fprintf(obuf
, "%sStatus: %s\n",
253 prefix
== NULL
? "" : prefix
, statout
);
257 * Interface between the argument list and the mail1 routine
258 * which does all the dirty work.
261 mail(to
, cc
, bcc
, smopts
, subject
, replyto
)
262 struct name
*to
, *cc
, *bcc
, *smopts
;
263 char *subject
, *replyto
;
268 head
.h_subject
= subject
;
271 head
.h_smopts
= smopts
;
272 head
.h_replyto
= replyto
;
273 head
.h_inreplyto
= NULL
;
280 * Send mail to a bunch of user names. The interface is through
281 * the mail routine below.
289 head
.h_to
= extract(str
, GTO
);
290 head
.h_subject
= NULL
;
293 head
.h_smopts
= NULL
;
294 head
.h_replyto
= value("REPLYTO");
295 head
.h_inreplyto
= NULL
;
301 * Mail a message on standard input to the people indicated
302 * in the passed header. (Internal interface).
305 mail1(hp
, printheaders
)
313 struct name
*to
, *nsto
= NULL
;
317 * Collect user's mail from standard input.
318 * Get the result as mtf.
320 if ((mtf
= collect(hp
, printheaders
)) == NULL
)
322 if (value("interactive") != NULL
) {
323 if (value("askcc") != NULL
|| value("askbcc") != NULL
) {
324 if (value("askcc") != NULL
)
326 if (value("askbcc") != NULL
)
330 (void)fflush(stdout
);
333 if (fsize(mtf
) == 0) {
334 if (value("dontsendempty") != NULL
)
336 if (hp
->h_subject
== NULL
)
337 printf("No message, no subject; hope that's ok\n");
339 printf("Null message body; hope that's ok\n");
342 * Now, take the user names from the combined
343 * to and cc lists and do all the alias
347 to
= usermap(cat(hp
->h_bcc
, cat(hp
->h_to
, hp
->h_cc
)));
349 printf("No recipients specified\n");
353 * Look through the recipient list for names with /'s
354 * in them which we write to as files directly.
356 to
= outof(to
, mtf
, hp
);
362 if (value("recordrecip") != NULL
) {
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.
370 if ((nsto
= malloc(sizeof(struct name
))) == NULL
)
371 err(1, "Out of memory");
372 bcopy(hp
->h_to
, nsto
, sizeof(struct name
));
375 if ((mtf
= infix(hp
, mtf
)) == NULL
) {
376 fprintf(stderr
, ". . . message lost, sorry.\n");
379 namelist
= unpack(cat(hp
->h_smopts
, to
));
383 printf("Sendmail arguments:");
384 for (t
= namelist
; *t
!= NULL
; t
++)
385 printf(" \"%s\"", *t
);
389 if (value("recordrecip") != NULL
) {
391 * Extract first recipient username from saved To: and use it
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
);
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
);
414 (void)savemail(expanded_record_name
, mtf
);
418 * Fork, set up the temporary mail file as standard
419 * input for "mail", and exec with the user list we generated
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
)
446 if (value("verbose") != NULL
)
447 (void)wait_child(pid
);
455 * Fix the header by glopping all of the expanded names from
456 * the distribution list into the appropriate fields.
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
)
472 if ((np
->n_type
& GMASK
) == GTO
)
474 cat(hp
->h_to
, nalloc(np
->n_name
, np
->n_type
));
475 else if ((np
->n_type
& GMASK
) == GCC
)
477 cat(hp
->h_cc
, nalloc(np
->n_name
, np
->n_type
));
478 else if ((np
->n_type
& GMASK
) == GBCC
)
480 cat(hp
->h_bcc
, nalloc(np
->n_name
, np
->n_type
));
485 * Prepend a header in front of the collected stuff
486 * and return the new file.
495 char tempname
[PATHSIZE
];
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
);
504 if ((nfi
= Fopen(tempname
, "r")) == NULL
) {
505 warn("%s", tempname
);
511 (void)puthead(hp
, nfo
,
512 GTO
|GSUBJECT
|GCC
|GBCC
|GREPLYTO
|GINREPLYTO
|GNL
|GCOMMA
);
525 warn("%s", tempname
);
538 * Dump the to, subject, cc header on the
539 * passed file buffer.
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
);
568 * Format the given header line to not exceed 72 characters.
571 fmt(str
, np
, fo
, comma
)
579 comma
= comma
? 1 : 0;
583 for (; np
!= NULL
; np
= np
->n_flink
) {
584 if (np
->n_flink
== NULL
)
586 len
= strlen(np
->n_name
);
587 col
++; /* for the space */
588 if (col
+ len
+ comma
> 72 && col
> 4) {
593 fputs(np
->n_name
, fo
);
602 * Save the outgoing mail on the passed file.
616 if ((fo
= Fopen(name
, "a")) == NULL
) {
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
);