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