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