Commit fbb38622 fbb3862280d6c6ef5ca49a05d4e571a23df8a241 by Sergey Poznyakoff

Implemented store mode.

1 parent 43c9a8a3
Showing 1 changed file with 749 additions and 76 deletions
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
18 /* MH mhn command */ 18 /* MH mhn command */
19 19
20 #include <mh.h> 20 #include <mh.h>
21 #define obstack_chunk_alloc xmalloc
22 #define obstack_chunk_free free
23 #include <obstack.h>
21 24
22 const char *argp_program_version = "mhn (" PACKAGE_STRING ")"; 25 const char *argp_program_version = "mhn (" PACKAGE_STRING ")";
23 static char doc[] = N_("GNU MH mhn\v" 26 static char doc[] = N_("GNU MH mhn\v"
...@@ -57,15 +60,15 @@ static struct argp_option options[] = { ...@@ -57,15 +60,15 @@ static struct argp_option options[] = {
57 {"form", ARG_FORM, N_("FILE"), 0, 60 {"form", ARG_FORM, N_("FILE"), 0,
58 N_("Read mhl format from FILE"), 22}, 61 N_("Read mhl format from FILE"), 22},
59 {"pause", ARG_PAUSE, N_("BOOL"), OPTION_ARG_OPTIONAL, 62 {"pause", ARG_PAUSE, N_("BOOL"), OPTION_ARG_OPTIONAL,
60 N_("* Pause prior to displaying content"), 22}, 63 N_("Pause prior to displaying content"), 22},
61 {"nopause", ARG_NOPAUSE, NULL, OPTION_HIDDEN, "", 22 }, 64 {"nopause", ARG_NOPAUSE, NULL, OPTION_HIDDEN, "", 22 },
62 65
63 {N_("Saving options"), 0, NULL, OPTION_DOC, NULL, 30}, 66 {N_("Saving options"), 0, NULL, OPTION_DOC, NULL, 30},
64 {"store", ARG_STORE, N_("BOOL"), OPTION_ARG_OPTIONAL, 67 {"store", ARG_STORE, N_("BOOL"), OPTION_ARG_OPTIONAL,
65 N_("* Store the contents of the messages on disk"), 31}, 68 N_("Store the contents of the messages on disk"), 31},
66 {"nostore", ARG_NOSTORE, NULL, OPTION_HIDDEN, "", 31 }, 69 {"nostore", ARG_NOSTORE, NULL, OPTION_HIDDEN, "", 31 },
67 {"auto", ARG_AUTO, N_("BOOL"), OPTION_ARG_OPTIONAL, 70 {"auto", ARG_AUTO, N_("BOOL"), OPTION_ARG_OPTIONAL,
68 N_("* Use filenames from the content headers"), 31}, 71 N_("Use filenames from the content headers"), 31},
69 {"noauto", ARG_NOAUTO, NULL, OPTION_HIDDEN, "", 31 }, 72 {"noauto", ARG_NOAUTO, NULL, OPTION_HIDDEN, "", 31 },
70 73
71 {N_("Other options"), 0, NULL, OPTION_DOC, NULL, 40}, 74 {N_("Other options"), 0, NULL, OPTION_DOC, NULL, 40},
...@@ -76,6 +79,8 @@ static struct argp_option options[] = { ...@@ -76,6 +79,8 @@ static struct argp_option options[] = {
76 {"verbose", ARG_VERBOSE, N_("BOOL"), OPTION_ARG_OPTIONAL, 79 {"verbose", ARG_VERBOSE, N_("BOOL"), OPTION_ARG_OPTIONAL,
77 N_("Print additional information"), 41 }, 80 N_("Print additional information"), 41 },
78 {"noverbose", ARG_NOVERBOSE, NULL, OPTION_HIDDEN, "", 41 }, 81 {"noverbose", ARG_NOVERBOSE, NULL, OPTION_HIDDEN, "", 41 },
82 {"quiet", ARG_QUIET, 0, 0,
83 N_("Be quiet")},
79 {NULL} 84 {NULL}
80 }; 85 };
81 86
...@@ -125,6 +130,7 @@ static enum mhn_mode mode = mode_compose; ...@@ -125,6 +130,7 @@ static enum mhn_mode mode = mode_compose;
125 #define OPT_AUTO 010 130 #define OPT_AUTO 010
126 #define OPT_SERIALONLY 020 131 #define OPT_SERIALONLY 020
127 #define OPT_VERBOSE 040 132 #define OPT_VERBOSE 040
133 #define OPT_QUIET 100
128 134
129 static int mode_options = OPT_HEADERS; 135 static int mode_options = OPT_HEADERS;
130 static char *formfile; 136 static char *formfile;
...@@ -138,6 +144,12 @@ static mailbox_t mbox; ...@@ -138,6 +144,12 @@ static mailbox_t mbox;
138 static message_t message; 144 static message_t message;
139 static msg_part_t req_part; 145 static msg_part_t req_part;
140 146
147 /* Show flags */
148 #define MHN_EXCLUSIVE_EXEC 001
149 #define MHN_STDIN 002
150 #define MHN_LISTING 004
151 #define MHN_CONFIRM 010
152
141 void 153 void
142 sfree (char **ptr) 154 sfree (char **ptr)
143 { 155 {
...@@ -170,6 +182,60 @@ split_content (const char *content, char **type, char **subtype) ...@@ -170,6 +182,60 @@ split_content (const char *content, char **type, char **subtype)
170 } 182 }
171 } 183 }
172 184
185 int
186 _get_hdr_value (header_t hdr, const char *name, char **value)
187 {
188 int status = header_aget_value (hdr, name, value);
189 if (status == 0)
190 {
191 /* Remove the newlines. */
192 char *nl;
193 while ((nl = strchr (*value, '\n')) != NULL)
194 *nl = ' ';
195 }
196 return status;
197 }
198
199 int
200 _get_content_type (header_t hdr, char **value, char **rest)
201 {
202 char *type = NULL;
203 _get_hdr_value (hdr, MU_HEADER_CONTENT_TYPE, &type);
204 if (type == NULL || *type == '\0')
205 {
206 if (type)
207 free (type);
208 type = strdup ("text/plain"); /* Default. */
209 if (rest)
210 *rest = NULL;
211 }
212 else
213 {
214 char *p = strchr (type, ';');
215 if (p)
216 *p++ = 0;
217 if (rest)
218 *rest = p;
219 }
220 *value = type;
221 return 0;
222 }
223
224 static int
225 _get_content_encoding (header_t hdr, char **value)
226 {
227 char *encoding = NULL;
228 _get_hdr_value (hdr, MU_HEADER_CONTENT_TRANSFER_ENCODING, &encoding);
229 if (encoding == NULL || *encoding == '\0')
230 {
231 if (encoding)
232 free (encoding);
233 encoding = strdup ("7bit"); /* Default. */
234 }
235 *value = encoding;
236 return 0;
237 }
238
173 static int 239 static int
174 opt_handler (int key, char *arg, void *unused, struct argp_state *state) 240 opt_handler (int key, char *arg, void *unused, struct argp_state *state)
175 { 241 {
...@@ -316,7 +382,11 @@ opt_handler (int key, char *arg, void *unused, struct argp_state *state) ...@@ -316,7 +382,11 @@ opt_handler (int key, char *arg, void *unused, struct argp_state *state)
316 case ARG_PART: 382 case ARG_PART:
317 req_part = msg_part_parse (arg); 383 req_part = msg_part_parse (arg);
318 break; 384 break;
319 385
386 case ARG_QUIET:
387 mode_options |= OPT_QUIET;
388 break;
389
320 default: 390 default:
321 return 1; 391 return 1;
322 } 392 }
...@@ -405,6 +475,50 @@ msg_part_print (msg_part_t p, int max_width) ...@@ -405,6 +475,50 @@ msg_part_print (msg_part_t p, int max_width)
405 putchar (' '); 475 putchar (' ');
406 } 476 }
407 477
478 char *
479 msg_part_format (msg_part_t p)
480 {
481 int i;
482 int width = 0;
483 char *str, *s;
484 char buf[64];
485
486 for (i = 1; i <= p->level; i++)
487 {
488 if (i > 1)
489 width++;
490 width += snprintf (buf, sizeof buf, "%lu", (unsigned long) p->part[i]);
491 }
492
493 str = s = xmalloc (width + 1);
494 for (i = 1; i <= p->level; i++)
495 {
496 if (i > 1)
497 *s++ = '.';
498 s += sprintf (s, "%lu", (unsigned long) p->part[i]);
499 }
500 *s = 0;
501 return str;
502 }
503
504 void
505 msg_part_format_stk (struct obstack *stk, msg_part_t p)
506 {
507 int i;
508 char buf[64];
509
510 for (i = 1; i <= p->level; i++)
511 {
512 int len;
513
514 if (i > 1)
515 obstack_1grow (stk, '.');
516
517 len = snprintf (buf, sizeof buf, "%lu", (unsigned long) p->part[i]);
518 obstack_grow (stk, buf, len);
519 }
520 }
521
408 msg_part_t 522 msg_part_t
409 msg_part_parse (char *str) 523 msg_part_parse (char *str)
410 { 524 {
...@@ -449,58 +563,239 @@ msg_part_subpart (msg_part_t p, int level) ...@@ -449,58 +563,239 @@ msg_part_subpart (msg_part_t p, int level)
449 } 563 }
450 564
451 565
452 /* ************************** Message iterators *************************** */ 566 /* *********************** Context file accessors ************************* */
453 567
454 int 568 char *
455 _get_hdr_value (header_t hdr, const char *name, char **value) 569 _mhn_profile_get (char *prefix, char *type, char *subtype, char *defval)
456 { 570 {
457 int status = header_aget_value (hdr, name, value); 571 char *str, *name;
458 if (status == 0) 572
573 if (subtype)
459 { 574 {
460 /* Remove the newlines. */ 575 asprintf (&name, "mhn-%s-%s/%s", prefix, type, subtype);
461 char *nl; 576 str = mh_global_profile_get (name, NULL);
462 while ((nl = strchr (*value, '\n')) != NULL) 577 free (name);
463 *nl = ' '; 578 if (!str)
579 return _mhn_profile_get (prefix, type, NULL, defval);
464 } 580 }
465 return status; 581 else
582 {
583 asprintf (&name, "mhn-%s-%s", prefix, type);
584 str = mh_global_profile_get (name, defval);
585 free (name);
586 }
587 return str;
466 } 588 }
467 589
468 int 590 char *
469 _get_content_type (header_t hdr, char **value) 591 mhn_show_command (message_t msg, msg_part_t part, int *flags, char **tempfile)
470 { 592 {
471 char *type = NULL; 593 char *p, *str, *tmp;
472 _get_hdr_value (hdr, MU_HEADER_CONTENT_TYPE, &type); 594 char *typestr, *type, *subtype, *typeargs;
473 if (type == NULL || *type == '\0') 595 struct obstack stk;
596 header_t hdr;
597
598 message_get_header (msg, &hdr);
599 _get_content_type (hdr, &typestr, &typeargs);
600 split_content (typestr, &type, &subtype);
601 str = _mhn_profile_get ("show", type, subtype, NULL);
602 if (!str) /* FIXME */
603 return NULL;
604
605 /* Expand macro-notations:
606 %a additional arguments
607 %e exclusive execution
608 %f filename containing content
609 %F %e, %f, and stdin is terminal not content
610 %l display listing prior to displaying content
611 %p %l, and ask for confirmation
612 %s subtype
613 %d content description */
614
615 obstack_init (&stk);
616 for (p = str; *p && isspace (*p); p++)
617 ;
618
619 if (*p == '|')
620 p++;
621
622 for ( ; *p; p++)
474 { 623 {
475 if (type) 624 if (*p == '%')
476 free (type); 625 {
477 type = strdup ("text/plain"); /* Default. */ 626 switch (*++p)
627 {
628 case 'a':
629 /* additional arguments */
630 obstack_grow (&stk, typeargs, strlen (typeargs));
631 break;
632
633 case 'e':
634 /* exclusive execution */
635 *flags |= MHN_EXCLUSIVE_EXEC;
636 break;
637
638 case 'f':
639 /* filename containing content */
640 if (!*tempfile)
641 *tempfile = mu_tempname (NULL);
642 obstack_grow (&stk, *tempfile, strlen (*tempfile));
643 break;
644
645 case 'F':
646 /* %e, %f, and stdin is terminal not content */
647 *flags |= MHN_STDIN|MHN_EXCLUSIVE_EXEC;
648 if (!*tempfile)
649 *tempfile = mu_tempname (NULL);
650 obstack_grow (&stk, *tempfile, strlen (*tempfile));
651 break;
652
653 case 'l':
654 /* display listing prior to displaying content */
655 *flags |= MHN_LISTING;
656 break;
657
658 case 'p':
659 /* %l, and ask for confirmation */
660 *flags |= MHN_LISTING|MHN_CONFIRM;
661 break;
662
663 case 's':
664 /* subtype */
665 obstack_grow (&stk, subtype, strlen (subtype));
666 break;
667
668 case 'd':
669 /* content description */
670 if (header_aget_value (hdr, MU_HEADER_CONTENT_DESCRIPTION,
671 &tmp) == 0)
672 {
673 obstack_grow (&stk, tmp, strlen (tmp));
674 free (tmp);
675 }
676 break;
677
678 default:
679 obstack_1grow (&stk, *p);
680 p++;
681 }
682 }
683 else
684 obstack_1grow (&stk, *p);
478 } 685 }
686 obstack_1grow (&stk, 0);
687
688 free (typestr);
689 free (type);
690 free (subtype);
691
692 str = obstack_finish (&stk);
693 for (p = str; *p && isspace (*p); p++)
694 ;
695 if (!*p)
696 str = NULL;
479 else 697 else
480 { 698 str = strdup (str);
481 char *p = strchr (type, ';'); 699
482 if (p) 700 obstack_free (&stk, NULL);
483 *p = 0; 701 return str;
484 }
485 *value = type;
486 return 0;
487 } 702 }
488 703
489 static int 704 char *
490 _get_content_encoding (header_t hdr, char **value) 705 mhn_store_command (message_t msg, msg_part_t part, char *name)
491 { 706 {
492 char *encoding = NULL; 707 char *p, *str, *tmp;
493 _get_hdr_value (hdr, MU_HEADER_CONTENT_TRANSFER_ENCODING, &encoding); 708 char *typestr, *type, *subtype, *typeargs;
494 if (encoding == NULL || *encoding == '\0') 709 struct obstack stk;
710 header_t hdr;
711 char buf[64];
712
713 message_get_header (msg, &hdr);
714 _get_content_type (hdr, &typestr, &typeargs);
715 split_content (typestr, &type, &subtype);
716 str = _mhn_profile_get ("show", type, subtype, "%m%P.%s");
717
718 /* Expand macro-notations:
719 %m message number
720 %P .part
721 %p part
722 %s subtype */
723
724 obstack_init (&stk);
725 for (p = str; *p; p++)
495 { 726 {
496 if (encoding) 727 if (*p == '%')
497 free (encoding); 728 {
498 encoding = strdup ("7bit"); /* Default. */ 729 switch (*++p)
730 {
731 case 'a':
732 /* additional arguments */
733 obstack_grow (&stk, typeargs, strlen (typeargs));
734 break;
735
736 case 'm':
737 if (name)
738 obstack_grow (&stk, name, strlen (name));
739 else
740 {
741 snprintf (buf, sizeof buf, "%lu",
742 (unsigned long) msg_part_subpart (part, 0));
743 obstack_grow (&stk, buf, strlen (buf));
744 }
745 break;
746
747 case 'P':
748 obstack_1grow (&stk, '.');
749 /*FALLTHRU*/
750 case 'p':
751 msg_part_format_stk (&stk, part);
752 break;
753
754 case 's':
755 /* subtype */
756 obstack_grow (&stk, subtype, strlen (subtype));
757 break;
758
759 case 'd':
760 /* content description */
761 if (header_aget_value (hdr, MU_HEADER_CONTENT_DESCRIPTION,
762 &tmp) == 0)
763 {
764 obstack_grow (&stk, tmp, strlen (tmp));
765 free (tmp);
766 }
767 break;
768
769 default:
770 obstack_1grow (&stk, *p);
771 p++;
772 }
773 }
774 else
775 obstack_1grow (&stk, *p);
499 } 776 }
500 *value = encoding; 777 obstack_1grow (&stk, *p);
501 return 0; 778
779 free (typestr);
780 free (type);
781 free (subtype);
782
783 str = obstack_finish (&stk);
784 for (p = str; *p && isspace (*p); p++)
785 ;
786 if (!*p)
787 str = NULL;
788 else
789 str = strdup (str);
790
791 obstack_free (&stk, NULL);
792 return str;
502 } 793 }
503 794
795
796 /* ************************** Message iterators *************************** */
797
798
504 typedef int (*msg_handler_t) __PMT((message_t msg, msg_part_t part, 799 typedef int (*msg_handler_t) __PMT((message_t msg, msg_part_t part,
505 char *type, char *encoding, 800 char *type, char *encoding,
506 void *data)); 801 void *data));
...@@ -518,8 +813,11 @@ match_content (char *content) ...@@ -518,8 +813,11 @@ match_content (char *content)
518 813
519 split_content (content, &type, &subtype); 814 split_content (content, &type, &subtype);
520 815
521 if (strcasecmp (content_type, type) == 0) 816 if ((rc = strcasecmp (content_type, type)) == 0)
522 rc = strcasecmp (content_subtype, subtype); 817 {
818 if (content_subtype)
819 rc = strcasecmp (content_subtype, subtype);
820 }
523 else 821 else
524 rc = strcasecmp (content_type, subtype); 822 rc = strcasecmp (content_type, subtype);
525 823
...@@ -548,7 +846,7 @@ handle_message (message_t msg, msg_part_t part, msg_handler_t fun, void *data) ...@@ -548,7 +846,7 @@ handle_message (message_t msg, msg_part_t part, msg_handler_t fun, void *data)
548 int ismime = 0; 846 int ismime = 0;
549 847
550 message_get_header (msg, &hdr); 848 message_get_header (msg, &hdr);
551 _get_content_type (hdr, &type); 849 _get_content_type (hdr, &type, NULL);
552 _get_content_encoding (hdr, &encoding); 850 _get_content_encoding (hdr, &encoding);
553 851
554 fun (msg, part, type, encoding, data); 852 fun (msg, part, type, encoding, data);
...@@ -570,12 +868,12 @@ handle_message (message_t msg, msg_part_t part, msg_handler_t fun, void *data) ...@@ -570,12 +868,12 @@ handle_message (message_t msg, msg_part_t part, msg_handler_t fun, void *data)
570 if (message_get_part (msg, i, &message) == 0) 868 if (message_get_part (msg, i, &message) == 0)
571 { 869 {
572 message_get_header (message, &hdr); 870 message_get_header (message, &hdr);
573 _get_content_type (hdr, &type); 871 _get_content_type (hdr, &type, NULL);
574 _get_content_encoding (hdr, &encoding); 872 _get_content_encoding (hdr, &encoding);
575 873
576 msg_part_set_subpart (part, i); 874 msg_part_set_subpart (part, i);
577 875
578 if (strcasecmp (type, "multipart/mixed") == 0) 876 if (message_is_multipart (message, &ismime) == 0 && ismime)
579 handle_message (message, part, fun, data); 877 handle_message (message, part, fun, data);
580 else 878 else
581 call_handler (message, part, type, encoding, fun, data); 879 call_handler (message, part, type, encoding, fun, data);
...@@ -641,18 +939,23 @@ list_handler (message_t msg, msg_part_t part, char *type, char *encoding, ...@@ -641,18 +939,23 @@ list_handler (message_t msg, msg_part_t part, char *type, char *encoding,
641 header_t hdr; 939 header_t hdr;
642 940
643 if (msg_part_level (part) == 0) 941 if (msg_part_level (part) == 0)
644 printf ("%3lu ", (unsigned long) msg_part_subpart (part, 0)); 942 printf ("%4lu ", (unsigned long) msg_part_subpart (part, 0));
645 else 943 else
646 { 944 {
647 printf (" "); 945 printf (" ");
648 msg_part_print (part, 5); 946 msg_part_print (part, 4);
649 putchar (' '); 947 putchar (' ');
650 } 948 }
651 printf ("%-26s", type); 949 printf ("%-25s", type);
652 950
653 mhn_message_size (msg, &size); 951 mhn_message_size (msg, &size);
654 printf ("%4lu", (unsigned long) size); 952 if (size < 1024)
655 953 printf (" %4lu", (unsigned long) size);
954 else if (size < 1024*1024)
955 printf ("%4luK", (unsigned long) size / 1024);
956 else
957 printf ("%4luM", (unsigned long) size / 1024 / 1024);
958
656 if (message_get_header (msg, &hdr) == 0) 959 if (message_get_header (msg, &hdr) == 0)
657 { 960 {
658 char *descr; 961 char *descr;
...@@ -670,7 +973,11 @@ list_handler (message_t msg, msg_part_t part, char *type, char *encoding, ...@@ -670,7 +973,11 @@ list_handler (message_t msg, msg_part_t part, char *type, char *encoding,
670 int 973 int
671 list_message (message_t msg, size_t num) 974 list_message (message_t msg, size_t num)
672 { 975 {
673 msg_part_t part = msg_part_create (num); 976 size_t uid;
977 msg_part_t part;
978
979 mh_message_number (msg, &uid);
980 part = msg_part_create (uid);
674 handle_message (msg, part, list_handler, NULL); 981 handle_message (msg, part, list_handler, NULL);
675 msg_part_destroy (part); 982 msg_part_destroy (part);
676 return 0; 983 return 0;
...@@ -688,7 +995,7 @@ mhn_list () ...@@ -688,7 +995,7 @@ mhn_list ()
688 int rc; 995 int rc;
689 996
690 if (mode_options & OPT_HEADERS) 997 if (mode_options & OPT_HEADERS)
691 printf (_("msg part type/subtype size description\n")); 998 printf (_(" msg part type/subtype size description\n"));
692 999
693 if (message) 1000 if (message)
694 rc = list_message (message, 0); 1001 rc = list_message (message, 0);
...@@ -717,43 +1024,189 @@ cat_message (stream_t out, stream_t in) ...@@ -717,43 +1024,189 @@ cat_message (stream_t out, stream_t in)
717 } 1024 }
718 1025
719 int 1026 int
1027 show_internal (message_t msg, msg_part_t part, char *encoding, stream_t out)
1028 {
1029 int rc;
1030 body_t body = NULL;
1031 stream_t dstr, bstr;
1032
1033 if ((rc = message_get_body (msg, &body)))
1034 {
1035 mh_error (_("%lu: can't get message body: %s"),
1036 (unsigned long) msg_part_subpart (part, 0),
1037 mu_strerror (rc));
1038 return 0;
1039 }
1040 body_get_stream (body, &bstr);
1041 rc = filter_create(&dstr, bstr, encoding,
1042 MU_FILTER_DECODE, MU_STREAM_READ);
1043 if (rc == 0)
1044 bstr = dstr;
1045 cat_message (out, bstr);
1046 if (dstr)
1047 stream_destroy (&dstr, stream_get_owner (dstr));
1048 return 0;
1049 }
1050
1051 int
1052 exec_internal (message_t msg, msg_part_t part, char *encoding, char *cmd)
1053 {
1054 int rc;
1055 stream_t tmp;
1056
1057 rc = prog_stream_create (&tmp, cmd, MU_STREAM_WRITE);
1058 if (rc)
1059 {
1060 mh_error (_("can't create proc stream (command %s): %s"),
1061 cmd, mu_strerror (rc));
1062 return rc;
1063 }
1064 rc = stream_open (tmp);
1065 if (rc)
1066 {
1067 mh_error (_("can't open proc stream (command %s): %s"),
1068 cmd, mu_strerror (rc));
1069 return rc;
1070 }
1071 show_internal (msg, part, encoding, tmp);
1072 stream_destroy (&tmp, stream_get_owner (tmp));
1073 return rc;
1074 }
1075
1076 /* FIXME: stdin is always opened when invoking subprocess, no matter
1077 what the MHN_STDIN bit is */
1078
1079 int
1080 mhn_run_command (message_t msg, msg_part_t part,
1081 char *encoding,
1082 char *cmd, int flags,
1083 char *tempfile)
1084 {
1085 int rc = 0;
1086
1087 if (tempfile)
1088 {
1089 /* pass content via a tempfile */
1090 int status;
1091 int argc;
1092 char **argv;
1093 stream_t tmp;
1094
1095 if (argcv_get (cmd, "", "#", &argc, &argv))
1096 {
1097 mh_error (_("can't parse command line `%s'"), cmd);
1098 return ENOSYS;
1099 }
1100
1101 rc = file_stream_create (&tmp, tempfile, MU_STREAM_RDWR);
1102 if (rc)
1103 {
1104 mh_error (_("can't create temporary stream (file %s): %s"),
1105 tempfile, mu_strerror (rc));
1106 argcv_free (argc, argv);
1107 return rc;
1108 }
1109 rc = stream_open (tmp);
1110 if (rc)
1111 {
1112 mh_error (_("can't open temporary stream (file %s): %s"),
1113 tempfile, mu_strerror (rc));
1114 stream_destroy (&tmp, stream_get_owner (tmp));
1115 argcv_free (argc, argv);
1116 return rc;
1117 }
1118 show_internal (msg, part, encoding, tmp);
1119 stream_destroy (&tmp, stream_get_owner (tmp));
1120 rc = mu_spawnvp (argv[0], argv, &status);
1121 if (status)
1122 rc = status;
1123 argcv_free (argc, argv);
1124 }
1125 else
1126 rc = exec_internal (msg, part, encoding, cmd);
1127
1128 return rc;
1129 }
1130
1131 int
720 show_handler (message_t msg, msg_part_t part, char *type, char *encoding, 1132 show_handler (message_t msg, msg_part_t part, char *type, char *encoding,
721 void *data) 1133 void *data)
722 { 1134 {
723 stream_t out = data; 1135 stream_t out = data;
724 1136 char *cmd;
725 if (strncasecmp (type, "multipart", 9) == 0) 1137 int flags = 0;
1138 char buf[64];
1139 int fd = 1;
1140 char *tempfile = NULL;
1141 int ismime;
1142
1143 if (message_is_multipart (msg, &ismime) == 0 && ismime)
726 return 0; 1144 return 0;
727 if (mhl_format) 1145
728 mhl_format_run (mhl_format, width, 0, MHL_DECODE, msg, out); 1146 stream_get_fd (out, &fd);
729 else 1147
1148 if (mode_options & OPT_PAUSE)
1149 flags |= MHN_CONFIRM;
1150 cmd = mhn_show_command (msg, part, &flags, &tempfile);
1151 if (!cmd)
1152 flags |= MHN_LISTING;
1153 if (flags & MHN_LISTING)
730 { 1154 {
731 int rc; 1155 char *str;
732 body_t body = NULL; 1156 size_t size = 0;
733 stream_t dstr, bstr; 1157
734 1158 str = _("part ");
735 if ((rc = message_get_body (msg, &body))) 1159 stream_sequential_write (out, str, strlen (str));
1160 str = msg_part_format (part);
1161 stream_sequential_write (out, str, strlen (str));
1162 free (str);
1163 stream_sequential_write (out, " ", 1);
1164 stream_sequential_write (out, type, strlen (type));
1165 mhn_message_size (msg, &size);
1166 snprintf (buf, sizeof buf, " %lu", (unsigned long) size);
1167 stream_sequential_write (out, buf, strlen (buf));
1168 stream_sequential_write (out, "\n", 1);
1169 stream_flush (out);
1170 }
1171
1172 if (flags & MHN_CONFIRM)
1173 {
1174 if (isatty (fd) && isatty (0))
736 { 1175 {
737 mh_error (_("%lu: can't get message body: %s"), 1176 printf (_("Press <return> to show content..."));
738 (unsigned long) msg_part_subpart (part, 0), 1177 if (!fgets (buf, sizeof buf, stdin) || buf[0] != '\n')
739 mu_strerror (rc)); 1178 return 0;
740 return 0;
741 } 1179 }
742 body_get_stream (body, &bstr); 1180 }
743 rc = filter_create(&dstr, bstr, encoding, 1181
744 MU_FILTER_DECODE, MU_STREAM_READ); 1182 if (!cmd)
745 if (rc == 0) 1183 {
746 bstr = dstr; 1184 char *pager = mh_global_profile_get ("moreproc", getenv ("PAGER"));
747 cat_message (out, bstr); 1185 if (pager)
748 if (dstr) 1186 exec_internal (msg, part, encoding, pager);
749 stream_destroy (&dstr, stream_get_owner (dstr)); 1187 else
1188 show_internal (msg, part, encoding, out);
1189 }
1190 else
1191 {
1192 mhn_run_command (msg, part, encoding, cmd, flags, tempfile);
1193 free (cmd);
1194 unlink (tempfile);
1195 free (tempfile);
750 } 1196 }
1197
1198 return 0;
751 } 1199 }
752 1200
753 int 1201 int
754 show_message (message_t msg, size_t num, void *data) 1202 show_message (message_t msg, size_t num, void *data)
755 { 1203 {
756 msg_part_t part = msg_part_create (num); 1204 msg_part_t part = msg_part_create (num);
1205
1206 if (mhl_format)
1207 mhl_format_run (mhl_format, width, 0, MHL_DISABLE_BODY, msg,
1208 (stream_t) data);
1209
757 handle_message (msg, part, show_handler, data); 1210 handle_message (msg, part, show_handler, data);
758 msg_part_destroy (part); 1211 msg_part_destroy (part);
759 return 0; 1212 return 0;
...@@ -762,7 +1215,12 @@ show_message (message_t msg, size_t num, void *data) ...@@ -762,7 +1215,12 @@ show_message (message_t msg, size_t num, void *data)
762 void 1215 void
763 show_iterator (mailbox_t mbox, message_t msg, size_t num, void *data) 1216 show_iterator (mailbox_t mbox, message_t msg, size_t num, void *data)
764 { 1217 {
1218 msg_part_t part;
1219
1220 mh_message_number (msg, &num);
1221 part = msg_part_create (num);
765 show_message (msg, num, data); 1222 show_message (msg, num, data);
1223 msg_part_destroy (part);
766 } 1224 }
767 1225
768 int 1226 int
...@@ -807,6 +1265,222 @@ mhn_show () ...@@ -807,6 +1265,222 @@ mhn_show ()
807 rc = mh_iterate (mbox, &msgset, show_iterator, ostr); 1265 rc = mh_iterate (mbox, &msgset, show_iterator, ostr);
808 return rc; 1266 return rc;
809 } 1267 }
1268
1269 /* ***************************** Store Mode ****************************** */
1270
1271 char *
1272 normalize_path (char *cwd, char *path)
1273 {
1274 int len;
1275 char *p;
1276 char *pcwd = NULL;
1277
1278 if (!path)
1279 return path;
1280
1281 if (path[0] == '/')
1282 return NULL;
1283
1284 if (!cwd)
1285 cwd = pcwd = mu_getcwd ();
1286
1287 len = strlen (cwd) + strlen (path) + 2;
1288 p = xmalloc (len);
1289 sprintf (p, "%s/%s", cwd, path);
1290 path = p;
1291
1292 /* delete trailing delimiter if any */
1293 if (len && path[len-1] == '/')
1294 path[len-1] = 0;
1295
1296 /* Eliminate any /../ */
1297 for (p = strchr (path, '.'); p; p = strchr (p, '.'))
1298 {
1299 if (p > path && p[-1] == '/')
1300 {
1301 if (p[1] == '.' && (p[2] == 0 || p[2] == '/'))
1302 /* found */
1303 {
1304 char *q, *s;
1305
1306 /* Find previous delimiter */
1307 for (q = p-2; *q != '/' && q >= path; q--)
1308 ;
1309
1310 if (q < path)
1311 break;
1312 /* Copy stuff */
1313 s = p + 2;
1314 p = q;
1315 while (*q++ = *s++)
1316 ;
1317 continue;
1318 }
1319 }
1320
1321 p++;
1322 }
1323
1324 if (path[0] == 0)
1325 {
1326 path[0] = '/';
1327 path[1] = 0;
1328 }
1329
1330 len = strlen (cwd);
1331 if (strlen (path) < len || memcmp (path, cwd, len))
1332 sfree (&path);
1333 else
1334 memmove (path, path + len + 1, strlen (path) - len);
1335 free (pcwd);
1336 return path;
1337 }
1338
1339 int
1340 store_handler (message_t msg, msg_part_t part, char *type, char *encoding,
1341 void *data)
1342 {
1343 char *prefix = data;
1344 char *name = NULL;
1345 char *tmp;
1346 int ismime;
1347 int rc;
1348 stream_t out;
1349 char *dir = mh_global_profile_get ("mhn-storage", NULL);
1350
1351 if (message_is_multipart (msg, &ismime) == 0 && ismime)
1352 return 0;
1353
1354 if (mode_options & OPT_AUTO)
1355 {
1356 header_t hdr;
1357 char *val;
1358
1359 if (message_get_header (msg, &hdr) == 0
1360 && header_aget_value (hdr, MU_HEADER_CONTENT_DISPOSITION, &val) == 0)
1361 {
1362 int argc;
1363 char **argv;
1364
1365 if (argcv_get (val, "=", NULL, &argc, &argv) == 0)
1366 {
1367 int i;
1368
1369 for (i = 0; i < argc; i++)
1370 {
1371 if (strcmp (argv[i], "filename") == 0
1372 && ++i < argc
1373 && argv[i][0] == '='
1374 && ++i < argc)
1375 {
1376 name = normalize_path (dir, argv[i]);
1377 break;
1378 }
1379 }
1380 argcv_free (argc, argv);
1381 }
1382 free (val);
1383 }
1384 }
1385
1386 if (!name)
1387 {
1388 char *fname = mhn_store_command (msg, part, prefix);
1389 if (dir)
1390 asprintf (&name, "%s/%s", dir, fname);
1391 else
1392 name = fname;
1393 }
1394
1395 tmp = msg_part_format (part);
1396 if (prefix)
1397 printf (_("storing message %s part %s as file %s\n"),
1398 prefix,
1399 tmp,
1400 name);
1401 else
1402 printf (_("storing message %lu part %s as file %s\n"),
1403 (unsigned long) msg_part_subpart (part, 0),
1404 tmp,
1405 name);
1406 free (tmp);
1407
1408 if (!(mode_options & OPT_QUIET) && access (name, R_OK) == 0)
1409 {
1410 char *p;
1411 int rc;
1412
1413 asprintf (&p, _("File %s already exists. Rewrite"), name);
1414 rc = mh_getyn (p);
1415 free (p);
1416 if (!rc)
1417 {
1418 free (name);
1419 return 0;
1420 }
1421 }
1422
1423 rc = file_stream_create (&out, name, MU_STREAM_WRITE|MU_STREAM_CREAT);
1424 if (rc)
1425 {
1426 mh_error (_("can't create output stream (file %s): %s"),
1427 name, mu_strerror (rc));
1428 free (name);
1429 return rc;
1430 }
1431 rc = stream_open (out);
1432 if (rc)
1433 {
1434 mh_error (_("can't open output stream (file %s): %s"),
1435 name, mu_strerror (rc));
1436 free (name);
1437 stream_destroy (&out, stream_get_owner (out));
1438 return rc;
1439 }
1440 show_internal (msg, part, encoding, out);
1441
1442 stream_destroy (&out, stream_get_owner (out));
1443 free (name);
1444
1445 return 0;
1446 }
1447
1448 void
1449 store_message (message_t msg, void *data)
1450 {
1451 size_t uid;
1452 msg_part_t part;
1453
1454 mh_message_number (msg, &uid);
1455 part = msg_part_create (uid);
1456 handle_message (msg, part, store_handler, data);
1457 msg_part_destroy (part);
1458 }
1459
1460 void
1461 store_iterator (mailbox_t mbox, message_t msg, size_t num, void *data)
1462 {
1463 store_message (msg, data);
1464 }
1465
1466 int
1467 mhn_store ()
1468 {
1469 int rc = 0;
1470
1471 if (message)
1472 {
1473 char *p = strrchr (input_file, '/');
1474 if (p)
1475 p++;
1476 else
1477 p = input_file;
1478 store_message (message, p);
1479 }
1480 else
1481 rc = mh_iterate (mbox, &msgset, store_iterator, NULL);
1482 return rc;
1483 }
810 1484
811 1485
812 1486
...@@ -859,8 +1533,7 @@ main (int argc, char **argv) ...@@ -859,8 +1533,7 @@ main (int argc, char **argv)
859 break; 1533 break;
860 1534
861 case mode_store: 1535 case mode_store:
862 mh_error ("mode is not yet implemented"); 1536 rc = mhn_store ();
863 rc = 1;
864 break; 1537 break;
865 1538
866 default: 1539 default:
......