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>
16 #include <h/mhparse.h>
35 void content_error(char *, CT, char *, ...);
36 void flush_errors(void);
39 int list_switch(CT, int, int, int);
40 int list_content(CT, int, int, int);
45 void show_all_messages(CT *);
46 int show_content_aux(CT, int, char *, char *);
51 static void show_single_message(CT, char *);
52 static void DisplayMsgHeader(CT, char *);
53 static int show_switch(CT, int);
54 static int show_content(CT, int);
55 static int show_content_aux2(CT, int, char *, char *, int, int, int);
56 static int show_text(CT, int);
57 static int show_multi(CT, int);
58 static int show_multi_internal(CT, int);
59 static int show_multi_aux(CT, int, char *);
60 static int show_message_rfc822(CT, int);
61 static int show_partial(CT, int);
62 static int show_external(CT, int);
66 ** Top level entry point to show/display a group of messages
70 show_all_messages(CT *cts)
75 ** If form is not specified, then get default form
76 ** for showing headers of MIME messages.
79 formsw = getcpy(etcpath("mhl.headers"));
82 ** If form is "mhl.null", suppress display of header.
84 if (strcmp(formsw, "mhl.null")==0)
87 for (ctp = cts; *ctp; ctp++) {
90 if (!type_ok(ct, 1)) { /* top-level type */
97 printf(">>> Message %s\n\n", ct->c_file);
99 show_single_message(ct, formsw);
105 ** Entry point to show/display a single message
109 show_single_message(CT ct, char *form)
112 ** Allow user executable bit so that temporary directories created by
113 ** the viewer (e.g., lynx) are going to be accessible
115 umask(ct->c_umask & ~(0100));
118 ** If you have a format file, then display
119 ** the message headers.
122 DisplayMsgHeader(ct, form);
124 /* Show the body of the message */
131 if (ct->c_ceclosefnx)
132 (*ct->c_ceclosefnx) (ct);
139 ** Use mhl to show the header fields
142 DisplayMsgHeader(CT ct, char *form)
150 vec[vecp++] = "-form";
152 vec[vecp++] = "-nobody";
153 vec[vecp++] = ct->c_file;
158 switch (child_id = fork()) {
160 adios(EX_OSERR, "fork", "unable to");
165 fprintf(stderr, "unable to exec ");
171 pidcheck(pidwait(child_id, NOTOK));
178 ** Switching routine. Call the correct routine
179 ** based on content type.
183 show_switch(CT ct, int alternate)
185 switch (ct->c_type) {
187 return show_multi(ct, alternate);
191 switch (ct->c_subtype) {
192 case MESSAGE_PARTIAL:
193 return show_partial(ct, alternate);
196 case MESSAGE_EXTERNAL:
197 return show_external(ct, alternate);
202 return show_message_rfc822(ct, alternate);
208 return show_text(ct, alternate);
215 return show_content(ct, alternate);
219 adios(EX_DATAERR, NULL, "unknown content type %d", ct->c_type);
223 return 0; /* NOT REACHED */
228 ** Generic method for displaying content
232 show_content(CT ct, int alternate)
234 char *cp, buffer[BUFSIZ];
235 CI ci = &ct->c_ctinfo;
237 /* Check for mhshow-show-type/subtype */
238 snprintf(buffer, sizeof(buffer), "mhshow-show-%s/%s",
239 ci->ci_type, ci->ci_subtype);
240 if ((cp = context_find(buffer)) && *cp != '\0')
241 return show_content_aux(ct, alternate, cp, NULL);
243 /* Check for mhshow-show-type */
244 snprintf(buffer, sizeof(buffer), "mhshow-show-%s", ci->ci_type);
245 if ((cp = context_find(buffer)) && *cp != '\0')
246 return show_content_aux(ct, alternate, cp, NULL);
248 if ((cp = ct->c_showproc))
249 return show_content_aux(ct, alternate, cp, NULL);
251 /* complain if we are not a part of a multipart/alternative */
253 return show_content_aux(ct, alternate, "%l", NULL);
260 ** Parse the display string for displaying generic content
263 show_content_aux(CT ct, int alternate, char *cp, char *cracked)
265 int fd, len, buflen, quoted;
267 char *bp, *pp, *file, buffer[BUFSIZ];
268 CI ci = &ct->c_ctinfo;
270 if (!ct->c_ceopenfnx) {
272 content_error(NULL, ct,
273 "don't know how to decode content");
279 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
281 if (ct->c_showproc && strcmp(ct->c_showproc, "true")==0)
282 return (alternate ? DONE : OK);
288 strncpy(buffer, cp, sizeof(buffer));
292 /* get buffer ready to go */
294 buflen = sizeof(buffer) - 1;
295 bp[0] = bp[buflen] = '\0';
298 /* Now parse display string */
299 for ( ; *cp && buflen > 0; cp++) {
305 /* insert parameters from Content-Type field */
310 for (ap = ci->ci_attrs, ep = ci->ci_values;
312 snprintf(bp, buflen, "%s%s=\"%s\"",
324 strncpy(bp, ct->c_charset ? ct->c_charset :
329 /* insert content description */
333 s = trimcpy(ct->c_descr);
334 strncpy(bp, s, buflen);
340 /* %f, and stdin is terminal not content */
345 /* insert filename containing content */
346 snprintf(bp, buflen, "'%s'", file);
348 ** since we've quoted the file argument,
349 ** set things up to look past it, to avoid
350 ** problems with the quoting logic below.
351 ** (I know, I should figure out what's
352 ** broken with the quoting logic, but..)
362 ** display listing prior to displaying content
368 /* insert subtype of content */
369 strncpy(bp, ci->ci_subtype, buflen);
373 /* insert character % */
386 /* Did we actually insert something? */
389 ** Insert single quote if not inside quotes
392 if (!quoted && buflen) {
394 memmove(pp + 1, pp, len);
399 /* Escape existing quotes */
400 while ((pp = strchr(pp, '\'')) && buflen > 3) {
402 memmove(pp + 3, pp, len);
410 ** If pp is still set, that means we ran
415 if (!quoted && buflen) {
433 return show_content_aux2(ct, alternate, cracked, buffer,
439 ** Routine to actually display the content
442 show_content_aux2(CT ct, int alternate, char *cracked,
443 char *buffer, int fd, int xlist, int xstdin)
447 if (debugsw || cracked) {
450 fprintf(stderr, "%s msg %s", cracked ? "storing" : "show",
453 fprintf(stderr, " part %s", ct->c_partno);
455 fprintf(stderr, " using command (cd %s; %s)\n",
458 fprintf(stderr, " using command %s\n", buffer);
462 if (ct->c_type == CT_MULTIPART)
463 list_content(ct, -1, 0, 0);
465 list_switch(ct, -1, 0, 0);
470 switch (child_id = fork()) {
472 advise("fork", "unable to");
473 (*ct->c_ceclosefnx) (ct);
482 execlp("/bin/sh", "/bin/sh", "-c", buffer, NULL);
483 fprintf(stderr, "unable to exec ");
489 pidcheck(pidXwait(child_id, NULL));
492 (*ct->c_ceclosefnx) (ct);
493 return (alternate ? DONE : OK);
499 ** show content of type "text"
503 show_text(CT ct, int alternate)
505 char *cp, buffer[BUFSIZ];
506 CI ci = &ct->c_ctinfo;
508 /* Check for mhshow-show-type/subtype */
509 snprintf(buffer, sizeof(buffer), "mhshow-show-%s/%s",
510 ci->ci_type, ci->ci_subtype);
511 if ((cp = context_find(buffer)) && *cp != '\0')
512 return show_content_aux(ct, alternate, cp, NULL);
514 /* Check for mhshow-show-type */
515 snprintf(buffer, sizeof(buffer), "mhshow-show-%s", ci->ci_type);
516 if ((cp = context_find(buffer)) && *cp != '\0')
517 return show_content_aux(ct, alternate, cp, NULL);
520 ** Use default method if content is text/plain, or if
521 ** if it is not a text part of a multipart/alternative
523 if (!alternate || ct->c_subtype == TEXT_PLAIN) {
524 if (ct->c_charset && !is_native_charset(ct->c_charset)) {
525 snprintf(buffer, sizeof(buffer), "%%liconv -f '%s'",
528 snprintf(buffer, sizeof(buffer), "%%lcat");
530 ct->c_showproc = getcpy(buffer);
531 return show_content_aux(ct, alternate, ct->c_showproc, NULL);
539 ** show message body of type "multipart"
543 show_multi(CT ct, int alternate)
545 char *cp, buffer[BUFSIZ];
546 CI ci = &ct->c_ctinfo;
548 /* Check for mhshow-show-type/subtype */
549 snprintf(buffer, sizeof(buffer), "mhshow-show-%s/%s",
550 ci->ci_type, ci->ci_subtype);
551 if ((cp = context_find(buffer)) && *cp != '\0')
552 return show_multi_aux(ct, alternate, cp);
554 /* Check for mhshow-show-type */
555 snprintf(buffer, sizeof(buffer), "mhshow-show-%s", ci->ci_type);
556 if ((cp = context_find(buffer)) && *cp != '\0')
557 return show_multi_aux(ct, alternate, cp);
559 if ((cp = ct->c_showproc))
560 return show_multi_aux(ct, alternate, cp);
563 ** Use default method to display this multipart content
564 ** if it is not a (nested) part of a multipart/alternative,
565 ** or if it is one of the known subtypes of multipart.
567 if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
568 return show_multi_internal(ct, alternate);
575 ** show message body of subtypes of multipart that
576 ** we understand directly (mixed, alternate, etc...)
580 show_multi_internal(CT ct, int alternate)
582 int alternating, nowalternate, result;
583 struct multipart *m = (struct multipart *) ct->c_ctparams;
588 nowalternate = alternate;
590 if (ct->c_subtype == MULTI_ALTERNATE) {
596 ** Other possible multipart types are:
597 ** - multipart/parallel
599 ** - multipart/digest
600 ** - unknown subtypes of multipart (treat as mixed per rfc2046)
604 ** alternate -> we are a part inside an multipart/alternative
605 ** alternating -> we are a multipart/alternative
608 result = alternate ? NOTOK : OK;
610 for (part = m->mp_parts; part; part = part->mp_next) {
613 if (part_ok(p, 1) && type_ok(p, 1)) {
616 inneresult = show_switch(p, nowalternate);
617 switch (inneresult) {
619 if (alternate && !alternating) {
632 alternate = nowalternate = 0;
642 if (alternating && !part) {
644 content_error(NULL, ct, "don't know how to display any of the contents");
654 ** Parse display string for multipart content
655 ** and use external program to display it.
659 show_multi_aux(CT ct, int alternate, char *cp)
661 int len, buflen, quoted;
663 char *bp, *pp, *file, buffer[BUFSIZ];
664 struct multipart *m = (struct multipart *) ct->c_ctparams;
666 CI ci = &ct->c_ctinfo;
669 for (part = m->mp_parts; part; part = part->mp_next) {
672 if (!p->c_ceopenfnx) {
674 content_error(NULL, p, "don't know how to decode content");
678 if (p->c_storage == NULL) {
680 if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
683 /* I'm not sure if this is necessary? */
684 p->c_storage = getcpy(file);
686 if (p->c_showproc && strcmp(p->c_showproc, "true")==0)
687 return (alternate ? DONE : OK);
688 (*p->c_ceclosefnx) (p);
694 /* get buffer ready to go */
696 buflen = sizeof(buffer) - 1;
697 bp[0] = bp[buflen] = '\0';
700 /* Now parse display string */
701 for ( ; *cp && buflen > 0; cp++) {
706 /* insert parameters from Content-Type field */
711 for (ap = ci->ci_attrs, ep = ci->ci_values;
713 snprintf(bp, buflen, "%s%s=\"%s\"",
725 strncpy(bp, ct->c_charset ? ct->c_charset :
730 /* insert content description */
734 s = trimcpy(ct->c_descr);
735 strncpy(bp, s, buflen);
742 /* insert filename(s) containing content */
746 for (part = m->mp_parts; part;
747 part = part->mp_next) {
750 snprintf(bp, buflen, "%s'%s'",
758 ** set our starting pointer back to bp,
759 ** to avoid requoting the filenames we
768 ** display listing prior to displaying content
774 /* insert subtype of content */
775 strncpy(bp, ci->ci_subtype, buflen);
779 /* insert character % */
792 /* Did we actually insert something? */
795 ** Insert single quote if not inside quotes
798 if (!quoted && buflen) {
800 memmove(pp + 1, pp, len);
805 /* Escape existing quotes */
806 while ((pp = strchr(pp, '\'')) && buflen > 3) {
808 memmove(pp + 3, pp, len);
816 ** If pp is still set, that means we ran
821 if (!quoted && buflen) {
838 return show_content_aux2(ct, alternate, NULL, buffer,
844 ** show content of type "message/rfc822"
848 show_message_rfc822(CT ct, int alternate)
850 char *cp, buffer[BUFSIZ];
851 CI ci = &ct->c_ctinfo;
853 /* Check for mhshow-show-type/subtype */
854 snprintf(buffer, sizeof(buffer), "mhshow-show-%s/%s",
855 ci->ci_type, ci->ci_subtype);
856 if ((cp = context_find(buffer)) && *cp != '\0')
857 return show_content_aux(ct, alternate, cp, NULL);
859 /* Check for mhshow-show-type */
860 snprintf(buffer, sizeof(buffer), "mhshow-show-%s", ci->ci_type);
861 if ((cp = context_find(buffer)) && *cp != '\0')
862 return show_content_aux(ct, alternate, cp, NULL);
864 if ((cp = ct->c_showproc))
865 return show_content_aux(ct, alternate, cp, NULL);
867 /* default method for message/rfc822 */
868 if (ct->c_subtype == MESSAGE_RFC822) {
869 cp = (ct->c_showproc = getcpy("%lshow -file %F"));
870 return show_content_aux(ct, alternate, cp, NULL);
873 /* complain if we are not a part of a multipart/alternative */
875 return show_content_aux(ct, alternate, "%l", NULL);
882 ** Show content of type "message/partial".
886 show_partial(CT ct, int alternate)
888 show_content_aux(ct, alternate, "%l", NULL);
889 puts("in order to display this message, you must reassemble it");
895 ** Show how to retrieve content of type "message/external".
898 show_external(CT ct, int alternate)
905 msg = add("You need to fetch the contents yourself:\n", NULL);
906 ap = ct->c_ctinfo.ci_attrs;
907 ep = ct->c_ctinfo.ci_values;
908 for (; *ap; ap++, ep++) {
909 msg = add(concat("\t", *ap, ": ", *ep, NULL), msg);
911 if (!(fp = fopen(ct->c_file, "r"))) {
912 adios(EX_IOERR, ct->c_file, "unable to open");
914 fseek(fp, ct->c_begin, SEEK_SET);
915 while (!feof(fp) && ftell(fp) < ct->c_end) {
916 if (!fgets(buf, sizeof buf, fp)) {
917 adios(EX_IOERR, ct->c_file, "unable to read");
919 *strchr(buf, '\n') = '\0';
921 continue; /* skip empty lines */
923 msg = add(concat("\t", buf, "\n", NULL), msg);
926 show_content_aux(ct, alternate, "%l", NULL);