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>
36 void content_error(char *, CT, char *, ...);
37 void flush_errors(void);
40 int list_switch(CT, int, int, int, int);
41 int list_content(CT, int, int, int, int);
46 void show_all_messages(CT *);
47 int show_content_aux(CT, int, int, char *, char *);
52 static void show_single_message(CT, char *);
53 static void DisplayMsgHeader(CT, char *);
54 static int show_switch(CT, int, int);
55 static int show_content(CT, int, int);
56 static int show_content_aux2(CT, int, int, char *, char *, int, int, int, int);
57 static int show_text(CT, int, int);
58 static int show_multi(CT, int, int);
59 static int show_multi_internal(CT, int, int);
60 static int show_multi_aux(CT, int, int, char *);
61 static int show_message_rfc822(CT, int, int);
62 static int show_partial(CT, int, int);
63 static int show_external(CT, int, int);
67 ** Top level entry point to show/display a group of messages
71 show_all_messages(CT *cts)
76 ** If form is not specified, then get default form
77 ** for showing headers of MIME messages.
80 formsw = getcpy(etcpath("mhl.headers"));
83 ** If form is "mhl.null", suppress display of header.
85 if (strcmp(formsw, "mhl.null")==0)
88 for (ctp = cts; *ctp; ctp++) {
91 /* if top-level type is ok, then display message */
93 show_single_message(ct, formsw);
99 ** Entry point to show/display a single message
103 show_single_message(CT ct, char *form)
109 ** Allow user executable bit so that temporary directories created by
110 ** the viewer (e.g., lynx) are going to be accessible
112 umask(ct->c_umask & ~(0100));
115 ** If you have a format file, then display
116 ** the message headers.
119 DisplayMsgHeader(ct, form);
123 /* Show the body of the message */
124 show_switch(ct, 1, 0);
130 if (ct->c_ceclosefnx)
131 (*ct->c_ceclosefnx) (ct);
133 /* block a few signals */
135 sigaddset(&set, SIGHUP);
136 sigaddset(&set, SIGINT);
137 sigaddset(&set, SIGQUIT);
138 sigaddset(&set, SIGTERM);
139 sigprocmask(SIG_BLOCK, &set, &oset);
141 while (wait(&status) != NOTOK) {
146 /* reset the signal mask */
147 sigprocmask(SIG_SETMASK, &oset, &set);
155 ** Use mhl to show the header fields
158 DisplayMsgHeader(CT ct, char *form)
166 vec[vecp++] = "-form";
168 vec[vecp++] = "-nobody";
169 vec[vecp++] = ct->c_file;
174 switch (child_id = fork()) {
176 adios("fork", "unable to");
181 fprintf(stderr, "unable to exec ");
194 ** Switching routine. Call the correct routine
195 ** based on content type.
199 show_switch(CT ct, int serial, int alternate)
201 switch (ct->c_type) {
203 return show_multi(ct, serial, alternate);
207 switch (ct->c_subtype) {
208 case MESSAGE_PARTIAL:
209 return show_partial(ct, serial, alternate);
212 case MESSAGE_EXTERNAL:
213 return show_external(ct, serial, alternate);
218 return show_message_rfc822(ct, serial,
225 return show_text(ct, serial, alternate);
232 return show_content(ct, serial, alternate);
236 adios(NULL, "unknown content type %d", ct->c_type);
240 return 0; /* NOT REACHED */
245 ** Generic method for displaying content
249 show_content(CT ct, int serial, int alternate)
251 char *cp, buffer[BUFSIZ];
252 CI ci = &ct->c_ctinfo;
254 /* Check for mhshow-show-type/subtype */
255 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
256 invo_name, ci->ci_type, ci->ci_subtype);
257 if ((cp = context_find(buffer)) && *cp != '\0')
258 return show_content_aux(ct, serial, alternate, cp, NULL);
260 /* Check for mhshow-show-type */
261 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
262 if ((cp = context_find(buffer)) && *cp != '\0')
263 return show_content_aux(ct, serial, alternate, cp, NULL);
265 if ((cp = ct->c_showproc))
266 return show_content_aux(ct, serial, alternate, cp, NULL);
268 /* complain if we are not a part of a multipart/alternative */
270 content_error(NULL, ct, "don't know how to display content");
277 ** Parse the display string for displaying generic content
281 show_content_aux(CT ct, int serial, int alternate, char *cp, char *cracked)
283 int fd, len, buflen, quoted;
284 int xstdin, xlist, xtty;
285 char *bp, *pp, *file, buffer[BUFSIZ];
286 CI ci = &ct->c_ctinfo;
288 if (!ct->c_ceopenfnx) {
290 content_error(NULL, ct,
291 "don't know how to decode content");
297 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
299 if (ct->c_showproc && strcmp(ct->c_showproc, "true")==0)
300 return (alternate ? DONE : OK);
307 strncpy(buffer, cp, sizeof(buffer));
311 /* get buffer ready to go */
313 buflen = sizeof(buffer) - 1;
314 bp[0] = bp[buflen] = '\0';
317 /* Now parse display string */
318 for ( ; *cp && buflen > 0; cp++) {
324 /* insert parameters from Content-Type field */
329 for (ap = ci->ci_attrs, ep = ci->ci_values;
331 snprintf(bp, buflen, "%s%s=\"%s\"",
342 /* insert content description */
346 s = trimcpy(ct->c_descr);
347 strncpy(bp, s, buflen);
353 /* exclusive execution */
358 /* %e, %f, and stdin is terminal not content */
364 /* insert filename containing content */
365 snprintf(bp, buflen, "'%s'", file);
367 ** since we've quoted the file argument,
368 ** set things up to look past it, to avoid
369 ** problems with the quoting logic below.
370 ** (I know, I should figure out what's
371 ** broken with the quoting logic, but..)
382 ** display listing prior to displaying content
388 /* insert subtype of content */
389 strncpy(bp, ci->ci_subtype, buflen);
393 /* insert character % */
406 /* Did we actually insert something? */
409 ** Insert single quote if not inside quotes
412 if (!quoted && buflen) {
414 memmove(pp + 1, pp, len);
419 /* Escape existing quotes */
420 while ((pp = strchr(pp, '\'')) && buflen > 3) {
422 memmove(pp + 3, pp, len);
430 ** If pp is still set, that means we ran
435 if (!quoted && buflen) {
452 if (buflen <= 0 || (ct->c_termproc &&
453 (size_t)buflen <= strlen(ct->c_termproc))) {
455 ** content_error would provide a more useful error message
456 ** here, except that if we got overrun, it probably would
459 fprintf(stderr, "Buffer overflow constructing show command!\n");
463 /* use charset string to modify display method */
464 if (ct->c_termproc) {
467 strncpy(term, buffer, sizeof(term));
468 snprintf(buffer, sizeof(buffer), ct->c_termproc, term);
472 return show_content_aux2(ct, serial, alternate, cracked, buffer,
473 fd, xlist, xstdin, xtty);
478 ** Routine to actually display the content
482 show_content_aux2(CT ct, int serial, int alternate, char *cracked,
483 char *buffer, int fd, int xlist, int xstdin, int xtty)
486 char *vec[4], exec[BUFSIZ + sizeof "exec "];
488 if (debugsw || cracked) {
491 fprintf(stderr, "%s msg %s", cracked ? "storing" : "show",
494 fprintf(stderr, " part %s", ct->c_partno);
496 fprintf(stderr, " using command (cd %s; %s)\n",
499 fprintf(stderr, " using command %s\n", buffer);
502 if (xpid < 0 || (xtty && xpid)) {
505 pidcheck(pidwait(xpid, NOTOK));
510 if (ct->c_type == CT_MULTIPART)
511 list_content(ct, -1, 1, 0, 0);
513 list_switch(ct, -1, 1, 0, 0);
516 snprintf(exec, sizeof(exec), "exec %s", buffer);
525 switch (child_id = fork()) {
527 advise("fork", "unable to");
528 (*ct->c_ceclosefnx) (ct);
537 execvp("/bin/sh", vec);
538 fprintf(stderr, "unable to exec ");
545 ct->c_pid = child_id;
549 pidcheck(pidXwait(child_id, NULL));
553 (*ct->c_ceclosefnx) (ct);
554 return (alternate ? DONE : OK);
560 ** show content of type "text"
564 show_text(CT ct, int serial, int alternate)
566 char *cp, buffer[BUFSIZ];
567 CI ci = &ct->c_ctinfo;
569 /* Check for mhshow-show-type/subtype */
570 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
571 invo_name, ci->ci_type, ci->ci_subtype);
572 if ((cp = context_find(buffer)) && *cp != '\0')
573 return show_content_aux(ct, serial, alternate, cp, NULL);
575 /* Check for mhshow-show-type */
576 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
577 if ((cp = context_find(buffer)) && *cp != '\0')
578 return show_content_aux(ct, serial, alternate, cp, NULL);
581 ** Use default method if content is text/plain, or if
582 ** if it is not a text part of a multipart/alternative
584 if (!alternate || ct->c_subtype == TEXT_PLAIN) {
585 snprintf(buffer, sizeof(buffer), "%%p%s '%%F'", defaultpager);
586 cp = (ct->c_showproc = getcpy(buffer));
587 return show_content_aux(ct, serial, alternate, cp, NULL);
595 ** show message body of type "multipart"
599 show_multi(CT ct, int serial, int alternate)
601 char *cp, buffer[BUFSIZ];
602 CI ci = &ct->c_ctinfo;
604 /* Check for mhshow-show-type/subtype */
605 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
606 invo_name, ci->ci_type, ci->ci_subtype);
607 if ((cp = context_find(buffer)) && *cp != '\0')
608 return show_multi_aux(ct, serial, alternate, cp);
610 /* Check for mhshow-show-type */
611 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
612 if ((cp = context_find(buffer)) && *cp != '\0')
613 return show_multi_aux(ct, serial, alternate, cp);
615 if ((cp = ct->c_showproc))
616 return show_multi_aux(ct, serial, alternate, cp);
619 ** Use default method to display this multipart content
620 ** if it is not a (nested) part of a multipart/alternative,
621 ** or if it is one of the known subtypes of multipart.
623 if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
624 return show_multi_internal(ct, serial, alternate);
631 ** show message body of subtypes of multipart that
632 ** we understand directly (mixed, alternate, etc...)
636 show_multi_internal(CT ct, int serial, int alternate)
638 int alternating, nowalternate, nowserial, result;
639 struct multipart *m = (struct multipart *) ct->c_ctparams;
645 nowalternate = alternate;
647 if (ct->c_subtype == MULTI_PARALLEL) {
648 nowserial = serialsw;
649 } else if (ct->c_subtype == MULTI_ALTERNATE) {
657 ** unknown subtypes of multipart (treat as mixed per rfc2046)
662 /* block a few signals */
665 sigaddset(&set, SIGHUP);
666 sigaddset(&set, SIGINT);
667 sigaddset(&set, SIGQUIT);
668 sigaddset(&set, SIGTERM);
669 sigprocmask(SIG_BLOCK, &set, &oset);
673 ** alternate -> we are a part inside an multipart/alternative
674 ** alternating -> we are a multipart/alternative
677 result = alternate ? NOTOK : OK;
679 for (part = m->mp_parts; part; part = part->mp_next) {
682 if (part_ok(p, 1) && type_ok(p, 1)) {
685 inneresult = show_switch(p, nowserial, nowalternate);
686 switch (inneresult) {
688 if (alternate && !alternating) {
701 alternate = nowalternate = 0;
711 if (alternating && !part) {
713 content_error(NULL, ct, "don't know how to display any of the contents");
718 if (serial && !nowserial) {
724 for (part = m->mp_parts; part; part = part->mp_next) {
728 if (kill(p->c_pid, 0) == NOTOK)
735 while (kids > 0 && (pid = wait(&status)) != NOTOK) {
738 for (part = m->mp_parts; part; part = part->mp_next) {
743 if (p->c_pid == pid) {
754 /* reset the signal mask */
755 sigprocmask(SIG_SETMASK, &oset, &set);
763 ** Parse display string for multipart content
764 ** and use external program to display it.
768 show_multi_aux(CT ct, int serial, int alternate, char *cp)
770 int len, buflen, quoted;
772 char *bp, *pp, *file, buffer[BUFSIZ];
773 struct multipart *m = (struct multipart *) ct->c_ctparams;
775 CI ci = &ct->c_ctinfo;
778 for (part = m->mp_parts; part; part = part->mp_next) {
781 if (!p->c_ceopenfnx) {
783 content_error(NULL, p, "don't know how to decode content");
787 if (p->c_storage == NULL) {
789 if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
792 /* I'm not sure if this is necessary? */
793 p->c_storage = getcpy(file);
795 if (p->c_showproc && strcmp(p->c_showproc, "true")==0)
796 return (alternate ? DONE : OK);
797 (*p->c_ceclosefnx) (p);
804 /* get buffer ready to go */
806 buflen = sizeof(buffer) - 1;
807 bp[0] = bp[buflen] = '\0';
810 /* Now parse display string */
811 for ( ; *cp && buflen > 0; cp++) {
816 /* insert parameters from Content-Type field */
821 for (ap = ci->ci_attrs, ep = ci->ci_values;
823 snprintf(bp, buflen, "%s%s=\"%s\"",
834 /* insert content description */
838 s = trimcpy(ct->c_descr);
839 strncpy(bp, s, buflen);
845 /* exclusive execution */
855 /* insert filename(s) containing content */
859 for (part = m->mp_parts; part;
860 part = part->mp_next) {
863 snprintf(bp, buflen, "%s'%s'",
871 ** set our starting pointer back to bp,
872 ** to avoid requoting the filenames we
882 ** display listing prior to displaying content
888 /* insert subtype of content */
889 strncpy(bp, ci->ci_subtype, buflen);
893 /* insert character % */
906 /* Did we actually insert something? */
909 ** Insert single quote if not inside quotes
912 if (!quoted && buflen) {
914 memmove(pp + 1, pp, len);
919 /* Escape existing quotes */
920 while ((pp = strchr(pp, '\'')) && buflen > 3) {
922 memmove(pp + 3, pp, len);
930 ** If pp is still set, that means we ran
935 if (!quoted && buflen) {
952 if (buflen <= 0 || (ct->c_termproc &&
953 (size_t)buflen <= strlen(ct->c_termproc))) {
955 ** content_error would provide a more useful error message
956 ** here, except that if we got overrun, it probably would
959 fprintf(stderr, "Buffer overflow constructing show command!\n");
963 /* use charset string to modify display method */
964 if (ct->c_termproc) {
967 strncpy(term, buffer, sizeof(term));
968 snprintf(buffer, sizeof(buffer), ct->c_termproc, term);
971 return show_content_aux2(ct, serial, alternate, NULL, buffer,
972 NOTOK, xlist, 0, xtty);
977 ** show content of type "message/rfc822"
981 show_message_rfc822(CT ct, int serial, int alternate)
983 char *cp, buffer[BUFSIZ];
984 CI ci = &ct->c_ctinfo;
986 /* Check for mhshow-show-type/subtype */
987 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
988 invo_name, ci->ci_type, ci->ci_subtype);
989 if ((cp = context_find(buffer)) && *cp != '\0')
990 return show_content_aux(ct, serial, alternate, cp, NULL);
992 /* Check for mhshow-show-type */
993 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
994 if ((cp = context_find(buffer)) && *cp != '\0')
995 return show_content_aux(ct, serial, alternate, cp, NULL);
997 if ((cp = ct->c_showproc))
998 return show_content_aux(ct, serial, alternate, cp, NULL);
1000 /* default method for message/rfc822 */
1001 if (ct->c_subtype == MESSAGE_RFC822) {
1002 cp = (ct->c_showproc = getcpy("%pshow -file '%F'"));
1003 return show_content_aux(ct, serial, alternate, cp, NULL);
1006 /* complain if we are not a part of a multipart/alternative */
1008 content_error(NULL, ct, "don't know how to display content");
1015 ** Show content of type "message/partial".
1019 show_partial(CT ct, int serial, int alternate)
1021 content_error(NULL, ct,
1022 "in order to display this message, you must reassemble it");
1028 ** Show content of type "message/external".
1030 ** THE ERROR CHECKING IN THIS ONE IS NOT DONE YET.
1034 show_external(CT ct, int serial, int alternate)
1036 struct exbody *e = (struct exbody *) ct->c_ctparams;
1037 CT p = e->eb_content;
1042 return show_switch(p, serial, alternate);
1045 content_error(NULL, p, "don't know how to display content");