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