]> diplodocus.org Git - nmh/blob - uip/mhshowsbr.c
Added associations to mhn.defaults.sh from
[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 <setjmp.h>
15 #include <h/mts.h>
16 #include <h/tws.h>
17 #include <h/mime.h>
18 #include <h/mhparse.h>
19 #include <h/utils.h>
20 #ifdef HAVE_ICONV
21 # include <iconv.h>
22 #endif /* ! HAVE_ICONV */
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 /* mhmisc.c */
42 int part_ok (CT, int);
43 int type_ok (CT, int);
44 void content_error (char *, CT, char *, ...);
45 void flush_errors (void);
46
47 /* mhlistsbr.c */
48 int list_switch (CT, int, int, int, int);
49 int list_content (CT, int, int, int, int);
50
51 /*
52 * prototypes
53 */
54 void show_all_messages (CT *);
55 int show_content_aux (CT, int, int, char *, char *);
56
57 /*
58 * static prototypes
59 */
60 static void show_single_message (CT, char *);
61 static void DisplayMsgHeader (CT, char *);
62 static int show_switch (CT, int, int);
63 static int show_content (CT, int, int);
64 static int show_content_aux2 (CT, int, int, char *, char *, int, int, int, int, int);
65 static int show_text (CT, int, int);
66 static int show_multi (CT, int, int);
67 static int show_multi_internal (CT, int, int);
68 static int show_multi_aux (CT, int, int, char *);
69 static int show_message_rfc822 (CT, int, int);
70 static int show_partial (CT, int, int);
71 static int show_external (CT, int, int);
72 static int convert_content_charset (CT, char **);
73 static void intrser (int);
74
75
76 /*
77 * Top level entry point to show/display a group of messages
78 */
79
80 void
81 show_all_messages (CT *cts)
82 {
83 CT ct, *ctp;
84
85 /*
86 * If form is not specified, then get default form
87 * for showing headers of MIME messages.
88 */
89 if (!formsw)
90 formsw = getcpy (etcpath ("mhl.headers"));
91
92 /*
93 * If form is "mhl.null", suppress display of header.
94 */
95 if (!strcmp (formsw, "mhl.null"))
96 formsw = NULL;
97
98 for (ctp = cts; *ctp; ctp++) {
99 ct = *ctp;
100
101 /* if top-level type is ok, then display message */
102 if (type_ok (ct, 1))
103 show_single_message (ct, formsw);
104 }
105 }
106
107
108 /*
109 * Entry point to show/display a single message
110 */
111
112 static void
113 show_single_message (CT ct, char *form)
114 {
115 sigset_t set, oset;
116
117 int status;
118
119 /* Allow user executable bit so that temporary directories created by
120 * the viewer (e.g., lynx) are going to be accessible */
121 umask (ct->c_umask & ~(0100));
122
123 /*
124 * If you have a format file, then display
125 * the message headers.
126 */
127 if (form)
128 DisplayMsgHeader(ct, form);
129 else
130 xpid = 0;
131
132 /* Show the body of the message */
133 show_switch (ct, 1, 0);
134
135 if (ct->c_fp) {
136 fclose (ct->c_fp);
137 ct->c_fp = NULL;
138 }
139 if (ct->c_ceclosefnx)
140 (*ct->c_ceclosefnx) (ct);
141
142 /* block a few signals */
143 sigemptyset (&set);
144 sigaddset (&set, SIGHUP);
145 sigaddset (&set, SIGINT);
146 sigaddset (&set, SIGQUIT);
147 sigaddset (&set, SIGTERM);
148 sigprocmask (SIG_BLOCK, &set, &oset);
149
150 while (wait (&status) != NOTOK) {
151 pidcheck (status);
152 continue;
153 }
154
155 /* reset the signal mask */
156 sigprocmask (SIG_SETMASK, &oset, &set);
157
158 xpid = 0;
159 flush_errors ();
160 }
161
162
163 /*
164 * Use the mhlproc to show the header fields
165 */
166
167 static void
168 DisplayMsgHeader (CT ct, char *form)
169 {
170 pid_t child_id;
171 int i, vecp;
172 char **vec;
173 char *file;
174
175 vec = argsplit(mhlproc, &file, &vecp);
176 vec[vecp++] = getcpy("-form");
177 vec[vecp++] = getcpy(form);
178 vec[vecp++] = getcpy("-nobody");
179 vec[vecp++] = getcpy(ct->c_file);
180
181 /*
182 * If we've specified -(no)moreproc,
183 * then just pass that along.
184 */
185 if (nomore) {
186 vec[vecp++] = getcpy("-nomoreproc");
187 } else if (progsw) {
188 vec[vecp++] = getcpy("-moreproc");
189 vec[vecp++] = getcpy(progsw);
190 }
191 vec[vecp] = NULL;
192
193 fflush (stdout);
194
195 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
196 sleep (5);
197
198 switch (child_id) {
199 case NOTOK:
200 adios ("fork", "unable to");
201 /* NOTREACHED */
202
203 case OK:
204 execvp (file, vec);
205 fprintf (stderr, "unable to exec ");
206 perror (mhlproc);
207 _exit (-1);
208 /* NOTREACHED */
209
210 default:
211 xpid = -child_id;
212 break;
213 }
214
215 arglist_free(file, vec);
216 }
217
218
219 /*
220 * Switching routine. Call the correct routine
221 * based on content type.
222 */
223
224 static int
225 show_switch (CT ct, int serial, int alternate)
226 {
227 switch (ct->c_type) {
228 case CT_MULTIPART:
229 return show_multi (ct, serial, alternate);
230
231 case CT_MESSAGE:
232 switch (ct->c_subtype) {
233 case MESSAGE_PARTIAL:
234 return show_partial (ct, serial, alternate);
235
236 case MESSAGE_EXTERNAL:
237 return show_external (ct, serial, alternate);
238
239 case MESSAGE_RFC822:
240 default:
241 return show_message_rfc822 (ct, serial, alternate);
242 }
243
244 case CT_TEXT:
245 return show_text (ct, serial, alternate);
246
247 case CT_AUDIO:
248 case CT_IMAGE:
249 case CT_VIDEO:
250 case CT_APPLICATION:
251 return show_content (ct, serial, alternate);
252
253 default:
254 adios (NULL, "unknown content type %d", ct->c_type);
255 }
256
257 return 0; /* NOT REACHED */
258 }
259
260
261 /*
262 * Generic method for displaying content
263 */
264
265 static int
266 show_content (CT ct, int serial, int alternate)
267 {
268 char *cp, buffer[BUFSIZ];
269 CI ci = &ct->c_ctinfo;
270
271 /* Check for invo_name-show-type/subtype */
272 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
273 invo_name, ci->ci_type, ci->ci_subtype);
274 if ((cp = context_find (buffer)) && *cp != '\0')
275 return show_content_aux (ct, serial, alternate, cp, NULL);
276
277 /* Check for invo_name-show-type */
278 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
279 if ((cp = context_find (buffer)) && *cp != '\0')
280 return show_content_aux (ct, serial, alternate, cp, NULL);
281
282 if ((cp = ct->c_showproc))
283 return show_content_aux (ct, serial, alternate, cp, NULL);
284
285 /* complain if we are not a part of a multipart/alternative */
286 if (!alternate)
287 content_error (NULL, ct, "don't know how to display content");
288
289 return NOTOK;
290 }
291
292
293 /*
294 * Parse the display string for displaying generic content
295 */
296
297 int
298 show_content_aux (CT ct, int serial, int alternate, char *cp, char *cracked)
299 {
300 int fd, len, buflen, quoted;
301 int xstdin, xlist, xpause, xtty;
302 char *bp, *pp, *file, buffer[BUFSIZ];
303 CI ci = &ct->c_ctinfo;
304
305 if (!ct->c_ceopenfnx) {
306 if (!alternate)
307 content_error (NULL, ct, "don't know how to decode content");
308
309 return NOTOK;
310 }
311
312 file = NULL;
313 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
314 return NOTOK;
315 if (ct->c_showproc && !strcmp (ct->c_showproc, "true"))
316 return (alternate ? DONE : OK);
317
318 if (! strcmp(invo_name, "mhshow") &&
319 ct->c_type == CT_TEXT && ct->c_subtype == TEXT_PLAIN) {
320 /* This has to be done after calling c_ceopenfnx, so
321 unfortunately the type checks are necessary without
322 some code rearrangement. And to make this really ugly,
323 only do it in mhshow, not mhfixmsg, mhn, or mhstore. */
324 if (convert_content_charset (ct, &file) != OK) {
325 admonish (NULL, "unable to convert character set%s to %s",
326 ct->c_partno ? "of part " : "",
327 ct->c_partno ? ct->c_partno : "",
328 content_charset (ct));
329 }
330 }
331
332 xlist = 0;
333 xpause = 0;
334 xstdin = 0;
335 xtty = 0;
336
337 if (cracked) {
338 strncpy (buffer, cp, sizeof(buffer));
339 goto got_command;
340 }
341
342 /* get buffer ready to go */
343 bp = buffer;
344 buflen = sizeof(buffer) - 1;
345 bp[0] = bp[buflen] = '\0';
346 quoted = 0;
347
348 /* Now parse display string */
349 for ( ; *cp && buflen > 0; cp++) {
350 if (*cp == '%') {
351 pp = bp;
352
353 switch (*++cp) {
354 case 'a':
355 /* insert parameters from Content-Type field */
356 {
357 char **ap, **ep;
358 char *s = "";
359
360 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
361 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
362 len = strlen (bp);
363 bp += len;
364 buflen -= len;
365 s = " ";
366 }
367 }
368 break;
369
370 case 'd':
371 /* insert content description */
372 if (ct->c_descr) {
373 char *s;
374
375 s = trimcpy (ct->c_descr);
376 strncpy (bp, s, buflen);
377 free (s);
378 }
379 break;
380
381 case 'e':
382 /* exclusive execution */
383 xtty = 1;
384 break;
385
386 case 'F':
387 /* %e, %f, and stdin is terminal not content */
388 xstdin = 1;
389 xtty = 1;
390 /* and fall... */
391
392 case 'f':
393 /* insert filename containing content */
394 snprintf (bp, buflen, "'%s'", file);
395 /* since we've quoted the file argument, set things up
396 * to look past it, to avoid problems with the quoting
397 * logic below. (I know, I should figure out what's
398 * broken with the quoting logic, but..)
399 */
400 len = strlen(bp);
401 buflen -= len;
402 bp += len;
403 pp = bp;
404 break;
405
406 case 'p':
407 /* %l, and pause prior to displaying content */
408 xpause = pausesw;
409 /* and fall... */
410
411 case 'l':
412 /* display listing prior to displaying content */
413 xlist = !nolist;
414 break;
415
416 case 's':
417 /* insert subtype of content */
418 strncpy (bp, ci->ci_subtype, buflen);
419 break;
420
421 case '%':
422 /* insert character % */
423 goto raw;
424
425 default:
426 *bp++ = *--cp;
427 *bp = '\0';
428 buflen--;
429 continue;
430 }
431 len = strlen (bp);
432 bp += len;
433 buflen -= len;
434
435 /* Did we actually insert something? */
436 if (bp != pp) {
437 /* Insert single quote if not inside quotes already */
438 if (!quoted && buflen) {
439 len = strlen (pp);
440 memmove (pp + 1, pp, len);
441 *pp++ = '\'';
442 buflen--;
443 bp++;
444 }
445 /* Escape existing quotes */
446 while ((pp = strchr (pp, '\'')) && buflen > 3) {
447 len = strlen (pp++);
448 memmove (pp + 3, pp, len);
449 *pp++ = '\\';
450 *pp++ = '\'';
451 *pp++ = '\'';
452 buflen -= 3;
453 bp += 3;
454 }
455 /* If pp is still set, that means we ran out of space. */
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 ||
476 (ct->c_termproc && (size_t) buflen <= strlen(ct->c_termproc))) {
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, xpause, 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, char *buffer,
505 int fd, int xlist, int xpause, int xstdin, int xtty)
506 {
507 pid_t child_id;
508 int i;
509 char *vec[4], exec[BUFSIZ + sizeof "exec "];
510
511 if (debugsw || cracked) {
512 fflush (stdout);
513
514 fprintf (stderr, "%s msg %s", cracked ? "storing" : "show",
515 ct->c_file);
516 if (ct->c_partno)
517 fprintf (stderr, " part %s", ct->c_partno);
518 if (cracked)
519 fprintf (stderr, " using command (cd %s; %s)\n", 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 char prompt[BUFSIZ];
533
534 if (ct->c_type == CT_MULTIPART)
535 list_content (ct, -1, 1, 0, 0);
536 else
537 list_switch (ct, -1, 1, 0, 0);
538
539 if (xpause && isatty (fileno (stdout))) {
540 int intr;
541 SIGNAL_HANDLER istat;
542
543 if (SOprintf ("Press <return> to show content..."))
544 printf ("Press <return> to show content...");
545
546 istat = SIGNAL (SIGINT, intrser);
547 if ((intr = sigsetjmp (intrenv, 1)) == OK) {
548 fflush (stdout);
549 prompt[0] = 0;
550 read (fileno (stdout), prompt, sizeof(prompt));
551 }
552 SIGNAL (SIGINT, istat);
553 if (intr != OK || prompt[0] == 'n') {
554 (*ct->c_ceclosefnx) (ct);
555 return (alternate ? DONE : NOTOK);
556 }
557 if (prompt[0] == 'q') done(OK);
558 }
559 }
560
561 snprintf (exec, sizeof(exec), "exec %s", buffer);
562
563 vec[0] = "/bin/sh";
564 vec[1] = "-c";
565 vec[2] = exec;
566 vec[3] = NULL;
567
568 fflush (stdout);
569
570 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
571 sleep (5);
572 switch (child_id) {
573 case NOTOK:
574 advise ("fork", "unable to");
575 (*ct->c_ceclosefnx) (ct);
576 return NOTOK;
577
578 case OK:
579 if (cracked)
580 chdir (cracked);
581 if (!xstdin)
582 dup2 (fd, 0);
583 close (fd);
584 execvp ("/bin/sh", vec);
585 fprintf (stderr, "unable to exec ");
586 perror ("/bin/sh");
587 _exit (-1);
588 /* NOTREACHED */
589
590 default:
591 if (!serial) {
592 ct->c_pid = child_id;
593 if (xtty)
594 xpid = child_id;
595 } else {
596 pidcheck (pidXwait (child_id, NULL));
597 }
598
599 if (fd != NOTOK)
600 (*ct->c_ceclosefnx) (ct);
601 return (alternate ? DONE : OK);
602 }
603 }
604
605
606 /*
607 * show content of type "text"
608 */
609
610 static int
611 show_text (CT ct, int serial, int alternate)
612 {
613 char *cp, buffer[BUFSIZ];
614 CI ci = &ct->c_ctinfo;
615
616 /* Check for invo_name-show-type/subtype */
617 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
618 invo_name, ci->ci_type, ci->ci_subtype);
619 if ((cp = context_find (buffer)) && *cp != '\0')
620 return show_content_aux (ct, serial, alternate, cp, NULL);
621
622 /* Check for invo_name-show-type */
623 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
624 if ((cp = context_find (buffer)) && *cp != '\0')
625 return show_content_aux (ct, serial, alternate, cp, NULL);
626
627 /*
628 * Use default method if content is text/plain, or if
629 * if it is not a text part of a multipart/alternative
630 */
631 if (!alternate || ct->c_subtype == TEXT_PLAIN) {
632 snprintf (buffer, sizeof(buffer), "%%p%s '%%F'", progsw ? progsw :
633 moreproc && *moreproc ? moreproc : DEFAULT_PAGER);
634 cp = (ct->c_showproc = add (buffer, NULL));
635 return show_content_aux (ct, serial, alternate, cp, NULL);
636 }
637
638 return NOTOK;
639 }
640
641
642 /*
643 * show message body of type "multipart"
644 */
645
646 static int
647 show_multi (CT ct, int serial, int alternate)
648 {
649 char *cp, buffer[BUFSIZ];
650 CI ci = &ct->c_ctinfo;
651
652 /* Check for invo_name-show-type/subtype */
653 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
654 invo_name, ci->ci_type, ci->ci_subtype);
655 if ((cp = context_find (buffer)) && *cp != '\0')
656 return show_multi_aux (ct, serial, alternate, cp);
657
658 /* Check for invo_name-show-type */
659 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
660 if ((cp = context_find (buffer)) && *cp != '\0')
661 return show_multi_aux (ct, serial, alternate, cp);
662
663 if ((cp = ct->c_showproc))
664 return show_multi_aux (ct, serial, alternate, cp);
665
666 /*
667 * Use default method to display this multipart content. Even
668 * unknown types are displayable, since they're treated as mixed
669 * per RFC 2046.
670 */
671 return show_multi_internal (ct, serial, alternate);
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 && buflen <= (ssize_t) 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 invo_name-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 invo_name-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
1083
1084 int
1085 convert_charset (CT ct, char *dest_charset, int *message_mods) {
1086 char *src_charset = content_charset (ct);
1087 int status = OK;
1088
1089 /* norm_charmap() is case sensitive. */
1090 char *src_charset_u = upcase (src_charset);
1091 char *dest_charset_u = upcase (dest_charset);
1092 int different_charsets =
1093 strcmp (norm_charmap (src_charset), norm_charmap (dest_charset));
1094
1095 free (dest_charset_u);
1096 free (src_charset_u);
1097
1098 if (different_charsets) {
1099 #ifdef HAVE_ICONV
1100 iconv_t conv_desc = NULL;
1101 char *dest;
1102 int fd = -1;
1103 char **file = NULL;
1104 FILE **fp = NULL;
1105 size_t begin;
1106 size_t end;
1107 int opened_input_file = 0;
1108 char src_buffer[BUFSIZ];
1109 HF hf;
1110 char *tempfile;
1111
1112 if ((conv_desc = iconv_open (dest_charset, src_charset)) ==
1113 (iconv_t) -1) {
1114 advise (NULL, "Can't convert %s to %s", src_charset, dest_charset);
1115 return NOTOK;
1116 }
1117
1118 if ((tempfile = m_mktemp2 (NULL, invo_name, &fd, NULL)) == NULL) {
1119 adios (NULL, "unable to create temporary file in %s",
1120 get_temp_dir());
1121 }
1122 dest = add (tempfile, NULL);
1123
1124 if (ct->c_cefile.ce_file) {
1125 file = &ct->c_cefile.ce_file;
1126 fp = &ct->c_cefile.ce_fp;
1127 begin = end = 0;
1128 } else if (ct->c_file) {
1129 file = &ct->c_file;
1130 fp = &ct->c_fp;
1131 begin = (size_t) ct->c_begin;
1132 end = (size_t) ct->c_end;
1133 } /* else no input file: shouldn't happen */
1134
1135 if (file && *file && fp) {
1136 if (! *fp) {
1137 if ((*fp = fopen (*file, "r")) == NULL) {
1138 advise (*file, "unable to open for reading");
1139 status = NOTOK;
1140 } else {
1141 opened_input_file = 1;
1142 }
1143 }
1144 }
1145
1146 if (fp && *fp) {
1147 size_t inbytes;
1148 size_t bytes_to_read =
1149 end > 0 && end > begin ? end - begin : sizeof src_buffer;
1150
1151 fseeko (*fp, begin, SEEK_SET);
1152 while ((inbytes = fread (src_buffer, 1,
1153 min (bytes_to_read, sizeof src_buffer),
1154 *fp)) > 0) {
1155 char dest_buffer[BUFSIZ];
1156 ICONV_CONST char *ib = src_buffer;
1157 char *ob = dest_buffer;
1158 size_t outbytes = sizeof dest_buffer;
1159 size_t outbytes_before = outbytes;
1160
1161 if (end > 0) bytes_to_read -= inbytes;
1162
1163 if (iconv (conv_desc, &ib, &inbytes, &ob, &outbytes) ==
1164 (size_t) -1) {
1165 status = NOTOK;
1166 break;
1167 } else {
1168 write (fd, dest_buffer, outbytes_before - outbytes);
1169 }
1170 }
1171
1172 if (opened_input_file) {
1173 fclose (*fp);
1174 *fp = NULL;
1175 }
1176 }
1177
1178 iconv_close (conv_desc);
1179 close (fd);
1180
1181 if (status == OK) {
1182 /* Replace the decoded file with the converted one. */
1183 if (ct->c_cefile.ce_file) {
1184 if (ct->c_cefile.ce_unlink) {
1185 (void) m_unlink (ct->c_cefile.ce_file);
1186 }
1187 free (ct->c_cefile.ce_file);
1188 }
1189 ct->c_cefile.ce_file = dest;
1190 ct->c_cefile.ce_unlink = 1;
1191
1192 ++*message_mods;
1193
1194 /* Update ci_attrs. */
1195 src_charset = dest_charset;
1196
1197 /* Update ct->c_ctline. */
1198 if (ct->c_ctline) {
1199 char *ctline =
1200 update_attr (ct->c_ctline, "charset=", dest_charset);
1201
1202 free (ct->c_ctline);
1203 ct->c_ctline = ctline;
1204 } /* else no CT line, which is odd */
1205
1206 /* Update Content-Type header field. */
1207 for (hf = ct->c_first_hf; hf; hf = hf->next) {
1208 if (! strcasecmp (TYPE_FIELD, hf->name)) {
1209 char *ctline_less_newline =
1210 update_attr (hf->value, "charset=", dest_charset);
1211 char *ctline = concat (ctline_less_newline, "\n", NULL);
1212 free (ctline_less_newline);
1213
1214 free (hf->value);
1215 hf->value = ctline;
1216 break;
1217 }
1218 }
1219 } else {
1220 (void) m_unlink (dest);
1221 }
1222 #else /* ! HAVE_ICONV */
1223 NMH_UNUSED (message_mods);
1224
1225 advise (NULL, "Can't convert %s to %s without iconv", src_charset,
1226 dest_charset);
1227 status = NOTOK;
1228 #endif /* ! HAVE_ICONV */
1229 }
1230
1231 return status;
1232 }
1233
1234
1235 static int
1236 convert_content_charset (CT ct, char **file) {
1237 #ifdef HAVE_ICONV
1238 /* Using current locale, see if the content needs to be converted. */
1239
1240 /* content_charset() cannot return NULL. */
1241 char *charset = content_charset (ct);
1242
1243 if (! check_charset (charset, strlen (charset))) {
1244 int unused = 0;
1245
1246 if (convert_charset (ct, get_charset (), &unused) == 0) {
1247 *file = ct->c_cefile.ce_file;
1248 } else {
1249 return NOTOK;
1250 }
1251 }
1252 #endif /* ! HAVE_ICONV */
1253
1254 return OK;
1255 }
1256
1257
1258 static void
1259 intrser (int i)
1260 {
1261 NMH_UNUSED (i);
1262
1263 putchar ('\n');
1264 siglongjmp (intrenv, DONE);
1265 }