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