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