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