BSD 4_4 release
[unix-history] / usr / src / usr.bin / mail / send.c
CommitLineData
761330fe 1/*
ad787160
C
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
0c5f72fb 4 *
ad787160
C
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.
761330fe
DF
32 */
33
acfc7e9b 34#ifndef lint
ad787160 35static char sccsid[] = "@(#)send.c 8.1 (Berkeley) 6/6/93";
acfc7e9b 36#endif /* not lint */
3184019b
KS
37
38#include "rcv.h"
a0d23834 39#include "extern.h"
3184019b
KS
40
41/*
42 * Mail -- a mail program
43 *
44 * Mail to others.
45 */
46
3184019b
KS
47/*
48 * Send message described by the passed pointer to the
2de8fc95
EW
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.
3184019b 53 */
a0d23834 54int
2de8fc95 55send(mp, obuf, doign, prefix)
828615a1 56 register struct message *mp;
3184019b 57 FILE *obuf;
887efe38 58 struct ignoretab *doign;
2de8fc95 59 char *prefix;
3184019b 60{
828615a1
EW
61 long count;
62 register FILE *ibuf;
63 char line[LINESIZE];
2de8fc95 64 int ishead, infld, ignoring, dostat, firstline;
828615a1
EW
65 register char *cp, *cp2;
66 register int c;
67 int length;
ebbb3173 68 int prefixlen;
828615a1 69
ebbb3173
EW
70 /*
71 * Compute the prefix string, without trailing whitespace
72 */
902a3466
EW
73 if (prefix != NOSTR) {
74 cp2 = 0;
75 for (cp = prefix; *cp; cp++)
76 if (*cp != ' ' && *cp != '\t')
77 cp2 = cp;
78 prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
79 }
3184019b 80 ibuf = setinput(mp);
828615a1 81 count = mp->m_size;
b870137d 82 ishead = 1;
887efe38 83 dostat = doign == 0 || !isign("status", doign);
a6b3a6c5 84 infld = 0;
2de8fc95 85 firstline = 1;
828615a1
EW
86 /*
87 * Process headers first
88 */
89 while (count > 0 && ishead) {
90 if (fgets(line, LINESIZE, ibuf) == NULL)
91 break;
92 count -= length = strlen(line);
2de8fc95 93 if (firstline) {
b870137d
KS
94 /*
95 * First line is the From line, so no headers
96 * there to worry about
97 */
2de8fc95
EW
98 firstline = 0;
99 ignoring = doign == ignoreall;
828615a1 100 } else if (line[0] == '\n') {
b870137d
KS
101 /*
102 * If line is blank, we've reached end of
103 * headers, so force out status: field
104 * and note that we are no longer in header
105 * fields
106 */
828615a1 107 if (dostat) {
2de8fc95 108 statusput(mp, obuf, prefix);
828615a1 109 dostat = 0;
57079810 110 }
828615a1 111 ishead = 0;
2de8fc95 112 ignoring = doign == ignoreall;
828615a1 113 } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
b870137d 114 /*
df5134c8
BB
115 * If this line is a continuation (via space or tab)
116 * of a previous header field, just echo it
117 * (unless the field should be ignored).
828615a1 118 * In other words, nothing to do.
b870137d 119 */
828615a1 120 } else {
b870137d 121 /*
828615a1 122 * Pick up the header field if we have one.
b870137d 123 */
828615a1
EW
124 for (cp = line; (c = *cp++) && c != ':' && !isspace(c);)
125 ;
126 cp2 = --cp;
127 while (isspace(*cp++))
128 ;
129 if (cp[-1] != ':') {
130 /*
131 * Not a header line, force out status:
132 * This happens in uucp style mail where
133 * there are no headers at all.
134 */
b870137d 135 if (dostat) {
2de8fc95 136 statusput(mp, obuf, prefix);
b870137d
KS
137 dostat = 0;
138 }
2de8fc95
EW
139 if (doign != ignoreall)
140 /* add blank line */
141 (void) putc('\n', obuf);
a6b3a6c5 142 ishead = 0;
828615a1
EW
143 ignoring = 0;
144 } else {
145 /*
146 * If it is an ignored field and
147 * we care about such things, skip it.
148 */
149 *cp2 = 0; /* temporarily null terminate */
887efe38 150 if (doign && isign(line, doign))
828615a1
EW
151 ignoring = 1;
152 else if ((line[0] == 's' || line[0] == 'S') &&
470c33f3 153 strcasecmp(line, "status") == 0) {
828615a1
EW
154 /*
155 * If the field is "status," go compute
156 * and print the real Status: field
157 */
158 if (dostat) {
2de8fc95 159 statusput(mp, obuf, prefix);
828615a1
EW
160 dostat = 0;
161 }
162 ignoring = 1;
163 } else {
164 ignoring = 0;
165 *cp2 = c; /* restore */
2d7a1b5d 166 }
828615a1 167 infld = 1;
2d7a1b5d 168 }
57079810 169 }
828615a1 170 if (!ignoring) {
ebbb3173
EW
171 /*
172 * Strip trailing whitespace from prefix
173 * if line is blank.
174 */
175 if (prefix != NOSTR)
176 if (length > 1)
177 fputs(prefix, obuf);
178 else
179 (void) fwrite(prefix, sizeof *prefix,
180 prefixlen, obuf);
751b30cd 181 (void) fwrite(line, sizeof *line, length, obuf);
828615a1
EW
182 if (ferror(obuf))
183 return -1;
828615a1 184 }
3184019b 185 }
828615a1
EW
186 /*
187 * Copy out message body
188 */
2de8fc95
EW
189 if (doign == ignoreall)
190 count--; /* skip final blank line */
191 if (prefix != NOSTR)
192 while (count > 0) {
193 if (fgets(line, LINESIZE, ibuf) == NULL) {
194 c = 0;
195 break;
196 }
197 count -= c = strlen(line);
ebbb3173
EW
198 /*
199 * Strip trailing whitespace from prefix
200 * if line is blank.
201 */
2de8fc95
EW
202 if (c > 1)
203 fputs(prefix, obuf);
ebbb3173
EW
204 else
205 (void) fwrite(prefix, sizeof *prefix,
206 prefixlen, obuf);
2de8fc95
EW
207 (void) fwrite(line, sizeof *line, c, obuf);
208 if (ferror(obuf))
209 return -1;
210 }
211 else
212 while (count > 0) {
213 c = count < LINESIZE ? count : LINESIZE;
214 if ((c = fread(line, sizeof *line, c, ibuf)) <= 0)
215 break;
216 count -= c;
217 if (fwrite(line, sizeof *line, c, obuf) != c)
218 return -1;
219 }
220 if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
221 /* no final blank line */
222 if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
828615a1 223 return -1;
2de8fc95 224 return 0;
a6b3a6c5
KS
225}
226
57079810
KS
227/*
228 * Output a reasonable looking status field.
229 */
a0d23834 230void
2de8fc95 231statusput(mp, obuf, prefix)
57079810 232 register struct message *mp;
828615a1 233 FILE *obuf;
2de8fc95 234 char *prefix;
57079810
KS
235{
236 char statout[3];
828615a1 237 register char *cp = statout;
57079810 238
57079810 239 if (mp->m_flag & MREAD)
828615a1 240 *cp++ = 'R';
57079810 241 if ((mp->m_flag & MNEW) == 0)
828615a1
EW
242 *cp++ = 'O';
243 *cp = 0;
244 if (statout[0])
2de8fc95
EW
245 fprintf(obuf, "%sStatus: %s\n",
246 prefix == NOSTR ? "" : prefix, statout);
57079810
KS
247}
248
3184019b
KS
249/*
250 * Interface between the argument list and the mail1 routine
251 * which does all the dirty work.
252 */
a0d23834 253int
3d6f01e5 254mail(to, cc, bcc, smopts, subject)
790344bc 255 struct name *to, *cc, *bcc, *smopts;
3d6f01e5 256 char *subject;
3184019b 257{
3184019b
KS
258 struct header head;
259
3d6f01e5
EW
260 head.h_to = to;
261 head.h_subject = subject;
262 head.h_cc = cc;
263 head.h_bcc = bcc;
264 head.h_smopts = smopts;
322c8626 265 mail1(&head, 0);
3184019b
KS
266 return(0);
267}
268
269
270/*
271 * Send mail to a bunch of user names. The interface is through
272 * the mail routine below.
273 */
a0d23834 274int
3184019b
KS
275sendmail(str)
276 char *str;
277{
3184019b
KS
278 struct header head;
279
3d6f01e5 280 head.h_to = extract(str, GTO);
3184019b 281 head.h_subject = NOSTR;
3d6f01e5
EW
282 head.h_cc = NIL;
283 head.h_bcc = NIL;
284 head.h_smopts = NIL;
322c8626 285 mail1(&head, 0);
3184019b
KS
286 return(0);
287}
288
289/*
290 * Mail a message on standard input to the people indicated
291 * in the passed header. (Internal interface).
292 */
a0d23834 293void
3d6f01e5 294mail1(hp, printheaders)
3184019b 295 struct header *hp;
a0d23834 296 int printheaders;
3184019b 297{
322c8626
EW
298 char *cp;
299 int pid;
300 char **namelist;
301 struct name *to;
302 FILE *mtf;
3184019b
KS
303
304 /*
305 * Collect user's mail from standard input.
306 * Get the result as mtf.
307 */
3d6f01e5 308 if ((mtf = collect(hp, printheaders)) == NULL)
322c8626 309 return;
686f6134
EW
310 if (value("interactive") != NOSTR)
311 if (value("askcc") != NOSTR)
312 grabh(hp, GCC);
313 else {
314 printf("EOT\n");
315 (void) fflush(stdout);
316 }
322c8626
EW
317 if (fsize(mtf) == 0)
318 if (hp->h_subject == NOSTR)
319 printf("No message, no subject; hope that's ok\n");
320 else
321 printf("Null message body; hope that's ok\n");
3184019b
KS
322 /*
323 * Now, take the user names from the combined
324 * to and cc lists and do all the alias
325 * processing.
326 */
3184019b 327 senderr = 0;
3d6f01e5 328 to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
3184019b
KS
329 if (to == NIL) {
330 printf("No recipients specified\n");
322c8626 331 senderr++;
3184019b 332 }
3184019b
KS
333 /*
334 * Look through the recipient list for names with /'s
335 * in them which we write to as files directly.
336 */
3184019b 337 to = outof(to, mtf, hp);
62e28b79
EW
338 if (senderr)
339 savedeadletter(mtf);
3184019b 340 to = elide(to);
322c8626
EW
341 if (count(to) == 0)
342 goto out;
343 fixhead(hp, to);
344 if ((mtf = infix(hp, mtf)) == NULL) {
345 fprintf(stderr, ". . . message lost, sorry.\n");
346 return;
3184019b 347 }
3d6f01e5 348 namelist = unpack(cat(hp->h_smopts, to));
3184019b 349 if (debug) {
322c8626
EW
350 char **t;
351
3d6f01e5 352 printf("Sendmail arguments:");
3184019b
KS
353 for (t = namelist; *t != NOSTR; t++)
354 printf(" \"%s\"", *t);
355 printf("\n");
322c8626 356 goto out;
3184019b
KS
357 }
358 if ((cp = value("record")) != NOSTR)
751b30cd 359 (void) savemail(expand(cp), mtf);
3184019b 360 /*
322c8626
EW
361 * Fork, set up the temporary mail file as standard
362 * input for "mail", and exec with the user list we generated
363 * far above.
3184019b 364 */
3184019b
KS
365 pid = fork();
366 if (pid == -1) {
367 perror("fork");
62e28b79 368 savedeadletter(mtf);
3184019b
KS
369 goto out;
370 }
371 if (pid == 0) {
322c8626
EW
372 prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)|
373 sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU),
374 fileno(mtf), -1);
375 if ((cp = value("sendmail")) != NOSTR)
376 cp = expand(cp);
377 else
435e8dff 378 cp = _PATH_SENDMAIL;
322c8626
EW
379 execv(cp, namelist);
380 perror(cp);
7de887e1 381 _exit(1);
3184019b 382 }
322c8626
EW
383 if (value("verbose") != NOSTR)
384 (void) wait_child(pid);
385 else
386 free_child(pid);
3184019b 387out:
07b0d286 388 (void) Fclose(mtf);
3184019b
KS
389}
390
391/*
392 * Fix the header by glopping all of the expanded names from
393 * the distribution list into the appropriate fields.
3184019b 394 */
a0d23834 395void
3184019b
KS
396fixhead(hp, tolist)
397 struct header *hp;
398 struct name *tolist;
399{
3184019b
KS
400 register struct name *np;
401
3d6f01e5
EW
402 hp->h_to = NIL;
403 hp->h_cc = NIL;
404 hp->h_bcc = NIL;
405 for (np = tolist; np != NIL; np = np->n_flink)
406 if ((np->n_type & GMASK) == GTO)
407 hp->h_to =
408 cat(hp->h_to, nalloc(np->n_name, np->n_type));
409 else if ((np->n_type & GMASK) == GCC)
410 hp->h_cc =
411 cat(hp->h_cc, nalloc(np->n_name, np->n_type));
412 else if ((np->n_type & GMASK) == GBCC)
413 hp->h_bcc =
414 cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
3184019b
KS
415}
416
417/*
418 * Prepend a header in front of the collected stuff
419 * and return the new file.
420 */
3184019b
KS
421FILE *
422infix(hp, fi)
423 struct header *hp;
424 FILE *fi;
425{
426 extern char tempMail[];
427 register FILE *nfo, *nfi;
428 register int c;
429
07b0d286 430 if ((nfo = Fopen(tempMail, "w")) == NULL) {
3184019b
KS
431 perror(tempMail);
432 return(fi);
433 }
07b0d286 434 if ((nfi = Fopen(tempMail, "r")) == NULL) {
3184019b 435 perror(tempMail);
07b0d286 436 (void) Fclose(nfo);
3184019b
KS
437 return(fi);
438 }
a7585c01 439 (void) rm(tempMail);
3d6f01e5 440 (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
3184019b
KS
441 c = getc(fi);
442 while (c != EOF) {
751b30cd 443 (void) putc(c, nfo);
3184019b
KS
444 c = getc(fi);
445 }
446 if (ferror(fi)) {
447 perror("read");
322c8626 448 rewind(fi);
3184019b
KS
449 return(fi);
450 }
751b30cd 451 (void) fflush(nfo);
3184019b
KS
452 if (ferror(nfo)) {
453 perror(tempMail);
07b0d286
EW
454 (void) Fclose(nfo);
455 (void) Fclose(nfi);
322c8626 456 rewind(fi);
3184019b
KS
457 return(fi);
458 }
07b0d286
EW
459 (void) Fclose(nfo);
460 (void) Fclose(fi);
3184019b
KS
461 rewind(nfi);
462 return(nfi);
463}
464
465/*
466 * Dump the to, subject, cc header on the
467 * passed file buffer.
468 */
a0d23834 469int
3184019b
KS
470puthead(hp, fo, w)
471 struct header *hp;
472 FILE *fo;
a0d23834 473 int w;
3184019b
KS
474{
475 register int gotcha;
476
477 gotcha = 0;
3d6f01e5 478 if (hp->h_to != NIL && w & GTO)
322c8626 479 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
3184019b
KS
480 if (hp->h_subject != NOSTR && w & GSUBJECT)
481 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
3d6f01e5 482 if (hp->h_cc != NIL && w & GCC)
322c8626 483 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
3d6f01e5 484 if (hp->h_bcc != NIL && w & GBCC)
322c8626 485 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
3184019b 486 if (gotcha && w & GNL)
751b30cd 487 (void) putc('\n', fo);
3184019b
KS
488 return(0);
489}
490
491/*
322c8626 492 * Format the given header line to not exceed 72 characters.
3184019b 493 */
a0d23834 494void
3d6f01e5
EW
495fmt(str, np, fo, comma)
496 char *str;
497 register struct name *np;
498 FILE *fo;
499 int comma;
3184019b 500{
3d6f01e5 501 register col, len;
a1f76dd3 502
3d6f01e5 503 comma = comma ? 1 : 0;
a1f76dd3
CL
504 col = strlen(str);
505 if (col)
3d6f01e5 506 fputs(str, fo);
322c8626 507 for (; np != NIL; np = np->n_flink) {
3d6f01e5
EW
508 if (np->n_flink == NIL)
509 comma = 0;
510 len = strlen(np->n_name);
322c8626
EW
511 col++; /* for the space */
512 if (col + len + comma > 72 && col > 4) {
3d6f01e5
EW
513 fputs("\n ", fo);
514 col = 4;
322c8626 515 } else
3d6f01e5 516 putc(' ', fo);
322c8626
EW
517 fputs(np->n_name, fo);
518 if (comma)
519 putc(',', fo);
520 col += len + comma;
3184019b 521 }
3d6f01e5 522 putc('\n', fo);
3184019b
KS
523}
524
525/*
526 * Save the outgoing mail on the passed file.
527 */
528
828615a1 529/*ARGSUSED*/
a0d23834 530int
828615a1 531savemail(name, fi)
3184019b 532 char name[];
828615a1 533 register FILE *fi;
3184019b
KS
534{
535 register FILE *fo;
828615a1
EW
536 char buf[BUFSIZ];
537 register i;
538 time_t now, time();
828615a1 539 char *ctime();
3184019b 540
07b0d286 541 if ((fo = Fopen(name, "a")) == NULL) {
3184019b 542 perror(name);
828615a1 543 return (-1);
3184019b 544 }
751b30cd 545 (void) time(&now);
2f025913 546 fprintf(fo, "From %s %s", myname, ctime(&now));
828615a1 547 while ((i = fread(buf, 1, sizeof buf, fi)) > 0)
751b30cd
EW
548 (void) fwrite(buf, 1, i, fo);
549 (void) putc('\n', fo);
550 (void) fflush(fo);
3184019b
KS
551 if (ferror(fo))
552 perror(name);
07b0d286 553 (void) Fclose(fo);
322c8626 554 rewind(fi);
828615a1 555 return (0);
3184019b 556}