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>
18 #include <h/mhparse.h>
21 #ifdef HAVE_SYS_WAIT_H
22 # include <sys/wait.h>
26 ** Just use sigjmp/longjmp on older machines that
27 ** don't have sigsetjmp/siglongjmp.
29 #ifndef HAVE_SIGSETJMP
30 # define sigjmp_buf jmp_buf
31 # define sigsetjmp(env,mask) setjmp(env)
32 # define siglongjmp(env,val) longjmp(env,val)
49 void content_error(char *, CT, char *, ...);
50 void flush_errors(void);
53 int list_switch(CT, int, int, int, int);
54 int list_content(CT, int, int, int, int);
59 void show_all_messages(CT *);
60 int show_content_aux(CT, int, int, char *, char *);
65 static void show_single_message(CT, char *);
66 static void DisplayMsgHeader(CT, char *);
67 static int show_switch(CT, int, int);
68 static int show_content(CT, int, int);
69 static int show_content_aux2(CT, int, int, char *, char *, int, int, int, int);
70 static int show_text(CT, int, int);
71 static int show_multi(CT, int, int);
72 static int show_multi_internal(CT, int, int);
73 static int show_multi_aux(CT, int, int, char *);
74 static int show_message_rfc822(CT, int, int);
75 static int show_partial(CT, int, int);
76 static int show_external(CT, int, int);
80 ** Top level entry point to show/display a group of messages
84 show_all_messages(CT *cts)
89 ** If form is not specified, then get default form
90 ** for showing headers of MIME messages.
93 formsw = getcpy(etcpath("mhl.headers"));
96 ** If form is "mhl.null", suppress display of header.
98 if (strcmp(formsw, "mhl.null")==0)
101 for (ctp = cts; *ctp; ctp++) {
104 /* if top-level type is ok, then display message */
106 show_single_message(ct, formsw);
112 ** Entry point to show/display a single message
116 show_single_message(CT ct, char *form)
120 #ifdef HAVE_UNION_WAIT
127 ** Allow user executable bit so that temporary directories created by
128 ** the viewer (e.g., lynx) are going to be accessible
130 umask(ct->c_umask & ~(0100));
133 ** If you have a format file, then display
134 ** the message headers.
137 DisplayMsgHeader(ct, form);
141 /* Show the body of the message */
142 show_switch(ct, 1, 0);
148 if (ct->c_ceclosefnx)
149 (*ct->c_ceclosefnx) (ct);
151 /* block a few signals */
153 sigaddset(&set, SIGHUP);
154 sigaddset(&set, SIGINT);
155 sigaddset(&set, SIGQUIT);
156 sigaddset(&set, SIGTERM);
157 SIGPROCMASK(SIG_BLOCK, &set, &oset);
159 while (wait(&status) != NOTOK) {
160 #ifdef HAVE_UNION_WAIT
161 pidcheck(status.w_status);
168 /* reset the signal mask */
169 SIGPROCMASK(SIG_SETMASK, &oset, &set);
177 ** Use mhl to show the header fields
180 DisplayMsgHeader(CT ct, char *form)
188 vec[vecp++] = "-form";
190 vec[vecp++] = "-nobody";
191 vec[vecp++] = ct->c_file;
196 switch (child_id = fork()) {
198 adios("fork", "unable to");
203 fprintf(stderr, "unable to exec ");
216 ** Switching routine. Call the correct routine
217 ** based on content type.
221 show_switch(CT ct, int serial, int alternate)
223 switch (ct->c_type) {
225 return show_multi(ct, serial, alternate);
229 switch (ct->c_subtype) {
230 case MESSAGE_PARTIAL:
231 return show_partial(ct, serial, alternate);
234 case MESSAGE_EXTERNAL:
235 return show_external(ct, serial, alternate);
240 return show_message_rfc822(ct, serial,
247 return show_text(ct, serial, alternate);
254 return show_content(ct, serial, alternate);
258 adios(NULL, "unknown content type %d", ct->c_type);
262 return 0; /* NOT REACHED */
267 ** Generic method for displaying content
271 show_content(CT ct, int serial, int alternate)
273 char *cp, buffer[BUFSIZ];
274 CI ci = &ct->c_ctinfo;
276 /* Check for mhshow-show-type/subtype */
277 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
278 invo_name, ci->ci_type, ci->ci_subtype);
279 if ((cp = context_find(buffer)) && *cp != '\0')
280 return show_content_aux(ct, serial, alternate, cp, NULL);
282 /* Check for mhshow-show-type */
283 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
284 if ((cp = context_find(buffer)) && *cp != '\0')
285 return show_content_aux(ct, serial, alternate, cp, NULL);
287 if ((cp = ct->c_showproc))
288 return show_content_aux(ct, serial, alternate, cp, NULL);
290 /* complain if we are not a part of a multipart/alternative */
292 content_error(NULL, ct, "don't know how to display content");
299 ** Parse the display string for displaying generic content
303 show_content_aux(CT ct, int serial, int alternate, char *cp, char *cracked)
305 int fd, len, buflen, quoted;
306 int xstdin, xlist, xtty;
307 char *bp, *pp, *file, buffer[BUFSIZ];
308 CI ci = &ct->c_ctinfo;
310 if (!ct->c_ceopenfnx) {
312 content_error(NULL, ct,
313 "don't know how to decode content");
319 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
321 if (ct->c_showproc && strcmp(ct->c_showproc, "true")==0)
322 return (alternate ? DONE : OK);
329 strncpy(buffer, cp, sizeof(buffer));
333 /* get buffer ready to go */
335 buflen = sizeof(buffer) - 1;
336 bp[0] = bp[buflen] = '\0';
339 /* Now parse display string */
340 for ( ; *cp && buflen > 0; cp++) {
346 /* insert parameters from Content-Type field */
351 for (ap = ci->ci_attrs, ep = ci->ci_values;
353 snprintf(bp, buflen, "%s%s=\"%s\"",
364 /* insert content description */
368 s = trimcpy(ct->c_descr);
369 strncpy(bp, s, buflen);
375 /* exclusive execution */
380 /* %e, %f, and stdin is terminal not content */
386 /* insert filename containing content */
387 snprintf(bp, buflen, "'%s'", file);
389 ** since we've quoted the file argument,
390 ** set things up to look past it, to avoid
391 ** problems with the quoting logic below.
392 ** (I know, I should figure out what's
393 ** broken with the quoting logic, but..)
404 ** display listing prior to displaying content
410 /* insert subtype of content */
411 strncpy(bp, ci->ci_subtype, buflen);
415 /* insert character % */
428 /* Did we actually insert something? */
431 ** Insert single quote if not inside quotes
434 if (!quoted && buflen) {
436 memmove(pp + 1, pp, len);
441 /* Escape existing quotes */
442 while ((pp = strchr(pp, '\'')) && buflen > 3) {
444 memmove(pp + 3, pp, len);
452 ** If pp is still set, that means we ran
457 if (!quoted && buflen) {
474 if (buflen <= 0 || (ct->c_termproc && buflen <= strlen(ct->c_termproc))) {
476 ** content_error would provide a more useful error message
477 ** here, except that if we got overrun, it probably would
480 fprintf(stderr, "Buffer overflow constructing show command!\n");
484 /* use charset string to modify display method */
485 if (ct->c_termproc) {
488 strncpy(term, buffer, sizeof(term));
489 snprintf(buffer, sizeof(buffer), ct->c_termproc, term);
493 return show_content_aux2(ct, serial, alternate, cracked, buffer,
494 fd, xlist, xstdin, xtty);
499 ** Routine to actually display the content
503 show_content_aux2(CT ct, int serial, int alternate, char *cracked,
504 char *buffer, int fd, int xlist, int xstdin, int xtty)
507 char *vec[4], exec[BUFSIZ + sizeof "exec "];
509 if (debugsw || cracked) {
512 fprintf(stderr, "%s msg %s", cracked ? "storing" : "show",
515 fprintf(stderr, " part %s", ct->c_partno);
517 fprintf(stderr, " using command (cd %s; %s)\n",
520 fprintf(stderr, " using command %s\n", buffer);
523 if (xpid < 0 || (xtty && xpid)) {
526 pidcheck(pidwait(xpid, NOTOK));
531 if (ct->c_type == CT_MULTIPART)
532 list_content(ct, -1, 1, 0, 0);
534 list_switch(ct, -1, 1, 0, 0);
537 snprintf(exec, sizeof(exec), "exec %s", buffer);
546 switch (child_id = fork()) {
548 advise("fork", "unable to");
549 (*ct->c_ceclosefnx) (ct);
558 execvp("/bin/sh", vec);
559 fprintf(stderr, "unable to exec ");
566 ct->c_pid = child_id;
570 pidcheck(pidXwait(child_id, NULL));
574 (*ct->c_ceclosefnx) (ct);
575 return (alternate ? DONE : OK);
581 ** show content of type "text"
585 show_text(CT ct, int serial, int alternate)
587 char *cp, buffer[BUFSIZ];
588 CI ci = &ct->c_ctinfo;
590 /* Check for mhshow-show-type/subtype */
591 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
592 invo_name, ci->ci_type, ci->ci_subtype);
593 if ((cp = context_find(buffer)) && *cp != '\0')
594 return show_content_aux(ct, serial, alternate, cp, NULL);
596 /* Check for mhshow-show-type */
597 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
598 if ((cp = context_find(buffer)) && *cp != '\0')
599 return show_content_aux(ct, serial, alternate, cp, NULL);
602 ** Use default method if content is text/plain, or if
603 ** if it is not a text part of a multipart/alternative
605 if (!alternate || ct->c_subtype == TEXT_PLAIN) {
606 snprintf(buffer, sizeof(buffer), "%%p%s '%%F'", defaultpager);
607 cp = (ct->c_showproc = getcpy(buffer));
608 return show_content_aux(ct, serial, alternate, cp, NULL);
616 ** show message body of type "multipart"
620 show_multi(CT ct, int serial, int alternate)
622 char *cp, buffer[BUFSIZ];
623 CI ci = &ct->c_ctinfo;
625 /* Check for mhshow-show-type/subtype */
626 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
627 invo_name, ci->ci_type, ci->ci_subtype);
628 if ((cp = context_find(buffer)) && *cp != '\0')
629 return show_multi_aux(ct, serial, alternate, cp);
631 /* Check for mhshow-show-type */
632 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
633 if ((cp = context_find(buffer)) && *cp != '\0')
634 return show_multi_aux(ct, serial, alternate, cp);
636 if ((cp = ct->c_showproc))
637 return show_multi_aux(ct, serial, alternate, cp);
640 ** Use default method to display this multipart content
641 ** if it is not a (nested) part of a multipart/alternative,
642 ** or if it is one of the known subtypes of multipart.
644 if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
645 return show_multi_internal(ct, serial, alternate);
652 ** show message body of subtypes of multipart that
653 ** we understand directly (mixed, alternate, etc...)
657 show_multi_internal(CT ct, int serial, int alternate)
659 int alternating, nowalternate, nowserial, result;
660 struct multipart *m = (struct multipart *) ct->c_ctparams;
666 nowalternate = alternate;
668 if (ct->c_subtype == MULTI_PARALLEL) {
669 nowserial = serialsw;
670 } else if (ct->c_subtype == MULTI_ALTERNATE) {
678 ** unknown subtypes of multipart (treat as mixed per rfc2046)
683 /* block a few signals */
686 sigaddset(&set, SIGHUP);
687 sigaddset(&set, SIGINT);
688 sigaddset(&set, SIGQUIT);
689 sigaddset(&set, SIGTERM);
690 SIGPROCMASK(SIG_BLOCK, &set, &oset);
694 ** alternate -> we are a part inside an multipart/alternative
695 ** alternating -> we are a multipart/alternative
698 result = alternate ? NOTOK : OK;
700 for (part = m->mp_parts; part; part = part->mp_next) {
703 if (part_ok(p, 1) && type_ok(p, 1)) {
706 inneresult = show_switch(p, nowserial, nowalternate);
707 switch (inneresult) {
709 if (alternate && !alternating) {
722 alternate = nowalternate = 0;
732 if (alternating && !part) {
734 content_error(NULL, ct, "don't know how to display any of the contents");
739 if (serial && !nowserial) {
742 #ifdef HAVE_UNION_WAIT
749 for (part = m->mp_parts; part; part = part->mp_next) {
753 if (kill(p->c_pid, 0) == NOTOK)
760 while (kids > 0 && (pid = wait(&status)) != NOTOK) {
761 #ifdef HAVE_UNION_WAIT
762 pidcheck(status.w_status);
767 for (part = m->mp_parts; part; part = part->mp_next) {
772 if (p->c_pid == pid) {
783 /* reset the signal mask */
784 SIGPROCMASK(SIG_SETMASK, &oset, &set);
792 ** Parse display string for multipart content
793 ** and use external program to display it.
797 show_multi_aux(CT ct, int serial, int alternate, char *cp)
799 int len, buflen, quoted;
801 char *bp, *pp, *file, buffer[BUFSIZ];
802 struct multipart *m = (struct multipart *) ct->c_ctparams;
804 CI ci = &ct->c_ctinfo;
807 for (part = m->mp_parts; part; part = part->mp_next) {
810 if (!p->c_ceopenfnx) {
812 content_error(NULL, p, "don't know how to decode content");
816 if (p->c_storage == NULL) {
818 if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
821 /* I'm not sure if this is necessary? */
822 p->c_storage = getcpy(file);
824 if (p->c_showproc && strcmp(p->c_showproc, "true")==0)
825 return (alternate ? DONE : OK);
826 (*p->c_ceclosefnx) (p);
833 /* get buffer ready to go */
835 buflen = sizeof(buffer) - 1;
836 bp[0] = bp[buflen] = '\0';
839 /* Now parse display string */
840 for ( ; *cp && buflen > 0; cp++) {
845 /* insert parameters from Content-Type field */
850 for (ap = ci->ci_attrs, ep = ci->ci_values;
852 snprintf(bp, buflen, "%s%s=\"%s\"",
863 /* insert content description */
867 s = trimcpy(ct->c_descr);
868 strncpy(bp, s, buflen);
874 /* exclusive execution */
884 /* insert filename(s) containing content */
888 for (part = m->mp_parts; part;
889 part = part->mp_next) {
892 snprintf(bp, buflen, "%s'%s'",
900 ** set our starting pointer back to bp,
901 ** to avoid requoting the filenames we
911 ** display listing prior to displaying content
917 /* insert subtype of content */
918 strncpy(bp, ci->ci_subtype, buflen);
922 /* insert character % */
935 /* Did we actually insert something? */
938 ** Insert single quote if not inside quotes
941 if (!quoted && buflen) {
943 memmove(pp + 1, pp, len);
948 /* Escape existing quotes */
949 while ((pp = strchr(pp, '\'')) && buflen > 3) {
951 memmove(pp + 3, pp, len);
959 ** If pp is still set, that means we ran
964 if (!quoted && buflen) {
982 (ct->c_termproc && buflen <= strlen(ct->c_termproc))) {
984 ** content_error would provide a more useful error message
985 ** here, except that if we got overrun, it probably would
988 fprintf(stderr, "Buffer overflow constructing show command!\n");
992 /* use charset string to modify display method */
993 if (ct->c_termproc) {
996 strncpy(term, buffer, sizeof(term));
997 snprintf(buffer, sizeof(buffer), ct->c_termproc, term);
1000 return show_content_aux2(ct, serial, alternate, NULL, buffer,
1001 NOTOK, xlist, 0, xtty);
1006 ** show content of type "message/rfc822"
1010 show_message_rfc822(CT ct, int serial, int alternate)
1012 char *cp, buffer[BUFSIZ];
1013 CI ci = &ct->c_ctinfo;
1015 /* Check for mhshow-show-type/subtype */
1016 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
1017 invo_name, ci->ci_type, ci->ci_subtype);
1018 if ((cp = context_find(buffer)) && *cp != '\0')
1019 return show_content_aux(ct, serial, alternate, cp, NULL);
1021 /* Check for mhshow-show-type */
1022 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
1023 if ((cp = context_find(buffer)) && *cp != '\0')
1024 return show_content_aux(ct, serial, alternate, cp, NULL);
1026 if ((cp = ct->c_showproc))
1027 return show_content_aux(ct, serial, alternate, cp, NULL);
1029 /* default method for message/rfc822 */
1030 if (ct->c_subtype == MESSAGE_RFC822) {
1031 cp = (ct->c_showproc = getcpy("%pshow -file '%F'"));
1032 return show_content_aux(ct, serial, alternate, cp, NULL);
1035 /* complain if we are not a part of a multipart/alternative */
1037 content_error(NULL, ct, "don't know how to display content");
1044 ** Show content of type "message/partial".
1048 show_partial(CT ct, int serial, int alternate)
1050 content_error(NULL, ct,
1051 "in order to display this message, you must reassemble it");
1057 ** Show content of type "message/external".
1059 ** THE ERROR CHECKING IN THIS ONE IS NOT DONE YET.
1063 show_external(CT ct, int serial, int alternate)
1065 struct exbody *e = (struct exbody *) ct->c_ctparams;
1066 CT p = e->eb_content;
1071 return show_switch(p, serial, alternate);
1074 content_error(NULL, p, "don't know how to display content");