/src/dovecot/src/lib/istream-base64-encoder.c
Line | Count | Source |
1 | | /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "buffer.h" |
5 | | #include "base64.h" |
6 | | #include "istream-private.h" |
7 | | #include "istream-base64.h" |
8 | | |
9 | | struct base64_encoder_istream { |
10 | | struct istream_private istream; |
11 | | |
12 | | struct base64_encoder encoder; |
13 | | }; |
14 | | |
15 | | static int i_stream_read_parent(struct istream_private *stream) |
16 | 0 | { |
17 | 0 | size_t size; |
18 | 0 | ssize_t ret; |
19 | |
|
20 | 0 | size = i_stream_get_data_size(stream->parent); |
21 | 0 | if (size > 0) |
22 | 0 | return 1; |
23 | | |
24 | 0 | ret = i_stream_read_memarea(stream->parent); |
25 | 0 | if (ret <= 0) { |
26 | 0 | stream->istream.stream_errno = stream->parent->stream_errno; |
27 | 0 | return ret; |
28 | 0 | } |
29 | 0 | size = i_stream_get_data_size(stream->parent); |
30 | 0 | i_assert(size != 0); |
31 | 0 | return 1; |
32 | 0 | } |
33 | | |
34 | | static int |
35 | | i_stream_base64_try_encode(struct base64_encoder_istream *bstream) |
36 | 0 | { |
37 | 0 | struct istream_private *stream = &bstream->istream; |
38 | 0 | struct base64_encoder *b64enc = &bstream->encoder; |
39 | 0 | const unsigned char *data; |
40 | 0 | size_t size, pos, out_size, avail; |
41 | 0 | buffer_t buf; |
42 | |
|
43 | 0 | data = i_stream_get_data(stream->parent, &size); |
44 | 0 | if (size == 0) |
45 | 0 | return 0; |
46 | | |
47 | 0 | out_size = base64_encode_get_size(b64enc, size); |
48 | 0 | if (!i_stream_try_alloc(stream, out_size, &avail)) |
49 | 0 | return -2; |
50 | | |
51 | 0 | buffer_create_from_data(&buf, stream->w_buffer + stream->pos, avail); |
52 | 0 | base64_encode_more(b64enc, data, size, &pos, &buf); |
53 | 0 | i_assert(buf.used > 0); |
54 | | |
55 | 0 | stream->pos += buf.used; |
56 | 0 | i_stream_skip(stream->parent, pos); |
57 | 0 | return 1; |
58 | 0 | } |
59 | | |
60 | | static int |
61 | | i_stream_base64_finish_encode(struct base64_encoder_istream *bstream) |
62 | 0 | { |
63 | 0 | struct istream_private *stream = &bstream->istream; |
64 | 0 | struct base64_encoder *b64enc = &bstream->encoder; |
65 | 0 | size_t out_size, buffer_avail; |
66 | 0 | buffer_t buf; |
67 | |
|
68 | 0 | out_size = base64_encode_get_size(b64enc, 0); |
69 | 0 | if (out_size == 0) { |
70 | 0 | if (base64_encode_finish(b64enc, NULL)) |
71 | 0 | stream->istream.eof = TRUE; |
72 | 0 | return 1; |
73 | 0 | } |
74 | | |
75 | 0 | if (!i_stream_try_alloc(stream, out_size, &buffer_avail)) |
76 | 0 | return -2; |
77 | | |
78 | 0 | buffer_create_from_data(&buf, stream->w_buffer + stream->pos, |
79 | 0 | buffer_avail); |
80 | 0 | if (base64_encode_finish(b64enc, &buf)) |
81 | 0 | stream->istream.eof = TRUE; |
82 | 0 | i_assert(buf.used > 0); |
83 | | |
84 | 0 | stream->pos += buf.used; |
85 | 0 | return 1; |
86 | 0 | } |
87 | | |
88 | | static ssize_t i_stream_base64_encoder_read(struct istream_private *stream) |
89 | 0 | { |
90 | 0 | struct base64_encoder_istream *bstream = |
91 | 0 | container_of(stream, struct base64_encoder_istream, istream); |
92 | 0 | size_t pre_count, post_count; |
93 | 0 | int ret; |
94 | |
|
95 | 0 | if (base64_encode_is_finished(&bstream->encoder)) { |
96 | 0 | stream->istream.eof = TRUE; |
97 | 0 | return -1; |
98 | 0 | } |
99 | | |
100 | 0 | pre_count = post_count = 0; |
101 | 0 | do { |
102 | 0 | ret = i_stream_read_parent(stream); |
103 | 0 | if (ret == 0) |
104 | 0 | return 0; |
105 | 0 | if (ret < 0) { |
106 | 0 | if (stream->istream.stream_errno != 0) |
107 | 0 | return -1; |
108 | 0 | if (i_stream_get_data_size(stream->parent) == 0) |
109 | 0 | break; |
110 | | /* add the final partial block */ |
111 | 0 | } |
112 | | |
113 | | /* encode as many lines as fits into destination buffer */ |
114 | 0 | pre_count = stream->pos - stream->skip; |
115 | 0 | while ((ret = i_stream_base64_try_encode(bstream)) > 0) ; |
116 | 0 | post_count = stream->pos - stream->skip; |
117 | 0 | } while (ret == 0 && pre_count == post_count); |
118 | | |
119 | 0 | if (ret == -2) { |
120 | 0 | if (pre_count == post_count) |
121 | 0 | return -2; |
122 | 0 | } else if (ret < 0) { |
123 | 0 | if (i_stream_get_data_size(stream->parent) == 0) { |
124 | 0 | i_assert(post_count == pre_count); |
125 | 0 | pre_count = stream->pos - stream->skip; |
126 | 0 | ret = i_stream_base64_finish_encode(bstream); |
127 | 0 | post_count = stream->pos - stream->skip; |
128 | 0 | if (ret <= 0) |
129 | 0 | return ret; |
130 | 0 | } |
131 | 0 | if (pre_count == post_count) { |
132 | 0 | stream->istream.eof = TRUE; |
133 | 0 | return -1; |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | 0 | i_assert(post_count > pre_count); |
138 | 0 | return post_count - pre_count; |
139 | 0 | } |
140 | | |
141 | | static void |
142 | | i_stream_base64_encoder_seek(struct istream_private *stream, |
143 | | uoff_t v_offset, bool mark) |
144 | 0 | { |
145 | 0 | struct base64_encoder_istream *bstream = |
146 | 0 | container_of(stream, struct base64_encoder_istream, istream); |
147 | |
|
148 | 0 | if (v_offset < stream->istream.v_offset) { |
149 | | /* seeking backwards - go back to beginning and seek |
150 | | forward from there. */ |
151 | 0 | stream->parent_expected_offset = stream->parent_start_offset; |
152 | 0 | stream->skip = stream->pos = 0; |
153 | 0 | stream->istream.v_offset = 0; |
154 | 0 | i_stream_seek(stream->parent, 0); |
155 | |
|
156 | 0 | base64_encode_reset(&bstream->encoder); |
157 | 0 | } |
158 | 0 | i_stream_default_seek_nonseekable(stream, v_offset, mark); |
159 | 0 | } |
160 | | |
161 | | static int |
162 | | i_stream_base64_encoder_stat(struct istream_private *stream, |
163 | | bool exact ATTR_UNUSED) |
164 | 0 | { |
165 | 0 | struct base64_encoder_istream *bstream = |
166 | 0 | container_of(stream, struct base64_encoder_istream, istream); |
167 | 0 | const struct stat *st; |
168 | |
|
169 | 0 | if (i_stream_stat(stream->parent, exact, &st) < 0) { |
170 | 0 | stream->istream.stream_errno = stream->parent->stream_errno; |
171 | 0 | return -1; |
172 | 0 | } |
173 | | |
174 | 0 | stream->statbuf = *st; |
175 | 0 | if (st->st_size == 0) |
176 | 0 | return 0; |
177 | | |
178 | 0 | stream->statbuf.st_size = |
179 | 0 | base64_get_full_encoded_size(&bstream->encoder, st->st_size); |
180 | 0 | return 0; |
181 | 0 | } |
182 | | |
183 | | static struct istream * |
184 | | i_stream_create_base64_encoder_common(const struct base64_scheme *b64, |
185 | | struct istream *input, |
186 | | unsigned int chars_per_line, bool crlf) |
187 | 0 | { |
188 | 0 | struct base64_encoder_istream *bstream; |
189 | 0 | enum base64_encode_flags b64_flags = 0; |
190 | |
|
191 | 0 | i_assert(chars_per_line % 4 == 0); |
192 | | |
193 | 0 | bstream = i_new(struct base64_encoder_istream, 1); |
194 | 0 | bstream->istream.max_buffer_size = input->real_stream->max_buffer_size; |
195 | |
|
196 | 0 | bstream->istream.read = i_stream_base64_encoder_read; |
197 | 0 | bstream->istream.seek = i_stream_base64_encoder_seek; |
198 | 0 | bstream->istream.stat = i_stream_base64_encoder_stat; |
199 | |
|
200 | 0 | bstream->istream.istream.readable_fd = FALSE; |
201 | 0 | bstream->istream.istream.blocking = input->blocking; |
202 | 0 | bstream->istream.istream.seekable = input->seekable; |
203 | |
|
204 | 0 | if (crlf) |
205 | 0 | b64_flags |= BASE64_ENCODE_FLAG_CRLF; |
206 | 0 | base64_encode_init(&bstream->encoder, b64, b64_flags, chars_per_line); |
207 | |
|
208 | 0 | return i_stream_create(&bstream->istream, input, |
209 | 0 | i_stream_get_fd(input), 0); |
210 | 0 | } |
211 | | |
212 | | struct istream * |
213 | | i_stream_create_base64_encoder(struct istream *input, |
214 | | unsigned int chars_per_line, bool crlf) |
215 | 0 | { |
216 | 0 | return i_stream_create_base64_encoder_common(&base64_scheme, input, |
217 | 0 | chars_per_line, crlf); |
218 | 0 | } |
219 | | |
220 | | struct istream * |
221 | | i_stream_create_base64url_encoder(struct istream *input, |
222 | | unsigned int chars_per_line, bool crlf) |
223 | 0 | { |
224 | 0 | return i_stream_create_base64_encoder_common(&base64url_scheme, input, |
225 | 0 | chars_per_line, crlf); |
226 | 0 | } |