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