istream-crlf.c revision 9511a40d933181045343110c8101b75887062aae
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2007 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "lib.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "istream-internal.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "istream-crlf.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct crlf_istream {
6564208826b0f46a00f010d1b5711d85944c3c88Timo Sirainen struct istream_private istream;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int pending_cr:1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int last_cr:1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen};
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void i_stream_crlf_destroy(struct iostream_private *stream)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct crlf_istream *cstream = (struct crlf_istream *)stream;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_free(cstream->istream.w_buffer);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_unref(&cstream->istream.parent);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void
4c1deab456fe8877bf025d11843167ac1f36327aTimo Siraineni_stream_crlf_set_max_buffer_size(struct iostream_private *stream,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen size_t max_size)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct crlf_istream *cstream = (struct crlf_istream *)stream;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen cstream->istream.max_buffer_size = max_size;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_set_max_buffer_size(cstream->istream.parent, max_size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int i_stream_crlf_read_common(struct crlf_istream *cstream)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct istream_private *stream = &cstream->istream;
010affb9f4b9cca9b94e1d8d570cf179daff08d7Timo Sirainen const unsigned char *data;
010affb9f4b9cca9b94e1d8d570cf179daff08d7Timo Sirainen size_t size;
010affb9f4b9cca9b94e1d8d570cf179daff08d7Timo Sirainen ssize_t ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen data = i_stream_get_data(stream->parent, &size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (size == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = i_stream_read(stream->parent);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret <= 0 && (ret != -2 || stream->skip == 0)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->istream.stream_errno =
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->parent->stream_errno;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->istream.eof = stream->parent->eof;
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen return ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen data = i_stream_get_data(stream->parent, &size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(size != 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
a81b240cfe84231eac64084efd5b0e1e91a9e817Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!i_stream_get_buffer_space(stream, size, NULL))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -2;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen}
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainenstatic ssize_t i_stream_crlf_read_crlf(struct istream_private *stream)
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen{
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen struct crlf_istream *cstream = (struct crlf_istream *)stream;
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen const unsigned char *data;
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen size_t i, dest, size;
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen ssize_t ret;
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen ret = i_stream_crlf_read_common(cstream);
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen if (ret <= 0)
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen return ret;
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen data = i_stream_get_data(stream->parent, &size);
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen /* @UNSAFE: add missing CRs */
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen dest = stream->pos;
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen for (i = 0; i < size && dest < stream->buffer_size; i++) {
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen if (data[i] == '\n') {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (i == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!cstream->last_cr)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->w_buffer[dest++] = '\r';
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (data[i-1] != '\r')
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen stream->w_buffer[dest++] = '\r';
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen }
d7b205394bd07152718152976dfa089e13253d7eTimo Sirainen if (dest == stream->buffer_size)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->w_buffer[dest++] = data[i];
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
d7b205394bd07152718152976dfa089e13253d7eTimo Sirainen cstream->last_cr = stream->w_buffer[dest-1] == '\r';
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_skip(stream->parent, i);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
427ad9fea54628d52f04c8a76269749a8e6fae7aTimo Sirainen ret = dest - stream->pos;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(ret != 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->pos = dest;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic ssize_t i_stream_crlf_read_lf(struct istream_private *stream)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct crlf_istream *cstream = (struct crlf_istream *)stream;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const unsigned char *data;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen size_t i, dest, size;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ssize_t ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int diff;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen ret = i_stream_crlf_read_common(cstream);
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen if (ret <= 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen data = i_stream_get_data(stream->parent, &size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* @UNSAFE */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen dest = stream->pos;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (data[0] == '\n') {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->w_buffer[dest++] = '\n';
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen cstream->pending_cr = FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (cstream->pending_cr) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* CR without LF */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->w_buffer[dest++] = '\r';
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (dest == stream->buffer_size) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen cstream->pending_cr = FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen if (data[0] != '\r')
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen stream->w_buffer[dest++] = data[0];
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen diff = -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 1; i < size && dest < stream->buffer_size; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (data[i] == '\r') {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (data[i-1] != '\r')
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen continue;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (data[i-1] == '\r' && data[i] != '\n') {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen stream->w_buffer[dest++] = '\r';
ea1f67e14d727496179ee4ff391f592bce8f4f2dTimo Sirainen if (dest == stream->buffer_size) {
ea1f67e14d727496179ee4ff391f592bce8f4f2dTimo Sirainen diff = 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
ea1f67e14d727496179ee4ff391f592bce8f4f2dTimo Sirainen stream->w_buffer[dest++] = data[i];
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen cstream->pending_cr = data[i+diff] == '\r';
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_skip(stream->parent, i);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
ea1f67e14d727496179ee4ff391f592bce8f4f2dTimo Sirainen ret = dest - stream->pos;
ea1f67e14d727496179ee4ff391f592bce8f4f2dTimo Sirainen if (ret == 0) {
ea1f67e14d727496179ee4ff391f592bce8f4f2dTimo Sirainen i_assert(cstream->pending_cr && size == 1);
b8d314c6355009ad0b9e332b6acecdfac5cc8891Timo Sirainen return i_stream_crlf_read_lf(stream);
b8d314c6355009ad0b9e332b6acecdfac5cc8891Timo Sirainen }
b8d314c6355009ad0b9e332b6acecdfac5cc8891Timo Sirainen stream->pos = dest;
b8d314c6355009ad0b9e332b6acecdfac5cc8891Timo Sirainen return ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
b8d314c6355009ad0b9e332b6acecdfac5cc8891Timo Sirainenstatic void ATTR_NORETURN
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Siraineni_stream_crlf_seek(struct istream_private *stream ATTR_UNUSED,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uoff_t v_offset ATTR_UNUSED, bool mark ATTR_UNUSED)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_panic("crlf-istream: seeking unsupported currently");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic const struct stat *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Siraineni_stream_crlf_stat(struct istream_private *stream, bool exact)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
d22301419109ed4a38351715e6760011421dadecTimo Sirainen return i_stream_stat(stream->parent, exact);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic struct istream *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Siraineni_stream_create_crlf_full(struct istream *input, bool crlf)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct crlf_istream *cstream;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainen i_stream_ref(input);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen cstream = i_new(struct crlf_istream, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen cstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen cstream->istream.iostream.destroy = i_stream_crlf_destroy;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen cstream->istream.iostream.set_max_buffer_size =
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_crlf_set_max_buffer_size;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen cstream->istream.read = crlf ? i_stream_crlf_read_crlf :
d22301419109ed4a38351715e6760011421dadecTimo Sirainen i_stream_crlf_read_lf;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen cstream->istream.seek = i_stream_crlf_seek;
0dc7a67fb62b2aac82ff1c03319bd4976c56dbc1Timo Sirainen cstream->istream.stat = i_stream_crlf_stat;
0dc7a67fb62b2aac82ff1c03319bd4976c56dbc1Timo Sirainen
0dc7a67fb62b2aac82ff1c03319bd4976c56dbc1Timo Sirainen cstream->istream.istream.blocking = input->blocking;
0dc7a67fb62b2aac82ff1c03319bd4976c56dbc1Timo Sirainen cstream->istream.istream.seekable = input->seekable;
0dc7a67fb62b2aac82ff1c03319bd4976c56dbc1Timo Sirainen return i_stream_create(&cstream->istream, input,
0dc7a67fb62b2aac82ff1c03319bd4976c56dbc1Timo Sirainen i_stream_get_fd(input));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct istream *i_stream_create_crlf(struct istream *input)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return i_stream_create_crlf_full(input, TRUE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct istream *i_stream_create_lf(struct istream *input)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return i_stream_create_crlf_full(input, FALSE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen