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