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