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