2 ** mhshowsbr.c -- routines to display the contents of MIME messages
4 ** This code is Copyright (c) 2002, by the authors of nmh. See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
11 #include <h/signals.h>
19 #include <h/mhparse.h>
22 #ifdef HAVE_SYS_WAIT_H
23 # include <sys/wait.h>
27 ** Just use sigjmp/longjmp on older machines that
28 ** don't have sigsetjmp/siglongjmp.
30 #ifndef HAVE_SIGSETJMP
31 # define sigjmp_buf jmp_buf
32 # define sigsetjmp(env,mask) setjmp(env)
33 # define siglongjmp(env,val) longjmp(env,val)
44 /* flags for moreproc/header display */
50 static sigjmp_buf intrenv;
54 int SOprintf (char *, ...);
60 int part_ok (CT, int);
61 int type_ok (CT, int);
62 void content_error (char *, CT, char *, ...);
63 void flush_errors (void);
66 int list_switch (CT, int, int, int, int);
67 int list_content (CT, int, int, int, int);
72 void show_all_messages (CT *);
73 int show_content_aux (CT, int, int, char *, char *);
78 static void show_single_message (CT, char *);
79 static void DisplayMsgHeader (CT, char *);
80 static int show_switch (CT, int, int);
81 static int show_content (CT, int, int);
82 static int show_content_aux2 (CT, int, int, char *, char *,
83 int, int, int, int, int);
84 static int show_text (CT, int, int);
85 static int show_multi (CT, int, int);
86 static int show_multi_internal (CT, int, int);
87 static int show_multi_aux (CT, int, int, char *);
88 static int show_message_rfc822 (CT, int, int);
89 static int show_partial (CT, int, int);
90 static int show_external (CT, int, int);
91 static RETSIGTYPE intrser (int);
95 ** Top level entry point to show/display a group of messages
99 show_all_messages (CT *cts)
104 ** If form is not specified, then get default form
105 ** for showing headers of MIME messages.
108 formsw = getcpy (etcpath ("mhl.headers"));
111 ** If form is "mhl.null", suppress display of header.
113 if (!strcmp (formsw, "mhl.null"))
116 for (ctp = cts; *ctp; ctp++) {
119 /* if top-level type is ok, then display message */
121 show_single_message (ct, formsw);
127 ** Entry point to show/display a single message
131 show_single_message (CT ct, char *form)
135 #ifdef HAVE_UNION_WAIT
142 ** Allow user executable bit so that temporary directories created by
143 ** the viewer (e.g., lynx) are going to be accessible
145 umask (ct->c_umask & ~(0100));
148 ** If you have a format file, then display
149 ** the message headers.
152 DisplayMsgHeader(ct, form);
156 /* Show the body of the message */
157 show_switch (ct, 1, 0);
163 if (ct->c_ceclosefnx)
164 (*ct->c_ceclosefnx) (ct);
166 /* block a few signals */
168 sigaddset (&set, SIGHUP);
169 sigaddset (&set, SIGINT);
170 sigaddset (&set, SIGQUIT);
171 sigaddset (&set, SIGTERM);
172 SIGPROCMASK (SIG_BLOCK, &set, &oset);
174 while (wait (&status) != NOTOK) {
175 #ifdef HAVE_UNION_WAIT
176 pidcheck (status.w_status);
183 /* reset the signal mask */
184 SIGPROCMASK (SIG_SETMASK, &oset, &set);
192 ** Use the mhlproc to show the header fields
196 DisplayMsgHeader (CT ct, char *form)
203 vec[vecp++] = r1bindex (mhlproc, '/');
204 vec[vecp++] = "-form";
206 vec[vecp++] = "-nobody";
207 vec[vecp++] = ct->c_file;
210 ** If we've specified -(no)moreproc,
211 ** then just pass that along.
214 vec[vecp++] = "-nomoreproc";
216 vec[vecp++] = "-moreproc";
217 vec[vecp++] = progsw;
223 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
228 adios ("fork", "unable to");
232 execvp (mhlproc, vec);
233 fprintf (stderr, "unable to exec ");
246 ** Switching routine. Call the correct routine
247 ** based on content type.
251 show_switch (CT ct, int serial, int alternate)
253 switch (ct->c_type) {
255 return show_multi (ct, serial, alternate);
259 switch (ct->c_subtype) {
260 case MESSAGE_PARTIAL:
261 return show_partial (ct, serial, alternate);
264 case MESSAGE_EXTERNAL:
265 return show_external (ct, serial, alternate);
270 return show_message_rfc822 (ct, serial, alternate);
276 return show_text (ct, serial, alternate);
283 return show_content (ct, serial, alternate);
287 adios (NULL, "unknown content type %d", ct->c_type);
291 return 0; /* NOT REACHED */
296 ** Generic method for displaying content
300 show_content (CT ct, int serial, int alternate)
302 char *cp, buffer[BUFSIZ];
303 CI ci = &ct->c_ctinfo;
305 /* Check for mhshow-show-type/subtype */
306 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
307 invo_name, ci->ci_type, ci->ci_subtype);
308 if ((cp = context_find (buffer)) && *cp != '\0')
309 return show_content_aux (ct, serial, alternate, cp, NULL);
311 /* Check for mhshow-show-type */
312 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
313 if ((cp = context_find (buffer)) && *cp != '\0')
314 return show_content_aux (ct, serial, alternate, cp, NULL);
316 if ((cp = ct->c_showproc))
317 return show_content_aux (ct, serial, alternate, cp, NULL);
319 /* complain if we are not a part of a multipart/alternative */
321 content_error (NULL, ct, "don't know how to display content");
328 ** Parse the display string for displaying generic content
332 show_content_aux (CT ct, int serial, int alternate, char *cp, char *cracked)
334 int fd, len, buflen, quoted;
335 int xstdin, xlist, xpause, xtty;
336 char *bp, *pp, *file, buffer[BUFSIZ];
337 CI ci = &ct->c_ctinfo;
339 if (!ct->c_ceopenfnx) {
341 content_error (NULL, ct,
342 "don't know how to decode content");
348 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
350 if (ct->c_showproc && !strcmp (ct->c_showproc, "true"))
351 return (alternate ? DONE : OK);
359 strncpy (buffer, cp, sizeof(buffer));
363 /* get buffer ready to go */
365 buflen = sizeof(buffer) - 1;
366 bp[0] = bp[buflen] = '\0';
369 /* Now parse display string */
370 for ( ; *cp && buflen > 0; cp++) {
376 /* insert parameters from Content-Type field */
381 for (ap = ci->ci_attrs, ep = ci->ci_values;
383 snprintf (bp, buflen, "%s%s=\"%s\"",
394 /* insert content description */
398 s = trimcpy (ct->c_descr);
399 strncpy (bp, s, buflen);
405 /* exclusive execution */
410 /* %e, %f, and stdin is terminal not content */
416 /* insert filename containing content */
417 snprintf (bp, buflen, "'%s'", file);
419 ** since we've quoted the file argument,
420 ** set things up to look past it, to avoid
421 ** problems with the quoting logic below.
422 ** (I know, I should figure out what's
423 ** broken with the quoting logic, but..)
432 /* %l, and pause prior to displaying content */
438 ** display listing prior to displaying content
444 /* insert subtype of content */
445 strncpy (bp, ci->ci_subtype, buflen);
449 /* insert character % */
462 /* Did we actually insert something? */
465 ** Insert single quote if not inside quotes
468 if (!quoted && buflen) {
470 memmove (pp + 1, pp, len);
475 /* Escape existing quotes */
476 while ((pp = strchr (pp, '\'')) && buflen > 3) {
478 memmove (pp + 3, pp, len);
486 ** If pp is still set, that means we ran
491 if (!quoted && buflen) {
508 if (buflen <= 0 || (ct->c_termproc && buflen <= strlen(ct->c_termproc))) {
510 ** content_error would provide a more useful error message
511 ** here, except that if we got overrun, it probably would
514 fprintf(stderr, "Buffer overflow constructing show command!\n");
518 /* use charset string to modify display method */
519 if (ct->c_termproc) {
522 strncpy (term, buffer, sizeof(term));
523 snprintf (buffer, sizeof(buffer), ct->c_termproc, term);
527 return show_content_aux2 (ct, serial, alternate, cracked, buffer,
528 fd, xlist, xpause, xstdin, xtty);
533 ** Routine to actually display the content
537 show_content_aux2 (CT ct, int serial, int alternate, char *cracked,
538 char *buffer, int fd, int xlist, int xpause, int xstdin, int xtty)
542 char *vec[4], exec[BUFSIZ + sizeof "exec "];
544 if (debugsw || cracked) {
547 fprintf (stderr, "%s msg %s", cracked ? "storing" : "show",
550 fprintf (stderr, " part %s", ct->c_partno);
552 fprintf (stderr, " using command (cd %s; %s)\n",
555 fprintf (stderr, " using command %s\n", buffer);
558 if (xpid < 0 || (xtty && xpid)) {
561 pidcheck(pidwait (xpid, NOTOK));
568 if (ct->c_type == CT_MULTIPART)
569 list_content (ct, -1, 1, 0, 0);
571 list_switch (ct, -1, 1, 0, 0);
573 if (xpause && SOprintf ("Press <return> to show content..."))
574 printf ("Press <return> to show content...");
578 SIGNAL_HANDLER istat;
580 istat = SIGNAL (SIGINT, intrser);
581 if ((intr = sigsetjmp (intrenv, 1)) == OK) {
584 read (fileno (stdout), prompt, sizeof(prompt));
586 SIGNAL (SIGINT, istat);
587 if (intr != OK || prompt[0] == 'n') {
588 (*ct->c_ceclosefnx) (ct);
589 return (alternate ? DONE : NOTOK);
591 if (prompt[0] == 'q') done(OK);
595 snprintf (exec, sizeof(exec), "exec %s", buffer);
604 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
608 advise ("fork", "unable to");
609 (*ct->c_ceclosefnx) (ct);
618 execvp ("/bin/sh", vec);
619 fprintf (stderr, "unable to exec ");
626 ct->c_pid = child_id;
630 pidcheck (pidXwait (child_id, NULL));
634 (*ct->c_ceclosefnx) (ct);
635 return (alternate ? DONE : OK);
641 ** show content of type "text"
645 show_text (CT ct, int serial, int alternate)
647 char *cp, buffer[BUFSIZ];
648 CI ci = &ct->c_ctinfo;
650 /* Check for mhshow-show-type/subtype */
651 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
652 invo_name, ci->ci_type, ci->ci_subtype);
653 if ((cp = context_find (buffer)) && *cp != '\0')
654 return show_content_aux (ct, serial, alternate, cp, NULL);
656 /* Check for mhshow-show-type */
657 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
658 if ((cp = context_find (buffer)) && *cp != '\0')
659 return show_content_aux (ct, serial, alternate, cp, NULL);
662 ** Use default method if content is text/plain, or if
663 ** if it is not a text part of a multipart/alternative
665 if (!alternate || ct->c_subtype == TEXT_PLAIN) {
666 snprintf (buffer, sizeof(buffer), "%%p%s '%%F'", progsw ? progsw :
667 moreproc && *moreproc ? moreproc : "more");
668 cp = (ct->c_showproc = add (buffer, NULL));
669 return show_content_aux (ct, serial, alternate, cp, NULL);
677 ** show message body of type "multipart"
681 show_multi (CT ct, int serial, int alternate)
683 char *cp, buffer[BUFSIZ];
684 CI ci = &ct->c_ctinfo;
686 /* Check for mhshow-show-type/subtype */
687 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
688 invo_name, ci->ci_type, ci->ci_subtype);
689 if ((cp = context_find (buffer)) && *cp != '\0')
690 return show_multi_aux (ct, serial, alternate, cp);
692 /* Check for mhshow-show-type */
693 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
694 if ((cp = context_find (buffer)) && *cp != '\0')
695 return show_multi_aux (ct, serial, alternate, cp);
697 if ((cp = ct->c_showproc))
698 return show_multi_aux (ct, serial, alternate, cp);
701 ** Use default method to display this multipart content
702 ** if it is not a (nested) part of a multipart/alternative,
703 ** or if it is one of the known subtypes of multipart.
705 if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
706 return show_multi_internal (ct, serial, alternate);
713 ** show message body of subtypes of multipart that
714 ** we understand directly (mixed, alternate, etc...)
718 show_multi_internal (CT ct, int serial, int alternate)
720 int alternating, nowalternate, nowserial, result;
721 struct multipart *m = (struct multipart *) ct->c_ctparams;
727 nowalternate = alternate;
729 if (ct->c_subtype == MULTI_PARALLEL) {
730 nowserial = serialsw;
731 } else if (ct->c_subtype == MULTI_ALTERNATE) {
739 ** unknown subtypes of multipart (treat as mixed per rfc2046)
744 /* block a few signals */
747 sigaddset (&set, SIGHUP);
748 sigaddset (&set, SIGINT);
749 sigaddset (&set, SIGQUIT);
750 sigaddset (&set, SIGTERM);
751 SIGPROCMASK (SIG_BLOCK, &set, &oset);
755 ** alternate -> we are a part inside an multipart/alternative
756 ** alternating -> we are a multipart/alternative
759 result = alternate ? NOTOK : OK;
761 for (part = m->mp_parts; part; part = part->mp_next) {
764 if (part_ok (p, 0) && type_ok (p, 0)) {
767 inneresult = show_switch (p, nowserial, nowalternate);
768 switch (inneresult) {
770 if (alternate && !alternating) {
783 alternate = nowalternate = 0;
793 if (alternating && !part) {
795 content_error (NULL, ct, "don't know how to display any of the contents");
800 if (serial && !nowserial) {
803 #ifdef HAVE_UNION_WAIT
810 for (part = m->mp_parts; part; part = part->mp_next) {
814 if (kill (p->c_pid, 0) == NOTOK)
821 while (kids > 0 && (pid = wait (&status)) != NOTOK) {
822 #ifdef HAVE_UNION_WAIT
823 pidcheck (status.w_status);
828 for (part = m->mp_parts; part; part = part->mp_next) {
833 if (p->c_pid == pid) {
844 /* reset the signal mask */
845 SIGPROCMASK (SIG_SETMASK, &oset, &set);
853 ** Parse display string for multipart content
854 ** and use external program to display it.
858 show_multi_aux (CT ct, int serial, int alternate, char *cp)
860 int len, buflen, quoted;
861 int xlist, xpause, xtty;
862 char *bp, *pp, *file, buffer[BUFSIZ];
863 struct multipart *m = (struct multipart *) ct->c_ctparams;
865 CI ci = &ct->c_ctinfo;
868 for (part = m->mp_parts; part; part = part->mp_next) {
871 if (!p->c_ceopenfnx) {
873 content_error (NULL, p, "don't know how to decode content");
877 if (p->c_storage == NULL) {
879 if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
882 /* I'm not sure if this is necessary? */
883 p->c_storage = add (file, NULL);
885 if (p->c_showproc && !strcmp (p->c_showproc, "true"))
886 return (alternate ? DONE : OK);
887 (*p->c_ceclosefnx) (p);
895 /* get buffer ready to go */
897 buflen = sizeof(buffer) - 1;
898 bp[0] = bp[buflen] = '\0';
901 /* Now parse display string */
902 for ( ; *cp && buflen > 0; cp++) {
907 /* insert parameters from Content-Type field */
912 for (ap = ci->ci_attrs, ep = ci->ci_values;
914 snprintf (bp, buflen, "%s%s=\"%s\"",
925 /* insert content description */
929 s = trimcpy (ct->c_descr);
930 strncpy (bp, s, buflen);
936 /* exclusive execution */
946 /* insert filename(s) containing content */
950 for (part = m->mp_parts; part;
951 part = part->mp_next) {
954 snprintf (bp, buflen, "%s'%s'", s, p->c_storage);
961 ** set our starting pointer back to bp,
962 ** to avoid requoting the filenames we
970 /* %l, and pause prior to displaying content */
976 ** display listing prior to displaying content
982 /* insert subtype of content */
983 strncpy (bp, ci->ci_subtype, buflen);
987 /* insert character % */
1000 /* Did we actually insert something? */
1003 ** Insert single quote if not inside quotes
1006 if (!quoted && buflen) {
1008 memmove (pp + 1, pp, len);
1013 /* Escape existing quotes */
1014 while ((pp = strchr (pp, '\'')) && buflen > 3) {
1015 len = strlen (pp++);
1016 memmove (pp + 3, pp, len);
1024 ** If pp is still set, that means we ran
1029 if (!quoted && buflen) {
1046 if (buflen <= 0 || (ct->c_termproc && buflen <= strlen(ct->c_termproc))) {
1048 ** content_error would provide a more useful error message
1049 ** here, except that if we got overrun, it probably would
1052 fprintf(stderr, "Buffer overflow constructing show command!\n");
1056 /* use charset string to modify display method */
1057 if (ct->c_termproc) {
1060 strncpy (term, buffer, sizeof(term));
1061 snprintf (buffer, sizeof(buffer), ct->c_termproc, term);
1064 return show_content_aux2 (ct, serial, alternate, NULL, buffer,
1065 NOTOK, xlist, xpause, 0, xtty);
1070 ** show content of type "message/rfc822"
1074 show_message_rfc822 (CT ct, int serial, int alternate)
1076 char *cp, buffer[BUFSIZ];
1077 CI ci = &ct->c_ctinfo;
1079 /* Check for mhshow-show-type/subtype */
1080 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
1081 invo_name, ci->ci_type, ci->ci_subtype);
1082 if ((cp = context_find (buffer)) && *cp != '\0')
1083 return show_content_aux (ct, serial, alternate, cp, NULL);
1085 /* Check for mhshow-show-type */
1086 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
1087 if ((cp = context_find (buffer)) && *cp != '\0')
1088 return show_content_aux (ct, serial, alternate, cp, NULL);
1090 if ((cp = ct->c_showproc))
1091 return show_content_aux (ct, serial, alternate, cp, NULL);
1093 /* default method for message/rfc822 */
1094 if (ct->c_subtype == MESSAGE_RFC822) {
1095 cp = (ct->c_showproc = add ("%pshow -file '%F'", NULL));
1096 return show_content_aux (ct, serial, alternate, cp, NULL);
1099 /* complain if we are not a part of a multipart/alternative */
1101 content_error (NULL, ct, "don't know how to display content");
1108 ** Show content of type "message/partial".
1112 show_partial (CT ct, int serial, int alternate)
1114 content_error (NULL, ct,
1115 "in order to display this message, you must reassemble it");
1121 ** Show content of type "message/external".
1123 ** THE ERROR CHECKING IN THIS ONE IS NOT DONE YET.
1127 show_external (CT ct, int serial, int alternate)
1129 struct exbody *e = (struct exbody *) ct->c_ctparams;
1130 CT p = e->eb_content;
1132 if (!type_ok (p, 0))
1135 return show_switch (p, serial, alternate);
1138 content_error (NULL, p, "don't know how to display content");
1147 #ifndef RELIABLE_SIGNALS
1148 SIGNAL (SIGINT, intrser);
1152 siglongjmp (intrenv, DONE);