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