1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 1985, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 /*
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
32 * All Rights Reserved
33 *
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
36 * contributors.
37 */
38
39 /*
40 * mailx -- a modified version of a University of California at Berkeley
41 * mail program
42 *
43 * Collect input from standard input, handling
44 * ~ escapes.
45 */
46
47 #include "rcv.h"
48 #include <locale.h>
49
50 #ifdef SIGCONT
51 static void collcont(int);
52 #endif
53 static void collrub(int s);
54 static void cpout(char *str, FILE *ofd);
55 static int exwrite(char name[], FILE *ibuf);
56 static int forward(char ms[], FILE *obuf, int f);
57 static void intack(int);
58 static int forward(char ms[], FILE *obuf, int f);
59 static FILE *mesedit(FILE *ibuf, FILE *obuf, int c, struct header *hp);
60 static FILE *mespipe(FILE *ibuf, FILE *obuf, char cmd[]);
61 static void resetsigs(int resethup);
62 static int stripnulls(char *linebuf, int nread);
63 static void xhalt(void);
64 static char **Xaddone(char **hf, char news[]);
65 static int tabputs(const char *line, FILE *obuf);
66
67 /*
68 * Read a message from standard output and return a read file to it
69 * or NULL on error.
70 */
71
72 /*
73 * The following hokiness with global variables is so that on
74 * receipt of an interrupt signal, the partial message can be salted
75 * away on dead.letter. The output file must be available to flush,
76 * and the input to read. Several open files could be saved all through
77 * mailx if stdio allowed simultaneous read/write access.
78 */
79
80 static void (*savesig)(int); /* Previous SIGINT value */
81 static void (*savehup)(int); /* Previous SIGHUP value */
82 #ifdef SIGCONT
83 static void (*savecont)(int); /* Previous SIGCONT value */
84 #endif
85 static FILE *newi; /* File for saving away */
86 static FILE *newo; /* Output side of same */
87 static int ignintr; /* Ignore interrups */
88 static int hadintr; /* Have seen one SIGINT so far */
89 static struct header *savehp;
90 static jmp_buf coljmp; /* To get back to work */
91
92 FILE *
collect(struct header * hp)93 collect(struct header *hp)
94 {
95 FILE *ibuf, *fbuf, *obuf;
96 int escape, eof;
97 long lc, cc;
98 int c, t;
99 int hdrs;
100 char linebuf[LINESIZE+1], *cp;
101 char *iprompt;
102 int inhead;
103 void (*sigpipe)(int), (*sigint)(int);
104 int fd = -1;
105
106 noreset++;
107 ibuf = obuf = NULL;
108 newi = newo = NULL;
109 if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 ||
110 (obuf = fdopen(fd, "w")) == NULL) {
111 perror(tempMail);
112 goto err;
113 }
114 newo = obuf;
115 if ((ibuf = fopen(tempMail, "r")) == NULL) {
116 perror(tempMail);
117 newo = NULL;
118 fclose(obuf);
119 goto err;
120 }
121 newi = ibuf;
122 removefile(tempMail);
123
124 ignintr = (int)value("ignore");
125 hadintr = 1;
126 inhead = 1;
127 savehp = hp;
128 # ifdef VMUNIX
129 if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN)
130 sigset(SIGINT, ignintr ? intack : collrub), sigblock(sigmask(SIGINT));
131 if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN)
132 sigset(SIGHUP, collrub), sigblock(sigmask(SIGHUP));
133 # else /* VMUNIX */
134 # ifdef OLD_BSD_SIGS
135 if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN)
136 sigset(SIGINT, ignintr ? intack : collrub);
137 if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN)
138 sigset(SIGHUP, collrub);
139 # else
140 if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) {
141 sigset_t mask;
142
143 sigemptyset(&mask);
144 sigaddset(&mask, SIGINT);
145 sigset(SIGINT, ignintr ? intack : collrub);
146 sigprocmask(SIG_BLOCK, &mask, NULL);
147 }
148 if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) {
149 sigset_t mask;
150
151 sigemptyset(&mask);
152 sigaddset(&mask, SIGHUP);
153 sigset(SIGHUP, collrub);
154 sigprocmask(SIG_BLOCK, &mask, NULL);
155 }
156 # endif
157 # endif /* VMUNIX */
158 #ifdef SIGCONT
159 savecont = sigset(SIGCONT, collcont);
160 #endif
161 /*
162 * If we are going to prompt for subject/cc/bcc,
163 * refrain from printing a newline after
164 * the headers (since some people mind).
165 */
166
167 if (hp->h_subject == NOSTR) {
168 hp->h_subject = sflag;
169 sflag = NOSTR;
170 }
171 if (hp->h_cc == NOSTR) {
172 hp->h_cc = cflag;
173 cflag = NOSTR;
174 }
175 if (hp->h_bcc == NOSTR) {
176 hp->h_bcc = bflag;
177 bflag = NOSTR;
178 }
179 t = GMASK;
180 hdrs = 0;
181 if (intty && !tflag) {
182 if (hp->h_to == NOSTR)
183 hdrs |= GTO;
184 if (hp->h_subject == NOSTR && value("asksub"))
185 hdrs |= GSUBJECT;
186 if (hp->h_cc == NOSTR && value("askcc"))
187 hdrs |= GCC;
188 if (hp->h_bcc == NOSTR && value("askbcc"))
189 hdrs |= GBCC;
190 if (hdrs)
191 t &= ~GNL;
192 }
193 if (hp->h_seq != 0) {
194 puthead(hp, stdout, t, 0);
195 fflush(stdout);
196 }
197 if (setjmp(coljmp))
198 goto err;
199 escape = SENDESC;
200 if ((cp = value("escape")) != NOSTR)
201 escape = *cp;
202 eof = 0;
203 if ((cp = value("MAILX_HEAD")) != NOSTR) {
204 cpout( cp, obuf);
205 if (isatty(fileno(stdin)))
206 cpout( cp, stdout);
207 }
208 iprompt = value("iprompt");
209 fflush(obuf);
210 hadintr = 0;
211 for (;;) {
212 int nread, hasnulls;
213 # ifdef VMUNIX
214 int omask = sigblock(0) &~ (sigmask(SIGINT)|sigmask(SIGHUP));
215 # else
216 # ifndef OLD_BSD_SIGS
217 sigset_t omask;
218 sigprocmask(0, NULL, &omask);
219 sigdelset(&omask, SIGINT);
220 sigdelset(&omask, SIGHUP);
221 # endif
222 # endif
223
224 setjmp(coljmp);
225 # ifdef VMUNIX
226 sigsetmask(omask);
227 # else /* VMUNIX */
228 # ifdef OLD_BSD_SIGS
229 sigrelse(SIGINT);
230 sigrelse(SIGHUP);
231 # else
232 sigprocmask(SIG_SETMASK, &omask, NULL);
233 # endif
234 # endif /* VMUNIX */
235 if (intty && !tflag && outtty && iprompt)
236 fputs(iprompt, stdout);
237 flush();
238 if (hdrs) {
239 grabh(hp, hdrs, 1);
240 hdrs = 0;
241 continue;
242 }
243 if ((nread = getaline(linebuf,LINESIZE,stdin,&hasnulls)) == 0) {
244 if (intty && value("ignoreeof") != NOSTR) {
245 if (++eof > 35)
246 break;
247 printf(gettext(
248 "Use \".\" to terminate letter\n"));
249 continue;
250 }
251 break;
252 }
253 eof = 0;
254 hadintr = 0;
255 if (intty && equal(".\n", linebuf) &&
256 (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
257 break;
258 /*
259 * If -t, scan text for headers.
260 */
261 if (tflag) {
262 char *cp2;
263
264 if (!inhead) {
265 writeit:
266 if (write(fileno(obuf),linebuf,nread) != nread)
267 goto werr;
268 continue;
269 }
270 if (linebuf[0] == '\n') {
271 /* got blank line after header, ignore it */
272 inhead = 0;
273 continue;
274 }
275 if (!headerp(linebuf)) {
276 /* got non-header line, save it */
277 inhead = 0;
278 goto writeit;
279 }
280 if (hasnulls)
281 nread = stripnulls(linebuf, nread);
282 for (;;) {
283 char line2[LINESIZE];
284
285 c = getc(stdin);
286 ungetc(c, stdin);
287 if (!isspace(c) || c == '\n')
288 break;
289 if (readline(stdin, line2) < 0)
290 break;
291 for (cp2 = line2; *cp2 != 0 && isspace(*cp2);
292 cp2++)
293 ;
294 if (strlen(linebuf) + strlen(cp2) >=
295 (unsigned)LINESIZE-2)
296 break;
297 cp = &linebuf[strlen(linebuf)];
298 while (cp > linebuf &&
299 (isspace(cp[-1]) || cp[-1] == '\\'))
300 cp--;
301 *cp++ = ' ';
302 strcpy(cp, cp2);
303 }
304 if ((c = strlen(linebuf)) > 0) {
305 cp = &linebuf[c-1];
306 while (cp > linebuf && isspace(*cp))
307 cp--;
308 *++cp = 0;
309 }
310 if (ishfield(linebuf, "to"))
311 hp->h_to = addto(hp->h_to, hcontents(linebuf));
312 else if (ishfield(linebuf, "subject"))
313 hp->h_subject =
314 addone(hp->h_subject, hcontents(linebuf));
315 else if (ishfield(linebuf, "cc"))
316 hp->h_cc = addto(hp->h_cc, hcontents(linebuf));
317 else if (ishfield(linebuf, "bcc"))
318 hp->h_bcc =
319 addto(hp->h_bcc, hcontents(linebuf));
320 else if (ishfield(linebuf, "default-options"))
321 hp->h_defopt =
322 addone(hp->h_defopt, hcontents(linebuf));
323 else
324 hp->h_others = Xaddone(hp->h_others, linebuf);
325 hp->h_seq++;
326 continue;
327 }
328 if ((linebuf[0] != escape) || (rflag != NOSTR) ||
329 (!intty && !(int)value("escapeok"))) {
330 if (write(fileno(obuf),linebuf,nread) != nread)
331 goto werr;
332 continue;
333 }
334 /*
335 * On double escape, just send the single one.
336 */
337 if ((nread > 1) && (linebuf[1] == escape)) {
338 if (write(fileno(obuf),linebuf+1,nread-1) != (nread-1))
339 goto werr;
340 continue;
341 }
342 if (hasnulls)
343 nread = stripnulls(linebuf, nread);
344 c = linebuf[1];
345 linebuf[nread - 1] = '\0';
346 switch (c) {
347 default:
348 /*
349 * Otherwise, it's an error.
350 */
351 printf(gettext("Unknown tilde escape.\n"));
352 break;
353
354 case 'a':
355 case 'A':
356 /*
357 * autograph; sign the letter.
358 */
359
360 if (cp = value(c=='a' ? "sign":"Sign")) {
361 if (*cp)
362 cpout( cp, obuf);
363 if (isatty(fileno(stdin))) {
364 if (*cp)
365 cpout( cp, stdout);
366 }
367 }
368
369 break;
370
371 case 'i':
372 /*
373 * insert string
374 */
375 for (cp = &linebuf[2]; any(*cp, " \t"); cp++)
376 ;
377 if (*cp)
378 cp = value(cp);
379 if (cp != NOSTR) {
380 if (*cp)
381 cpout(cp, obuf);
382 if (isatty(fileno(stdout))) {
383 if (*cp)
384 cpout(cp, stdout);
385 }
386 }
387 break;
388
389 case '!':
390 /*
391 * Shell escape, send the balance of the
392 * line to sh -c.
393 */
394
395 shell(&linebuf[2]);
396 break;
397
398 case ':':
399 case '_':
400 /*
401 * Escape to command mode, but be nice!
402 */
403
404 execute(&linebuf[2], 1);
405 iprompt = value("iprompt");
406 if (cp = value("escape"))
407 escape = *cp;
408 printf(gettext("(continue)\n"));
409 break;
410
411 case '.':
412 /*
413 * Simulate end of file on input.
414 */
415 goto eofl;
416
417 case 'q':
418 case 'Q':
419 /*
420 * Force a quit of sending mail.
421 * Act like an interrupt happened.
422 */
423
424 hadintr++;
425 collrub(SIGINT);
426 exit(1);
427 /* NOTREACHED */
428
429 case 'x':
430 xhalt();
431 break; /* not reached */
432
433 case 'h':
434 /*
435 * Grab a bunch of headers.
436 */
437 if (!intty || !outtty) {
438 printf(gettext("~h: no can do!?\n"));
439 break;
440 }
441 grabh(hp, GMASK, (int)value("bsdcompat"));
442 printf(gettext("(continue)\n"));
443 break;
444
445 case 't':
446 /*
447 * Add to the To list.
448 */
449
450 hp->h_to = addto(hp->h_to, &linebuf[2]);
451 hp->h_seq++;
452 break;
453
454 case 's':
455 /*
456 * Set the Subject list.
457 */
458
459 cp = &linebuf[2];
460 while (any(*cp, " \t"))
461 cp++;
462 hp->h_subject = savestr(cp);
463 hp->h_seq++;
464 break;
465
466 case 'c':
467 /*
468 * Add to the CC list.
469 */
470
471 hp->h_cc = addto(hp->h_cc, &linebuf[2]);
472 hp->h_seq++;
473 break;
474
475 case 'b':
476 /*
477 * Add stuff to blind carbon copies list.
478 */
479 hp->h_bcc = addto(hp->h_bcc, &linebuf[2]);
480 hp->h_seq++;
481 break;
482
483 case 'R':
484 hp->h_defopt = addone(hp->h_defopt, myname);
485 hp->h_seq++;
486 fprintf(stderr, gettext("Return receipt marked.\n"));
487 receipt_flg = 1;
488 break;
489
490 case 'd':
491 copy(Getf("DEAD"), &linebuf[2]);
492 /* FALLTHROUGH */
493
494 case '<':
495 case 'r': {
496 int ispip;
497 /*
498 * Invoke a file:
499 * Search for the file name,
500 * then open it and copy the contents to obuf.
501 *
502 * if name begins with '!', read from a command
503 */
504
505 cp = &linebuf[2];
506 while (any(*cp, " \t"))
507 cp++;
508 if (*cp == '\0') {
509 printf(gettext("Interpolate what file?\n"));
510 break;
511 }
512 if (*cp=='!') {
513 /* take input from a command */
514 ispip = 1;
515 if ((fbuf = npopen(++cp, "r"))==NULL) {
516 perror("");
517 break;
518 }
519 sigint = sigset(SIGINT, SIG_IGN);
520 } else {
521 ispip = 0;
522 cp = expand(cp);
523 if (cp == NOSTR)
524 break;
525 if (isdir(cp)) {
526 printf(gettext("%s: directory\n"), cp);
527 break;
528 }
529 if ((fbuf = fopen(cp, "r")) == NULL) {
530 perror(cp);
531 break;
532 }
533 }
534 printf("\"%s\" ", cp);
535 flush();
536 lc = cc = 0;
537 while ((t = getc(fbuf)) != EOF) {
538 if (t == '\n')
539 lc++;
540 if (putc(t, obuf) == EOF) {
541 if (ispip) {
542 npclose(fbuf);
543 sigset(SIGINT, sigint);
544 } else
545 fclose(fbuf);
546 goto werr;
547 }
548 cc++;
549 }
550 if (ispip) {
551 npclose(fbuf);
552 sigset(SIGINT, sigint);
553 } else
554 fclose(fbuf);
555 printf("%ld/%ld\n", lc, cc);
556 fflush(obuf);
557 break;
558 }
559
560 case 'w':
561 /*
562 * Write the message on a file.
563 */
564
565 cp = &linebuf[2];
566 while (any(*cp, " \t"))
567 cp++;
568 if (*cp == '\0') {
569 fprintf(stderr, gettext("Write what file!?\n"));
570 break;
571 }
572 if ((cp = expand(cp)) == NOSTR)
573 break;
574 fflush(obuf);
575 rewind(ibuf);
576 exwrite(cp, ibuf);
577 break;
578
579 case 'm':
580 case 'M':
581 case 'f':
582 case 'F':
583 /*
584 * Interpolate the named messages, if we
585 * are in receiving mail mode. Does the
586 * standard list processing garbage.
587 * If ~f or ~F is given, we don't shift over.
588 */
589
590 if (!rcvmode) {
591 printf(gettext(
592 "No messages to send from!?!\n"));
593 break;
594 }
595 cp = &linebuf[2];
596 while (any(*cp, " \t"))
597 cp++;
598 if (forward(cp, obuf, c) < 0)
599 goto werr;
600 fflush(obuf);
601 printf(gettext("(continue)\n"));
602 break;
603
604 case '?':
605 if ((fbuf = fopen(THELPFILE, "r")) == NULL) {
606 printf(gettext("No help just now.\n"));
607 break;
608 }
609 t = getc(fbuf);
610 while (t != -1) {
611 putchar(t);
612 t = getc(fbuf);
613 }
614 fclose(fbuf);
615 break;
616
617 case 'p': {
618 /*
619 * Print out the current state of the
620 * message without altering anything.
621 */
622 int nlines;
623 extern jmp_buf pipestop;
624 extern void brokpipe(int);
625
626 fflush(obuf);
627 rewind(ibuf);
628 fbuf = stdout;
629 if (setjmp(pipestop))
630 goto ret0;
631 if (intty && outtty && (cp = value("crt")) != NOSTR) {
632 nlines =
633 (*cp == '\0' ? screensize() : atoi(cp)) - 7;
634 /* 7 for hdr lines */
635 while ((t = getc(ibuf)) != EOF) {
636 if (t == '\n')
637 if (--nlines <= 0)
638 break;
639 }
640 rewind(ibuf);
641 if (nlines <= 0) {
642 fbuf = npopen(MORE, "w");
643 if (fbuf == NULL) {
644 perror(MORE);
645 fbuf = stdout;
646 } else {
647 sigint = sigset(SIGINT, SIG_IGN);
648 sigpipe = sigset(SIGPIPE, brokpipe);
649 }
650 }
651 }
652 fprintf(fbuf, gettext("-------\nMessage contains:\n"));
653 puthead(hp, fbuf, GMASK, 0);
654 while ((t = getc(ibuf))!=EOF)
655 putc(t, fbuf);
656 ret0:
657 if (fbuf != stdout) {
658 npclose(fbuf);
659 sigset(SIGPIPE, sigpipe);
660 sigset(SIGINT, sigint);
661 }
662 printf(gettext("(continue)\n"));
663 break;
664 }
665
666 case '^':
667 case '|':
668 /*
669 * Pipe message through command.
670 * Collect output as new message.
671 */
672
673 obuf = mespipe(ibuf, obuf, &linebuf[2]);
674 newo = obuf;
675 ibuf = newi;
676 newi = ibuf;
677 printf(gettext("(continue)\n"));
678 break;
679
680 case 'v':
681 case 'e':
682 /*
683 * Edit the current message.
684 * 'e' means to use EDITOR
685 * 'v' means to use VISUAL
686 */
687
688 if ((obuf = mesedit(ibuf, obuf, c, hp)) == NULL)
689 goto err;
690 newo = obuf;
691 ibuf = newi;
692 printf(gettext("(continue)\n"));
693 break;
694 }
695 fflush(obuf);
696 }
697 eofl:
698 fflush(obuf);
699 if ((cp = value("MAILX_TAIL")) != NOSTR) {
700 cpout( cp, obuf);
701 if (isatty(fileno(stdin)))
702 cpout( cp, stdout);
703 }
704 fclose(obuf);
705 rewind(ibuf);
706 resetsigs(0);
707 noreset = 0;
708 return(ibuf);
709
710 werr:
711 /*
712 * Write error occurred on tmp file, save partial
713 * message in dead.letter.
714 */
715 perror(tempMail);
716 fflush(obuf);
717 rewind(ibuf);
718 if (fsize(ibuf) > 0) {
719 char *deadletter;
720
721 deadletter = Getf("DEAD");
722 fprintf(stderr, gettext("Saving partial message in %s\n"),
723 deadletter);
724 if ((fbuf = fopen(deadletter,
725 value("appenddeadletter") == NOSTR ? "w" : "a")) != NULL) {
726 chmod(deadletter, DEADPERM);
727 puthead(hp, fbuf, GMASK|GCLEN, fsize(ibuf));
728 lcwrite(deadletter, ibuf, fbuf, value("appenddeadletter") != NOSTR);
729 fclose(fbuf);
730 } else
731 perror(deadletter);
732 }
733
734 err:
735 if (ibuf != NULL)
736 fclose(ibuf);
737 if (obuf != NULL)
738 fclose(obuf);
739 resetsigs(0);
740 noreset = 0;
741 return(NULL);
742 }
743
744 static void
resetsigs(int resethup)745 resetsigs(int resethup)
746 {
747 (void) sigset(SIGINT, savesig);
748 if (resethup)
749 (void) sigset(SIGHUP, savehup);
750 #ifdef SIGCONT
751 # ifdef preSVr4
752 (void) sigset(SIGCONT, savecont);
753 # else
754 {
755 struct sigaction nsig;
756 nsig.sa_handler = (void (*)())savecont;
757 sigemptyset(&nsig.sa_mask);
758 nsig.sa_flags = SA_RESTART;
759 (void) sigaction(SIGCONT, &nsig, (struct sigaction*)0);
760 }
761 # endif
762 #endif
763 }
764
765 /*
766 * Write a file ex-like.
767 */
768
769 static int
exwrite(char name[],FILE * ibuf)770 exwrite(char name[], FILE *ibuf)
771 {
772 FILE *of;
773 struct stat junk;
774 void (*sigint)(int), (*sigpipe)(int);
775 int pi = (*name == '!');
776
777 if ((of = pi ? npopen(++name, "w") : fopen(name, "a")) == NULL) {
778 perror(name);
779 return(-1);
780 }
781 if (pi) {
782 sigint = sigset(SIGINT, SIG_IGN);
783 sigpipe = sigset(SIGPIPE, SIG_IGN);
784 }
785 lcwrite(name, ibuf, of, 0);
786 pi ? npclose(of) : fclose(of);
787 if (pi) {
788 sigset(SIGPIPE, sigpipe);
789 sigset(SIGINT, sigint);
790 }
791 return(0);
792 }
793
794 void
lcwrite(char * fn,FILE * fi,FILE * fo,int addnl)795 lcwrite(char *fn, FILE *fi, FILE *fo, int addnl)
796 {
797 int c;
798 long lc, cc;
799
800 printf("\"%s\" ", fn);
801 fflush(stdout);
802 lc = cc = 0;
803 while ((c = getc(fi)) != EOF) {
804 cc++;
805 if (putc(c, fo) == '\n')
806 lc++;
807 if (ferror(fo)) {
808 perror("");
809 return;
810 }
811 }
812 if (addnl) {
813 putc('\n', fo);
814 lc++;
815 cc++;
816 }
817 fflush(fo);
818 if (fferror(fo)) {
819 perror("");
820 return;
821 }
822 printf("%ld/%ld\n", lc, cc);
823 fflush(stdout);
824 }
825
826 /*
827 * Edit the message being collected on ibuf and obuf.
828 * Write the message out onto some poorly-named temp file
829 * and point an editor at it.
830 *
831 * On return, make the edit file the new temp file.
832 */
833
834 static FILE *
mesedit(FILE * ibuf,FILE * obuf,int c,struct header * hp)835 mesedit(FILE *ibuf, FILE *obuf, int c, struct header *hp)
836 {
837 pid_t pid;
838 FILE *fbuf;
839 int t;
840 void (*sigint)(int);
841 #ifdef SIGCONT
842 void (*sigcont)(int);
843 #endif
844 struct stat sbuf;
845 char *edit;
846 char hdr[LINESIZE];
847 char *oto, *osubject, *occ, *obcc, **oothers;
848 int fd = -1;
849
850 if (stat(tempEdit, &sbuf) >= 0) {
851 printf(gettext("%s: file exists\n"), tempEdit);
852 goto out;
853 }
854 if ((fd = open(tempEdit, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 ||
855 (fbuf = fdopen(fd, "w")) == NULL) {
856 perror(tempEdit);
857 goto out;
858 }
859 fflush(obuf);
860 rewind(ibuf);
861 puthead(hp, fbuf, GMASK, 0);
862 while ((t = getc(ibuf)) != EOF)
863 putc(t, fbuf);
864 fflush(fbuf);
865 if (fferror(fbuf)) {
866 perror(tempEdit);
867 removefile(tempEdit);
868 goto out;
869 }
870 fclose(fbuf);
871 if ((edit = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR ||
872 *edit == '\0')
873 edit = c == 'e' ? EDITOR : VISUAL;
874 edit = safeexpand(edit);
875
876 /*
877 * Fork/execlp the editor on the edit file
878 */
879
880 pid = vfork();
881 if (pid == (pid_t)-1) {
882 perror("fork");
883 removefile(tempEdit);
884 goto out;
885 }
886 if (pid == 0) {
887 char ecmd[BUFSIZ];
888 char *Shell;
889
890 sigchild();
891 execlp(edit, edit, tempEdit, (char *)0);
892 /*
893 * If execlp fails, "edit" might really be a complete
894 * shell command, not a simple pathname. Try using
895 * the shell to run it.
896 */
897 snprintf(ecmd, sizeof (ecmd), "exec %s %s", edit, tempEdit);
898 if ((Shell = value("SHELL")) == NULL || *Shell=='\0')
899 Shell = SHELL;
900 execlp(Shell, Shell, "-c", ecmd, NULL);
901 perror(edit);
902 _exit(1);
903 }
904 sigint = sigset(SIGINT, SIG_IGN);
905 #ifdef SIGCONT
906 sigcont = sigset(SIGCONT, SIG_DFL);
907 #endif
908 while (wait((int *)0) != pid)
909 ;
910 sigset(SIGINT, sigint);
911 #ifdef SIGCONT
912 sigset(SIGCONT, sigcont);
913 #endif
914 /*
915 * Now switch to new file.
916 */
917
918 if ((fbuf = fopen(tempEdit, "r")) == NULL) {
919 perror(tempEdit);
920 removefile(tempEdit);
921 goto out;
922 }
923 removefile(tempEdit);
924
925 /* save the old headers, in case they are accidentally deleted */
926 osubject = hp->h_subject;
927 oto = hp->h_to;
928 occ = hp->h_cc;
929 obcc = hp->h_bcc;
930 oothers = hp->h_others;
931 hp->h_to = hp->h_subject = hp->h_cc = hp->h_bcc = hp->h_defopt = NOSTR;
932 hp->h_others = NOSTRPTR;
933 hp->h_seq = 0;
934 while (gethfield(fbuf, hdr, 9999L) > 0) {
935 if (ishfield(hdr, "to"))
936 hp->h_to = addto(hp->h_to, hcontents(hdr));
937 else if (ishfield(hdr, "subject"))
938 hp->h_subject = addone(hp->h_subject, hcontents(hdr));
939 else if (ishfield(hdr, "cc"))
940 hp->h_cc = addto(hp->h_cc, hcontents(hdr));
941 else if (ishfield(hdr, "bcc"))
942 hp->h_bcc = addto(hp->h_bcc, hcontents(hdr));
943 else if (ishfield(hdr, "default-options"))
944 hp->h_defopt = addone(hp->h_defopt, hcontents(hdr));
945 else
946 hp->h_others = Xaddone(hp->h_others, hdr);
947 hp->h_seq++;
948 }
949 if (hp->h_seq == 0) {
950 /* if we didn't see any headers, restore the original headers */
951 hp->h_subject = osubject;
952 hp->h_to = oto;
953 hp->h_cc = occ;
954 hp->h_bcc = obcc;
955 hp->h_others = oothers;
956 printf(gettext(
957 "(Deleted headers restored to original values)\n"));
958 }
959 if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 ||
960 (obuf = fdopen(fd, "w")) == NULL) {
961 perror(tempMail);
962 fclose(fbuf);
963 goto out;
964 }
965 if ((ibuf = fopen(tempMail, "r")) == NULL) {
966 perror(tempMail);
967 removefile(tempMail);
968 fclose(fbuf);
969 fclose(obuf);
970 goto out;
971 }
972 removefile(tempMail);
973 if (strlen(hdr) != 0) {
974 fputs(hdr, obuf);
975 putc('\n', obuf);
976 }
977 while ((t = getc(fbuf)) != EOF)
978 putc(t, obuf);
979 fclose(fbuf);
980 fclose(newo);
981 fclose(newi);
982 newo = obuf;
983 newi = ibuf;
984 out:
985 return(newo);
986 }
987
988 /*
989 * Pipe the message through the command.
990 * Old message is on stdin of command;
991 * New message collected from stdout.
992 * Sh -c must return 0 to accept the new message.
993 */
994
995 static FILE *
mespipe(FILE * ibuf,FILE * obuf,char cmd[])996 mespipe(FILE *ibuf, FILE *obuf, char cmd[])
997 {
998 FILE *ni, *no;
999 pid_t pid;
1000 int s;
1001 void (*sigint)(int);
1002 char *Shell;
1003 int fd = -1;
1004
1005 newi = ibuf;
1006 if ((fd = open(tempEdit, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 ||
1007 (no = fdopen(fd, "w")) == NULL) {
1008 perror(tempEdit);
1009 return(obuf);
1010 }
1011 if ((ni = fopen(tempEdit, "r")) == NULL) {
1012 perror(tempEdit);
1013 fclose(no);
1014 removefile(tempEdit);
1015 return(obuf);
1016 }
1017 removefile(tempEdit);
1018 fflush(obuf);
1019 rewind(ibuf);
1020 if ((Shell = value("SHELL")) == NULL || *Shell=='\0')
1021 Shell = SHELL;
1022 if ((pid = vfork()) == (pid_t)-1) {
1023 perror("fork");
1024 goto err;
1025 }
1026 if (pid == 0) {
1027 /*
1028 * stdin = current message.
1029 * stdout = new message.
1030 */
1031
1032 sigchild();
1033 close(0);
1034 dup(fileno(ibuf));
1035 close(1);
1036 dup(fileno(no));
1037 for (s = 4; s < 15; s++)
1038 close(s);
1039 execlp(Shell, Shell, "-c", cmd, (char *)0);
1040 perror(Shell);
1041 _exit(1);
1042 }
1043 sigint = sigset(SIGINT, SIG_IGN);
1044 while (wait(&s) != pid)
1045 ;
1046 sigset(SIGINT, sigint);
1047 if (s != 0 || pid == (pid_t)-1) {
1048 fprintf(stderr, gettext("\"%s\" failed!?\n"), cmd);
1049 goto err;
1050 }
1051 if (fsize(ni) == 0) {
1052 fprintf(stderr, gettext("No bytes from \"%s\" !?\n"), cmd);
1053 goto err;
1054 }
1055
1056 /*
1057 * Take new files.
1058 */
1059
1060 newi = ni;
1061 fclose(ibuf);
1062 fclose(obuf);
1063 return(no);
1064
1065 err:
1066 fclose(no);
1067 fclose(ni);
1068 return(obuf);
1069 }
1070
1071 static char *indentprefix; /* used instead of tab by tabputs */
1072
1073 /*
1074 * Interpolate the named messages into the current
1075 * message, preceding each line with a tab.
1076 * Return a count of the number of characters now in
1077 * the message, or -1 if an error is encountered writing
1078 * the message temporary. The flag argument is 'm' if we
1079 * should shift over and 'f' if not.
1080 */
1081 static int
forward(char ms[],FILE * obuf,int f)1082 forward(char ms[], FILE *obuf, int f)
1083 {
1084 int *msgvec, *ip;
1085
1086 msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
1087 if (msgvec == NOINTPTR)
1088 return(0);
1089 if (getmsglist(ms, msgvec, 0) < 0)
1090 return(0);
1091 if (*msgvec == 0) {
1092 *msgvec = first(0, MMNORM);
1093 if (*msgvec == 0) {
1094 printf(gettext("No appropriate messages\n"));
1095 return(0);
1096 }
1097 msgvec[1] = 0;
1098 }
1099 if (tolower(f) == 'm')
1100 indentprefix = value("indentprefix");
1101 printf(gettext("Interpolating:"));
1102 for (ip = msgvec; *ip != 0; ip++) {
1103 touch(*ip);
1104 printf(" %d", *ip);
1105 if (msend(&message[*ip-1], obuf, islower(f) ? M_IGNORE : 0,
1106 tolower(f) == 'm' ? tabputs : fputs) < 0) {
1107 perror(tempMail);
1108 return(-1);
1109 }
1110 }
1111 fflush(obuf);
1112 if (fferror(obuf)) {
1113 perror(tempMail);
1114 return(-1);
1115 }
1116 printf("\n");
1117 return(0);
1118 }
1119
1120 static int
tabputs(const char * line,FILE * obuf)1121 tabputs(const char *line, FILE *obuf)
1122 {
1123
1124 if (indentprefix)
1125 fputs(indentprefix, obuf);
1126 /* Don't create lines with only a tab on them */
1127 else if (line[0] != '\n')
1128 fputc('\t', obuf);
1129 return (fputs(line, obuf));
1130 }
1131
1132 /*
1133 * Print (continue) when continued after ^Z.
1134 */
1135 #ifdef SIGCONT
1136 static void
1137 #ifdef __cplusplus
collcont(int)1138 collcont(int)
1139 #else
1140 /* ARGSUSED */
1141 collcont(int s)
1142 #endif
1143 {
1144 printf(gettext("(continue)\n"));
1145 fflush(stdout);
1146 }
1147 #endif /* SIGCONT */
1148
1149 /*
1150 * On interrupt, go here to save the partial
1151 * message on ~/dead.letter.
1152 * Then restore signals and execute the normal
1153 * signal routine. We only come here if signals
1154 * were previously set anyway.
1155 */
1156 static void
collrub(int s)1157 collrub(int s)
1158 {
1159 FILE *dbuf;
1160 char *deadletter;
1161
1162 # ifdef OLD_BSD_SIGS
1163 if (s == SIGHUP)
1164 sigignore(SIGHUP);
1165 # endif
1166 if (s == SIGINT && hadintr == 0) {
1167 hadintr++;
1168 fflush(stdout);
1169 fprintf(stderr,
1170 gettext("\n(Interrupt -- one more to kill letter)\n"));
1171 # ifdef OLD_BSD_SIGS
1172 sigrelse(s);
1173 # endif
1174 longjmp(coljmp, 1);
1175 }
1176 fclose(newo);
1177 rewind(newi);
1178 if (s == SIGINT && value("save")==NOSTR || fsize(newi) == 0)
1179 goto done;
1180 deadletter = Getf("DEAD");
1181 if ((dbuf = fopen(deadletter,
1182 (value("appenddeadletter") == NOSTR ? "w" : "a"))) == NULL) {
1183 perror(deadletter);
1184 goto done;
1185 }
1186 chmod(deadletter, DEADPERM);
1187 puthead(savehp, dbuf, GMASK|GCLEN, fsize(newi));
1188 lcwrite(deadletter, newi, dbuf, value("appenddeadletter") != NOSTR);
1189 fclose(dbuf);
1190 done:
1191 fclose(newi);
1192 resetsigs(1);
1193 if (rcvmode) {
1194 if (s == SIGHUP)
1195 hangup(s);
1196 else
1197 stop(s);
1198 }
1199 else
1200 exit(1);
1201 }
1202
1203 /*
1204 * Acknowledge an interrupt signal from the tty by typing an @
1205 */
1206 static void
1207 #ifdef __cplusplus
intack(int)1208 intack(int)
1209 #else
1210 /* ARGSUSED */
1211 intack(int s)
1212 #endif
1213 {
1214
1215 puts("@");
1216 fflush(stdout);
1217 clearerr(stdin);
1218 longjmp(coljmp,1);
1219 }
1220
1221 /* Read line from stdin, noting any NULL characters.
1222 Return the number of characters read. Note that the buffer
1223 passed must be 1 larger than "size" for the trailing NUL byte.
1224 */
1225 int
getaline(char * line,int size,FILE * f,int * hasnulls)1226 getaline(char *line, int size, FILE *f, int *hasnulls)
1227 {
1228 int i, ch;
1229 for (i = 0; (i < size) && ((ch=getc(f)) != EOF); ) {
1230 if ( ch == '\0' )
1231 *hasnulls = 1;
1232 if ((line[i++] = (char)ch) == '\n') break;
1233 }
1234 line[i] = '\0';
1235 return(i);
1236 }
1237
1238 void
1239 #ifdef __cplusplus
savedead(int)1240 savedead(int)
1241 #else
1242 /* ARGSUSED */
1243 savedead(int s)
1244 #endif
1245 {
1246 collrub(SIGINT);
1247 exit(1);
1248 /* NOTREACHED */
1249 }
1250
1251 /*
1252 * Add a list of addresses to the end of a header entry field.
1253 */
1254 char *
addto(char hf[],char news[])1255 addto(char hf[], char news[])
1256 {
1257 char name[LINESIZE];
1258 int comma = docomma(news);
1259
1260 while (news = yankword(news, name, sizeof (name), comma)) {
1261 nstrcat(name, sizeof (name), ", ");
1262 hf = addone(hf, name);
1263 }
1264 return hf;
1265 }
1266
1267 /*
1268 * Add a string to the end of a header entry field.
1269 */
1270 char *
addone(char hf[],char news[])1271 addone(char hf[], char news[])
1272 {
1273 char *cp, *cp2, *linebuf;
1274
1275 if (hf == NOSTR)
1276 hf = savestr("");
1277 if (*news == '\0')
1278 return(hf);
1279 linebuf = (char *)srealloc(hf, (unsigned)(strlen(hf) + strlen(news) + 2));
1280 cp2 = strchr(linebuf, '\0');
1281 if (cp2 > linebuf && cp2[-1] != ' ')
1282 *cp2++ = ' ';
1283 for (cp = news; any(*cp, " \t"); cp++)
1284 ;
1285 while (*cp != '\0')
1286 *cp2++ = *cp++;
1287 *cp2 = '\0';
1288 return(linebuf);
1289 }
1290
1291 static int
nptrs(char ** hf)1292 nptrs(char **hf)
1293 {
1294 int i;
1295
1296 if (!hf)
1297 return(0);
1298 for (i = 0; *hf; hf++)
1299 i++;
1300 return(i);
1301 }
1302
1303 /*
1304 * Add a non-standard header to the end of the non-standard headers.
1305 */
1306 static char **
Xaddone(char ** hf,char news[])1307 Xaddone(char **hf, char news[])
1308 {
1309 char *linebuf;
1310 char **ohf = hf;
1311 int nhf = nptrs(hf);
1312
1313 if (hf == NOSTRPTR)
1314 hf = (char**)salloc(sizeof(char*) * 2);
1315 else
1316 hf = (char**)srealloc(hf, sizeof(char*) * (nhf + 2));
1317 if (hf == NOSTRPTR) {
1318 fprintf(stderr, gettext("No room, header lost: %s\n"), news);
1319 return(ohf);
1320 }
1321 linebuf = (char *)salloc((unsigned)(strlen(news) + 1));
1322 strcpy(linebuf, news);
1323 hf[nhf++] = linebuf;
1324 hf[nhf] = NOSTR;
1325 return(hf);
1326 }
1327
1328 static void
cpout(char * str,FILE * ofd)1329 cpout(char *str, FILE *ofd)
1330 {
1331 char *cp = str;
1332
1333 while (*cp) {
1334 if (*cp == '\\') {
1335 switch (*(cp+1)) {
1336 case 'n':
1337 putc('\n', ofd);
1338 cp++;
1339 break;
1340 case 't':
1341 putc('\t', ofd);
1342 cp++;
1343 break;
1344 default:
1345 putc('\\', ofd);
1346 }
1347 } else {
1348 putc(*cp, ofd);
1349 }
1350 cp++;
1351 }
1352 putc('\n', ofd);
1353 fflush(ofd);
1354 }
1355
1356 static void
xhalt(void)1357 xhalt(void)
1358 {
1359 fclose(newo);
1360 fclose(newi);
1361 sigset(SIGINT, savesig);
1362 sigset(SIGHUP, savehup);
1363 if (rcvmode)
1364 stop(0);
1365 exit(1);
1366 /* NOTREACHED */
1367 }
1368
1369 /*
1370 * Strip the nulls from a buffer of length n
1371 */
1372 static int
stripnulls(char * linebuf,int nread)1373 stripnulls(char *linebuf, int nread)
1374 {
1375 int i, j;
1376
1377 for (i = 0; i < nread; i++)
1378 if (linebuf[i] == '\0')
1379 break;
1380 for (j = i; j < nread; j++)
1381 if (linebuf[j] != '\0')
1382 linebuf[i++] = linebuf[j];
1383 linebuf[i] = '\0';
1384 return(i);
1385 }
1386