bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi#include "lib.h"
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi#include "str.h"
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi#include "ostream.h"
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi#include "ostream-private.h"
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi#include "ostream-escaped.h"
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomistruct escaped_ostream {
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi struct ostream_private ostream;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi ostream_escaped_escape_formatter_t format;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi string_t *buf;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi bool flushed;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi};
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomistatic ssize_t
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomio_stream_escaped_send_outbuf(struct escaped_ostream *estream)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi{
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi ssize_t ret;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi if (estream->flushed)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi return 1; /* nothing to send */
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi ret = o_stream_send(estream->ostream.parent, str_data(estream->buf), str_len(estream->buf));
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi if (ret < 0) {
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi o_stream_copy_error_from_parent(&estream->ostream);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi return -1;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi }
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi if ((size_t)ret != str_len(estream->buf)) {
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi /* move data */
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi str_delete(estream->buf, 0, ret);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi return 0;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi }
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi str_truncate(estream->buf, 0);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi estream->flushed = TRUE;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi return 1;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi}
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomistatic ssize_t
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomio_stream_escaped_send_chunk(struct escaped_ostream *estream,
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi const unsigned char *data, size_t len)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi{
bcab95c672604588f1645e4a52e43f52ee567593Timo Sirainen size_t i, max_buffer_size;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi ssize_t ret;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi max_buffer_size = I_MIN(o_stream_get_max_buffer_size(estream->ostream.parent),
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi estream->ostream.max_buffer_size);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi if (max_buffer_size > IO_BLOCK_SIZE) {
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi /* avoid using up too much memory in case of large buffers */
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi max_buffer_size = IO_BLOCK_SIZE;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi }
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi for (i = 0; i < len; i++) {
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi if (str_len(estream->buf) + 2 > max_buffer_size) { /* escaping takes at least two bytes */
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi ret = o_stream_escaped_send_outbuf(estream);
bcab95c672604588f1645e4a52e43f52ee567593Timo Sirainen if (ret < 0) {
bcab95c672604588f1645e4a52e43f52ee567593Timo Sirainen estream->ostream.ostream.offset += i;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi return ret;
bcab95c672604588f1645e4a52e43f52ee567593Timo Sirainen }
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi if (ret == 0)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi break;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi }
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi estream->format(estream->buf, data[i]);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi estream->flushed = FALSE;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi }
bcab95c672604588f1645e4a52e43f52ee567593Timo Sirainen estream->ostream.ostream.offset += i;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi return i;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi}
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomistatic ssize_t
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomio_stream_escaped_sendv(struct ostream_private *stream,
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi const struct const_iovec *iov, unsigned int iov_count)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi{
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi struct escaped_ostream *estream = (struct escaped_ostream *)stream;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi unsigned int iov_cur;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi ssize_t ret, bytes = 0;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi for (iov_cur = 0; iov_cur < iov_count; iov_cur++) {
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi ret = o_stream_escaped_send_chunk(estream,
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi iov[iov_cur].iov_base, iov[iov_cur].iov_len);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi if (ret < 0)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi return ret;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi bytes += ret;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi if ((size_t)ret != iov[iov_cur].iov_len)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi break;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi }
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi if (o_stream_escaped_send_outbuf(estream) < 0)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi return -1;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi return bytes;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi}
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomistatic int
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomio_stream_escaped_flush(struct ostream_private *stream)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi{
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi struct escaped_ostream *estream = (struct escaped_ostream *)stream;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi int ret;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi if ((ret = o_stream_escaped_send_outbuf(estream)) <= 0)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi return ret;
8109f3187f5ece5565de1813209af42dc7bb768bTimo Sirainen return o_stream_flush_parent(stream);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi}
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomistatic void o_stream_escaped_destroy(struct iostream_private *stream)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi{
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi struct escaped_ostream *estream = (struct escaped_ostream *)stream;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi str_free(&estream->buf);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi o_stream_unref(&estream->ostream.parent);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi}
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomivoid ostream_escaped_hex_format(string_t *dest, unsigned char chr)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi{
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi str_printfa(dest, "%02x", chr);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi}
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomistruct ostream *
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomio_stream_create_escaped(struct ostream *output,
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi ostream_escaped_escape_formatter_t format)
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi{
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi struct escaped_ostream *estream;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi estream = i_new(struct escaped_ostream, 1);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi estream->ostream.sendv = o_stream_escaped_sendv;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi estream->ostream.flush = o_stream_escaped_flush;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi estream->ostream.max_buffer_size = o_stream_get_max_buffer_size(output);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi estream->ostream.iostream.destroy = o_stream_escaped_destroy;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi estream->buf = str_new(default_pool, 512);
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi estream->format = format;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi estream->flushed = FALSE;
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi return o_stream_create(&estream->ostream, output, o_stream_get_fd(output));
662bb64be818407c6719a69780411f7ca8d6c96fAki Tuomi}