summaryrefslogtreecommitdiffstats
path: root/src/lib-mail/istream-qp-decoder.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
commitf7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch)
treea3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-mail/istream-qp-decoder.c
parentInitial commit. (diff)
downloaddovecot-upstream.tar.xz
dovecot-upstream.zip
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-mail/istream-qp-decoder.c')
-rw-r--r--src/lib-mail/istream-qp-decoder.c140
1 files changed, 140 insertions, 0 deletions
diff --git a/src/lib-mail/istream-qp-decoder.c b/src/lib-mail/istream-qp-decoder.c
new file mode 100644
index 0000000..7ee0580
--- /dev/null
+++ b/src/lib-mail/istream-qp-decoder.c
@@ -0,0 +1,140 @@
+/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "hex-binary.h"
+#include "qp-decoder.h"
+#include "istream-private.h"
+#include "istream-qp.h"
+
+struct qp_decoder_istream {
+ struct istream_private istream;
+ buffer_t *buf;
+ struct qp_decoder *qp;
+};
+
+static void i_stream_qp_decoder_close(struct iostream_private *stream,
+ bool close_parent)
+{
+ struct qp_decoder_istream *bstream =
+ (struct qp_decoder_istream *)stream;
+
+ if (bstream->qp != NULL)
+ qp_decoder_deinit(&bstream->qp);
+ buffer_free(&bstream->buf);
+ if (close_parent)
+ i_stream_close(bstream->istream.parent);
+}
+
+static ssize_t i_stream_qp_decoder_read(struct istream_private *stream)
+{
+ struct qp_decoder_istream *bstream =
+ (struct qp_decoder_istream *)stream;
+ const unsigned char *data;
+ size_t size, error_pos, max_buffer_size;
+ const char *error;
+ int ret;
+
+ max_buffer_size = i_stream_get_max_buffer_size(&stream->istream);
+ for (;;) {
+ /* remove skipped data from buffer */
+ if (stream->skip > 0) {
+ i_assert(stream->skip <= bstream->buf->used);
+ buffer_delete(bstream->buf, 0, stream->skip);
+ stream->pos -= stream->skip;
+ stream->skip = 0;
+ }
+
+ stream->buffer = bstream->buf->data;
+
+ i_assert(stream->pos <= bstream->buf->used);
+ if (stream->pos >= max_buffer_size) {
+ /* stream buffer still at maximum */
+ return -2;
+ }
+
+ /* if something is already decoded, return as much of it as
+ we can */
+ if (bstream->buf->used > 0) {
+ size_t new_pos, bytes;
+
+ /* only return up to max_buffer_size bytes, even when buffer
+ actually has more, as not to confuse the caller */
+ new_pos = I_MIN(bstream->buf->used, max_buffer_size);
+ bytes = new_pos - stream->pos;
+ stream->pos = new_pos;
+
+ return (ssize_t)bytes;
+ }
+
+ /* need to read more input */
+ ret = i_stream_read_more_memarea(stream->parent, &data, &size);
+ if (ret <= 0) {
+ stream->istream.stream_errno = stream->parent->stream_errno;
+ stream->istream.eof = stream->parent->eof;
+ if (ret != -1 || stream->istream.stream_errno != 0)
+ return ret;
+ /* end of quoted-printable stream. verify that the
+ ending is ok. */
+ if (qp_decoder_finish(bstream->qp, &error) == 0) {
+ i_assert(bstream->buf->used == 0);
+ return -1;
+ }
+ io_stream_set_error(&stream->iostream,
+ "Invalid quoted-printable input trailer: %s", error);
+ stream->istream.stream_errno = EPIPE;
+ return -1;
+ }
+ if (qp_decoder_more(bstream->qp, data, size,
+ &error_pos, &error) < 0) {
+ i_assert(error_pos < size);
+ io_stream_set_error(&stream->iostream,
+ "Invalid quoted-printable input 0x%s: %s",
+ binary_to_hex(data+error_pos, I_MIN(size-error_pos, 8)), error);
+ stream->istream.stream_errno = EINVAL;
+ return -1;
+ }
+ i_stream_skip(stream->parent, size);
+ }
+}
+
+static void
+i_stream_qp_decoder_seek(struct istream_private *stream,
+ uoff_t v_offset, bool mark)
+{
+ struct qp_decoder_istream *bstream =
+ (struct qp_decoder_istream *)stream;
+ const char *error;
+
+ if (v_offset < stream->istream.v_offset) {
+ /* seeking backwards - go back to beginning and seek
+ forward from there. */
+ stream->parent_expected_offset = stream->parent_start_offset;
+ stream->skip = stream->pos = 0;
+ stream->istream.v_offset = 0;
+ i_stream_seek(stream->parent, 0);
+ (void)qp_decoder_finish(bstream->qp, &error);
+ buffer_set_used_size(bstream->buf, 0);
+ }
+ i_stream_default_seek_nonseekable(stream, v_offset, mark);
+}
+
+struct istream *i_stream_create_qp_decoder(struct istream *input)
+{
+ struct qp_decoder_istream *bstream;
+
+ bstream = i_new(struct qp_decoder_istream, 1);
+ bstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
+ bstream->buf = buffer_create_dynamic(default_pool, 128);
+ bstream->qp = qp_decoder_init(bstream->buf);
+
+ bstream->istream.iostream.close = i_stream_qp_decoder_close;
+ bstream->istream.read = i_stream_qp_decoder_read;
+ bstream->istream.seek = i_stream_qp_decoder_seek;
+
+ bstream->istream.istream.readable_fd = FALSE;
+ bstream->istream.istream.blocking = input->blocking;
+ bstream->istream.istream.seekable = input->seekable;
+ return i_stream_create(&bstream->istream, input,
+ i_stream_get_fd(input), 0);
+}