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 && buflen <= strlen(ct->c_termproc))) {
454 ** content_error would provide a more useful error message
455 ** here, except that if we got overrun, it probably would
458 fprintf(stderr, "Buffer overflow constructing show command!\n");
462 /* use charset string to modify display method */
463 if (ct->c_termproc) {
466 strncpy(term, buffer, sizeof(term));
467 snprintf(buffer, sizeof(buffer), ct->c_termproc, term);
471 return show_content_aux2(ct, serial, alternate, cracked, buffer,
472 fd, xlist, xstdin, xtty);
477 ** Routine to actually display the content
481 show_content_aux2(CT ct, int serial, int alternate, char *cracked,
482 char *buffer, int fd, int xlist, int xstdin, int xtty)
485 char *vec[4], exec[BUFSIZ + sizeof "exec "];
487 if (debugsw || cracked) {
490 fprintf(stderr, "%s msg %s", cracked ? "storing" : "show",
493 fprintf(stderr, " part %s", ct->c_partno);
495 fprintf(stderr, " using command (cd %s; %s)\n",
498 fprintf(stderr, " using command %s\n", buffer);
501 if (xpid < 0 || (xtty && xpid)) {
504 pidcheck(pidwait(xpid, NOTOK));
509 if (ct->c_type == CT_MULTIPART)
510 list_content(ct, -1, 1, 0, 0);
512 list_switch(ct, -1, 1, 0, 0);
515 snprintf(exec, sizeof(exec), "exec %s", buffer);
524 switch (child_id = fork()) {
526 advise("fork", "unable to");
527 (*ct->c_ceclosefnx) (ct);
536 execvp("/bin/sh", vec);
537 fprintf(stderr, "unable to exec ");
544 ct->c_pid = child_id;
548 pidcheck(pidXwait(child_id, NULL));
552 (*ct->c_ceclosefnx) (ct);
553 return (alternate ? DONE : OK);
559 ** show content of type "text"
563 show_text(CT ct, int serial, int alternate)
565 char *cp, buffer[BUFSIZ];
566 CI ci = &ct->c_ctinfo;
568 /* Check for mhshow-show-type/subtype */
569 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
570 invo_name, ci->ci_type, ci->ci_subtype);
571 if ((cp = context_find(buffer)) && *cp != '\0')
572 return show_content_aux(ct, serial, alternate, cp, NULL);
574 /* Check for mhshow-show-type */
575 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
576 if ((cp = context_find(buffer)) && *cp != '\0')
577 return show_content_aux(ct, serial, alternate, cp, NULL);
580 ** Use default method if content is text/plain, or if
581 ** if it is not a text part of a multipart/alternative
583 if (!alternate || ct->c_subtype == TEXT_PLAIN) {
584 snprintf(buffer, sizeof(buffer), "%%p%s '%%F'", defaultpager);
585 cp = (ct->c_showproc = getcpy(buffer));
586 return show_content_aux(ct, serial, alternate, cp, NULL);
594 ** show message body of type "multipart"
598 show_multi(CT ct, int serial, int alternate)
600 char *cp, buffer[BUFSIZ];
601 CI ci = &ct->c_ctinfo;
603 /* Check for mhshow-show-type/subtype */
604 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
605 invo_name, ci->ci_type, ci->ci_subtype);
606 if ((cp = context_find(buffer)) && *cp != '\0')
607 return show_multi_aux(ct, serial, alternate, cp);
609 /* Check for mhshow-show-type */
610 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
611 if ((cp = context_find(buffer)) && *cp != '\0')
612 return show_multi_aux(ct, serial, alternate, cp);
614 if ((cp = ct->c_showproc))
615 return show_multi_aux(ct, serial, alternate, cp);
618 ** Use default method to display this multipart content
619 ** if it is not a (nested) part of a multipart/alternative,
620 ** or if it is one of the known subtypes of multipart.
622 if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
623 return show_multi_internal(ct, serial, alternate);
630 ** show message body of subtypes of multipart that
631 ** we understand directly (mixed, alternate, etc...)
635 show_multi_internal(CT ct, int serial, int alternate)
637 int alternating, nowalternate, nowserial, result;
638 struct multipart *m = (struct multipart *) ct->c_ctparams;
644 nowalternate = alternate;
646 if (ct->c_subtype == MULTI_PARALLEL) {
647 nowserial = serialsw;
648 } else if (ct->c_subtype == MULTI_ALTERNATE) {
656 ** unknown subtypes of multipart (treat as mixed per rfc2046)
661 /* block a few signals */
664 sigaddset(&set, SIGHUP);
665 sigaddset(&set, SIGINT);
666 sigaddset(&set, SIGQUIT);
667 sigaddset(&set, SIGTERM);
668 sigprocmask(SIG_BLOCK, &set, &oset);
672 ** alternate -> we are a part inside an multipart/alternative
673 ** alternating -> we are a multipart/alternative
676 result = alternate ? NOTOK : OK;
678 for (part = m->mp_parts; part; part = part->mp_next) {
681 if (part_ok(p, 1) && type_ok(p, 1)) {
684 inneresult = show_switch(p, nowserial, nowalternate);
685 switch (inneresult) {
687 if (alternate && !alternating) {
700 alternate = nowalternate = 0;
710 if (alternating && !part) {
712 content_error(NULL, ct, "don't know how to display any of the contents");
717 if (serial && !nowserial) {
723 for (part = m->mp_parts; part; part = part->mp_next) {
727 if (kill(p->c_pid, 0) == NOTOK)
734 while (kids > 0 && (pid = wait(&status)) != NOTOK) {
737 for (part = m->mp_parts; part; part = part->mp_next) {
742 if (p->c_pid == pid) {
753 /* reset the signal mask */
754 sigprocmask(SIG_SETMASK, &oset, &set);
762 ** Parse display string for multipart content
763 ** and use external program to display it.
767 show_multi_aux(CT ct, int serial, int alternate, char *cp)
769 int len, buflen, quoted;
771 char *bp, *pp, *file, buffer[BUFSIZ];
772 struct multipart *m = (struct multipart *) ct->c_ctparams;
774 CI ci = &ct->c_ctinfo;
777 for (part = m->mp_parts; part; part = part->mp_next) {
780 if (!p->c_ceopenfnx) {
782 content_error(NULL, p, "don't know how to decode content");
786 if (p->c_storage == NULL) {
788 if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
791 /* I'm not sure if this is necessary? */
792 p->c_storage = getcpy(file);
794 if (p->c_showproc && strcmp(p->c_showproc, "true")==0)
795 return (alternate ? DONE : OK);
796 (*p->c_ceclosefnx) (p);
803 /* get buffer ready to go */
805 buflen = sizeof(buffer) - 1;
806 bp[0] = bp[buflen] = '\0';
809 /* Now parse display string */
810 for ( ; *cp && buflen > 0; cp++) {
815 /* insert parameters from Content-Type field */
820 for (ap = ci->ci_attrs, ep = ci->ci_values;
822 snprintf(bp, buflen, "%s%s=\"%s\"",
833 /* insert content description */
837 s = trimcpy(ct->c_descr);
838 strncpy(bp, s, buflen);
844 /* exclusive execution */
854 /* insert filename(s) containing content */
858 for (part = m->mp_parts; part;
859 part = part->mp_next) {
862 snprintf(bp, buflen, "%s'%s'",
870 ** set our starting pointer back to bp,
871 ** to avoid requoting the filenames we
881 ** display listing prior to displaying content
887 /* insert subtype of content */
888 strncpy(bp, ci->ci_subtype, buflen);
892 /* insert character % */
905 /* Did we actually insert something? */
908 ** Insert single quote if not inside quotes
911 if (!quoted && buflen) {
913 memmove(pp + 1, pp, len);
918 /* Escape existing quotes */
919 while ((pp = strchr(pp, '\'')) && buflen > 3) {
921 memmove(pp + 3, pp, len);
929 ** If pp is still set, that means we ran
934 if (!quoted && buflen) {
952 (ct->c_termproc && buflen <= strlen(ct->c_termproc))) {
954 ** content_error would provide a more useful error message
955 ** here, except that if we got overrun, it probably would
958 fprintf(stderr, "Buffer overflow constructing show command!\n");
962 /* use charset string to modify display method */
963 if (ct->c_termproc) {
966 strncpy(term, buffer, sizeof(term));
967 snprintf(buffer, sizeof(buffer), ct->c_termproc, term);
970 return show_content_aux2(ct, serial, alternate, NULL, buffer,
971 NOTOK, xlist, 0, xtty);
976 ** show content of type "message/rfc822"
980 show_message_rfc822(CT ct, int serial, int alternate)
982 char *cp, buffer[BUFSIZ];
983 CI ci = &ct->c_ctinfo;
985 /* Check for mhshow-show-type/subtype */
986 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
987 invo_name, ci->ci_type, ci->ci_subtype);
988 if ((cp = context_find(buffer)) && *cp != '\0')
989 return show_content_aux(ct, serial, alternate, cp, NULL);
991 /* Check for mhshow-show-type */
992 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
993 if ((cp = context_find(buffer)) && *cp != '\0')
994 return show_content_aux(ct, serial, alternate, cp, NULL);
996 if ((cp = ct->c_showproc))
997 return show_content_aux(ct, serial, alternate, cp, NULL);
999 /* default method for message/rfc822 */
1000 if (ct->c_subtype == MESSAGE_RFC822) {
1001 cp = (ct->c_showproc = getcpy("%pshow -file '%F'"));
1002 return show_content_aux(ct, serial, alternate, cp, NULL);
1005 /* complain if we are not a part of a multipart/alternative */
1007 content_error(NULL, ct, "don't know how to display content");
1014 ** Show content of type "message/partial".
1018 show_partial(CT ct, int serial, int alternate)
1020 content_error(NULL, ct,
1021 "in order to display this message, you must reassemble it");
1027 ** Show content of type "message/external".
1029 ** THE ERROR CHECKING IN THIS ONE IS NOT DONE YET.
1033 show_external(CT ct, int serial, int alternate)
1035 struct exbody *e = (struct exbody *) ct->c_ctparams;
1036 CT p = e->eb_content;
1041 return show_switch(p, serial, alternate);
1044 content_error(NULL, p, "don't know how to display content");