Coverage Report

Created: 2026-05-30 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}