Renamed r1bindex() to mhbasename(), to make its function becomes clear.
[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++] = mhbasename(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,
262                                                         alternate);
263                                         break;
264
265                                 case MESSAGE_EXTERNAL:
266                                         return show_external(ct, serial,
267                                                         alternate);
268                                         break;
269
270                                 case MESSAGE_RFC822:
271                                 default:
272                                         return show_message_rfc822(ct, serial,
273                                                         alternate);
274                                         break;
275                         }
276                         break;
277
278                 case CT_TEXT:
279                         return show_text(ct, serial, alternate);
280                         break;
281
282                 case CT_AUDIO:
283                 case CT_IMAGE:
284                 case CT_VIDEO:
285                 case CT_APPLICATION:
286                         return show_content(ct, serial, alternate);
287                         break;
288
289                 default:
290                         adios(NULL, "unknown content type %d", ct->c_type);
291                         break;
292         }
293
294         return 0;  /* NOT REACHED */
295 }
296
297
298 /*
299 ** Generic method for displaying content
300 */
301
302 static int
303 show_content(CT ct, int serial, int alternate)
304 {
305         char *cp, buffer[BUFSIZ];
306         CI ci = &ct->c_ctinfo;
307
308         /* Check for mhshow-show-type/subtype */
309         snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
310                                 invo_name, ci->ci_type, ci->ci_subtype);
311         if ((cp = context_find(buffer)) && *cp != '\0')
312                 return show_content_aux(ct, serial, alternate, cp, NULL);
313
314         /* Check for mhshow-show-type */
315         snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
316         if ((cp = context_find(buffer)) && *cp != '\0')
317                 return show_content_aux(ct, serial, alternate, cp, NULL);
318
319         if ((cp = ct->c_showproc))
320                 return show_content_aux(ct, serial, alternate, cp, NULL);
321
322         /* complain if we are not a part of a multipart/alternative */
323         if (!alternate)
324                 content_error(NULL, ct, "don't know how to display content");
325
326         return NOTOK;
327 }
328
329
330 /*
331 ** Parse the display string for displaying generic content
332 */
333
334 int
335 show_content_aux(CT ct, int serial, int alternate, char *cp, char *cracked)
336 {
337         int fd, len, buflen, quoted;
338         int xstdin, xlist, xpause, xtty;
339         char *bp, *pp, *file, buffer[BUFSIZ];
340         CI ci = &ct->c_ctinfo;
341
342         if (!ct->c_ceopenfnx) {
343                 if (!alternate)
344                         content_error(NULL, ct,
345                                         "don't know how to decode content");
346
347                 return NOTOK;
348         }
349
350         file = NULL;
351         if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
352                 return NOTOK;
353         if (ct->c_showproc && !strcmp(ct->c_showproc, "true"))
354                 return (alternate ? DONE : OK);
355
356         xlist  = 0;
357         xpause = 0;
358         xstdin = 0;
359         xtty   = 0;
360
361         if (cracked) {
362                 strncpy(buffer, cp, sizeof(buffer));
363                 goto got_command;
364         }
365
366         /* get buffer ready to go */
367         bp = buffer;
368         buflen = sizeof(buffer) - 1;
369         bp[0] = bp[buflen] = '\0';
370         quoted = 0;
371
372         /* Now parse display string */
373         for ( ; *cp && buflen > 0; cp++) {
374                 if (*cp == '%') {
375                         pp = bp;
376
377                         switch (*++cp) {
378                         case 'a':
379                                 /* insert parameters from Content-Type field */
380                         {
381                                 char **ap, **ep;
382                                 char *s = "";
383
384                                 for (ap = ci->ci_attrs, ep = ci->ci_values;
385                                                 *ap; ap++, ep++) {
386                                         snprintf(bp, buflen, "%s%s=\"%s\"",
387                                                         s, *ap, *ep);
388                                         len = strlen(bp);
389                                         bp += len;
390                                         buflen -= len;
391                                         s = " ";
392                                 }
393                         }
394                         break;
395
396                         case 'd':
397                                 /* insert content description */
398                                 if (ct->c_descr) {
399                                         char *s;
400
401                                         s = trimcpy(ct->c_descr);
402                                         strncpy(bp, s, buflen);
403                                         free(s);
404                                 }
405                                 break;
406
407                         case 'e':
408                                 /* exclusive execution */
409                                 xtty = 1;
410                                 break;
411
412                         case 'F':
413                                 /* %e, %f, and stdin is terminal not content */
414                                 xstdin = 1;
415                                 xtty = 1;
416                                 /* and fall... */
417
418                         case 'f':
419                                 /* insert filename containing content */
420                                 snprintf(bp, buflen, "'%s'", file);
421                                 /*
422                                 ** since we've quoted the file argument,
423                                 ** set things up to look past it, to avoid
424                                 ** problems with the quoting logic below.
425                                 ** (I know, I should figure out what's
426                                 ** broken with the quoting logic, but..)
427                                 */
428                                 len = strlen(bp);
429                                 buflen -= len;
430                                 bp += len;
431                                 pp = bp;
432                                 break;
433
434                         case 'p':
435                                 /* %l, and pause prior to displaying content */
436                                 xpause = pausesw;
437                                 /* and fall... */
438
439                         case 'l':
440                                 /*
441                                 ** display listing prior to displaying content
442                                 */
443                                 xlist = !nolist;
444                                 break;
445
446                         case 's':
447                                 /* insert subtype of content */
448                                 strncpy(bp, ci->ci_subtype, buflen);
449                                 break;
450
451                         case '%':
452                                 /* insert character % */
453                                 goto raw;
454
455                         default:
456                                 *bp++ = *--cp;
457                                 *bp = '\0';
458                                 buflen--;
459                                 continue;
460                         }
461                         len = strlen(bp);
462                         bp += len;
463                         buflen -= len;
464
465                         /* Did we actually insert something? */
466                         if (bp != pp) {
467                                 /*
468                                 ** Insert single quote if not inside quotes
469                                 ** already
470                                 */
471                                 if (!quoted && buflen) {
472                                         len = strlen(pp);
473                                         memmove(pp + 1, pp, len);
474                                         *pp++ = '\'';
475                                         buflen--;
476                                         bp++;
477                                 }
478                                 /* Escape existing quotes */
479                                 while ((pp = strchr(pp, '\'')) && buflen > 3) {
480                                         len = strlen(pp++);
481                                         memmove(pp + 3, pp, len);
482                                         *pp++ = '\\';
483                                         *pp++ = '\'';
484                                         *pp++ = '\'';
485                                         buflen -= 3;
486                                         bp += 3;
487                                 }
488                                 /*
489                                 ** If pp is still set, that means we ran
490                                 ** out of space.
491                                 */
492                                 if (pp)
493                                         buflen = 0;
494                                 if (!quoted && buflen) {
495                                         *bp++ = '\'';
496                                         *bp = '\0';
497                                         buflen--;
498                                 }
499                         }
500                 } else {
501 raw:
502                         *bp++ = *cp;
503                         *bp = '\0';
504                         buflen--;
505
506                         if (*cp == '\'')
507                                 quoted = !quoted;
508                 }
509         }
510
511         if (buflen <= 0 || (ct->c_termproc && buflen <= strlen(ct->c_termproc))) {
512                 /*
513                 ** content_error would provide a more useful error message
514                 ** here, except that if we got overrun, it probably would
515                 ** too.
516                 */
517                 fprintf(stderr, "Buffer overflow constructing show command!\n");
518                 return NOTOK;
519         }
520
521         /* use charset string to modify display method */
522         if (ct->c_termproc) {
523                 char term[BUFSIZ];
524
525                 strncpy(term, buffer, sizeof(term));
526                 snprintf(buffer, sizeof(buffer), ct->c_termproc, term);
527         }
528
529 got_command:
530         return show_content_aux2(ct, serial, alternate, cracked, buffer,
531                         fd, xlist, xpause, xstdin, xtty);
532 }
533
534
535 /*
536 ** Routine to actually display the content
537 */
538
539 static int
540 show_content_aux2(CT ct, int serial, int alternate, char *cracked,
541         char *buffer, int fd, int xlist, int xpause, int xstdin, int xtty)
542 {
543         pid_t child_id;
544         int i;
545         char *vec[4], exec[BUFSIZ + sizeof "exec "];
546
547         if (debugsw || cracked) {
548                 fflush(stdout);
549
550                 fprintf(stderr, "%s msg %s", cracked ? "storing" : "show",
551                                  ct->c_file);
552                 if (ct->c_partno)
553                         fprintf(stderr, " part %s", ct->c_partno);
554                 if (cracked)
555                         fprintf(stderr, " using command (cd %s; %s)\n",
556                                         cracked, buffer);
557                 else
558                         fprintf(stderr, " using command %s\n", buffer);
559         }
560
561         if (xpid < 0 || (xtty && xpid)) {
562                 if (xpid < 0)
563                         xpid = -xpid;
564                 pidcheck(pidwait(xpid, NOTOK));
565                 xpid = 0;
566         }
567
568         if (xlist) {
569                 char prompt[BUFSIZ];
570
571                 if (ct->c_type == CT_MULTIPART)
572                         list_content(ct, -1, 1, 0, 0);
573                 else
574                         list_switch(ct, -1, 1, 0, 0);
575
576                 if (xpause && SOprintf("Press <return> to show content..."))
577                         printf("Press <return> to show content...");
578
579                 if (xpause) {
580                         int intr;
581                         SIGNAL_HANDLER istat;
582
583                         istat = SIGNAL(SIGINT, intrser);
584                         if ((intr = sigsetjmp(intrenv, 1)) == OK) {
585                                 fflush(stdout);
586                                 prompt[0] = 0;
587                                 read(fileno(stdout), prompt, sizeof(prompt));
588                         }
589                         SIGNAL(SIGINT, istat);
590                         if (intr != OK || prompt[0] == 'n') {
591                                 (*ct->c_ceclosefnx) (ct);
592                                 return (alternate ? DONE : NOTOK);
593                         }
594                         if (prompt[0] == 'q') done(OK);
595                 }
596         }
597
598         snprintf(exec, sizeof(exec), "exec %s", buffer);
599
600         vec[0] = "/bin/sh";
601         vec[1] = "-c";
602         vec[2] = exec;
603         vec[3] = NULL;
604
605         fflush(stdout);
606
607         for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
608                 sleep(5);
609         switch (child_id) {
610                 case NOTOK:
611                         advise("fork", "unable to");
612                         (*ct->c_ceclosefnx) (ct);
613                         return NOTOK;
614
615                 case OK:
616                         if (cracked)
617                                 chdir(cracked);
618                         if (!xstdin)
619                                 dup2(fd, 0);
620                         close(fd);
621                         execvp("/bin/sh", vec);
622                         fprintf(stderr, "unable to exec ");
623                         perror("/bin/sh");
624                         _exit(-1);
625                         /* NOTREACHED */
626
627                 default:
628                         if (!serial) {
629                                 ct->c_pid = child_id;
630                                 if (xtty)
631                                         xpid = child_id;
632                         } else {
633                                 pidcheck(pidXwait(child_id, NULL));
634                         }
635
636                         if (fd != NOTOK)
637                                 (*ct->c_ceclosefnx) (ct);
638                         return (alternate ? DONE : OK);
639         }
640 }
641
642
643 /*
644 ** show content of type "text"
645 */
646
647 static int
648 show_text(CT ct, int serial, int alternate)
649 {
650         char *cp, buffer[BUFSIZ];
651         CI ci = &ct->c_ctinfo;
652
653         /* Check for mhshow-show-type/subtype */
654         snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
655                         invo_name, ci->ci_type, ci->ci_subtype);
656         if ((cp = context_find(buffer)) && *cp != '\0')
657                 return show_content_aux(ct, serial, alternate, cp, NULL);
658
659         /* Check for mhshow-show-type */
660         snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
661         if ((cp = context_find(buffer)) && *cp != '\0')
662                 return show_content_aux(ct, serial, alternate, cp, NULL);
663
664         /*
665         ** Use default method if content is text/plain, or if
666         ** if it is not a text part of a multipart/alternative
667         */
668         if (!alternate || ct->c_subtype == TEXT_PLAIN) {
669                 snprintf(buffer, sizeof(buffer), "%%p%s '%%F'",
670                                 progsw ? progsw : moreproc && *moreproc ?
671                                 moreproc : "more");
672                 cp = (ct->c_showproc = add(buffer, NULL));
673                 return show_content_aux(ct, serial, alternate, cp, NULL);
674         }
675
676         return NOTOK;
677 }
678
679
680 /*
681 ** show message body of type "multipart"
682 */
683
684 static int
685 show_multi(CT ct, int serial, int alternate)
686 {
687         char *cp, buffer[BUFSIZ];
688         CI ci = &ct->c_ctinfo;
689
690         /* Check for mhshow-show-type/subtype */
691         snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
692                         invo_name, ci->ci_type, ci->ci_subtype);
693         if ((cp = context_find(buffer)) && *cp != '\0')
694                 return show_multi_aux(ct, serial, alternate, cp);
695
696         /* Check for mhshow-show-type */
697         snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
698         if ((cp = context_find(buffer)) && *cp != '\0')
699                 return show_multi_aux(ct, serial, alternate, cp);
700
701         if ((cp = ct->c_showproc))
702                 return show_multi_aux(ct, serial, alternate, cp);
703
704         /*
705         ** Use default method to display this multipart content
706         ** if it is not a (nested) part of a multipart/alternative,
707         ** or if it is one of the known subtypes of multipart.
708         */
709         if (!alternate || ct->c_subtype != MULTI_UNKNOWN)
710                 return show_multi_internal(ct, serial, alternate);
711
712         return NOTOK;
713 }
714
715
716 /*
717 ** show message body of subtypes of multipart that
718 ** we understand directly (mixed, alternate, etc...)
719 */
720
721 static int
722 show_multi_internal(CT ct, int serial, int alternate)
723 {
724         int alternating, nowalternate, nowserial, result;
725         struct multipart *m = (struct multipart *) ct->c_ctparams;
726         struct part *part;
727         CT p;
728         sigset_t set, oset;
729
730         alternating = 0;
731         nowalternate = alternate;
732
733         if (ct->c_subtype == MULTI_PARALLEL) {
734                 nowserial = serialsw;
735         } else if (ct->c_subtype == MULTI_ALTERNATE) {
736                 nowalternate = 1;
737                 alternating  = 1;
738                 nowserial = serial;
739         } else {
740                 /*
741                 ** multipart/mixed
742                 ** mutlipart/digest
743                 ** unknown subtypes of multipart (treat as mixed per rfc2046)
744                 */
745                 nowserial = serial;
746         }
747
748         /* block a few signals */
749         if (!nowserial) {
750                 sigemptyset(&set);
751                 sigaddset(&set, SIGHUP);
752                 sigaddset(&set, SIGINT);
753                 sigaddset(&set, SIGQUIT);
754                 sigaddset(&set, SIGTERM);
755                 SIGPROCMASK(SIG_BLOCK, &set, &oset);
756         }
757
758 /*
759 ** alternate   -> we are a part inside an multipart/alternative
760 ** alternating -> we are a multipart/alternative
761 */
762
763         result = alternate ? NOTOK : OK;
764
765         for (part = m->mp_parts; part; part = part->mp_next) {
766                 p = part->mp_part;
767
768                 if (part_ok(p, 0) && type_ok(p, 0)) {
769                         int inneresult;
770
771                         inneresult = show_switch(p, nowserial, nowalternate);
772                         switch (inneresult) {
773                                 case NOTOK:
774                                         if (alternate && !alternating) {
775                                                 result = NOTOK;
776                                                 goto out;
777                                         }
778                                         continue;
779
780                                 case OK:
781                                 case DONE:
782                                         if (alternating) {
783                                                 result = DONE;
784                                                 break;
785                                         }
786                                         if (alternate) {
787                                                 alternate = nowalternate = 0;
788                                                 if (result == NOTOK)
789                                                         result = inneresult;
790                                         }
791                                         continue;
792                         }
793                         break;
794                 }
795         }
796
797         if (alternating && !part) {
798                 if (!alternate)
799                         content_error(NULL, ct, "don't know how to display any of the contents");
800                 result = NOTOK;
801                 goto out;
802         }
803
804         if (serial && !nowserial) {
805                 pid_t pid;
806                 int kids;
807 #ifdef HAVE_UNION_WAIT
808                 union wait status;
809 #else
810                 int status;
811 #endif
812
813                 kids = 0;
814                 for (part = m->mp_parts; part; part = part->mp_next) {
815                         p = part->mp_part;
816
817                         if (p->c_pid > OK) {
818                                 if (kill(p->c_pid, 0) == NOTOK)
819                                         p->c_pid = 0;
820                                 else
821                                         kids++;
822                         }
823                 }
824
825                 while (kids > 0 && (pid = wait(&status)) != NOTOK) {
826 #ifdef HAVE_UNION_WAIT
827                         pidcheck(status.w_status);
828 #else
829                         pidcheck(status);
830 #endif
831
832                         for (part = m->mp_parts; part; part = part->mp_next) {
833                                 p = part->mp_part;
834
835                                 if (xpid == pid)
836                                         xpid = 0;
837                                 if (p->c_pid == pid) {
838                                         p->c_pid = 0;
839                                         kids--;
840                                         break;
841                                 }
842                         }
843                 }
844         }
845
846 out:
847         if (!nowserial) {
848                 /* reset the signal mask */
849                 SIGPROCMASK(SIG_SETMASK, &oset, &set);
850         }
851
852         return result;
853 }
854
855
856 /*
857 ** Parse display string for multipart content
858 ** and use external program to display it.
859 */
860
861 static int
862 show_multi_aux(CT ct, int serial, int alternate, char *cp)
863 {
864         int len, buflen, quoted;
865         int xlist, xpause, xtty;
866         char *bp, *pp, *file, buffer[BUFSIZ];
867         struct multipart *m = (struct multipart *) ct->c_ctparams;
868         struct part *part;
869         CI ci = &ct->c_ctinfo;
870         CT p;
871
872         for (part = m->mp_parts; part; part = part->mp_next) {
873                 p = part->mp_part;
874
875                 if (!p->c_ceopenfnx) {
876                         if (!alternate)
877                                 content_error(NULL, p, "don't know how to decode content");
878                         return NOTOK;
879                 }
880
881                 if (p->c_storage == NULL) {
882                         file = NULL;
883                         if ((*p->c_ceopenfnx) (p, &file) == NOTOK)
884                                 return NOTOK;
885
886                         /* I'm not sure if this is necessary? */
887                         p->c_storage = add(file, NULL);
888
889                         if (p->c_showproc && !strcmp(p->c_showproc, "true"))
890                                 return (alternate ? DONE : OK);
891                         (*p->c_ceclosefnx) (p);
892                 }
893         }
894
895         xlist = 0;
896         xpause = 0;
897         xtty = 0;
898
899         /* get buffer ready to go */
900         bp = buffer;
901         buflen = sizeof(buffer) - 1;
902         bp[0] = bp[buflen] = '\0';
903         quoted = 0;
904
905         /* Now parse display string */
906         for ( ; *cp && buflen > 0; cp++) {
907                 if (*cp == '%') {
908                         pp = bp;
909                         switch (*++cp) {
910                         case 'a':
911                                 /* insert parameters from Content-Type field */
912                         {
913                                 char **ap, **ep;
914                                 char *s = "";
915
916                                 for (ap = ci->ci_attrs, ep = ci->ci_values;
917                                                 *ap; ap++, ep++) {
918                                         snprintf(bp, buflen, "%s%s=\"%s\"",
919                                                         s, *ap, *ep);
920                                         len = strlen(bp);
921                                         bp += len;
922                                         buflen -= len;
923                                         s = " ";
924                                 }
925                         }
926                         break;
927
928                         case 'd':
929                                 /* insert content description */
930                                 if (ct->c_descr) {
931                                         char *s;
932
933                                         s = trimcpy(ct->c_descr);
934                                         strncpy(bp, s, buflen);
935                                         free(s);
936                                 }
937                                 break;
938
939                         case 'e':
940                                 /* exclusive execution */
941                                 xtty = 1;
942                                 break;
943
944                         case 'F':
945                                 /* %e and %f */
946                                 xtty = 1;
947                                 /* and fall... */
948
949                         case 'f':
950                                 /* insert filename(s) containing content */
951                         {
952                                 char *s = "";
953
954                                 for (part = m->mp_parts; part;
955                                                 part = part->mp_next) {
956                                         p = part->mp_part;
957
958                                         snprintf(bp, buflen, "%s'%s'",
959                                                         s, p->c_storage);
960                                         len = strlen(bp);
961                                         bp += len;
962                                         buflen -= len;
963                                         s = " ";
964                                 }
965                                 /*
966                                 ** set our starting pointer back to bp,
967                                 ** to avoid requoting the filenames we
968                                 ** just added
969                                 */
970                                 pp = bp;
971                         }
972                         break;
973
974                         case 'p':
975                                 /* %l, and pause prior to displaying content */
976                                 xpause = pausesw;
977                                 /* and fall... */
978
979                         case 'l':
980                                 /*
981                                 ** display listing prior to displaying content
982                                 */
983                                 xlist = !nolist;
984                                 break;
985
986                         case 's':
987                                 /* insert subtype of content */
988                                 strncpy(bp, ci->ci_subtype, buflen);
989                                 break;
990
991                         case '%':
992                                 /* insert character % */
993                                 goto raw;
994
995                         default:
996                                 *bp++ = *--cp;
997                                 *bp = '\0';
998                                 buflen--;
999                                 continue;
1000                         }
1001                         len = strlen(bp);
1002                         bp += len;
1003                         buflen -= len;
1004
1005                         /* Did we actually insert something? */
1006                         if (bp != pp) {
1007                                 /*
1008                                 ** Insert single quote if not inside quotes
1009                                 ** already
1010                                 */
1011                                 if (!quoted && buflen) {
1012                                         len = strlen(pp);
1013                                         memmove(pp + 1, pp, len);
1014                                         *pp++ = '\'';
1015                                         buflen--;
1016                                         bp++;
1017                                 }
1018                                 /* Escape existing quotes */
1019                                 while ((pp = strchr(pp, '\'')) && buflen > 3) {
1020                                         len = strlen(pp++);
1021                                         memmove(pp + 3, pp, len);
1022                                         *pp++ = '\\';
1023                                         *pp++ = '\'';
1024                                         *pp++ = '\'';
1025                                         buflen -= 3;
1026                                         bp += 3;
1027                                 }
1028                                 /*
1029                                 ** If pp is still set, that means we ran
1030                                 ** out of space.
1031                                 */
1032                                 if (pp)
1033                                         buflen = 0;
1034                                 if (!quoted && buflen) {
1035                                         *bp++ = '\'';
1036                                         *bp = '\0';
1037                                         buflen--;
1038                                 }
1039                         }
1040                 } else {
1041 raw:
1042                         *bp++ = *cp;
1043                         *bp = '\0';
1044                         buflen--;
1045
1046                         if (*cp == '\'')
1047                                 quoted = !quoted;
1048                 }
1049         }
1050
1051         if (buflen <= 0 ||
1052                         (ct->c_termproc && buflen <= strlen(ct->c_termproc))) {
1053                 /*
1054                 ** content_error would provide a more useful error message
1055                 ** here, except that if we got overrun, it probably would
1056                 ** too.
1057                 */
1058                 fprintf(stderr, "Buffer overflow constructing show command!\n");
1059                 return NOTOK;
1060         }
1061
1062         /* use charset string to modify display method */
1063         if (ct->c_termproc) {
1064                 char term[BUFSIZ];
1065
1066                 strncpy(term, buffer, sizeof(term));
1067                 snprintf(buffer, sizeof(buffer), ct->c_termproc, term);
1068         }
1069
1070         return show_content_aux2(ct, serial, alternate, NULL, buffer,
1071                         NOTOK, xlist, xpause, 0, xtty);
1072 }
1073
1074
1075 /*
1076 ** show content of type "message/rfc822"
1077 */
1078
1079 static int
1080 show_message_rfc822(CT ct, int serial, int alternate)
1081 {
1082         char *cp, buffer[BUFSIZ];
1083         CI ci = &ct->c_ctinfo;
1084
1085         /* Check for mhshow-show-type/subtype */
1086         snprintf(buffer, sizeof(buffer), "%s-show-%s/%s",
1087                                 invo_name, ci->ci_type, ci->ci_subtype);
1088         if ((cp = context_find(buffer)) && *cp != '\0')
1089                 return show_content_aux(ct, serial, alternate, cp, NULL);
1090
1091         /* Check for mhshow-show-type */
1092         snprintf(buffer, sizeof(buffer), "%s-show-%s", invo_name, ci->ci_type);
1093         if ((cp = context_find(buffer)) && *cp != '\0')
1094                 return show_content_aux(ct, serial, alternate, cp, NULL);
1095
1096         if ((cp = ct->c_showproc))
1097                 return show_content_aux(ct, serial, alternate, cp, NULL);
1098
1099         /* default method for message/rfc822 */
1100         if (ct->c_subtype == MESSAGE_RFC822) {
1101                 cp = (ct->c_showproc = add("%pshow -file '%F'", NULL));
1102                 return show_content_aux(ct, serial, alternate, cp, NULL);
1103         }
1104
1105         /* complain if we are not a part of a multipart/alternative */
1106         if (!alternate)
1107                 content_error(NULL, ct, "don't know how to display content");
1108
1109         return NOTOK;
1110 }
1111
1112
1113 /*
1114 ** Show content of type "message/partial".
1115 */
1116
1117 static int
1118 show_partial(CT ct, int serial, int alternate)
1119 {
1120         content_error(NULL, ct,
1121                 "in order to display this message, you must reassemble it");
1122         return NOTOK;
1123 }
1124
1125
1126 /*
1127 ** Show content of type "message/external".
1128 **
1129 ** THE ERROR CHECKING IN THIS ONE IS NOT DONE YET.
1130 */
1131
1132 static int
1133 show_external(CT ct, int serial, int alternate)
1134 {
1135         struct exbody *e = (struct exbody *) ct->c_ctparams;
1136         CT p = e->eb_content;
1137
1138         if (!type_ok(p, 0))
1139                 return OK;
1140
1141         return show_switch(p, serial, alternate);
1142
1143 #if 0
1144         content_error(NULL, p, "don't know how to display content");
1145         return NOTOK;
1146 #endif
1147 }
1148
1149
1150 static RETSIGTYPE
1151 intrser(int i)
1152 {
1153 #ifndef RELIABLE_SIGNALS
1154         SIGNAL(SIGINT, intrser);
1155 #endif
1156
1157         putchar('\n');
1158         siglongjmp(intrenv, DONE);
1159 }