e2cde4dd9ef8b9c20bc1c1779331d0659e9a2162
[mmh] / uip / mhshowsbr.c
1 /*
2 ** mhshowsbr.c -- routines to display the contents of MIME messages
3 **
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.
7 */
8
9 #include <h/mh.h>
10 #include <fcntl.h>
11 #include <h/signals.h>
12 #include <errno.h>
13 #include <setjmp.h>
14 #include <signal.h>
15 #include <h/tws.h>
16 #include <h/mime.h>
17 #include <h/mhparse.h>
18 #include <h/utils.h>
19 #include <sys/wait.h>
20
21 extern int debugsw;
22
23 int nolist   = 0;
24 char *formsw = NULL;
25 pid_t xpid = 0;
26
27
28 /* mhparse.c */
29 int pidcheck(int);
30
31 /* mhmisc.c */
32 int part_ok(CT, int);
33 int type_ok(CT, int);
34 void content_error(char *, CT, char *, ...);
35 void flush_errors(void);
36
37 /* mhlistsbr.c */
38 int list_switch(CT, int, int, int, int);
39 int list_content(CT, int, int, int, int);
40
41 /*
42 ** prototypes
43 */
44 void show_all_messages(CT *);
45 int show_content_aux(CT, int, char *, char *);
46
47 /*
48 ** static prototypes
49 */
50 static void show_single_message(CT, char *);
51 static void DisplayMsgHeader(CT, char *);
52 static int show_switch(CT, int);
53 static int show_content(CT, int);
54 static int show_content_aux2(CT, int, char *, char *, int, int, int, int);
55 static int show_text(CT, int);
56 static int show_multi(CT, int);
57 static int show_multi_internal(CT, int);
58 static int show_multi_aux(CT, int, char *);
59 static int show_message_rfc822(CT, int);
60 static int show_partial(CT, int);
61 static int show_external(CT, int);
62
63
64 /*
65 ** Top level entry point to show/display a group of messages
66 */
67
68 void
69 show_all_messages(CT *cts)
70 {
71         CT ct, *ctp;
72
73         /*
74         ** If form is not specified, then get default form
75         ** for showing headers of MIME messages.
76         */
77         if (!formsw)
78                 formsw = getcpy(etcpath("mhl.headers"));
79
80         /*
81         ** If form is "mhl.null", suppress display of header.
82         */
83         if (strcmp(formsw, "mhl.null")==0)
84                 formsw = NULL;
85
86         for (ctp = cts; *ctp; ctp++) {
87                 ct = *ctp;
88
89                 /* if top-level type is ok, then display message */
90                 if (type_ok(ct, 1))
91                         show_single_message(ct, formsw);
92         }
93 }
94
95
96 /*
97 ** Entry point to show/display a single message
98 */
99
100 static void
101 show_single_message(CT ct, char *form)
102 {
103         sigset_t set, oset;
104         int status;
105
106         /*
107         ** Allow user executable bit so that temporary directories created by
108         ** the viewer (e.g., lynx) are going to be accessible
109         */
110         umask(ct->c_umask & ~(0100));
111
112         /*
113         ** If you have a format file, then display
114         ** the message headers.
115         */
116         if (form)
117                 DisplayMsgHeader(ct, form);
118         else
119                 xpid = 0;
120
121         /* Show the body of the message */
122         show_switch(ct, 0);
123
124         if (ct->c_fp) {
125                 fclose(ct->c_fp);
126                 ct->c_fp = NULL;
127         }
128         if (ct->c_ceclosefnx)
129                 (*ct->c_ceclosefnx) (ct);
130
131         /* block a few signals */
132         sigemptyset(&set);
133         sigaddset(&set, SIGHUP);
134         sigaddset(&set, SIGINT);
135         sigaddset(&set, SIGQUIT);
136         sigaddset(&set, SIGTERM);
137         sigprocmask(SIG_BLOCK, &set, &oset);
138
139         while (wait(&status) != NOTOK) {
140                 pidcheck(status);
141                 continue;
142         }
143
144         /* reset the signal mask */
145         sigprocmask(SIG_SETMASK, &oset, &set);
146
147         xpid = 0;
148         flush_errors();
149 }
150
151
152 /*
153 ** Use mhl to show the header fields
154 */
155 static void
156 DisplayMsgHeader(CT ct, char *form)
157 {
158         pid_t child_id;
159         int vecp;
160         char *vec[8];
161
162         vecp = 0;
163         vec[vecp++] = "mhl";
164         vec[vecp++] = "-form";
165         vec[vecp++] = form;
166         vec[vecp++] = "-nobody";
167         vec[vecp++] = ct->c_file;
168         vec[vecp] = NULL;
169
170         fflush(stdout);
171
172         switch (child_id = fork()) {
173         case NOTOK:
174                 adios("fork", "unable to");
175                 /* NOTREACHED */
176
177         case OK:
178                 execvp("mhl", vec);
179                 fprintf(stderr, "unable to exec ");
180                 perror("mhl");
181                 _exit(-1);
182                 /* NOTREACHED */
183
184         default:
185                 xpid = -child_id;
186                 break;
187         }
188 }
189
190
191 /*
192 ** Switching routine.  Call the correct routine
193 ** based on content type.
194 */
195
196 static int
197 show_switch(CT ct, int alternate)
198 {
199         switch (ct->c_type) {
200         case CT_MULTIPART:
201                 return show_multi(ct, alternate);
202                 break;
203
204         case CT_MESSAGE:
205                 switch (ct->c_subtype) {
206                         case MESSAGE_PARTIAL:
207                                 return show_partial(ct, alternate);
208                                 break;
209
210                         case MESSAGE_EXTERNAL:
211                                 return show_external(ct, alternate);
212                                 break;
213
214                         case MESSAGE_RFC822:
215                         default:
216                                 return show_message_rfc822(ct, alternate);
217                                 break;
218                 }
219                 break;
220
221         case CT_TEXT:
222                 return show_text(ct, alternate);
223                 break;
224
225         case CT_AUDIO:
226         case CT_IMAGE:
227         case CT_VIDEO:
228         case CT_APPLICATION:
229                 return show_content(ct, alternate);
230                 break;
231
232         default:
233                 adios(NULL, "unknown content type %d", ct->c_type);
234                 break;
235         }
236
237         return 0;  /* NOT REACHED */
238 }
239
240
241 /*
242 ** Generic method for displaying content
243 */
244
245 static int
246 show_content(CT ct, int alternate)
247 {
248         char *cp, buffer[BUFSIZ];
249         CI ci = &ct->c_ctinfo;
250
251         /* Check for mhshow-show-type/subtype */
252         snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
253                                 invo_name, ci->ci_type, ci->ci_subtype);
254         if ((cp = context_find(buffer)) && *cp != '\0')
255                 return show_content_aux(ct, alternate, cp, NULL);
256
257         /* Check for mhshow-show-type */
258         snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
259         if ((cp = context_find(buffer)) && *cp != '\0')
260                 return show_content_aux(ct, alternate, cp, NULL);
261
262         if ((cp = ct->c_showproc))
263                 return show_content_aux(ct, alternate, cp, NULL);
264
265         /* complain if we are not a part of a multipart/alternative */
266         if (!alternate)
267                 content_error(NULL, ct, "don't know how to display content");
268
269         return NOTOK;
270 }
271
272
273 /*
274 ** Parse the display string for displaying generic content
275 */
276
277 int
278 show_content_aux(CT ct, int alternate, char *cp, char *cracked)
279 {
280         int fd, len, buflen, quoted;
281         int xstdin, xlist, xtty;
282         char *bp, *pp, *file, buffer[BUFSIZ];
283         CI ci = &ct->c_ctinfo;
284
285         if (!ct->c_ceopenfnx) {
286                 if (!alternate)
287                         content_error(NULL, ct,
288                                         "don't know how to decode content");
289
290                 return NOTOK;
291         }
292
293         file = NULL;
294         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
295                 return NOTOK;
296         if (ct->c_showproc && strcmp(ct->c_showproc, "true")==0)
297                 return (alternate ? DONE : OK);
298
299         xlist  = 0;
300         xstdin = 0;
301         xtty   = 0;
302
303         if (cracked) {
304                 strncpy(buffer, cp, sizeof(buffer));
305                 goto got_command;
306         }
307
308         /* get buffer ready to go */
309         bp = buffer;
310         buflen = sizeof(buffer) - 1;
311         bp[0] = bp[buflen] = '\0';
312         quoted = 0;
313
314         /* Now parse display string */
315         for ( ; *cp && buflen > 0; cp++) {
316                 if (*cp == '%') {
317                         pp = bp;
318
319                         switch (*++cp) {
320                         case 'a':
321                                 /* insert parameters from Content-Type field */
322                                 {
323                                 char **ap, **ep;
324                                 char *s = "";
325
326                                 for (ap = ci->ci_attrs, ep = ci->ci_values;
327                                                 *ap; ap++, ep++) {
328                                         snprintf(bp, buflen, "%s%s=\"%s\"",
329                                                         s, *ap, *ep);
330                                         len = strlen(bp);
331                                         bp += len;
332                                         buflen -= len;
333                                         s = " ";
334                                 }
335                                 }
336                                 break;
337
338                         case 'c':
339                                 /* insert charset */
340                                 if (ct->c_charset) {
341                                         strncpy(bp, ct->c_charset, buflen);
342                                 }
343                                 break;
344
345                         case 'd':
346                                 /* insert content description */
347                                 if (ct->c_descr) {
348                                         char *s;
349
350                                         s = trimcpy(ct->c_descr);
351                                         strncpy(bp, s, buflen);
352                                         free(s);
353                                 }
354                                 break;
355
356                         case 'e':
357                                 /* exclusive execution */
358                                 xtty = 1;
359                                 break;
360
361                         case 'F':
362                                 /* %e, %f, and stdin is terminal not content */
363                                 xstdin = 1;
364                                 xtty = 1;
365                                 /* and fall... */
366
367                         case 'f':
368                                 /* insert filename containing content */
369                                 snprintf(bp, buflen, "'%s'", file);
370                                 /*
371                                 ** since we've quoted the file argument,
372                                 ** set things up to look past it, to avoid
373                                 ** problems with the quoting logic below.
374                                 ** (I know, I should figure out what's
375                                 ** broken with the quoting logic, but..)
376                                 */
377                                 len = strlen(bp);
378                                 buflen -= len;
379                                 bp += len;
380                                 pp = bp;
381                                 break;
382
383                         case 'p':
384                         case 'l':
385                                 /*
386                                 ** display listing prior to displaying content
387                                 */
388                                 xlist = !nolist;
389                                 break;
390
391                         case 's':
392                                 /* insert subtype of content */
393                                 strncpy(bp, ci->ci_subtype, buflen);
394                                 break;
395
396                         case '%':
397                                 /* insert character % */
398                                 goto raw;
399
400                         default:
401                                 *bp++ = *--cp;
402                                 *bp = '\0';
403                                 buflen--;
404                                 continue;
405                         }
406                         len = strlen(bp);
407                         bp += len;
408                         buflen -= len;
409
410                         /* Did we actually insert something? */
411                         if (bp != pp) {
412                                 /*
413                                 ** Insert single quote if not inside quotes
414                                 ** already
415                                 */
416                                 if (!quoted && buflen) {
417                                         len = strlen(pp);
418                                         memmove(pp + 1, pp, len);
419                                         *pp++ = '\'';
420                                         buflen--;
421                                         bp++;
422                                 }
423                                 /* Escape existing quotes */
424                                 while ((pp = strchr(pp, '\'')) && buflen > 3) {
425                                         len = strlen(pp++);
426                                         memmove(pp + 3, pp, len);
427                                         *pp++ = '\\';
428                                         *pp++ = '\'';
429                                         *pp++ = '\'';
430                                         buflen -= 3;
431                                         bp += 3;
432                                 }
433                                 /*
434                                 ** If pp is still set, that means we ran
435                                 ** out of space.
436                                 */
437                                 if (pp)
438                                         buflen = 0;
439                                 if (!quoted && buflen) {
440                                         *bp++ = '\'';
441                                         *bp = '\0';
442                                         buflen--;
443                                 }
444                         }
445                 } else {
446 raw:
447                         *bp++ = *cp;
448                         *bp = '\0';
449                         buflen--;
450
451                         if (*cp == '\'')
452                                 quoted = !quoted;
453                 }
454         }
455
456 got_command:
457         return show_content_aux2(ct, alternate, cracked, buffer,
458                         fd, xlist, xstdin, xtty);
459 }
460
461
462 /*
463 ** Routine to actually display the content
464 */
465
466 static int
467 show_content_aux2(CT ct, int alternate, char *cracked,
468         char *buffer, int fd, int xlist, int xstdin, int xtty)
469 {
470         pid_t child_id;
471
472         if (debugsw || cracked) {
473                 fflush(stdout);
474
475                 fprintf(stderr, "%s msg %s", cracked ? "storing" : "show",
476                                  ct->c_file);
477                 if (ct->c_partno)
478                         fprintf(stderr, " part %s", ct->c_partno);
479                 if (cracked)
480                         fprintf(stderr, " using command (cd %s; %s)\n",
481                                         cracked, buffer);
482                 else
483                         fprintf(stderr, " using command %s\n", buffer);
484         }
485
486         if (xpid < 0 || (xtty && xpid)) {
487                 if (xpid < 0)
488                         xpid = -xpid;
489                 pidcheck(pidwait(xpid, NOTOK));
490                 xpid = 0;
491         }
492
493         if (xlist) {
494                 if (ct->c_type == CT_MULTIPART)
495                         list_content(ct, -1, 1, 0, 0);
496                 else
497                         list_switch(ct, -1, 1, 0, 0);
498         }
499
500         fflush(stdout);
501
502         switch (child_id = fork()) {
503         case NOTOK:
504                 advise("fork", "unable to");
505                 (*ct->c_ceclosefnx) (ct);
506                 return NOTOK;
507
508         case OK:
509                 if (cracked)
510                         chdir(cracked);
511                 if (!xstdin)
512                         dup2(fd, 0);
513                 close(fd);
514                 execlp("/bin/sh", "/bin/sh", "-c", buffer, NULL);
515                 fprintf(stderr, "unable to exec ");
516                 perror("/bin/sh");
517                 _exit(-1);
518                 /* NOTREACHED */
519
520         default:
521                 pidcheck(pidXwait(child_id, NULL));
522
523                 if (fd != NOTOK)
524                         (*ct->c_ceclosefnx) (ct);
525                 return (alternate ? DONE : OK);
526         }
527 }
528
529
530 /*
531 ** show content of type "text"
532 */
533
534 static int
535 show_text(CT ct, int alternate)
536 {
537         char *cp, buffer[BUFSIZ];
538         CI ci = &ct->c_ctinfo;
539
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_content_aux(ct, alternate, cp, NULL);
545
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_content_aux(ct, alternate, cp, NULL);
550
551         /*
552         ** Use default method if content is text/plain, or if
553         ** if it is not a text part of a multipart/alternative
554         */
555         if (!alternate || ct->c_subtype == TEXT_PLAIN) {
556                 if (!check_charset(ct->c_charset, strlen(ct->c_charset))) {
557                         snprintf(buffer, sizeof(buffer), "%%liconv -f '%s'",
558                                         ct->c_charset);
559                 } else {
560                         snprintf(buffer, sizeof(buffer), "%%lcat");
561                 }
562                 cp = (ct->c_showproc = getcpy(buffer));
563                 return show_content_aux(ct, alternate, cp, NULL);
564         }
565
566         return NOTOK;
567 }
568
569
570 /*
571 ** show message body of type "multipart"
572 */
573
574 static int
575 show_multi(CT ct, int alternate)
576 {
577         char *cp, buffer[BUFSIZ];
578         CI ci = &ct->c_ctinfo;
579
580         /* Check for mhshow-show-type/subtype */
581         snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
582                         invo_name, ci->ci_type, ci->ci_subtype);
583         if ((cp = context_find(buffer)) && *cp != '\0')
584                 return show_multi_aux(ct, alternate, cp);
585
586         /* Check for mhshow-show-type */
587         snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
588         if ((cp = context_find(buffer)) && *cp != '\0')
589                 return show_multi_aux(ct, alternate, cp);
590
591         if ((cp = ct->c_showproc))
592                 return show_multi_aux(ct, alternate, cp);
593
594         /*
595         ** Use default method to display this multipart content
596         ** if it is not a (nested) part of a multipart/alternative,
597         ** or if it is one of the known subtypes of multipart.
598         */
599         if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
600                 return show_multi_internal(ct, alternate);
601
602         return NOTOK;
603 }
604
605
606 /*
607 ** show message body of subtypes of multipart that
608 ** we understand directly (mixed, alternate, etc...)
609 */
610
611 static int
612 show_multi_internal(CT ct, int alternate)
613 {
614         int alternating, nowalternate, result;
615         struct multipart *m = (struct multipart *) ct->c_ctparams;
616         struct part *part;
617         CT p;
618
619         alternating = 0;
620         nowalternate = alternate;
621
622         if (ct->c_subtype == MULTI_ALTERNATE) {
623                 nowalternate = 1;
624                 alternating  = 1;
625         }
626
627         /*
628         ** Other possible multipart types are:
629         ** - multipart/parallel
630         ** - multipart/mixed
631         ** - multipart/digest
632         ** - unknown subtypes of multipart (treat as mixed per rfc2046)
633         */
634
635         /*
636         ** alternate   -> we are a part inside an multipart/alternative
637         ** alternating -> we are a multipart/alternative
638         */
639
640         result = alternate ? NOTOK : OK;
641
642         for (part = m->mp_parts; part; part = part->mp_next) {
643                 p = part->mp_part;
644
645                 if (part_ok(p, 1) && type_ok(p, 1)) {
646                         int inneresult;
647
648                         inneresult = show_switch(p, nowalternate);
649                         switch (inneresult) {
650                         case NOTOK:
651                                 if (alternate && !alternating) {
652                                         result = NOTOK;
653                                         goto out;
654                                 }
655                                 continue;
656
657                         case OK:
658                         case DONE:
659                                 if (alternating) {
660                                         result = DONE;
661                                         break;
662                                 }
663                                 if (alternate) {
664                                         alternate = nowalternate = 0;
665                                         if (result == NOTOK)
666                                                 result = inneresult;
667                                 }
668                                 continue;
669                         }
670                         break;
671                 }
672         }
673
674         if (alternating && !part) {
675                 if (!alternate)
676                         content_error(NULL, ct, "don't know how to display any of the contents");
677                 result = NOTOK;
678                 goto out;
679         }
680 out:
681         return result;
682 }
683
684
685 /*
686 ** Parse display string for multipart content
687 ** and use external program to display it.
688 */
689
690 static int
691 show_multi_aux(CT ct, int alternate, char *cp)
692 {
693         int len, buflen, quoted;
694         int xlist, xtty;
695         char *bp, *pp, *file, buffer[BUFSIZ];
696         struct multipart *m = (struct multipart *) ct->c_ctparams;
697         struct part *part;
698         CI ci = &ct->c_ctinfo;
699         CT p;
700
701         for (part = m->mp_parts; part; part = part->mp_next) {
702                 p = part->mp_part;
703
704                 if (!p->c_ceopenfnx) {
705                         if (!alternate)
706                                 content_error(NULL, p, "don't know how to decode content");
707                         return NOTOK;
708                 }
709
710                 if (p->c_storage == NULL) {
711                         file = NULL;
712                         if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
713                                 return NOTOK;
714
715                         /* I'm not sure if this is necessary? */
716                         p->c_storage = getcpy(file);
717
718                         if (p->c_showproc && strcmp(p->c_showproc, "true")==0)
719                                 return (alternate ? DONE : OK);
720                         (*p->c_ceclosefnx) (p);
721                 }
722         }
723
724         xlist = 0;
725         xtty = 0;
726
727         /* get buffer ready to go */
728         bp = buffer;
729         buflen = sizeof(buffer) - 1;
730         bp[0] = bp[buflen] = '\0';
731         quoted = 0;
732
733         /* Now parse display string */
734         for ( ; *cp && buflen > 0; cp++) {
735                 if (*cp == '%') {
736                         pp = bp;
737                         switch (*++cp) {
738                         case 'a':
739                                 /* insert parameters from Content-Type field */
740                                 {
741                                 char **ap, **ep;
742                                 char *s = "";
743
744                                 for (ap = ci->ci_attrs, ep = ci->ci_values;
745                                                 *ap; ap++, ep++) {
746                                         snprintf(bp, buflen, "%s%s=\"%s\"",
747                                                         s, *ap, *ep);
748                                         len = strlen(bp);
749                                         bp += len;
750                                         buflen -= len;
751                                         s = " ";
752                                 }
753                                 }
754                                 break;
755
756                         case 'c':
757                                 /* insert charset */
758                                 if (ct->c_charset) {
759                                         strncpy(bp, ct->c_charset, buflen);
760                                 }
761                                 break;
762
763                         case 'd':
764                                 /* insert content description */
765                                 if (ct->c_descr) {
766                                         char *s;
767
768                                         s = trimcpy(ct->c_descr);
769                                         strncpy(bp, s, buflen);
770                                         free(s);
771                                 }
772                                 break;
773
774                         case 'e':
775                                 /* exclusive execution */
776                                 xtty = 1;
777                                 break;
778
779                         case 'F':
780                                 /* %e and %f */
781                                 xtty = 1;
782                                 /* and fall... */
783
784                         case 'f':
785                                 /* insert filename(s) containing content */
786                         {
787                                 char *s = "";
788
789                                 for (part = m->mp_parts; part;
790                                                 part = part->mp_next) {
791                                         p = part->mp_part;
792
793                                         snprintf(bp, buflen, "%s'%s'",
794                                                         s, p->c_storage);
795                                         len = strlen(bp);
796                                         bp += len;
797                                         buflen -= len;
798                                         s = " ";
799                                 }
800                                 /*
801                                 ** set our starting pointer back to bp,
802                                 ** to avoid requoting the filenames we
803                                 ** just added
804                                 */
805                                 pp = bp;
806                         }
807                         break;
808
809                         case 'p':
810                         case 'l':
811                                 /*
812                                 ** display listing prior to displaying content
813                                 */
814                                 xlist = !nolist;
815                                 break;
816
817                         case 's':
818                                 /* insert subtype of content */
819                                 strncpy(bp, ci->ci_subtype, buflen);
820                                 break;
821
822                         case '%':
823                                 /* insert character % */
824                                 goto raw;
825
826                         default:
827                                 *bp++ = *--cp;
828                                 *bp = '\0';
829                                 buflen--;
830                                 continue;
831                         }
832                         len = strlen(bp);
833                         bp += len;
834                         buflen -= len;
835
836                         /* Did we actually insert something? */
837                         if (bp != pp) {
838                                 /*
839                                 ** Insert single quote if not inside quotes
840                                 ** already
841                                 */
842                                 if (!quoted && buflen) {
843                                         len = strlen(pp);
844                                         memmove(pp + 1, pp, len);
845                                         *pp++ = '\'';
846                                         buflen--;
847                                         bp++;
848                                 }
849                                 /* Escape existing quotes */
850                                 while ((pp = strchr(pp, '\'')) && buflen > 3) {
851                                         len = strlen(pp++);
852                                         memmove(pp + 3, pp, len);
853                                         *pp++ = '\\';
854                                         *pp++ = '\'';
855                                         *pp++ = '\'';
856                                         buflen -= 3;
857                                         bp += 3;
858                                 }
859                                 /*
860                                 ** If pp is still set, that means we ran
861                                 ** out of space.
862                                 */
863                                 if (pp)
864                                         buflen = 0;
865                                 if (!quoted && buflen) {
866                                         *bp++ = '\'';
867                                         *bp = '\0';
868                                         buflen--;
869                                 }
870                         }
871                 } else {
872 raw:
873                         *bp++ = *cp;
874                         *bp = '\0';
875                         buflen--;
876
877                         if (*cp == '\'')
878                                 quoted = !quoted;
879                 }
880         }
881
882         return show_content_aux2(ct, alternate, NULL, buffer,
883                         NOTOK, xlist, 0, xtty);
884 }
885
886
887 /*
888 ** show content of type "message/rfc822"
889 */
890
891 static int
892 show_message_rfc822(CT ct, int alternate)
893 {
894         char *cp, buffer[BUFSIZ];
895         CI ci = &ct->c_ctinfo;
896
897         /* Check for mhshow-show-type/subtype */
898         snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
899                                 invo_name, ci->ci_type, ci->ci_subtype);
900         if ((cp = context_find(buffer)) && *cp != '\0')
901                 return show_content_aux(ct, alternate, cp, NULL);
902
903         /* Check for mhshow-show-type */
904         snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
905         if ((cp = context_find(buffer)) && *cp != '\0')
906                 return show_content_aux(ct, alternate, cp, NULL);
907
908         if ((cp = ct->c_showproc))
909                 return show_content_aux(ct, alternate, cp, NULL);
910
911         /* default method for message/rfc822 */
912         if (ct->c_subtype == MESSAGE_RFC822) {
913                 cp = (ct->c_showproc = getcpy("%pshow -file '%F'"));
914                 return show_content_aux(ct, alternate, cp, NULL);
915         }
916
917         /* complain if we are not a part of a multipart/alternative */
918         if (!alternate)
919                 content_error(NULL, ct, "don't know how to display content");
920
921         return NOTOK;
922 }
923
924
925 /*
926 ** Show content of type "message/partial".
927 */
928
929 static int
930 show_partial(CT ct, int alternate)
931 {
932         content_error(NULL, ct,
933                 "in order to display this message, you must reassemble it");
934         return NOTOK;
935 }
936
937
938 /*
939 ** Show how to retrieve content of type "message/external".
940 */
941 static int
942 show_external(CT ct, int alternate)
943 {
944         char **ap, **ep;
945         char *msg;
946         FILE *fp;
947         char buf[BUFSIZ];
948
949         msg = add("You need to fetch the contents yourself:", NULL);
950         ap = ct->c_ctinfo.ci_attrs;
951         ep = ct->c_ctinfo.ci_values;
952         for (; *ap; ap++, ep++) {
953                 msg = add(concat("\n\t", *ap, ": ", *ep, NULL), msg);
954         }
955         if (!(fp = fopen(ct->c_file, "r"))) {
956                 adios(ct->c_file, "unable to open");
957         }
958         fseek(fp, ct->c_begin, SEEK_SET);
959         while (!feof(fp) && ftell(fp) < ct->c_end) {
960                 if (!fgets(buf, sizeof buf, fp)) {
961                         adios(ct->c_file, "unable to read");
962                 }
963                 *strchr(buf, '\n') = '\0';
964                 msg = add(concat("\n\t", buf, NULL), msg);
965         }
966         fclose(fp);
967         content_error(NULL, ct, msg);
968         return OK;
969 }