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>
23 ** Just use sigjmp/longjmp on older machines that
24 ** don't have sigsetjmp/siglongjmp.
26 #ifndef HAVE_SIGSETJMP
27 # define sigjmp_buf jmp_buf
28 # define sigsetjmp(env,mask) setjmp(env)
29 # define siglongjmp(env,val) longjmp(env,val)
46 void content_error(char *, CT, char *, ...);
47 void flush_errors(void);
50 int list_switch(CT, int, int, int, int);
51 int list_content(CT, int, int, int, int);
56 void show_all_messages(CT *);
57 int show_content_aux(CT, int, int, char *, char *);
62 static void show_single_message(CT, char *);
63 static void DisplayMsgHeader(CT, char *);
64 static int show_switch(CT, int, int);
65 static int show_content(CT, int, int);
66 static int show_content_aux2(CT, int, int, char *, char *, int, int, int, int);
67 static int show_text(CT, int, int);
68 static int show_multi(CT, int, int);
69 static int show_multi_internal(CT, int, int);
70 static int show_multi_aux(CT, int, int, char *);
71 static int show_message_rfc822(CT, int, int);
72 static int show_partial(CT, int, int);
73 static int show_external(CT, int, int);
77 ** Top level entry point to show/display a group of messages
81 show_all_messages(CT *cts)
86 ** If form is not specified, then get default form
87 ** for showing headers of MIME messages.
90 formsw = getcpy(etcpath("mhl.headers"));
93 ** If form is "mhl.null", suppress display of header.
95 if (strcmp(formsw, "mhl.null")==0)
98 for (ctp = cts; *ctp; ctp++) {
101 /* if top-level type is ok, then display message */
103 show_single_message(ct, formsw);
109 ** Entry point to show/display a single message
113 show_single_message(CT ct, char *form)
119 ** Allow user executable bit so that temporary directories created by
120 ** the viewer (e.g., lynx) are going to be accessible
122 umask(ct->c_umask & ~(0100));
125 ** If you have a format file, then display
126 ** the message headers.
129 DisplayMsgHeader(ct, form);
133 /* Show the body of the message */
134 show_switch(ct, 1, 0);
140 if (ct->c_ceclosefnx)
141 (*ct->c_ceclosefnx) (ct);
143 /* block a few signals */
145 sigaddset(&set, SIGHUP);
146 sigaddset(&set, SIGINT);
147 sigaddset(&set, SIGQUIT);
148 sigaddset(&set, SIGTERM);
149 SIGPROCMASK(SIG_BLOCK, &set, &oset);
151 while (wait(&status) != NOTOK) {
156 /* reset the signal mask */
157 SIGPROCMASK(SIG_SETMASK, &oset, &set);
165 ** Use mhl to show the header fields
168 DisplayMsgHeader(CT ct, char *form)
176 vec[vecp++] = "-form";
178 vec[vecp++] = "-nobody";
179 vec[vecp++] = ct->c_file;
184 switch (child_id = fork()) {
186 adios("fork", "unable to");
191 fprintf(stderr, "unable to exec ");
204 ** Switching routine. Call the correct routine
205 ** based on content type.
209 show_switch(CT ct, int serial, int alternate)
211 switch (ct->c_type) {
213 return show_multi(ct, serial, alternate);
217 switch (ct->c_subtype) {
218 case MESSAGE_PARTIAL:
219 return show_partial(ct, serial, alternate);
222 case MESSAGE_EXTERNAL:
223 return show_external(ct, serial, alternate);
228 return show_message_rfc822(ct, serial,
235 return show_text(ct, serial, alternate);
242 return show_content(ct, serial, alternate);
246 adios(NULL, "unknown content type %d", ct->c_type);
250 return 0; /* NOT REACHED */
255 ** Generic method for displaying content
259 show_content(CT ct, int serial, int alternate)
261 char *cp, buffer[BUFSIZ];
262 CI ci = &ct->c_ctinfo;
264 /* Check for mhshow-show-type/subtype */
265 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
266 invo_name, ci->ci_type, ci->ci_subtype);
267 if ((cp = context_find(buffer)) && *cp != '\0')
268 return show_content_aux(ct, serial, alternate, cp, NULL);
270 /* Check for mhshow-show-type */
271 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
272 if ((cp = context_find(buffer)) && *cp != '\0')
273 return show_content_aux(ct, serial, alternate, cp, NULL);
275 if ((cp = ct->c_showproc))
276 return show_content_aux(ct, serial, alternate, cp, NULL);
278 /* complain if we are not a part of a multipart/alternative */
280 content_error(NULL, ct, "don't know how to display content");
287 ** Parse the display string for displaying generic content
291 show_content_aux(CT ct, int serial, int alternate, char *cp, char *cracked)
293 int fd, len, buflen, quoted;
294 int xstdin, xlist, xtty;
295 char *bp, *pp, *file, buffer[BUFSIZ];
296 CI ci = &ct->c_ctinfo;
298 if (!ct->c_ceopenfnx) {
300 content_error(NULL, ct,
301 "don't know how to decode content");
307 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
309 if (ct->c_showproc && strcmp(ct->c_showproc, "true")==0)
310 return (alternate ? DONE : OK);
317 strncpy(buffer, cp, sizeof(buffer));
321 /* get buffer ready to go */
323 buflen = sizeof(buffer) - 1;
324 bp[0] = bp[buflen] = '\0';
327 /* Now parse display string */
328 for ( ; *cp && buflen > 0; cp++) {
334 /* insert parameters from Content-Type field */
339 for (ap = ci->ci_attrs, ep = ci->ci_values;
341 snprintf(bp, buflen, "%s%s=\"%s\"",
352 /* insert content description */
356 s = trimcpy(ct->c_descr);
357 strncpy(bp, s, buflen);
363 /* exclusive execution */
368 /* %e, %f, and stdin is terminal not content */
374 /* insert filename containing content */
375 snprintf(bp, buflen, "'%s'", file);
377 ** since we've quoted the file argument,
378 ** set things up to look past it, to avoid
379 ** problems with the quoting logic below.
380 ** (I know, I should figure out what's
381 ** broken with the quoting logic, but..)
392 ** display listing prior to displaying content
398 /* insert subtype of content */
399 strncpy(bp, ci->ci_subtype, buflen);
403 /* insert character % */
416 /* Did we actually insert something? */
419 ** Insert single quote if not inside quotes
422 if (!quoted && buflen) {
424 memmove(pp + 1, pp, len);
429 /* Escape existing quotes */
430 while ((pp = strchr(pp, '\'')) && buflen > 3) {
432 memmove(pp + 3, pp, len);
440 ** If pp is still set, that means we ran
445 if (!quoted && buflen) {
462 if (buflen <= 0 || (ct->c_termproc && buflen <= strlen(ct->c_termproc))) {
464 ** content_error would provide a more useful error message
465 ** here, except that if we got overrun, it probably would
468 fprintf(stderr, "Buffer overflow constructing show command!\n");
472 /* use charset string to modify display method */
473 if (ct->c_termproc) {
476 strncpy(term, buffer, sizeof(term));
477 snprintf(buffer, sizeof(buffer), ct->c_termproc, term);
481 return show_content_aux2(ct, serial, alternate, cracked, buffer,
482 fd, xlist, xstdin, xtty);
487 ** Routine to actually display the content
491 show_content_aux2(CT ct, int serial, int alternate, char *cracked,
492 char *buffer, int fd, int xlist, int xstdin, int xtty)
495 char *vec[4], exec[BUFSIZ + sizeof "exec "];
497 if (debugsw || cracked) {
500 fprintf(stderr, "%s msg %s", cracked ? "storing" : "show",
503 fprintf(stderr, " part %s", ct->c_partno);
505 fprintf(stderr, " using command (cd %s; %s)\n",
508 fprintf(stderr, " using command %s\n", buffer);
511 if (xpid < 0 || (xtty && xpid)) {
514 pidcheck(pidwait(xpid, NOTOK));
519 if (ct->c_type == CT_MULTIPART)
520 list_content(ct, -1, 1, 0, 0);
522 list_switch(ct, -1, 1, 0, 0);
525 snprintf(exec, sizeof(exec), "exec %s", buffer);
534 switch (child_id = fork()) {
536 advise("fork", "unable to");
537 (*ct->c_ceclosefnx) (ct);
546 execvp("/bin/sh", vec);
547 fprintf(stderr, "unable to exec ");
554 ct->c_pid = child_id;
558 pidcheck(pidXwait(child_id, NULL));
562 (*ct->c_ceclosefnx) (ct);
563 return (alternate ? DONE : OK);
569 ** show content of type "text"
573 show_text(CT ct, int serial, int alternate)
575 char *cp, buffer[BUFSIZ];
576 CI ci = &ct->c_ctinfo;
578 /* Check for mhshow-show-type/subtype */
579 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
580 invo_name, ci->ci_type, ci->ci_subtype);
581 if ((cp = context_find(buffer)) && *cp != '\0')
582 return show_content_aux(ct, serial, alternate, cp, NULL);
584 /* Check for mhshow-show-type */
585 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
586 if ((cp = context_find(buffer)) && *cp != '\0')
587 return show_content_aux(ct, serial, alternate, cp, NULL);
590 ** Use default method if content is text/plain, or if
591 ** if it is not a text part of a multipart/alternative
593 if (!alternate || ct->c_subtype == TEXT_PLAIN) {
594 snprintf(buffer, sizeof(buffer), "%%p%s '%%F'", defaultpager);
595 cp = (ct->c_showproc = getcpy(buffer));
596 return show_content_aux(ct, serial, alternate, cp, NULL);
604 ** show message body of type "multipart"
608 show_multi(CT ct, int serial, int alternate)
610 char *cp, buffer[BUFSIZ];
611 CI ci = &ct->c_ctinfo;
613 /* Check for mhshow-show-type/subtype */
614 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
615 invo_name, ci->ci_type, ci->ci_subtype);
616 if ((cp = context_find(buffer)) && *cp != '\0')
617 return show_multi_aux(ct, serial, alternate, cp);
619 /* Check for mhshow-show-type */
620 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
621 if ((cp = context_find(buffer)) && *cp != '\0')
622 return show_multi_aux(ct, serial, alternate, cp);
624 if ((cp = ct->c_showproc))
625 return show_multi_aux(ct, serial, alternate, cp);
628 ** Use default method to display this multipart content
629 ** if it is not a (nested) part of a multipart/alternative,
630 ** or if it is one of the known subtypes of multipart.
632 if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
633 return show_multi_internal(ct, serial, alternate);
640 ** show message body of subtypes of multipart that
641 ** we understand directly (mixed, alternate, etc...)
645 show_multi_internal(CT ct, int serial, int alternate)
647 int alternating, nowalternate, nowserial, result;
648 struct multipart *m = (struct multipart *) ct->c_ctparams;
654 nowalternate = alternate;
656 if (ct->c_subtype == MULTI_PARALLEL) {
657 nowserial = serialsw;
658 } else if (ct->c_subtype == MULTI_ALTERNATE) {
666 ** unknown subtypes of multipart (treat as mixed per rfc2046)
671 /* block a few signals */
674 sigaddset(&set, SIGHUP);
675 sigaddset(&set, SIGINT);
676 sigaddset(&set, SIGQUIT);
677 sigaddset(&set, SIGTERM);
678 SIGPROCMASK(SIG_BLOCK, &set, &oset);
682 ** alternate -> we are a part inside an multipart/alternative
683 ** alternating -> we are a multipart/alternative
686 result = alternate ? NOTOK : OK;
688 for (part = m->mp_parts; part; part = part->mp_next) {
691 if (part_ok(p, 1) && type_ok(p, 1)) {
694 inneresult = show_switch(p, nowserial, nowalternate);
695 switch (inneresult) {
697 if (alternate && !alternating) {
710 alternate = nowalternate = 0;
720 if (alternating && !part) {
722 content_error(NULL, ct, "don't know how to display any of the contents");
727 if (serial && !nowserial) {
733 for (part = m->mp_parts; part; part = part->mp_next) {
737 if (kill(p->c_pid, 0) == NOTOK)
744 while (kids > 0 && (pid = wait(&status)) != NOTOK) {
747 for (part = m->mp_parts; part; part = part->mp_next) {
752 if (p->c_pid == pid) {
763 /* reset the signal mask */
764 SIGPROCMASK(SIG_SETMASK, &oset, &set);
772 ** Parse display string for multipart content
773 ** and use external program to display it.
777 show_multi_aux(CT ct, int serial, int alternate, char *cp)
779 int len, buflen, quoted;
781 char *bp, *pp, *file, buffer[BUFSIZ];
782 struct multipart *m = (struct multipart *) ct->c_ctparams;
784 CI ci = &ct->c_ctinfo;
787 for (part = m->mp_parts; part; part = part->mp_next) {
790 if (!p->c_ceopenfnx) {
792 content_error(NULL, p, "don't know how to decode content");
796 if (p->c_storage == NULL) {
798 if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
801 /* I'm not sure if this is necessary? */
802 p->c_storage = getcpy(file);
804 if (p->c_showproc && strcmp(p->c_showproc, "true")==0)
805 return (alternate ? DONE : OK);
806 (*p->c_ceclosefnx) (p);
813 /* get buffer ready to go */
815 buflen = sizeof(buffer) - 1;
816 bp[0] = bp[buflen] = '\0';
819 /* Now parse display string */
820 for ( ; *cp && buflen > 0; cp++) {
825 /* insert parameters from Content-Type field */
830 for (ap = ci->ci_attrs, ep = ci->ci_values;
832 snprintf(bp, buflen, "%s%s=\"%s\"",
843 /* insert content description */
847 s = trimcpy(ct->c_descr);
848 strncpy(bp, s, buflen);
854 /* exclusive execution */
864 /* insert filename(s) containing content */
868 for (part = m->mp_parts; part;
869 part = part->mp_next) {
872 snprintf(bp, buflen, "%s'%s'",
880 ** set our starting pointer back to bp,
881 ** to avoid requoting the filenames we
891 ** display listing prior to displaying content
897 /* insert subtype of content */
898 strncpy(bp, ci->ci_subtype, buflen);
902 /* insert character % */
915 /* Did we actually insert something? */
918 ** Insert single quote if not inside quotes
921 if (!quoted && buflen) {
923 memmove(pp + 1, pp, len);
928 /* Escape existing quotes */
929 while ((pp = strchr(pp, '\'')) && buflen > 3) {
931 memmove(pp + 3, pp, len);
939 ** If pp is still set, that means we ran
944 if (!quoted && buflen) {
962 (ct->c_termproc && buflen <= strlen(ct->c_termproc))) {
964 ** content_error would provide a more useful error message
965 ** here, except that if we got overrun, it probably would
968 fprintf(stderr, "Buffer overflow constructing show command!\n");
972 /* use charset string to modify display method */
973 if (ct->c_termproc) {
976 strncpy(term, buffer, sizeof(term));
977 snprintf(buffer, sizeof(buffer), ct->c_termproc, term);
980 return show_content_aux2(ct, serial, alternate, NULL, buffer,
981 NOTOK, xlist, 0, xtty);
986 ** show content of type "message/rfc822"
990 show_message_rfc822(CT ct, int serial, int alternate)
992 char *cp, buffer[BUFSIZ];
993 CI ci = &ct->c_ctinfo;
995 /* Check for mhshow-show-type/subtype */
996 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
997 invo_name, ci->ci_type, ci->ci_subtype);
998 if ((cp = context_find(buffer)) && *cp != '\0')
999 return show_content_aux(ct, serial, alternate, cp, NULL);
1001 /* Check for mhshow-show-type */
1002 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
1003 if ((cp = context_find(buffer)) && *cp != '\0')
1004 return show_content_aux(ct, serial, alternate, cp, NULL);
1006 if ((cp = ct->c_showproc))
1007 return show_content_aux(ct, serial, alternate, cp, NULL);
1009 /* default method for message/rfc822 */
1010 if (ct->c_subtype == MESSAGE_RFC822) {
1011 cp = (ct->c_showproc = getcpy("%pshow -file '%F'"));
1012 return show_content_aux(ct, serial, alternate, cp, NULL);
1015 /* complain if we are not a part of a multipart/alternative */
1017 content_error(NULL, ct, "don't know how to display content");
1024 ** Show content of type "message/partial".
1028 show_partial(CT ct, int serial, int alternate)
1030 content_error(NULL, ct,
1031 "in order to display this message, you must reassemble it");
1037 ** Show content of type "message/external".
1039 ** THE ERROR CHECKING IN THIS ONE IS NOT DONE YET.
1043 show_external(CT ct, int serial, int alternate)
1045 struct exbody *e = (struct exbody *) ct->c_ctparams;
1046 CT p = e->eb_content;
1051 return show_switch(p, serial, alternate);
1054 content_error(NULL, p, "don't know how to display content");