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