comparison src/lib-imap/imap-bodystructure.c @ 21628:a22181aad13a

lib-imap: imap-bodystructure: Grouped together functions relating to the conversion of BODYSTRUCTURE to BODY. This only moves code.
author Stephan Bosch <stephan.bosch@dovecot.fi>
date Sun, 23 Oct 2016 17:27:07 +0200
parents 6ced094b1759
children 4a61612dab4f
comparison
equal deleted inserted replaced
21627:6ced094b1759 21628:a22181aad13a
519 { 519 {
520 if (part->flags & MESSAGE_PART_FLAG_MULTIPART) 520 if (part->flags & MESSAGE_PART_FLAG_MULTIPART)
521 part_write_body_multipart(part, dest, extended); 521 part_write_body_multipart(part, dest, extended);
522 else 522 else
523 part_write_body(part, dest, extended); 523 part_write_body(part, dest, extended);
524 }
525
526 static int
527 imap_bodystructure_strlist_parse(const struct imap_arg *arg,
528 pool_t pool, const char *const **list_r)
529 {
530 const char *item, **list;
531 const struct imap_arg *list_args;
532 unsigned int list_count, i;
533
534 if (arg->type == IMAP_ARG_NIL) {
535 *list_r = NULL;
536 return 0;
537 }
538 if (imap_arg_get_nstring(arg, &item)) {
539 list = p_new(pool, const char *, 2);
540 list[0] = p_strdup(pool, item);
541 } else {
542 if (!imap_arg_get_list_full(arg, &list_args, &list_count))
543 return -1;
544
545 list = p_new(pool, const char *, list_count+1);
546 for (i = 0; i < list_count; i++) {
547 if (!imap_arg_get_nstring(&list_args[i], &item))
548 return -1;
549 list[i] = p_strdup(pool, item);
550 }
551 }
552 *list_r = list;
553 return 0;
554 }
555
556 static int
557 imap_bodystructure_params_parse(const struct imap_arg *arg,
558 pool_t pool, const struct message_part_param **params_r,
559 unsigned int *count_r)
560 {
561 struct message_part_param *params;
562 const struct imap_arg *list_args;
563 unsigned int list_count, params_count, i;
564
565 if (arg->type == IMAP_ARG_NIL) {
566 *params_r = NULL;
567 return 0;
568 }
569 if (!imap_arg_get_list_full(arg, &list_args, &list_count))
570 return -1;
571 if ((list_count % 2) != 0)
572 return -1;
573
574 params_count = list_count/2;
575 params = p_new(pool, struct message_part_param, params_count+1);
576 for (i = 0; i < params_count; i++) {
577 const char *name, *value;
578
579 if (!imap_arg_get_nstring(&list_args[i*2+0], &name))
580 return -1;
581 if (!imap_arg_get_nstring(&list_args[i*2+1], &value))
582 return -1;
583 params[i].name = p_strdup(pool, name);
584 params[i].value = p_strdup(pool, value);
585 }
586 *params_r = params;
587 *count_r = params_count;
588 return 0;
589 }
590
591 static int
592 imap_bodystructure_parse_args_common(struct message_part *part,
593 pool_t pool, const struct imap_arg *args,
594 const char **error_r)
595 {
596 struct message_part_data *data = part->data;
597 const struct imap_arg *list_args;
598
599 if (args->type == IMAP_ARG_EOL)
600 return 0;
601 if (args->type == IMAP_ARG_NIL)
602 args++;
603 else if (!imap_arg_get_list(args, &list_args)) {
604 *error_r = "Invalid content-disposition list";
605 return -1;
606 } else {
607 if (!imap_arg_get_nstring
608 (list_args++, &data->content_disposition)) {
609 *error_r = "Invalid content-disposition";
610 return -1;
611 }
612 data->content_disposition = p_strdup(pool, data->content_disposition);
613 if (imap_bodystructure_params_parse(list_args, pool,
614 &data->content_disposition_params,
615 &data->content_disposition_params_count) < 0) {
616 *error_r = "Invalid content-disposition params";
617 return -1;
618 }
619 args++;
620 }
621 if (args->type == IMAP_ARG_EOL)
622 return 0;
623 if (imap_bodystructure_strlist_parse
624 (args++, pool, &data->content_language) < 0) {
625 *error_r = "Invalid content-language";
626 return -1;
627 }
628 if (args->type == IMAP_ARG_EOL)
629 return 0;
630 if (!imap_arg_get_nstring
631 (args++, &data->content_location)) {
632 *error_r = "Invalid content-location";
633 return -1;
634 }
635 data->content_location = p_strdup(pool, data->content_location);
636 return 0;
637 }
638
639 static int
640 imap_bodystructure_parse_args(const struct imap_arg *args, pool_t pool,
641 struct message_part *part,
642 const char **error_r)
643 {
644 struct message_part_data *data;
645 struct message_part *child_part;
646 const struct imap_arg *list_args;
647 const char *value, *content_type, *subtype, *error;
648 bool multipart, text, message_rfc822, has_lines;
649 unsigned int lines;
650 uoff_t vsize;
651
652 i_assert(part->data == NULL);
653 part->data = data = p_new(pool, struct message_part_data, 1);
654
655 multipart = FALSE;
656 child_part = part->children;
657 while (args->type == IMAP_ARG_LIST) {
658 if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 ||
659 child_part == NULL) {
660 *error_r = "message_part hierarchy doesn't match BODYSTRUCTURE";
661 return -1;
662 }
663
664 list_args = imap_arg_as_list(args);
665 if (imap_bodystructure_parse_args(list_args, pool,
666 child_part, error_r) < 0)
667 return -1;
668 child_part = child_part->next;
669
670 multipart = TRUE;
671 args++;
672 }
673
674 if (multipart) {
675 if (child_part != NULL) {
676 *error_r = "message_part hierarchy doesn't match BODYSTRUCTURE";
677 return -1;
678 }
679 data->content_type = "multipart";
680 if (!imap_arg_get_nstring(args++, &data->content_subtype)) {
681 *error_r = "Invalid multipart content-type";
682 return -1;
683 }
684 data->content_subtype = p_strdup(pool, data->content_subtype);
685 if (args->type == IMAP_ARG_EOL)
686 return 0;
687 if (imap_bodystructure_params_parse(args++, pool,
688 &data->content_type_params,
689 &data->content_type_params_count) < 0) {
690 *error_r = "Invalid content params";
691 return -1;
692 }
693 return imap_bodystructure_parse_args_common
694 (part, pool, args, error_r);
695 }
696 if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) {
697 *error_r = "message_part multipart flag doesn't match BODYSTRUCTURE";
698 return -1;
699 }
700
701 /* "content type" "subtype" */
702 if (!imap_arg_get_astring(&args[0], &content_type) ||
703 !imap_arg_get_astring(&args[1], &subtype)) {
704 *error_r = "Invalid content-type";
705 return -1;
706 }
707 data->content_type = p_strdup(pool, content_type);
708 data->content_subtype = p_strdup(pool, subtype);
709 args += 2;
710
711 text = strcasecmp(content_type, "text") == 0;
712 message_rfc822 = strcasecmp(content_type, "message") == 0 &&
713 strcasecmp(subtype, "rfc822") == 0;
714
715 #if 0
716 /* Disabled for now. Earlier Dovecot versions handled broken
717 Content-Type headers by writing them as "text" "plain" to
718 BODYSTRUCTURE reply, but the message_part didn't have
719 MESSAGE_PART_FLAG_TEXT. */
720 if (text != ((part->flags & MESSAGE_PART_FLAG_TEXT) != 0)) {
721 *error_r = "message_part text flag doesn't match BODYSTRUCTURE";
722 return -1;
723 }
724 #endif
725 if (message_rfc822 != ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0)) {
726 *error_r = "message_part message/rfc822 flag doesn't match BODYSTRUCTURE";
727 return -1;
728 }
729
730 /* ("content type param key" "value" ...) | NIL */
731 if (imap_bodystructure_params_parse(args++, pool,
732 &data->content_type_params,
733 &data->content_type_params_count) < 0) {
734 *error_r = "Invalid content params";
735 return -1;
736 }
737
738 /* "content id" "content description" "transfer encoding" size */
739 if (!imap_arg_get_nstring(args++, &data->content_id)) {
740 *error_r = "Invalid content-id";
741 return -1;
742 }
743 if (!imap_arg_get_nstring(args++, &data->content_description)) {
744 *error_r = "Invalid content-description";
745 return -1;
746 }
747 if (!imap_arg_get_nstring(args++, &data->content_transfer_encoding)) {
748 *error_r = "Invalid content-transfer-encoding";
749 return -1;
750 }
751 data->content_id = p_strdup(pool, data->content_id);
752 data->content_description = p_strdup(pool, data->content_description);
753 data->content_transfer_encoding =
754 p_strdup(pool, data->content_transfer_encoding);
755 if (!imap_arg_get_atom(args++, &value) ||
756 str_to_uoff(value, &vsize) < 0) {
757 *error_r = "Invalid size field";
758 return -1;
759 }
760 if (vsize != part->body_size.virtual_size) {
761 *error_r = "message_part virtual_size doesn't match "
762 "size in BODYSTRUCTURE";
763 return -1;
764 }
765
766 if (text) {
767 /* text/xxx - text lines */
768 if (!imap_arg_get_atom(args++, &value) ||
769 str_to_uint(value, &lines) < 0) {
770 *error_r = "Invalid lines field";
771 return -1;
772 }
773 i_assert(part->children == NULL);
774 has_lines = TRUE;
775 } else if (message_rfc822) {
776 /* message/rfc822 - envelope + bodystructure + text lines */
777
778 i_assert(part->children != NULL &&
779 part->children->next == NULL);
780
781 if (!imap_arg_get_list(&args[1], &list_args)) {
782 *error_r = "Child bodystructure list expected";
783 return -1;
784 }
785 if (imap_bodystructure_parse_args
786 (list_args, pool, part->children, error_r) < 0)
787 return -1;
788
789 if (!imap_arg_get_list(&args[0], &list_args)) {
790 *error_r = "Envelope list expected";
791 return -1;
792 }
793 if (!imap_envelope_parse_args(list_args, pool,
794 &part->children->data->envelope, &error)) {
795 *error_r = t_strdup_printf
796 ("Invalid envelope list: %s", error);
797 return -1;
798 }
799 args += 2;
800 if (!imap_arg_get_atom(args++, &value) ||
801 str_to_uint(value, &lines) < 0) {
802 *error_r = "Invalid lines field";
803 return -1;
804 }
805 has_lines = TRUE;
806 } else {
807 i_assert(part->children == NULL);
808 has_lines = FALSE;
809 }
810 if (has_lines && lines != part->body_size.lines) {
811 *error_r = "message_part lines "
812 "doesn't match lines in BODYSTRUCTURE";
813 return -1;
814 }
815 if (args->type == IMAP_ARG_EOL)
816 return 0;
817 if (!imap_arg_get_nstring(args++, &data->content_md5)) {
818 *error_r = "Invalid content-md5";
819 return -1;
820 }
821 data->content_md5 = p_strdup(pool, data->content_md5);
822 return imap_bodystructure_parse_args_common
823 (part, pool, args, error_r);
824 }
825
826 int imap_bodystructure_parse(const char *bodystructure,
827 pool_t pool, struct message_part *parts,
828 const char **error_r)
829 {
830 struct istream *input;
831 struct imap_parser *parser;
832 const struct imap_arg *args;
833 char *error;
834 int ret;
835 bool fatal;
836
837 i_assert(parts != NULL);
838 i_assert(parts->next == NULL);
839
840 input = i_stream_create_from_data(bodystructure, strlen(bodystructure));
841 (void)i_stream_read(input);
842
843 parser = imap_parser_create(input, NULL, (size_t)-1);
844 ret = imap_parser_finish_line(parser, 0,
845 IMAP_PARSE_FLAG_LITERAL_TYPE, &args);
846 if (ret < 0) {
847 *error_r = t_strdup_printf("IMAP parser failed: %s",
848 imap_parser_get_error(parser, &fatal));
849 } else if (ret == 0) {
850 *error_r = "Empty bodystructure";
851 ret = -1;
852 } else {
853 T_BEGIN {
854 ret = imap_bodystructure_parse_args
855 (args, pool, parts, error_r);
856 if (ret < 0)
857 error = i_strdup(*error_r);
858 } T_END;
859
860 if (ret < 0) {
861 *error_r = t_strdup(error);
862 i_free(error);
863 }
864 }
865
866 imap_parser_unref(&parser);
867 i_stream_destroy(&input);
868 return ret;
524 } 869 }
525 870
526 static bool str_append_nstring(string_t *str, const struct imap_arg *arg) 871 static bool str_append_nstring(string_t *str, const struct imap_arg *arg)
527 { 872 {
528 const char *cstr; 873 const char *cstr;
589 imap_write_envelope(const struct imap_arg *args, string_t *str) 934 imap_write_envelope(const struct imap_arg *args, string_t *str)
590 { 935 {
591 imap_write_envelope_list(args, str, TRUE); 936 imap_write_envelope_list(args, str, TRUE);
592 } 937 }
593 938
594 static int
595 imap_bodystructure_strlist_parse(const struct imap_arg *arg,
596 pool_t pool, const char *const **list_r)
597 {
598 const char *item, **list;
599 const struct imap_arg *list_args;
600 unsigned int list_count, i;
601
602 if (arg->type == IMAP_ARG_NIL) {
603 *list_r = NULL;
604 return 0;
605 }
606 if (imap_arg_get_nstring(arg, &item)) {
607 list = p_new(pool, const char *, 2);
608 list[0] = p_strdup(pool, item);
609 } else {
610 if (!imap_arg_get_list_full(arg, &list_args, &list_count))
611 return -1;
612
613 list = p_new(pool, const char *, list_count+1);
614 for (i = 0; i < list_count; i++) {
615 if (!imap_arg_get_nstring(&list_args[i], &item))
616 return -1;
617 list[i] = p_strdup(pool, item);
618 }
619 }
620 *list_r = list;
621 return 0;
622 }
623
624 static int
625 imap_bodystructure_params_parse(const struct imap_arg *arg,
626 pool_t pool, const struct message_part_param **params_r,
627 unsigned int *count_r)
628 {
629 struct message_part_param *params;
630 const struct imap_arg *list_args;
631 unsigned int list_count, params_count, i;
632
633 if (arg->type == IMAP_ARG_NIL) {
634 *params_r = NULL;
635 return 0;
636 }
637 if (!imap_arg_get_list_full(arg, &list_args, &list_count))
638 return -1;
639 if ((list_count % 2) != 0)
640 return -1;
641
642 params_count = list_count/2;
643 params = p_new(pool, struct message_part_param, params_count+1);
644 for (i = 0; i < params_count; i++) {
645 const char *name, *value;
646
647 if (!imap_arg_get_nstring(&list_args[i*2+0], &name))
648 return -1;
649 if (!imap_arg_get_nstring(&list_args[i*2+1], &value))
650 return -1;
651 params[i].name = p_strdup(pool, name);
652 params[i].value = p_strdup(pool, value);
653 }
654 *params_r = params;
655 *count_r = params_count;
656 return 0;
657 }
658
659 static int
660 imap_bodystructure_parse_args_common(struct message_part *part,
661 pool_t pool, const struct imap_arg *args,
662 const char **error_r)
663 {
664 struct message_part_data *data = part->data;
665 const struct imap_arg *list_args;
666
667 if (args->type == IMAP_ARG_EOL)
668 return 0;
669 if (args->type == IMAP_ARG_NIL)
670 args++;
671 else if (!imap_arg_get_list(args, &list_args)) {
672 *error_r = "Invalid content-disposition list";
673 return -1;
674 } else {
675 if (!imap_arg_get_nstring
676 (list_args++, &data->content_disposition)) {
677 *error_r = "Invalid content-disposition";
678 return -1;
679 }
680 data->content_disposition = p_strdup(pool, data->content_disposition);
681 if (imap_bodystructure_params_parse(list_args, pool,
682 &data->content_disposition_params,
683 &data->content_disposition_params_count) < 0) {
684 *error_r = "Invalid content-disposition params";
685 return -1;
686 }
687 args++;
688 }
689 if (args->type == IMAP_ARG_EOL)
690 return 0;
691 if (imap_bodystructure_strlist_parse
692 (args++, pool, &data->content_language) < 0) {
693 *error_r = "Invalid content-language";
694 return -1;
695 }
696 if (args->type == IMAP_ARG_EOL)
697 return 0;
698 if (!imap_arg_get_nstring
699 (args++, &data->content_location)) {
700 *error_r = "Invalid content-location";
701 return -1;
702 }
703 data->content_location = p_strdup(pool, data->content_location);
704 return 0;
705 }
706
707 static int
708 imap_bodystructure_parse_args(const struct imap_arg *args, pool_t pool,
709 struct message_part *part,
710 const char **error_r)
711 {
712 struct message_part_data *data;
713 struct message_part *child_part;
714 const struct imap_arg *list_args;
715 const char *value, *content_type, *subtype, *error;
716 bool multipart, text, message_rfc822, has_lines;
717 unsigned int lines;
718 uoff_t vsize;
719
720 i_assert(part->data == NULL);
721 part->data = data = p_new(pool, struct message_part_data, 1);
722
723 multipart = FALSE;
724 child_part = part->children;
725 while (args->type == IMAP_ARG_LIST) {
726 if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0 ||
727 child_part == NULL) {
728 *error_r = "message_part hierarchy doesn't match BODYSTRUCTURE";
729 return -1;
730 }
731
732 list_args = imap_arg_as_list(args);
733 if (imap_bodystructure_parse_args(list_args, pool,
734 child_part, error_r) < 0)
735 return -1;
736 child_part = child_part->next;
737
738 multipart = TRUE;
739 args++;
740 }
741
742 if (multipart) {
743 if (child_part != NULL) {
744 *error_r = "message_part hierarchy doesn't match BODYSTRUCTURE";
745 return -1;
746 }
747 data->content_type = "multipart";
748 if (!imap_arg_get_nstring(args++, &data->content_subtype)) {
749 *error_r = "Invalid multipart content-type";
750 return -1;
751 }
752 data->content_subtype = p_strdup(pool, data->content_subtype);
753 if (args->type == IMAP_ARG_EOL)
754 return 0;
755 if (imap_bodystructure_params_parse(args++, pool,
756 &data->content_type_params,
757 &data->content_type_params_count) < 0) {
758 *error_r = "Invalid content params";
759 return -1;
760 }
761 return imap_bodystructure_parse_args_common
762 (part, pool, args, error_r);
763 }
764 if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) {
765 *error_r = "message_part multipart flag doesn't match BODYSTRUCTURE";
766 return -1;
767 }
768
769 /* "content type" "subtype" */
770 if (!imap_arg_get_astring(&args[0], &content_type) ||
771 !imap_arg_get_astring(&args[1], &subtype)) {
772 *error_r = "Invalid content-type";
773 return -1;
774 }
775 data->content_type = p_strdup(pool, content_type);
776 data->content_subtype = p_strdup(pool, subtype);
777 args += 2;
778
779 text = strcasecmp(content_type, "text") == 0;
780 message_rfc822 = strcasecmp(content_type, "message") == 0 &&
781 strcasecmp(subtype, "rfc822") == 0;
782
783 #if 0
784 /* Disabled for now. Earlier Dovecot versions handled broken
785 Content-Type headers by writing them as "text" "plain" to
786 BODYSTRUCTURE reply, but the message_part didn't have
787 MESSAGE_PART_FLAG_TEXT. */
788 if (text != ((part->flags & MESSAGE_PART_FLAG_TEXT) != 0)) {
789 *error_r = "message_part text flag doesn't match BODYSTRUCTURE";
790 return -1;
791 }
792 #endif
793 if (message_rfc822 != ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0)) {
794 *error_r = "message_part message/rfc822 flag doesn't match BODYSTRUCTURE";
795 return -1;
796 }
797
798 /* ("content type param key" "value" ...) | NIL */
799 if (imap_bodystructure_params_parse(args++, pool,
800 &data->content_type_params,
801 &data->content_type_params_count) < 0) {
802 *error_r = "Invalid content params";
803 return -1;
804 }
805
806 /* "content id" "content description" "transfer encoding" size */
807 if (!imap_arg_get_nstring(args++, &data->content_id)) {
808 *error_r = "Invalid content-id";
809 return -1;
810 }
811 if (!imap_arg_get_nstring(args++, &data->content_description)) {
812 *error_r = "Invalid content-description";
813 return -1;
814 }
815 if (!imap_arg_get_nstring(args++, &data->content_transfer_encoding)) {
816 *error_r = "Invalid content-transfer-encoding";
817 return -1;
818 }
819 data->content_id = p_strdup(pool, data->content_id);
820 data->content_description = p_strdup(pool, data->content_description);
821 data->content_transfer_encoding =
822 p_strdup(pool, data->content_transfer_encoding);
823 if (!imap_arg_get_atom(args++, &value) ||
824 str_to_uoff(value, &vsize) < 0) {
825 *error_r = "Invalid size field";
826 return -1;
827 }
828 if (vsize != part->body_size.virtual_size) {
829 *error_r = "message_part virtual_size doesn't match "
830 "size in BODYSTRUCTURE";
831 return -1;
832 }
833
834 if (text) {
835 /* text/xxx - text lines */
836 if (!imap_arg_get_atom(args++, &value) ||
837 str_to_uint(value, &lines) < 0) {
838 *error_r = "Invalid lines field";
839 return -1;
840 }
841 i_assert(part->children == NULL);
842 has_lines = TRUE;
843 } else if (message_rfc822) {
844 /* message/rfc822 - envelope + bodystructure + text lines */
845
846 i_assert(part->children != NULL &&
847 part->children->next == NULL);
848
849 if (!imap_arg_get_list(&args[1], &list_args)) {
850 *error_r = "Child bodystructure list expected";
851 return -1;
852 }
853 if (imap_bodystructure_parse_args
854 (list_args, pool, part->children, error_r) < 0)
855 return -1;
856
857 if (!imap_arg_get_list(&args[0], &list_args)) {
858 *error_r = "Envelope list expected";
859 return -1;
860 }
861 if (!imap_envelope_parse_args(list_args, pool,
862 &part->children->data->envelope, &error)) {
863 *error_r = t_strdup_printf
864 ("Invalid envelope list: %s", error);
865 return -1;
866 }
867 args += 2;
868 if (!imap_arg_get_atom(args++, &value) ||
869 str_to_uint(value, &lines) < 0) {
870 *error_r = "Invalid lines field";
871 return -1;
872 }
873 has_lines = TRUE;
874 } else {
875 i_assert(part->children == NULL);
876 has_lines = FALSE;
877 }
878 if (has_lines && lines != part->body_size.lines) {
879 *error_r = "message_part lines "
880 "doesn't match lines in BODYSTRUCTURE";
881 return -1;
882 }
883 if (args->type == IMAP_ARG_EOL)
884 return 0;
885 if (!imap_arg_get_nstring(args++, &data->content_md5)) {
886 *error_r = "Invalid content-md5";
887 return -1;
888 }
889 data->content_md5 = p_strdup(pool, data->content_md5);
890 return imap_bodystructure_parse_args_common
891 (part, pool, args, error_r);
892 }
893
894 int imap_bodystructure_parse(const char *bodystructure,
895 pool_t pool, struct message_part *parts,
896 const char **error_r)
897 {
898 struct istream *input;
899 struct imap_parser *parser;
900 const struct imap_arg *args;
901 char *error;
902 int ret;
903 bool fatal;
904
905 i_assert(parts != NULL);
906 i_assert(parts->next == NULL);
907
908 input = i_stream_create_from_data(bodystructure, strlen(bodystructure));
909 (void)i_stream_read(input);
910
911 parser = imap_parser_create(input, NULL, (size_t)-1);
912 ret = imap_parser_finish_line(parser, 0,
913 IMAP_PARSE_FLAG_LITERAL_TYPE, &args);
914 if (ret < 0) {
915 *error_r = t_strdup_printf("IMAP parser failed: %s",
916 imap_parser_get_error(parser, &fatal));
917 } else if (ret == 0) {
918 *error_r = "Empty bodystructure";
919 ret = -1;
920 } else {
921 T_BEGIN {
922 ret = imap_bodystructure_parse_args
923 (args, pool, parts, error_r);
924 if (ret < 0)
925 error = i_strdup(*error_r);
926 } T_END;
927
928 if (ret < 0) {
929 *error_r = t_strdup(error);
930 i_free(error);
931 }
932 }
933
934 imap_parser_unref(&parser);
935 i_stream_destroy(&input);
936 return ret;
937 }
938
939 static int imap_parse_bodystructure_args(const struct imap_arg *args, 939 static int imap_parse_bodystructure_args(const struct imap_arg *args,
940 string_t *str, const char **error_r) 940 string_t *str, const char **error_r)
941 { 941 {
942 const struct imap_arg *subargs; 942 const struct imap_arg *subargs;
943 const struct imap_arg *list_args; 943 const struct imap_arg *list_args;