bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch#include "lib.h"
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch#include "array.h"
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch#include "ostream-private.h"
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch#include "ostream-dot.h"
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Boschenum dot_ostream_state {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch STREAM_STATE_INIT = 0,
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch STREAM_STATE_NONE,
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch STREAM_STATE_CR,
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch STREAM_STATE_CRLF,
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen STREAM_STATE_DONE
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch};
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Boschstruct dot_ostream {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch struct ostream_private ostream;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch enum dot_ostream_state state;
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen bool force_extra_crlf;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch};
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainenstatic int o_stream_dot_finish(struct ostream_private *stream)
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch{
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch struct dot_ostream *dstream = (struct dot_ostream *)stream;
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen int ret;
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen if (dstream->state == STREAM_STATE_DONE)
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen return 1;
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen if (o_stream_get_buffer_avail_size(stream->parent) < 5) {
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen /* make space for the dot line */
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen if ((ret = o_stream_flush(stream->parent)) <= 0) {
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen if (ret < 0)
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen o_stream_copy_error_from_parent(stream);
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen return ret;
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen }
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen }
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen if (dstream->state == STREAM_STATE_CRLF &&
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen !dstream->force_extra_crlf) {
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen ret = o_stream_send(stream->parent, ".\r\n", 3);
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen i_assert(ret == 3);
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen } else {
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen ret = o_stream_send(stream->parent, "\r\n.\r\n", 5);
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen i_assert(ret == 5);
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen }
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen dstream->state = STREAM_STATE_DONE;
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen return 1;
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen}
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainenstatic int
a2583397219ebbb877fcf84107f0ca7f56362760Timo Siraineno_stream_dot_flush(struct ostream_private *stream)
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen{
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen int ret;
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen if (stream->finished) {
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen if ((ret = o_stream_dot_finish(stream)) <= 0)
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen return ret;
a2583397219ebbb877fcf84107f0ca7f56362760Timo Sirainen }
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen
8109f3187f5ece5565de1813209af42dc7bb768bTimo Sirainen return o_stream_flush_parent(stream);
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen}
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainenstatic void
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Siraineno_stream_dot_close(struct iostream_private *stream, bool close_parent)
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen{
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen struct dot_ostream *dstream = (struct dot_ostream *)stream;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch if (close_parent)
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch o_stream_close(dstream->ostream.parent);
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch}
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Boschstatic ssize_t
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Boscho_stream_dot_sendv(struct ostream_private *stream,
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch const struct const_iovec *iov, unsigned int iov_count)
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch{
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch struct dot_ostream *dstream = (struct dot_ostream *)stream;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch ARRAY(struct const_iovec) iov_arr;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch const struct const_iovec *iov_new;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch size_t max_bytes, sent, added;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch unsigned int count, i;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch ssize_t ret;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen i_assert(dstream->state != STREAM_STATE_DONE);
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch if ((ret=o_stream_flush(stream->parent)) <= 0) {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch /* error / we still couldn't flush existing data to
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch parent stream. */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch o_stream_copy_error_from_parent(stream);
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch return ret;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch }
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch /* check for dots */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch t_array_init(&iov_arr, iov_count + 32);
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen max_bytes = o_stream_get_buffer_avail_size(stream->parent);
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen i_assert(max_bytes > 0); /* FIXME: not supported currently */
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch sent = added = 0;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch for (i = 0; i < iov_count && max_bytes > 0; i++) {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch size_t size = iov[i].iov_len, chunk;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch const char *data = iov[i].iov_base, *p, *pend;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch struct const_iovec iovn;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch p = data;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch pend = CONST_PTR_OFFSET(data, size);
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch for (; p < pend && (size_t)(p-data) < (max_bytes-2); p++) {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch char add = 0;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch switch (dstream->state) {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch /* none */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch case STREAM_STATE_NONE:
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch switch (*p) {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch case '\n':
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch dstream->state = STREAM_STATE_CRLF;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch /* add missing CR */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch add = '\r';
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch break;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch case '\r':
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch dstream->state = STREAM_STATE_CR;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch break;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch }
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch break;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch /* got CR */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch case STREAM_STATE_CR:
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch switch (*p) {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch case '\r':
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch break;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch case '\n':
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch dstream->state = STREAM_STATE_CRLF;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch break;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch default:
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch dstream->state = STREAM_STATE_NONE;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch break;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch }
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch break;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch /* got CRLF, or the first line */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch case STREAM_STATE_INIT:
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch case STREAM_STATE_CRLF:
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch switch (*p) {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch case '\r':
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch dstream->state = STREAM_STATE_CR;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch break;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch case '\n':
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch dstream->state = STREAM_STATE_CRLF;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch /* add missing CR */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch add = '\r';
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch break;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch case '.':
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch /* add dot */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch add = '.';
8409959d66804dc963bc6fcdcc9a01da0d56a978Timo Sirainen /* fall through */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch default:
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch dstream->state = STREAM_STATE_NONE;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch break;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch }
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch break;
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen case STREAM_STATE_DONE:
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen i_unreached();
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch }
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch if (add != 0) {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch chunk = (size_t)(p - data);
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch if (chunk > 0) {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch /* forward chunk to new iovec */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch iovn.iov_base = data;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch iovn.iov_len = chunk;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch array_append(&iov_arr, &iovn, 1);
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch data = p;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch max_bytes -= chunk;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch sent += chunk;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch }
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch /* insert byte (substitute one with pair) */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch data++;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch iovn.iov_base = (add == '\r' ? "\r\n" : "..");
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch iovn.iov_len = 2;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch array_append(&iov_arr, &iovn, 1);
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch max_bytes -= 2;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch added++;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch sent++;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch }
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch }
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch if (max_bytes == 0)
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch break;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch chunk = ((size_t)(p-data) >= (max_bytes-2) ?
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch max_bytes - 2 : (size_t)(p - data));
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch if (chunk > 0) {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch iovn.iov_base = data;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch iovn.iov_len = chunk;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch array_append(&iov_arr, &iovn, 1);
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch max_bytes -= chunk;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch sent += chunk;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch }
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch }
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch /* send */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch iov_new = array_get(&iov_arr, &count);
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch if (count == 0) {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch ret = 0;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch } else if ((ret=o_stream_sendv(stream->parent, iov_new, count)) <= 0) {
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch i_assert(ret < 0);
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch o_stream_copy_error_from_parent(stream);
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch return -1;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch }
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch /* all must be sent */
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch i_assert((size_t)ret == sent + added);
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch stream->ostream.offset += sent;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch return sent;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch}
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Boschstruct ostream *
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Siraineno_stream_create_dot(struct ostream *output, bool force_extra_crlf)
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch{
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch struct dot_ostream *dstream;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch dstream = i_new(struct dot_ostream, 1);
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch dstream->ostream.sendv = o_stream_dot_sendv;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch dstream->ostream.iostream.close = o_stream_dot_close;
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen dstream->ostream.flush = o_stream_dot_flush;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch dstream->ostream.max_buffer_size = output->real_stream->max_buffer_size;
83773f7eb5e12e68f5efee7278bdab35d2ee84c0Timo Sirainen dstream->force_extra_crlf = force_extra_crlf;
6519bf9a290ff036ede005ac66b1a339e5931c26Timo Sirainen (void)o_stream_create(&dstream->ostream, output, o_stream_get_fd(output));
6519bf9a290ff036ede005ac66b1a339e5931c26Timo Sirainen /* ostream-dot is always used inside another ostream that shouldn't
6519bf9a290ff036ede005ac66b1a339e5931c26Timo Sirainen get finished when the "." line is written. Disable it here so all
6519bf9a290ff036ede005ac66b1a339e5931c26Timo Sirainen of the callers don't have to set this. */
6519bf9a290ff036ede005ac66b1a339e5931c26Timo Sirainen o_stream_set_finish_also_parent(&dstream->ostream.ostream, FALSE);
6519bf9a290ff036ede005ac66b1a339e5931c26Timo Sirainen return &dstream->ostream.ostream;
836e690c0e2d7494b8c0a6f4984dd13248841f2fStephan Bosch}