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>
17 #include <h/mhparse.h>
33 void content_error(char *, CT, char *, ...);
34 void flush_errors(void);
37 int list_switch(CT, int, int, int, int);
38 int list_content(CT, int, int, int, int);
43 void show_all_messages(CT *);
44 int show_content_aux(CT, int, char *, char *);
49 static void show_single_message(CT, char *);
50 static void DisplayMsgHeader(CT, char *);
51 static int show_switch(CT, int);
52 static int show_content(CT, int);
53 static int show_content_aux2(CT, int, char *, char *, int, int, int);
54 static int show_text(CT, int);
55 static int show_multi(CT, int);
56 static int show_multi_internal(CT, int);
57 static int show_multi_aux(CT, int, char *);
58 static int show_message_rfc822(CT, int);
59 static int show_partial(CT, int);
60 static int show_external(CT, int);
64 ** Top level entry point to show/display a group of messages
68 show_all_messages(CT *cts)
73 ** If form is not specified, then get default form
74 ** for showing headers of MIME messages.
77 formsw = getcpy(etcpath("mhl.headers"));
80 ** If form is "mhl.null", suppress display of header.
82 if (strcmp(formsw, "mhl.null")==0)
85 for (ctp = cts; *ctp; ctp++) {
88 /* if top-level type is ok, then display message */
90 show_single_message(ct, formsw);
96 ** Entry point to show/display a single message
100 show_single_message(CT ct, char *form)
103 ** Allow user executable bit so that temporary directories created by
104 ** the viewer (e.g., lynx) are going to be accessible
106 umask(ct->c_umask & ~(0100));
109 ** If you have a format file, then display
110 ** the message headers.
113 DisplayMsgHeader(ct, form);
115 /* Show the body of the message */
122 if (ct->c_ceclosefnx)
123 (*ct->c_ceclosefnx) (ct);
130 ** Use mhl to show the header fields
133 DisplayMsgHeader(CT ct, char *form)
141 vec[vecp++] = "-form";
143 vec[vecp++] = "-nobody";
144 vec[vecp++] = ct->c_file;
149 switch (child_id = fork()) {
151 adios("fork", "unable to");
156 fprintf(stderr, "unable to exec ");
162 pidcheck(pidwait(child_id, NOTOK));
169 ** Switching routine. Call the correct routine
170 ** based on content type.
174 show_switch(CT ct, int alternate)
176 switch (ct->c_type) {
178 return show_multi(ct, alternate);
182 switch (ct->c_subtype) {
183 case MESSAGE_PARTIAL:
184 return show_partial(ct, alternate);
187 case MESSAGE_EXTERNAL:
188 return show_external(ct, alternate);
193 return show_message_rfc822(ct, alternate);
199 return show_text(ct, alternate);
206 return show_content(ct, alternate);
210 adios(NULL, "unknown content type %d", ct->c_type);
214 return 0; /* NOT REACHED */
219 ** Generic method for displaying content
223 show_content(CT ct, int alternate)
225 char *cp, buffer[BUFSIZ];
226 CI ci = &ct->c_ctinfo;
228 /* Check for mhshow-show-type/subtype */
229 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
230 invo_name, ci->ci_type, ci->ci_subtype);
231 if ((cp = context_find(buffer)) && *cp != '\0')
232 return show_content_aux(ct, alternate, cp, NULL);
234 /* Check for mhshow-show-type */
235 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
236 if ((cp = context_find(buffer)) && *cp != '\0')
237 return show_content_aux(ct, alternate, cp, NULL);
239 if ((cp = ct->c_showproc))
240 return show_content_aux(ct, alternate, cp, NULL);
242 /* complain if we are not a part of a multipart/alternative */
244 content_error(NULL, ct, "don't know how to display content");
251 ** Parse the display string for displaying generic content
254 show_content_aux(CT ct, int alternate, char *cp, char *cracked)
256 int fd, len, buflen, quoted;
258 char *bp, *pp, *file, buffer[BUFSIZ];
259 CI ci = &ct->c_ctinfo;
261 if (!ct->c_ceopenfnx) {
263 content_error(NULL, ct,
264 "don't know how to decode content");
270 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
272 if (ct->c_showproc && strcmp(ct->c_showproc, "true")==0)
273 return (alternate ? DONE : OK);
279 strncpy(buffer, cp, sizeof(buffer));
283 /* get buffer ready to go */
285 buflen = sizeof(buffer) - 1;
286 bp[0] = bp[buflen] = '\0';
289 /* Now parse display string */
290 for ( ; *cp && buflen > 0; cp++) {
296 /* insert parameters from Content-Type field */
301 for (ap = ci->ci_attrs, ep = ci->ci_values;
303 snprintf(bp, buflen, "%s%s=\"%s\"",
316 strncpy(bp, ct->c_charset, buflen);
321 /* insert content description */
325 s = trimcpy(ct->c_descr);
326 strncpy(bp, s, buflen);
332 /* %f, and stdin is terminal not content */
337 /* insert filename containing content */
338 snprintf(bp, buflen, "'%s'", file);
340 ** since we've quoted the file argument,
341 ** set things up to look past it, to avoid
342 ** problems with the quoting logic below.
343 ** (I know, I should figure out what's
344 ** broken with the quoting logic, but..)
354 ** display listing prior to displaying content
360 /* insert subtype of content */
361 strncpy(bp, ci->ci_subtype, buflen);
365 /* insert character % */
378 /* Did we actually insert something? */
381 ** Insert single quote if not inside quotes
384 if (!quoted && buflen) {
386 memmove(pp + 1, pp, len);
391 /* Escape existing quotes */
392 while ((pp = strchr(pp, '\'')) && buflen > 3) {
394 memmove(pp + 3, pp, len);
402 ** If pp is still set, that means we ran
407 if (!quoted && buflen) {
425 return show_content_aux2(ct, alternate, cracked, buffer,
431 ** Routine to actually display the content
434 show_content_aux2(CT ct, int alternate, char *cracked,
435 char *buffer, int fd, int xlist, int xstdin)
439 if (debugsw || cracked) {
442 fprintf(stderr, "%s msg %s", cracked ? "storing" : "show",
445 fprintf(stderr, " part %s", ct->c_partno);
447 fprintf(stderr, " using command (cd %s; %s)\n",
450 fprintf(stderr, " using command %s\n", buffer);
454 if (ct->c_type == CT_MULTIPART)
455 list_content(ct, -1, 1, 0, 0);
457 list_switch(ct, -1, 1, 0, 0);
462 switch (child_id = fork()) {
464 advise("fork", "unable to");
465 (*ct->c_ceclosefnx) (ct);
474 execlp("/bin/sh", "/bin/sh", "-c", buffer, NULL);
475 fprintf(stderr, "unable to exec ");
481 pidcheck(pidXwait(child_id, NULL));
484 (*ct->c_ceclosefnx) (ct);
485 return (alternate ? DONE : OK);
491 ** show content of type "text"
495 show_text(CT ct, int alternate)
497 char *cp, buffer[BUFSIZ];
498 CI ci = &ct->c_ctinfo;
500 /* Check for mhshow-show-type/subtype */
501 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
502 invo_name, ci->ci_type, ci->ci_subtype);
503 if ((cp = context_find(buffer)) && *cp != '\0')
504 return show_content_aux(ct, alternate, cp, NULL);
506 /* Check for mhshow-show-type */
507 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
508 if ((cp = context_find(buffer)) && *cp != '\0')
509 return show_content_aux(ct, alternate, cp, NULL);
512 ** Use default method if content is text/plain, or if
513 ** if it is not a text part of a multipart/alternative
515 if (!alternate || ct->c_subtype == TEXT_PLAIN) {
516 if (!check_charset(ct->c_charset, strlen(ct->c_charset))) {
517 snprintf(buffer, sizeof(buffer), "%%liconv -f '%s'",
520 snprintf(buffer, sizeof(buffer), "%%lcat");
522 cp = (ct->c_showproc = getcpy(buffer));
523 return show_content_aux(ct, alternate, cp, NULL);
531 ** show message body of type "multipart"
535 show_multi(CT ct, int alternate)
537 char *cp, buffer[BUFSIZ];
538 CI ci = &ct->c_ctinfo;
540 /* Check for mhshow-show-type/subtype */
541 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
542 invo_name, ci->ci_type, ci->ci_subtype);
543 if ((cp = context_find(buffer)) && *cp != '\0')
544 return show_multi_aux(ct, alternate, cp);
546 /* Check for mhshow-show-type */
547 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
548 if ((cp = context_find(buffer)) && *cp != '\0')
549 return show_multi_aux(ct, alternate, cp);
551 if ((cp = ct->c_showproc))
552 return show_multi_aux(ct, alternate, cp);
555 ** Use default method to display this multipart content
556 ** if it is not a (nested) part of a multipart/alternative,
557 ** or if it is one of the known subtypes of multipart.
559 if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
560 return show_multi_internal(ct, alternate);
567 ** show message body of subtypes of multipart that
568 ** we understand directly (mixed, alternate, etc...)
572 show_multi_internal(CT ct, int alternate)
574 int alternating, nowalternate, result;
575 struct multipart *m = (struct multipart *) ct->c_ctparams;
580 nowalternate = alternate;
582 if (ct->c_subtype == MULTI_ALTERNATE) {
588 ** Other possible multipart types are:
589 ** - multipart/parallel
591 ** - multipart/digest
592 ** - unknown subtypes of multipart (treat as mixed per rfc2046)
596 ** alternate -> we are a part inside an multipart/alternative
597 ** alternating -> we are a multipart/alternative
600 result = alternate ? NOTOK : OK;
602 for (part = m->mp_parts; part; part = part->mp_next) {
605 if (part_ok(p, 1) && type_ok(p, 1)) {
608 inneresult = show_switch(p, nowalternate);
609 switch (inneresult) {
611 if (alternate && !alternating) {
624 alternate = nowalternate = 0;
634 if (alternating && !part) {
636 content_error(NULL, ct, "don't know how to display any of the contents");
646 ** Parse display string for multipart content
647 ** and use external program to display it.
651 show_multi_aux(CT ct, int alternate, char *cp)
653 int len, buflen, quoted;
655 char *bp, *pp, *file, buffer[BUFSIZ];
656 struct multipart *m = (struct multipart *) ct->c_ctparams;
658 CI ci = &ct->c_ctinfo;
661 for (part = m->mp_parts; part; part = part->mp_next) {
664 if (!p->c_ceopenfnx) {
666 content_error(NULL, p, "don't know how to decode content");
670 if (p->c_storage == NULL) {
672 if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
675 /* I'm not sure if this is necessary? */
676 p->c_storage = getcpy(file);
678 if (p->c_showproc && strcmp(p->c_showproc, "true")==0)
679 return (alternate ? DONE : OK);
680 (*p->c_ceclosefnx) (p);
686 /* get buffer ready to go */
688 buflen = sizeof(buffer) - 1;
689 bp[0] = bp[buflen] = '\0';
692 /* Now parse display string */
693 for ( ; *cp && buflen > 0; cp++) {
698 /* insert parameters from Content-Type field */
703 for (ap = ci->ci_attrs, ep = ci->ci_values;
705 snprintf(bp, buflen, "%s%s=\"%s\"",
718 strncpy(bp, ct->c_charset, buflen);
723 /* insert content description */
727 s = trimcpy(ct->c_descr);
728 strncpy(bp, s, buflen);
735 /* insert filename(s) containing content */
739 for (part = m->mp_parts; part;
740 part = part->mp_next) {
743 snprintf(bp, buflen, "%s'%s'",
751 ** set our starting pointer back to bp,
752 ** to avoid requoting the filenames we
761 ** display listing prior to displaying content
767 /* insert subtype of content */
768 strncpy(bp, ci->ci_subtype, buflen);
772 /* insert character % */
785 /* Did we actually insert something? */
788 ** Insert single quote if not inside quotes
791 if (!quoted && buflen) {
793 memmove(pp + 1, pp, len);
798 /* Escape existing quotes */
799 while ((pp = strchr(pp, '\'')) && buflen > 3) {
801 memmove(pp + 3, pp, len);
809 ** If pp is still set, that means we ran
814 if (!quoted && buflen) {
831 return show_content_aux2(ct, alternate, NULL, buffer,
837 ** show content of type "message/rfc822"
841 show_message_rfc822(CT ct, int alternate)
843 char *cp, buffer[BUFSIZ];
844 CI ci = &ct->c_ctinfo;
846 /* Check for mhshow-show-type/subtype */
847 snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
848 invo_name, ci->ci_type, ci->ci_subtype);
849 if ((cp = context_find(buffer)) && *cp != '\0')
850 return show_content_aux(ct, alternate, cp, NULL);
852 /* Check for mhshow-show-type */
853 snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
854 if ((cp = context_find(buffer)) && *cp != '\0')
855 return show_content_aux(ct, alternate, cp, NULL);
857 if ((cp = ct->c_showproc))
858 return show_content_aux(ct, alternate, cp, NULL);
860 /* default method for message/rfc822 */
861 if (ct->c_subtype == MESSAGE_RFC822) {
862 cp = (ct->c_showproc = getcpy("%lshow -file %F"));
863 return show_content_aux(ct, alternate, cp, NULL);
866 /* complain if we are not a part of a multipart/alternative */
868 content_error(NULL, ct, "don't know how to display content");
875 ** Show content of type "message/partial".
879 show_partial(CT ct, int alternate)
881 content_error(NULL, ct,
882 "in order to display this message, you must reassemble it");
888 ** Show how to retrieve content of type "message/external".
891 show_external(CT ct, int alternate)
898 msg = add("You need to fetch the contents yourself:", NULL);
899 ap = ct->c_ctinfo.ci_attrs;
900 ep = ct->c_ctinfo.ci_values;
901 for (; *ap; ap++, ep++) {
902 msg = add(concat("\n\t", *ap, ": ", *ep, NULL), msg);
904 if (!(fp = fopen(ct->c_file, "r"))) {
905 adios(ct->c_file, "unable to open");
907 fseek(fp, ct->c_begin, SEEK_SET);
908 while (!feof(fp) && ftell(fp) < ct->c_end) {
909 if (!fgets(buf, sizeof buf, fp)) {
910 adios(ct->c_file, "unable to read");
912 *strchr(buf, '\n') = '\0';
913 msg = add(concat("\n\t", buf, NULL), msg);
916 content_error(NULL, ct, msg);