]> diplodocus.org Git - nmh/blob - uip/mhshowsbr.c
fa449772c09d97140e6459f7c3338334f1dcc885
[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 char *display_charset = NULL;
32
33 /* flags for moreproc/header display */
34 int nomore = 0;
35 char *formsw = NULL;
36
37 pid_t xpid = 0;
38
39 static sigjmp_buf intrenv;
40
41
42 /* mhmisc.c */
43 int part_ok (CT, int);
44 int type_ok (CT, int);
45 void content_error (char *, CT, char *, ...);
46 void flush_errors (void);
47
48 /* mhlistsbr.c */
49 int list_switch (CT, int, int, int, int);
50 int list_content (CT, int, int, int, int);
51
52 /*
53 * prototypes
54 */
55 void show_all_messages (CT *);
56 int show_content_aux (CT, int, int, char *, char *);
57
58 /*
59 * static prototypes
60 */
61 static void show_single_message (CT, char *);
62 static void DisplayMsgHeader (CT, char *);
63 static int show_switch (CT, int, int);
64 static int show_content (CT, int, int);
65 static int show_content_aux2 (CT, int, int, char *, char *, int, int, int, int, int);
66 static int show_text (CT, int, int);
67 static int show_multi (CT, int, int);
68 static int show_multi_internal (CT, int, int);
69 static int show_multi_aux (CT, int, int, char *);
70 static int show_message_rfc822 (CT, int, int);
71 static int show_partial (CT, int, int);
72 static int show_external (CT, int, int);
73 static int convert_content_charset (CT, char **);
74 static void intrser (int);
75
76
77 /*
78 * Top level entry point to show/display a group of messages
79 */
80
81 void
82 show_all_messages (CT *cts)
83 {
84 CT ct, *ctp;
85
86 /*
87 * If form is not specified, then get default form
88 * for showing headers of MIME messages.
89 */
90 if (!formsw)
91 formsw = getcpy (etcpath ("mhl.headers"));
92
93 /*
94 * If form is "mhl.null", suppress display of header.
95 */
96 if (!strcmp (formsw, "mhl.null"))
97 formsw = NULL;
98
99 for (ctp = cts; *ctp; ctp++) {
100 ct = *ctp;
101
102 /* if top-level type is ok, then display message */
103 if (type_ok (ct, 1))
104 show_single_message (ct, formsw);
105 }
106 }
107
108
109 /*
110 * Entry point to show/display a single message
111 */
112
113 static void
114 show_single_message (CT ct, char *form)
115 {
116 sigset_t set, oset;
117
118 int status;
119
120 /* Allow user executable bit so that temporary directories created by
121 * the viewer (e.g., lynx) are going to be accessible */
122 umask (ct->c_umask & ~(0100));
123
124 /*
125 * If you have a format file, then display
126 * the message headers.
127 */
128 if (form)
129 DisplayMsgHeader(ct, form);
130 else
131 xpid = 0;
132
133 /* Show the body of the message */
134 show_switch (ct, 1, 0);
135
136 if (ct->c_fp) {
137 fclose (ct->c_fp);
138 ct->c_fp = NULL;
139 }
140 if (ct->c_ceclosefnx)
141 (*ct->c_ceclosefnx) (ct);
142
143 /* block a few signals */
144 sigemptyset (&set);
145 sigaddset (&set, SIGHUP);
146 sigaddset (&set, SIGINT);
147 sigaddset (&set, SIGQUIT);
148 sigaddset (&set, SIGTERM);
149 sigprocmask (SIG_BLOCK, &set, &oset);
150
151 while (wait (&status) != NOTOK) {
152 pidcheck (status);
153 continue;
154 }
155
156 /* reset the signal mask */
157 sigprocmask (SIG_SETMASK, &oset, &set);
158
159 xpid = 0;
160 flush_errors ();
161 }
162
163
164 /*
165 * Use the mhlproc to show the header fields
166 */
167
168 static void
169 DisplayMsgHeader (CT ct, char *form)
170 {
171 pid_t child_id;
172 int i, vecp;
173 char **vec;
174 char *file;
175
176 vec = argsplit(mhlproc, &file, &vecp);
177 vec[vecp++] = getcpy("-form");
178 vec[vecp++] = getcpy(form);
179 vec[vecp++] = getcpy("-nobody");
180 vec[vecp++] = getcpy(ct->c_file);
181
182 /*
183 * If we've specified -(no)moreproc,
184 * then just pass that along.
185 */
186 if (nomore) {
187 vec[vecp++] = getcpy("-nomoreproc");
188 } else if (progsw) {
189 vec[vecp++] = getcpy("-moreproc");
190 vec[vecp++] = getcpy(progsw);
191 }
192 vec[vecp] = NULL;
193
194 fflush (stdout);
195
196 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
197 sleep (5);
198
199 switch (child_id) {
200 case NOTOK:
201 adios ("fork", "unable to");
202 /* NOTREACHED */
203
204 case OK:
205 execvp (file, vec);
206 fprintf (stderr, "unable to exec ");
207 perror (mhlproc);
208 _exit (-1);
209 /* NOTREACHED */
210
211 default:
212 xpid = -child_id;
213 break;
214 }
215
216 arglist_free(file, vec);
217 }
218
219
220 /*
221 * Switching routine. Call the correct routine
222 * based on content type.
223 */
224
225 static int
226 show_switch (CT ct, int serial, int alternate)
227 {
228 switch (ct->c_type) {
229 case CT_MULTIPART:
230 return show_multi (ct, serial, alternate);
231
232 case CT_MESSAGE:
233 switch (ct->c_subtype) {
234 case MESSAGE_PARTIAL:
235 return show_partial (ct, serial, alternate);
236
237 case MESSAGE_EXTERNAL:
238 return show_external (ct, serial, alternate);
239
240 case MESSAGE_RFC822:
241 default:
242 return show_message_rfc822 (ct, serial, alternate);
243 }
244
245 case CT_TEXT:
246 return show_text (ct, serial, alternate);
247
248 case CT_AUDIO:
249 case CT_IMAGE:
250 case CT_VIDEO:
251 case CT_APPLICATION:
252 return show_content (ct, serial, alternate);
253
254 default:
255 adios (NULL, "unknown content type %d", ct->c_type);
256 }
257
258 return 0; /* NOT REACHED */
259 }
260
261
262 /*
263 * Generic method for displaying content
264 */
265
266 static int
267 show_content (CT ct, int serial, int alternate)
268 {
269 char *cp, buffer[BUFSIZ];
270 CI ci = &ct->c_ctinfo;
271
272 /* Check for invo_name-show-type/subtype */
273 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
274 invo_name, ci->ci_type, ci->ci_subtype);
275 if ((cp = context_find (buffer)) && *cp != '\0')
276 return show_content_aux (ct, serial, alternate, cp, NULL);
277
278 /* Check for invo_name-show-type */
279 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
280 if ((cp = context_find (buffer)) && *cp != '\0')
281 return show_content_aux (ct, serial, alternate, cp, NULL);
282
283 if ((cp = ct->c_showproc))
284 return show_content_aux (ct, serial, alternate, cp, NULL);
285
286 /* complain if we are not a part of a multipart/alternative */
287 if (!alternate)
288 content_error (NULL, ct, "don't know how to display content");
289
290 return NOTOK;
291 }
292
293
294 /*
295 * Parse the display string for displaying generic content
296 */
297
298 int
299 show_content_aux (CT ct, int serial, int alternate, char *cp, char *cracked)
300 {
301 int fd, len, buflen, quoted;
302 int xstdin, xlist, xpause, xtty;
303 char *bp, *pp, *file, buffer[BUFSIZ];
304 CI ci = &ct->c_ctinfo;
305
306 if (!ct->c_ceopenfnx) {
307 if (!alternate)
308 content_error (NULL, ct, "don't know how to decode content");
309
310 return NOTOK;
311 }
312
313 file = NULL;
314 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
315 return NOTOK;
316 if (ct->c_showproc && !strcmp (ct->c_showproc, "true"))
317 return (alternate ? DONE : OK);
318
319 if (! strcmp(invo_name, "mhshow") &&
320 ct->c_type == CT_TEXT && ct->c_subtype == TEXT_PLAIN) {
321 /* This has to be done after calling c_ceopenfnx, so
322 unfortunately the type checks are necessary without
323 some code rearrangement. And to make this really ugly,
324 only do it in mhshow, not mhfixmsg, mhn, or mhstore. */
325 if (convert_content_charset (ct, &file) != OK) {
326 return NOTOK;
327 }
328 }
329
330 xlist = 0;
331 xpause = 0;
332 xstdin = 0;
333 xtty = 0;
334
335 if (cracked) {
336 strncpy (buffer, cp, sizeof(buffer));
337 goto got_command;
338 }
339
340 /* get buffer ready to go */
341 bp = buffer;
342 buflen = sizeof(buffer) - 1;
343 bp[0] = bp[buflen] = '\0';
344 quoted = 0;
345
346 /* Now parse display string */
347 for ( ; *cp && buflen > 0; cp++) {
348 if (*cp == '%') {
349 pp = bp;
350
351 switch (*++cp) {
352 case 'a':
353 /* insert parameters from Content-Type field */
354 {
355 char **ap, **ep;
356 char *s = "";
357
358 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
359 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
360 len = strlen (bp);
361 bp += len;
362 buflen -= len;
363 s = " ";
364 }
365 }
366 break;
367
368 case 'd':
369 /* insert content description */
370 if (ct->c_descr) {
371 char *s;
372
373 s = trimcpy (ct->c_descr);
374 strncpy (bp, s, buflen);
375 free (s);
376 }
377 break;
378
379 case 'e':
380 /* exclusive execution */
381 xtty = 1;
382 break;
383
384 case 'F':
385 /* %e, %f, and stdin is terminal not content */
386 xstdin = 1;
387 xtty = 1;
388 /* and fall... */
389
390 case 'f':
391 /* insert filename containing content */
392 snprintf (bp, buflen, "'%s'", file);
393 /* since we've quoted the file argument, set things up
394 * to look past it, to avoid problems with the quoting
395 * logic below. (I know, I should figure out what's
396 * broken with the quoting logic, but..)
397 */
398 len = strlen(bp);
399 buflen -= len;
400 bp += len;
401 pp = bp;
402 break;
403
404 case 'p':
405 /* %l, and pause prior to displaying content */
406 xpause = pausesw;
407 /* and fall... */
408
409 case 'l':
410 /* display listing prior to displaying content */
411 xlist = !nolist;
412 break;
413
414 case 's':
415 /* insert subtype of content */
416 strncpy (bp, ci->ci_subtype, buflen);
417 break;
418
419 case '%':
420 /* insert character % */
421 goto raw;
422
423 default:
424 *bp++ = *--cp;
425 *bp = '\0';
426 buflen--;
427 continue;
428 }
429 len = strlen (bp);
430 bp += len;
431 buflen -= len;
432
433 /* Did we actually insert something? */
434 if (bp != pp) {
435 /* Insert single quote if not inside quotes already */
436 if (!quoted && buflen) {
437 len = strlen (pp);
438 memmove (pp + 1, pp, len);
439 *pp++ = '\'';
440 buflen--;
441 bp++;
442 }
443 /* Escape existing quotes */
444 while ((pp = strchr (pp, '\'')) && buflen > 3) {
445 len = strlen (pp++);
446 memmove (pp + 3, pp, len);
447 *pp++ = '\\';
448 *pp++ = '\'';
449 *pp++ = '\'';
450 buflen -= 3;
451 bp += 3;
452 }
453 /* If pp is still set, that means we ran out of space. */
454 if (pp)
455 buflen = 0;
456 if (!quoted && buflen) {
457 *bp++ = '\'';
458 *bp = '\0';
459 buflen--;
460 }
461 }
462 } else {
463 raw:
464 *bp++ = *cp;
465 *bp = '\0';
466 buflen--;
467
468 if (*cp == '\'')
469 quoted = !quoted;
470 }
471 }
472
473 if (buflen <= 0 ||
474 (ct->c_termproc && (size_t) buflen <= strlen(ct->c_termproc))) {
475 /* content_error would provide a more useful error message
476 * here, except that if we got overrun, it probably would
477 * too.
478 */
479 fprintf(stderr, "Buffer overflow constructing show command!\n");
480 return NOTOK;
481 }
482
483 /* use charset string to modify display method */
484 if (ct->c_termproc) {
485 char term[BUFSIZ];
486
487 strncpy (term, buffer, sizeof(term));
488 snprintf (buffer, sizeof(buffer), ct->c_termproc, term);
489 }
490
491 got_command:
492 return show_content_aux2 (ct, serial, alternate, cracked, buffer,
493 fd, xlist, xpause, xstdin, xtty);
494 }
495
496
497 /*
498 * Routine to actually display the content
499 */
500
501 static int
502 show_content_aux2 (CT ct, int serial, int alternate, char *cracked, char *buffer,
503 int fd, int xlist, int xpause, int xstdin, int xtty)
504 {
505 pid_t child_id;
506 int i;
507 char *vec[4], exec[BUFSIZ + sizeof "exec "];
508
509 if (debugsw || cracked) {
510 fflush (stdout);
511
512 fprintf (stderr, "%s msg %s", cracked ? "storing" : "show",
513 ct->c_file);
514 if (ct->c_partno)
515 fprintf (stderr, " part %s", ct->c_partno);
516 if (cracked)
517 fprintf (stderr, " using command (cd %s; %s)\n", cracked, buffer);
518 else
519 fprintf (stderr, " using command %s\n", buffer);
520 }
521
522 if (xpid < 0 || (xtty && xpid)) {
523 if (xpid < 0)
524 xpid = -xpid;
525 pidcheck(pidwait (xpid, NOTOK));
526 xpid = 0;
527 }
528
529 if (xlist) {
530 char prompt[BUFSIZ];
531
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 if (xpause && isatty (fileno (stdout))) {
538 int intr;
539 SIGNAL_HANDLER istat;
540
541 if (SOprintf ("Press <return> to show content..."))
542 printf ("Press <return> to show content...");
543
544 istat = SIGNAL (SIGINT, intrser);
545 if ((intr = sigsetjmp (intrenv, 1)) == OK) {
546 fflush (stdout);
547 prompt[0] = 0;
548 read (fileno (stdout), prompt, sizeof(prompt));
549 }
550 SIGNAL (SIGINT, istat);
551 if (intr != OK || prompt[0] == 'n') {
552 (*ct->c_ceclosefnx) (ct);
553 return (alternate ? DONE : NOTOK);
554 }
555 if (prompt[0] == 'q') done(OK);
556 }
557 }
558
559 snprintf (exec, sizeof(exec), "exec %s", buffer);
560
561 vec[0] = "/bin/sh";
562 vec[1] = "-c";
563 vec[2] = exec;
564 vec[3] = NULL;
565
566 fflush (stdout);
567
568 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
569 sleep (5);
570 switch (child_id) {
571 case NOTOK:
572 advise ("fork", "unable to");
573 (*ct->c_ceclosefnx) (ct);
574 return NOTOK;
575
576 case OK:
577 if (cracked)
578 chdir (cracked);
579 if (!xstdin)
580 dup2 (fd, 0);
581 close (fd);
582 execvp ("/bin/sh", vec);
583 fprintf (stderr, "unable to exec ");
584 perror ("/bin/sh");
585 _exit (-1);
586 /* NOTREACHED */
587
588 default:
589 if (!serial) {
590 ct->c_pid = child_id;
591 if (xtty)
592 xpid = child_id;
593 } else {
594 pidcheck (pidXwait (child_id, NULL));
595 }
596
597 if (fd != NOTOK)
598 (*ct->c_ceclosefnx) (ct);
599 return (alternate ? DONE : OK);
600 }
601 }
602
603
604 /*
605 * show content of type "text"
606 */
607
608 static int
609 show_text (CT ct, int serial, int alternate)
610 {
611 char *cp, buffer[BUFSIZ];
612 CI ci = &ct->c_ctinfo;
613
614 /* Check for invo_name-show-type/subtype */
615 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
616 invo_name, ci->ci_type, ci->ci_subtype);
617 if ((cp = context_find (buffer)) && *cp != '\0')
618 return show_content_aux (ct, serial, alternate, cp, NULL);
619
620 /* Check for invo_name-show-type */
621 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
622 if ((cp = context_find (buffer)) && *cp != '\0')
623 return show_content_aux (ct, serial, alternate, cp, NULL);
624
625 /*
626 * Use default method if content is text/plain, or if
627 * if it is not a text part of a multipart/alternative
628 */
629 if (!alternate || ct->c_subtype == TEXT_PLAIN) {
630 snprintf (buffer, sizeof(buffer), "%%p%s '%%F'", progsw ? progsw :
631 moreproc && *moreproc ? moreproc : DEFAULT_PAGER);
632 cp = (ct->c_showproc = add (buffer, NULL));
633 return show_content_aux (ct, serial, alternate, cp, NULL);
634 }
635
636 return NOTOK;
637 }
638
639
640 /*
641 * show message body of type "multipart"
642 */
643
644 static int
645 show_multi (CT ct, int serial, int alternate)
646 {
647 char *cp, buffer[BUFSIZ];
648 CI ci = &ct->c_ctinfo;
649
650 /* Check for invo_name-show-type/subtype */
651 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
652 invo_name, ci->ci_type, ci->ci_subtype);
653 if ((cp = context_find (buffer)) && *cp != '\0')
654 return show_multi_aux (ct, serial, alternate, cp);
655
656 /* Check for invo_name-show-type */
657 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
658 if ((cp = context_find (buffer)) && *cp != '\0')
659 return show_multi_aux (ct, serial, alternate, cp);
660
661 if ((cp = ct->c_showproc))
662 return show_multi_aux (ct, serial, alternate, cp);
663
664 /*
665 * Use default method to display this multipart content. Even
666 * unknown types are displayable, since they're treated as mixed
667 * per RFC 2046.
668 */
669 return show_multi_internal (ct, serial, alternate);
670 }
671
672
673 /*
674 * show message body of subtypes of multipart that
675 * we understand directly (mixed, alternate, etc...)
676 */
677
678 static int
679 show_multi_internal (CT ct, int serial, int alternate)
680 {
681 int alternating, nowalternate, nowserial, result;
682 struct multipart *m = (struct multipart *) ct->c_ctparams;
683 struct part *part;
684 CT p;
685 sigset_t set, oset;
686
687 alternating = 0;
688 nowalternate = alternate;
689
690 if (ct->c_subtype == MULTI_PARALLEL) {
691 nowserial = serialsw;
692 } else if (ct->c_subtype == MULTI_ALTERNATE) {
693 nowalternate = 1;
694 alternating = 1;
695 nowserial = serial;
696 } else {
697 /*
698 * multipart/mixed
699 * mutlipart/digest
700 * unknown subtypes of multipart (treat as mixed per rfc2046)
701 */
702 nowserial = serial;
703 }
704
705 /* block a few signals */
706 if (!nowserial) {
707 sigemptyset (&set);
708 sigaddset (&set, SIGHUP);
709 sigaddset (&set, SIGINT);
710 sigaddset (&set, SIGQUIT);
711 sigaddset (&set, SIGTERM);
712 sigprocmask (SIG_BLOCK, &set, &oset);
713 }
714
715 /*
716 * alternate -> we are a part inside an multipart/alternative
717 * alternating -> we are a multipart/alternative
718 */
719
720 result = alternate ? NOTOK : OK;
721
722 for (part = m->mp_parts; part; part = part->mp_next) {
723 p = part->mp_part;
724
725 if (part_ok (p, 1) && type_ok (p, 1)) {
726 int inneresult;
727
728 inneresult = show_switch (p, nowserial, nowalternate);
729 switch (inneresult) {
730 case NOTOK:
731 if (alternate && !alternating) {
732 result = NOTOK;
733 goto out;
734 }
735 continue;
736
737 case OK:
738 case DONE:
739 if (alternating) {
740 result = DONE;
741 break;
742 }
743 if (alternate) {
744 alternate = nowalternate = 0;
745 if (result == NOTOK)
746 result = inneresult;
747 }
748 continue;
749 }
750 break;
751 }
752 }
753
754 if (alternating && !part) {
755 if (!alternate)
756 content_error (NULL, ct, "don't know how to display any of the contents");
757 result = NOTOK;
758 goto out;
759 }
760
761 if (serial && !nowserial) {
762 pid_t pid;
763 int kids;
764 int status;
765
766 kids = 0;
767 for (part = m->mp_parts; part; part = part->mp_next) {
768 p = part->mp_part;
769
770 if (p->c_pid > OK) {
771 if (kill (p->c_pid, 0) == NOTOK)
772 p->c_pid = 0;
773 else
774 kids++;
775 }
776 }
777
778 while (kids > 0 && (pid = wait (&status)) != NOTOK) {
779 pidcheck (status);
780
781 for (part = m->mp_parts; part; part = part->mp_next) {
782 p = part->mp_part;
783
784 if (xpid == pid)
785 xpid = 0;
786 if (p->c_pid == pid) {
787 p->c_pid = 0;
788 kids--;
789 break;
790 }
791 }
792 }
793 }
794
795 out:
796 if (!nowserial) {
797 /* reset the signal mask */
798 sigprocmask (SIG_SETMASK, &oset, &set);
799 }
800
801 return result;
802 }
803
804
805 /*
806 * Parse display string for multipart content
807 * and use external program to display it.
808 */
809
810 static int
811 show_multi_aux (CT ct, int serial, int alternate, char *cp)
812 {
813 int len, buflen, quoted;
814 int xlist, xpause, xtty;
815 char *bp, *pp, *file, buffer[BUFSIZ];
816 struct multipart *m = (struct multipart *) ct->c_ctparams;
817 struct part *part;
818 CI ci = &ct->c_ctinfo;
819 CT p;
820
821 for (part = m->mp_parts; part; part = part->mp_next) {
822 p = part->mp_part;
823
824 if (!p->c_ceopenfnx) {
825 if (!alternate)
826 content_error (NULL, p, "don't know how to decode content");
827 return NOTOK;
828 }
829
830 if (p->c_storage == NULL) {
831 file = NULL;
832 if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
833 return NOTOK;
834
835 /* I'm not sure if this is necessary? */
836 p->c_storage = add (file, NULL);
837
838 if (p->c_showproc && !strcmp (p->c_showproc, "true"))
839 return (alternate ? DONE : OK);
840 (*p->c_ceclosefnx) (p);
841 }
842 }
843
844 xlist = 0;
845 xpause = 0;
846 xtty = 0;
847
848 /* get buffer ready to go */
849 bp = buffer;
850 buflen = sizeof(buffer) - 1;
851 bp[0] = bp[buflen] = '\0';
852 quoted = 0;
853
854 /* Now parse display string */
855 for ( ; *cp && buflen > 0; cp++) {
856 if (*cp == '%') {
857 pp = bp;
858 switch (*++cp) {
859 case 'a':
860 /* insert parameters from Content-Type field */
861 {
862 char **ap, **ep;
863 char *s = "";
864
865 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
866 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
867 len = strlen (bp);
868 bp += len;
869 buflen -= len;
870 s = " ";
871 }
872 }
873 break;
874
875 case 'd':
876 /* insert content description */
877 if (ct->c_descr) {
878 char *s;
879
880 s = trimcpy (ct->c_descr);
881 strncpy (bp, s, buflen);
882 free (s);
883 }
884 break;
885
886 case 'e':
887 /* exclusive execution */
888 xtty = 1;
889 break;
890
891 case 'F':
892 /* %e and %f */
893 xtty = 1;
894 /* and fall... */
895
896 case 'f':
897 /* insert filename(s) containing content */
898 {
899 char *s = "";
900
901 for (part = m->mp_parts; part; part = part->mp_next) {
902 p = part->mp_part;
903
904 snprintf (bp, buflen, "%s'%s'", s, p->c_storage);
905 len = strlen (bp);
906 bp += len;
907 buflen -= len;
908 s = " ";
909 }
910 /* set our starting pointer back to bp, to avoid
911 * requoting the filenames we just added
912 */
913 pp = bp;
914 }
915 break;
916
917 case 'p':
918 /* %l, and pause prior to displaying content */
919 xpause = pausesw;
920 /* and fall... */
921
922 case 'l':
923 /* display listing prior to displaying content */
924 xlist = !nolist;
925 break;
926
927 case 's':
928 /* insert subtype of content */
929 strncpy (bp, ci->ci_subtype, buflen);
930 break;
931
932 case '%':
933 /* insert character % */
934 goto raw;
935
936 default:
937 *bp++ = *--cp;
938 *bp = '\0';
939 buflen--;
940 continue;
941 }
942 len = strlen (bp);
943 bp += len;
944 buflen -= len;
945
946 /* Did we actually insert something? */
947 if (bp != pp) {
948 /* Insert single quote if not inside quotes already */
949 if (!quoted && buflen) {
950 len = strlen (pp);
951 memmove (pp + 1, pp, len);
952 *pp++ = '\'';
953 buflen--;
954 bp++;
955 }
956 /* Escape existing quotes */
957 while ((pp = strchr (pp, '\'')) && buflen > 3) {
958 len = strlen (pp++);
959 memmove (pp + 3, pp, len);
960 *pp++ = '\\';
961 *pp++ = '\'';
962 *pp++ = '\'';
963 buflen -= 3;
964 bp += 3;
965 }
966 /* If pp is still set, that means we ran out of space. */
967 if (pp)
968 buflen = 0;
969 if (!quoted && buflen) {
970 *bp++ = '\'';
971 *bp = '\0';
972 buflen--;
973 }
974 }
975 } else {
976 raw:
977 *bp++ = *cp;
978 *bp = '\0';
979 buflen--;
980
981 if (*cp == '\'')
982 quoted = !quoted;
983 }
984 }
985
986 if (buflen <= 0 ||
987 (ct->c_termproc && buflen <= (ssize_t) strlen(ct->c_termproc))) {
988 /* content_error would provide a more useful error message
989 * here, except that if we got overrun, it probably would
990 * too.
991 */
992 fprintf(stderr, "Buffer overflow constructing show command!\n");
993 return NOTOK;
994 }
995
996 /* use charset string to modify display method */
997 if (ct->c_termproc) {
998 char term[BUFSIZ];
999
1000 strncpy (term, buffer, sizeof(term));
1001 snprintf (buffer, sizeof(buffer), ct->c_termproc, term);
1002 }
1003
1004 return show_content_aux2 (ct, serial, alternate, NULL, buffer,
1005 NOTOK, xlist, xpause, 0, xtty);
1006 }
1007
1008
1009 /*
1010 * show content of type "message/rfc822"
1011 */
1012
1013 static int
1014 show_message_rfc822 (CT ct, int serial, int alternate)
1015 {
1016 char *cp, buffer[BUFSIZ];
1017 CI ci = &ct->c_ctinfo;
1018
1019 /* Check for invo_name-show-type/subtype */
1020 snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
1021 invo_name, ci->ci_type, ci->ci_subtype);
1022 if ((cp = context_find (buffer)) && *cp != '\0')
1023 return show_content_aux (ct, serial, alternate, cp, NULL);
1024
1025 /* Check for invo_name-show-type */
1026 snprintf (buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
1027 if ((cp = context_find (buffer)) && *cp != '\0')
1028 return show_content_aux (ct, serial, alternate, cp, NULL);
1029
1030 if ((cp = ct->c_showproc))
1031 return show_content_aux (ct, serial, alternate, cp, NULL);
1032
1033 /* default method for message/rfc822 */
1034 if (ct->c_subtype == MESSAGE_RFC822) {
1035 cp = (ct->c_showproc = add ("%pshow -file '%F'", NULL));
1036 return show_content_aux (ct, serial, alternate, cp, NULL);
1037 }
1038
1039 /* complain if we are not a part of a multipart/alternative */
1040 if (!alternate)
1041 content_error (NULL, ct, "don't know how to display content");
1042
1043 return NOTOK;
1044 }
1045
1046
1047 /*
1048 * Show content of type "message/partial".
1049 */
1050
1051 static int
1052 show_partial (CT ct, int serial, int alternate)
1053 {
1054 NMH_UNUSED (serial);
1055 NMH_UNUSED (alternate);
1056
1057 content_error (NULL, ct,
1058 "in order to display this message, you must reassemble it");
1059 return NOTOK;
1060 }
1061
1062
1063 /*
1064 * Show content of type "message/external".
1065 *
1066 * THE ERROR CHECKING IN THIS ONE IS NOT DONE YET.
1067 */
1068
1069 static int
1070 show_external (CT ct, int serial, int alternate)
1071 {
1072 struct exbody *e = (struct exbody *) ct->c_ctparams;
1073 CT p = e->eb_content;
1074
1075 if (!type_ok (p, 0))
1076 return OK;
1077
1078 return show_switch (p, serial, alternate);
1079 }
1080
1081
1082 int
1083 convert_charset (CT ct, char *dest_charset, int *message_mods) {
1084 char *src_charset = content_charset (ct);
1085 int status = OK;
1086
1087 /* norm_charmap() is case sensitive. */
1088 char *src_charset_u = upcase (src_charset);
1089 char *dest_charset_u = upcase (dest_charset);
1090 int different_charsets =
1091 strcmp (norm_charmap (src_charset), norm_charmap (dest_charset));
1092
1093 free (dest_charset_u);
1094 free (src_charset_u);
1095
1096 if (different_charsets) {
1097 #ifdef HAVE_ICONV
1098 iconv_t conv_desc = NULL;
1099 char *dest;
1100 int fd = -1;
1101 char **file = NULL;
1102 FILE **fp = NULL;
1103 size_t begin;
1104 size_t end;
1105 int opened_input_file = 0;
1106 char src_buffer[BUFSIZ];
1107 HF hf;
1108 char *tempfile;
1109
1110 if ((conv_desc = iconv_open (dest_charset, src_charset)) ==
1111 (iconv_t) -1) {
1112 advise (NULL, "Can't convert %s to %s", src_charset, dest_charset);
1113 return NOTOK;
1114 }
1115
1116 if ((tempfile = m_mktemp2 (NULL, invo_name, &fd, NULL)) == NULL) {
1117 adios (NULL, "unable to create temporary file in %s",
1118 get_temp_dir());
1119 }
1120 dest = add (tempfile, NULL);
1121
1122 if (ct->c_cefile.ce_file) {
1123 file = &ct->c_cefile.ce_file;
1124 fp = &ct->c_cefile.ce_fp;
1125 begin = end = 0;
1126 } else if (ct->c_file) {
1127 file = &ct->c_file;
1128 fp = &ct->c_fp;
1129 begin = (size_t) ct->c_begin;
1130 end = (size_t) ct->c_end;
1131 } /* else no input file: shouldn't happen */
1132
1133 if (file && *file && fp) {
1134 if (! *fp) {
1135 if ((*fp = fopen (*file, "r")) == NULL) {
1136 advise (*file, "unable to open for reading");
1137 status = NOTOK;
1138 } else {
1139 opened_input_file = 1;
1140 }
1141 }
1142 }
1143
1144 if (fp && *fp) {
1145 size_t inbytes;
1146 size_t bytes_to_read =
1147 end > 0 && end > begin ? end - begin : sizeof src_buffer;
1148
1149 fseeko (*fp, begin, SEEK_SET);
1150 while ((inbytes = fread (src_buffer, 1,
1151 min (bytes_to_read, sizeof src_buffer),
1152 *fp)) > 0) {
1153 char dest_buffer[BUFSIZ];
1154 ICONV_CONST char *ib = src_buffer;
1155 char *ob = dest_buffer;
1156 size_t outbytes = sizeof dest_buffer;
1157 size_t outbytes_before = outbytes;
1158
1159 if (end > 0) bytes_to_read -= inbytes;
1160
1161 if (iconv (conv_desc, &ib, &inbytes, &ob, &outbytes) ==
1162 (size_t) -1) {
1163 status = NOTOK;
1164 break;
1165 } else {
1166 write (fd, dest_buffer, outbytes_before - outbytes);
1167 }
1168 }
1169
1170 if (opened_input_file) {
1171 fclose (*fp);
1172 *fp = NULL;
1173 }
1174 }
1175
1176 iconv_close (conv_desc);
1177 close (fd);
1178
1179 if (status == OK) {
1180 /* Replace the decoded file with the converted one. */
1181 if (ct->c_cefile.ce_file) {
1182 if (ct->c_cefile.ce_unlink) {
1183 (void) m_unlink (ct->c_cefile.ce_file);
1184 }
1185 free (ct->c_cefile.ce_file);
1186 }
1187 ct->c_cefile.ce_file = dest;
1188 ct->c_cefile.ce_unlink = 1;
1189
1190 ++*message_mods;
1191
1192 /* Update ci_attrs. */
1193 src_charset = dest_charset;
1194
1195 /* Update ct->c_ctline. */
1196 if (ct->c_ctline) {
1197 char *ctline =
1198 update_attr (ct->c_ctline, "charset=", dest_charset);
1199
1200 free (ct->c_ctline);
1201 ct->c_ctline = ctline;
1202 } /* else no CT line, which is odd */
1203
1204 /* Update Content-Type header field. */
1205 for (hf = ct->c_first_hf; hf; hf = hf->next) {
1206 if (! strcasecmp (TYPE_FIELD, hf->name)) {
1207 char *ctline_less_newline =
1208 update_attr (hf->value, "charset=", dest_charset);
1209 char *ctline = concat (ctline_less_newline, "\n", NULL);
1210 free (ctline_less_newline);
1211
1212 free (hf->value);
1213 hf->value = ctline;
1214 break;
1215 }
1216 }
1217 } else {
1218 (void) m_unlink (dest);
1219 }
1220 #else /* ! HAVE_ICONV */
1221 NMH_UNUSED (message_mods);
1222
1223 advise (NULL, "Can't convert %s to %s without iconv", src_charset,
1224 dest_charset);
1225 status = NOTOK;
1226 #endif /* ! HAVE_ICONV */
1227 }
1228
1229 return status;
1230 }
1231
1232
1233 static int
1234 convert_content_charset (CT ct, char **file) {
1235 /* Convert character set if needed and if built with iconv. */
1236 #ifdef HAVE_ICONV
1237 if (display_charset == NULL) {
1238 /* The user did not specify a display charset, so use
1239 current setting and see if the content will need to be
1240 converted. */
1241 char *charset = content_charset (ct);
1242
1243 if (! check_charset (charset, strlen (charset))) {
1244 int unused = 0;
1245 if (convert_charset (ct, get_charset (), &unused) == 0) {
1246 *file = ct->c_cefile.ce_file;
1247 } else {
1248 return NOTOK;
1249 }
1250 }
1251 } else {
1252 /* The user requested display with a specific charset. */
1253 int unused = 0;
1254 if (convert_charset (ct, display_charset, &unused) == 0) {
1255 *file = ct->c_cefile.ce_file;
1256 } else {
1257 return NOTOK;
1258 }
1259 }
1260 #endif /* ! HAVE_ICONV */
1261
1262 return OK;
1263 }
1264
1265
1266 static void
1267 intrser (int i)
1268 {
1269 NMH_UNUSED (i);
1270
1271 putchar ('\n');
1272 siglongjmp (intrenv, DONE);
1273 }