22607
|
1 /* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */
|
|
2
|
|
3 #include "lib.h"
|
|
4 #include "istream-private.h"
|
|
5 #include "istream-try.h"
|
|
6
|
|
7 struct try_istream {
|
|
8 struct istream_private istream;
|
|
9
|
|
10 unsigned int try_input_count;
|
|
11 struct istream **try_input;
|
|
12 unsigned int try_idx;
|
|
13
|
|
14 struct istream *final_input;
|
|
15 };
|
|
16
|
|
17 static void i_stream_unref_try_inputs(struct try_istream *tstream)
|
|
18 {
|
|
19 for (unsigned int i = 0; i < tstream->try_input_count; i++) {
|
|
20 if (tstream->try_input[i] != NULL)
|
|
21 i_stream_unref(&tstream->try_input[i]);
|
|
22 }
|
|
23 tstream->try_input_count = 0;
|
|
24 i_free(tstream->try_input);
|
|
25 }
|
|
26
|
|
27 static void i_stream_try_close(struct iostream_private *stream,
|
|
28 bool close_parent)
|
|
29 {
|
|
30 struct try_istream *tstream = (struct try_istream *)stream;
|
|
31
|
|
32 if (close_parent) {
|
|
33 if (tstream->istream.parent != NULL)
|
|
34 i_stream_close(tstream->istream.parent);
|
|
35 for (unsigned int i = 0; i < tstream->try_input_count; i++) {
|
|
36 if (tstream->try_input[i] != NULL)
|
|
37 i_stream_close(tstream->try_input[i]);
|
|
38 }
|
|
39 }
|
|
40 i_stream_unref_try_inputs(tstream);
|
|
41 }
|
|
42
|
|
43 static bool i_stream_try_is_buffer_full(struct istream *try_input)
|
|
44 {
|
|
45 /* See if one of the parent istreams have their buffer full.
|
|
46 This is mainly intended to check with istream-tee whether its
|
|
47 parent is full. That means that the try_input has already seen
|
|
48 a full buffer of input, but it hasn't decided to return anything
|
|
49 yet. But it also hasn't failed, so we'll assume that the input is
|
|
50 correct for it and it simply needs a lot more input before it can
|
|
51 return anything (e.g. istream-bzlib). */
|
|
52 while (try_input->real_stream->parent != NULL) {
|
|
53 try_input = try_input->real_stream->parent;
|
|
54 if (try_input->real_stream->pos == try_input->real_stream->buffer_size &&
|
|
55 try_input->real_stream->buffer_size > 0)
|
|
56 return TRUE;
|
|
57 }
|
|
58 return FALSE;
|
|
59 }
|
|
60
|
|
61 static int i_stream_try_detect(struct try_istream *tstream)
|
|
62 {
|
|
63 int ret;
|
|
64
|
|
65 for (; tstream->try_idx < tstream->try_input_count; tstream->try_idx++) {
|
|
66 struct istream *try_input =
|
|
67 tstream->try_input[tstream->try_idx];
|
|
68
|
|
69 ret = i_stream_read(try_input);
|
|
70 if (ret == 0 && i_stream_try_is_buffer_full(try_input))
|
|
71 ret = 1;
|
|
72 if (ret > 0) {
|
|
73 i_stream_init_parent(&tstream->istream, try_input);
|
|
74 i_stream_unref_try_inputs(tstream);
|
|
75 return 1;
|
|
76 }
|
|
77 if (ret == 0)
|
|
78 return 0;
|
|
79 if (try_input->stream_errno != EINVAL) {
|
|
80 tstream->istream.istream.stream_errno =
|
|
81 try_input->stream_errno;
|
|
82 io_stream_set_error(&tstream->istream.iostream,
|
|
83 "Unexpected error while detecting stream format: %s",
|
|
84 i_stream_get_error(try_input));
|
|
85 return -1;
|
|
86 }
|
|
87 }
|
|
88
|
|
89 /* All streams failed with EINVAL. */
|
|
90 io_stream_set_error(&tstream->istream.iostream,
|
|
91 "Failed to detect stream format");
|
|
92 tstream->istream.istream.stream_errno = EINVAL;
|
|
93 return -1;
|
|
94 }
|
|
95
|
|
96 static ssize_t
|
|
97 i_stream_try_read(struct istream_private *stream)
|
|
98 {
|
|
99 struct try_istream *tstream = (struct try_istream *)stream;
|
|
100 int ret;
|
|
101
|
|
102 if (stream->parent == NULL) {
|
|
103 if ((ret = i_stream_try_detect(tstream)) <= 0)
|
|
104 return ret;
|
|
105 }
|
|
106
|
|
107 i_stream_seek(stream->parent, stream->parent_start_offset +
|
|
108 stream->istream.v_offset);
|
|
109 return i_stream_read_copy_from_parent(&stream->istream);
|
|
110 }
|
|
111
|
|
112 struct istream *istream_try_create(struct istream *const input[])
|
|
113 {
|
|
114 struct try_istream *tstream;
|
|
115 unsigned int count;
|
|
116 size_t max_buffer_size = I_STREAM_MIN_SIZE;
|
|
117 bool blocking = TRUE, seekable = TRUE;
|
|
118
|
|
119 for (count = 0; input[count] != NULL; count++) {
|
|
120 max_buffer_size = I_MAX(max_buffer_size,
|
|
121 i_stream_get_max_buffer_size(input[count]));
|
|
122 if (!input[count]->blocking)
|
|
123 blocking = FALSE;
|
|
124 if (!input[count]->seekable)
|
|
125 seekable = FALSE;
|
|
126 i_stream_ref(input[count]);
|
|
127 }
|
|
128 i_assert(count != 0);
|
|
129
|
|
130 tstream = i_new(struct try_istream, 1);
|
|
131 tstream->try_input_count = count;
|
|
132 tstream->try_input = p_memdup(default_pool, input,
|
|
133 sizeof(*input) * count);
|
|
134
|
|
135 tstream->istream.iostream.close = i_stream_try_close;
|
|
136
|
|
137 tstream->istream.max_buffer_size = max_buffer_size;
|
|
138 tstream->istream.read = i_stream_try_read;
|
|
139
|
|
140 tstream->istream.istream.readable_fd = FALSE;
|
|
141 tstream->istream.istream.blocking = blocking;
|
|
142 tstream->istream.istream.seekable = seekable;
|
|
143 return i_stream_create(&tstream->istream, NULL, -1);
|
|
144 }
|