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