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