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