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