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)
43 /* flags for moreproc/header display */
51 int SOprintf(char *, ...);
59 void content_error(char *, CT, char *, ...);
60 void flush_errors(void);
63 int list_switch(CT, int, int, int, int);
64 int list_content(CT, int, int, int, int);
69 void show_all_messages(CT *);
70 int show_content_aux(CT, int, int, char *, char *);
75 static void show_single_message(CT, char *);
76 static void DisplayMsgHeader(CT, char *);
77 static int show_switch(CT, int, int);
78 static int show_content(CT, int, int);
79 static int show_content_aux2(CT, int, int, char *, char *, int, int, int, int);
80 static int show_text(CT, int, int);
81 static int show_multi(CT, int, int);
82 static int show_multi_internal(CT, int, int);
83 static int show_multi_aux(CT, int, int, char *);
84 static int show_message_rfc822(CT, int, int);
85 static int show_partial(CT, int, int);
86 static int show_external(CT, int, int);
90 ** Top level entry point to show/display a group of messages
94 show_all_messages(CT *cts)
99 ** If form is not specified, then get default form
100 ** for showing headers of MIME messages.
103 formsw = getcpy(etcpath("mhl.headers"));
106 ** If form is "mhl.null", suppress display of header.
108 if (strcmp(formsw, "mhl.null")==0)
111 for (ctp = cts; *ctp; ctp++) {
114 /* if top-level type is ok, then display message */
116 show_single_message(ct, formsw);
122 ** Entry point to show/display a single message
126 show_single_message(CT ct, char *form)
130 #ifdef HAVE_UNION_WAIT
137 ** Allow user executable bit so that temporary directories created by
138 ** the viewer (e.g., lynx) are going to be accessible
140 umask(ct->c_umask & ~(0100));
143 ** If you have a format file, then display
144 ** the message headers.
147 DisplayMsgHeader(ct, form);
151 /* Show the body of the message */
152 show_switch(ct, 1, 0);
158 if (ct->c_ceclosefnx)
159 (*ct->c_ceclosefnx) (ct);
161 /* block a few signals */
163 sigaddset(&set, SIGHUP);
164 sigaddset(&set, SIGINT);
165 sigaddset(&set, SIGQUIT);
166 sigaddset(&set, SIGTERM);
167 SIGPROCMASK(SIG_BLOCK, &set, &oset);
169 while (wait(&status) != NOTOK) {
170 #ifdef HAVE_UNION_WAIT
171 pidcheck(status.w_status);
178 /* reset the signal mask */
179 SIGPROCMASK(SIG_SETMASK, &oset, &set);
187 ** Use the mhlproc to show the header fields
191 DisplayMsgHeader(CT ct, char *form)
198 vec[vecp++] = mhbasename(mhlproc);
199 vec[vecp++] = "-form";
201 vec[vecp++] = "-nobody";
202 vec[vecp++] = ct->c_file;
205 ** If we've specified -(no)moreproc,
206 ** then just pass that along.
209 vec[vecp++] = "-nomoreproc";
211 vec[vecp++] = "-moreproc";
212 vec[vecp++] = progsw;
218 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
223 adios("fork", "unable to");
227 execvp(mhlproc, vec);
228 fprintf(stderr, "unable to exec ");
241 ** Switching routine. Call the correct routine
242 ** based on content type.
246 show_switch(CT ct, int serial, int alternate)
248 switch (ct->c_type) {
250 return show_multi(ct, serial, alternate);
254 switch (ct->c_subtype) {
255 case MESSAGE_PARTIAL:
256 return show_partial(ct, serial, alternate);
259 case MESSAGE_EXTERNAL:
260 return show_external(ct, serial, alternate);
265 return show_message_rfc822(ct, serial,
272 return show_text(ct, serial, alternate);
279 return show_content(ct, serial, alternate);
283 adios(NULL, "unknown content type %d", ct->c_type);
287 return 0; /* NOT REACHED */
292 ** Generic method for displaying content
296 show_content(CT ct, int serial, int alternate)
298 char *cp, buffer[BUFSIZ];
299 CI ci = &ct->c_ctinfo;
301 /* Check for mhshow-show-type/subtype */
302 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
303 invo_name, ci->ci_type, ci->ci_subtype);
304 if ((cp = context_find(buffer)) && *cp != '\0')
305 return show_content_aux(ct, serial, alternate, cp, NULL);
307 /* Check for mhshow-show-type */
308 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
309 if ((cp = context_find(buffer)) && *cp != '\0')
310 return show_content_aux(ct, serial, alternate, cp, NULL);
312 if ((cp = ct->c_showproc))
313 return show_content_aux(ct, serial, alternate, cp, NULL);
315 /* complain if we are not a part of a multipart/alternative */
317 content_error(NULL, ct, "don't know how to display content");
324 ** Parse the display string for displaying generic content
328 show_content_aux(CT ct, int serial, int alternate, char *cp, char *cracked)
330 int fd, len, buflen, quoted;
331 int xstdin, xlist, xtty;
332 char *bp, *pp, *file, buffer[BUFSIZ];
333 CI ci = &ct->c_ctinfo;
335 if (!ct->c_ceopenfnx) {
337 content_error(NULL, ct,
338 "don't know how to decode content");
344 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
346 if (ct->c_showproc && strcmp(ct->c_showproc, "true")==0)
347 return (alternate ? DONE : OK);
354 strncpy(buffer, cp, sizeof(buffer));
358 /* get buffer ready to go */
360 buflen = sizeof(buffer) - 1;
361 bp[0] = bp[buflen] = '\0';
364 /* Now parse display string */
365 for ( ; *cp && buflen > 0; cp++) {
371 /* insert parameters from Content-Type field */
376 for (ap = ci->ci_attrs, ep = ci->ci_values;
378 snprintf(bp, buflen, "%s%s=\"%s\"",
389 /* insert content description */
393 s = trimcpy(ct->c_descr);
394 strncpy(bp, s, buflen);
400 /* exclusive execution */
405 /* %e, %f, and stdin is terminal not content */
411 /* insert filename containing content */
412 snprintf(bp, buflen, "'%s'", file);
414 ** since we've quoted the file argument,
415 ** set things up to look past it, to avoid
416 ** problems with the quoting logic below.
417 ** (I know, I should figure out what's
418 ** broken with the quoting logic, but..)
429 ** display listing prior to displaying content
435 /* insert subtype of content */
436 strncpy(bp, ci->ci_subtype, buflen);
440 /* insert character % */
453 /* Did we actually insert something? */
456 ** Insert single quote if not inside quotes
459 if (!quoted && buflen) {
461 memmove(pp + 1, pp, len);
466 /* Escape existing quotes */
467 while ((pp = strchr(pp, '\'')) && buflen > 3) {
469 memmove(pp + 3, pp, len);
477 ** If pp is still set, that means we ran
482 if (!quoted && buflen) {
499 if (buflen <= 0 || (ct->c_termproc && buflen <= strlen(ct->c_termproc))) {
501 ** content_error would provide a more useful error message
502 ** here, except that if we got overrun, it probably would
505 fprintf(stderr, "Buffer overflow constructing show command!\n");
509 /* use charset string to modify display method */
510 if (ct->c_termproc) {
513 strncpy(term, buffer, sizeof(term));
514 snprintf(buffer, sizeof(buffer), ct->c_termproc, term);
518 return show_content_aux2(ct, serial, alternate, cracked, buffer,
519 fd, xlist, xstdin, xtty);
524 ** Routine to actually display the content
528 show_content_aux2(CT ct, int serial, int alternate, char *cracked,
529 char *buffer, int fd, int xlist, int xstdin, int xtty)
533 char *vec[4], exec[BUFSIZ + sizeof "exec "];
535 if (debugsw || cracked) {
538 fprintf(stderr, "%s msg %s", cracked ? "storing" : "show",
541 fprintf(stderr, " part %s", ct->c_partno);
543 fprintf(stderr, " using command (cd %s; %s)\n",
546 fprintf(stderr, " using command %s\n", buffer);
549 if (xpid < 0 || (xtty && xpid)) {
552 pidcheck(pidwait(xpid, NOTOK));
557 if (ct->c_type == CT_MULTIPART)
558 list_content(ct, -1, 1, 0, 0);
560 list_switch(ct, -1, 1, 0, 0);
563 snprintf(exec, sizeof(exec), "exec %s", buffer);
572 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
576 advise("fork", "unable to");
577 (*ct->c_ceclosefnx) (ct);
586 execvp("/bin/sh", vec);
587 fprintf(stderr, "unable to exec ");
594 ct->c_pid = child_id;
598 pidcheck(pidXwait(child_id, NULL));
602 (*ct->c_ceclosefnx) (ct);
603 return (alternate ? DONE : OK);
609 ** show content of type "text"
613 show_text(CT ct, int serial, int alternate)
615 char *cp, buffer[BUFSIZ];
616 CI ci = &ct->c_ctinfo;
618 /* Check for mhshow-show-type/subtype */
619 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
620 invo_name, ci->ci_type, ci->ci_subtype);
621 if ((cp = context_find(buffer)) && *cp != '\0')
622 return show_content_aux(ct, serial, alternate, cp, NULL);
624 /* Check for mhshow-show-type */
625 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
626 if ((cp = context_find(buffer)) && *cp != '\0')
627 return show_content_aux(ct, serial, alternate, cp, NULL);
630 ** Use default method if content is text/plain, or if
631 ** if it is not a text part of a multipart/alternative
633 if (!alternate || ct->c_subtype == TEXT_PLAIN) {
634 snprintf(buffer, sizeof(buffer), "%%p%s '%%F'",
635 progsw ? progsw : moreproc && *moreproc ?
637 cp = (ct->c_showproc = getcpy(buffer));
638 return show_content_aux(ct, serial, alternate, cp, NULL);
646 ** show message body of type "multipart"
650 show_multi(CT ct, int serial, int alternate)
652 char *cp, buffer[BUFSIZ];
653 CI ci = &ct->c_ctinfo;
655 /* Check for mhshow-show-type/subtype */
656 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
657 invo_name, ci->ci_type, ci->ci_subtype);
658 if ((cp = context_find(buffer)) && *cp != '\0')
659 return show_multi_aux(ct, serial, alternate, cp);
661 /* Check for mhshow-show-type */
662 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
663 if ((cp = context_find(buffer)) && *cp != '\0')
664 return show_multi_aux(ct, serial, alternate, cp);
666 if ((cp = ct->c_showproc))
667 return show_multi_aux(ct, serial, alternate, cp);
670 ** Use default method to display this multipart content
671 ** if it is not a (nested) part of a multipart/alternative,
672 ** or if it is one of the known subtypes of multipart.
674 if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
675 return show_multi_internal(ct, serial, alternate);
682 ** show message body of subtypes of multipart that
683 ** we understand directly (mixed, alternate, etc...)
687 show_multi_internal(CT ct, int serial, int alternate)
689 int alternating, nowalternate, nowserial, result;
690 struct multipart *m = (struct multipart *) ct->c_ctparams;
696 nowalternate = alternate;
698 if (ct->c_subtype == MULTI_PARALLEL) {
699 nowserial = serialsw;
700 } else if (ct->c_subtype == MULTI_ALTERNATE) {
708 ** unknown subtypes of multipart (treat as mixed per rfc2046)
713 /* block a few signals */
716 sigaddset(&set, SIGHUP);
717 sigaddset(&set, SIGINT);
718 sigaddset(&set, SIGQUIT);
719 sigaddset(&set, SIGTERM);
720 SIGPROCMASK(SIG_BLOCK, &set, &oset);
724 ** alternate -> we are a part inside an multipart/alternative
725 ** alternating -> we are a multipart/alternative
728 result = alternate ? NOTOK : OK;
730 for (part = m->mp_parts; part; part = part->mp_next) {
733 if (part_ok(p, 0) && type_ok(p, 0)) {
736 inneresult = show_switch(p, nowserial, nowalternate);
737 switch (inneresult) {
739 if (alternate && !alternating) {
752 alternate = nowalternate = 0;
762 if (alternating && !part) {
764 content_error(NULL, ct, "don't know how to display any of the contents");
769 if (serial && !nowserial) {
772 #ifdef HAVE_UNION_WAIT
779 for (part = m->mp_parts; part; part = part->mp_next) {
783 if (kill(p->c_pid, 0) == NOTOK)
790 while (kids > 0 && (pid = wait(&status)) != NOTOK) {
791 #ifdef HAVE_UNION_WAIT
792 pidcheck(status.w_status);
797 for (part = m->mp_parts; part; part = part->mp_next) {
802 if (p->c_pid == pid) {
813 /* reset the signal mask */
814 SIGPROCMASK(SIG_SETMASK, &oset, &set);
822 ** Parse display string for multipart content
823 ** and use external program to display it.
827 show_multi_aux(CT ct, int serial, int alternate, char *cp)
829 int len, buflen, quoted;
831 char *bp, *pp, *file, buffer[BUFSIZ];
832 struct multipart *m = (struct multipart *) ct->c_ctparams;
834 CI ci = &ct->c_ctinfo;
837 for (part = m->mp_parts; part; part = part->mp_next) {
840 if (!p->c_ceopenfnx) {
842 content_error(NULL, p, "don't know how to decode content");
846 if (p->c_storage == NULL) {
848 if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
851 /* I'm not sure if this is necessary? */
852 p->c_storage = getcpy(file);
854 if (p->c_showproc && strcmp(p->c_showproc, "true")==0)
855 return (alternate ? DONE : OK);
856 (*p->c_ceclosefnx) (p);
863 /* get buffer ready to go */
865 buflen = sizeof(buffer) - 1;
866 bp[0] = bp[buflen] = '\0';
869 /* Now parse display string */
870 for ( ; *cp && buflen > 0; cp++) {
875 /* insert parameters from Content-Type field */
880 for (ap = ci->ci_attrs, ep = ci->ci_values;
882 snprintf(bp, buflen, "%s%s=\"%s\"",
893 /* insert content description */
897 s = trimcpy(ct->c_descr);
898 strncpy(bp, s, buflen);
904 /* exclusive execution */
914 /* insert filename(s) containing content */
918 for (part = m->mp_parts; part;
919 part = part->mp_next) {
922 snprintf(bp, buflen, "%s'%s'",
930 ** set our starting pointer back to bp,
931 ** to avoid requoting the filenames we
941 ** display listing prior to displaying content
947 /* insert subtype of content */
948 strncpy(bp, ci->ci_subtype, buflen);
952 /* insert character % */
965 /* Did we actually insert something? */
968 ** Insert single quote if not inside quotes
971 if (!quoted && buflen) {
973 memmove(pp + 1, pp, len);
978 /* Escape existing quotes */
979 while ((pp = strchr(pp, '\'')) && buflen > 3) {
981 memmove(pp + 3, pp, len);
989 ** If pp is still set, that means we ran
994 if (!quoted && buflen) {
1012 (ct->c_termproc && buflen <= strlen(ct->c_termproc))) {
1014 ** content_error would provide a more useful error message
1015 ** here, except that if we got overrun, it probably would
1018 fprintf(stderr, "Buffer overflow constructing show command!\n");
1022 /* use charset string to modify display method */
1023 if (ct->c_termproc) {
1026 strncpy(term, buffer, sizeof(term));
1027 snprintf(buffer, sizeof(buffer), ct->c_termproc, term);
1030 return show_content_aux2(ct, serial, alternate, NULL, buffer,
1031 NOTOK, xlist, 0, xtty);
1036 ** show content of type "message/rfc822"
1040 show_message_rfc822(CT ct, int serial, int alternate)
1042 char *cp, buffer[BUFSIZ];
1043 CI ci = &ct->c_ctinfo;
1045 /* Check for mhshow-show-type/subtype */
1046 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
1047 invo_name, ci->ci_type, ci->ci_subtype);
1048 if ((cp = context_find(buffer)) && *cp != '\0')
1049 return show_content_aux(ct, serial, alternate, cp, NULL);
1051 /* Check for mhshow-show-type */
1052 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
1053 if ((cp = context_find(buffer)) && *cp != '\0')
1054 return show_content_aux(ct, serial, alternate, cp, NULL);
1056 if ((cp = ct->c_showproc))
1057 return show_content_aux(ct, serial, alternate, cp, NULL);
1059 /* default method for message/rfc822 */
1060 if (ct->c_subtype == MESSAGE_RFC822) {
1061 cp = (ct->c_showproc = getcpy("%pshow -file '%F'"));
1062 return show_content_aux(ct, serial, alternate, cp, NULL);
1065 /* complain if we are not a part of a multipart/alternative */
1067 content_error(NULL, ct, "don't know how to display content");
1074 ** Show content of type "message/partial".
1078 show_partial(CT ct, int serial, int alternate)
1080 content_error(NULL, ct,
1081 "in order to display this message, you must reassemble it");
1087 ** Show content of type "message/external".
1089 ** THE ERROR CHECKING IN THIS ONE IS NOT DONE YET.
1093 show_external(CT ct, int serial, int alternate)
1095 struct exbody *e = (struct exbody *) ct->c_ctparams;
1096 CT p = e->eb_content;
1101 return show_switch(p, serial, alternate);
1104 content_error(NULL, p, "don't know how to display content");