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