bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "lib.h"
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen#include "istream-private.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "istream-crlf.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstruct crlf_istream {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen struct istream_private istream;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool pending_cr:1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool last_cr:1;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen};
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainenstatic int i_stream_crlf_read_common(struct crlf_istream *cstream)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen{
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen struct istream_private *stream = &cstream->istream;
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen size_t size, avail;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen ssize_t ret;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen size = i_stream_get_data_size(stream->parent);
42dbeeb3462895b03e7633dbc59e8e191199734bTimo Sirainen if (size == 0) {
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen ret = i_stream_read_memarea(stream->parent);
22ee6e1e6193299034ab99f77a650290de2fd6caTimo Sirainen if (ret <= 0) {
22ee6e1e6193299034ab99f77a650290de2fd6caTimo Sirainen i_assert(ret != -2); /* 0 sized buffer can't be full */
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen stream->istream.stream_errno =
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen stream->parent->stream_errno;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen stream->istream.eof = stream->parent->eof;
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen return ret;
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen }
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen size = i_stream_get_data_size(stream->parent);
1a266561b099269bef75eee1a3742e61130ef780Timo Sirainen i_assert(size != 0);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen if (!i_stream_try_alloc(stream, size, &avail))
14175321ddb88619015866978c05a27786ca4814Timo Sirainen return -2;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen return 1;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen}
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainenstatic ssize_t i_stream_crlf_read_crlf(struct istream_private *stream)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen{
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen struct crlf_istream *cstream = (struct crlf_istream *)stream;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen const unsigned char *data, *ptr, *src, *src_end;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen unsigned char *dest, *dest_end;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen size_t size, copy_len;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen ssize_t ret;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen ret = i_stream_crlf_read_common(cstream);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (ret <= 0)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen return ret;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen /* at least one byte was read */
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen data = i_stream_get_data(stream->parent, &size);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen dest = stream->w_buffer + stream->pos;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen dest_end = stream->w_buffer + stream->buffer_size;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen src = data;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen src_end = data + size;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen /* @UNSAFE: add missing CRs */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (*src == '\n') {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (!cstream->last_cr && dest < dest_end)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen *dest++ = '\r';
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (dest < dest_end) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen *dest++ = '\n';
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen src++;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen while (dest < dest_end) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen ptr = memchr(src, '\n', src_end - src);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (ptr == NULL)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen ptr = src_end;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen /* copy data up to LF */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen copy_len = ptr - src;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (dest + copy_len > dest_end)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen copy_len = dest_end - dest;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (copy_len > 0) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen memcpy(dest, src, copy_len);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen dest += copy_len;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen src += copy_len;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen i_assert(dest <= dest_end && src <= src_end);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (dest == dest_end || src == src_end)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen break;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen /* add the CR if necessary and copy the LF.
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen (src >= data+1, because data[0]=='\n' was
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen handled before this loop) */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (src[-1] != '\r')
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen *dest++ = '\r';
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen if (dest == dest_end)
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen break;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen *dest++ = '\n';
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen src++;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen i_assert(src == ptr + 1);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen }
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen i_assert(dest != stream->w_buffer);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen cstream->last_cr = dest[-1] == '\r';
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen i_stream_skip(stream->parent, src - data);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen ret = (dest - stream->w_buffer) - stream->pos;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen i_assert(ret > 0);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen stream->pos = dest - stream->w_buffer;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen return ret;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen}
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainenstatic ssize_t i_stream_crlf_read_lf(struct istream_private *stream)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen{
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen struct crlf_istream *cstream = (struct crlf_istream *)stream;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen const unsigned char *data, *p;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen size_t i, dest, size, max;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen ssize_t ret;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen bool pending_cr;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen ret = i_stream_crlf_read_common(cstream);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (ret <= 0)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen return ret;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen data = i_stream_get_data(stream->parent, &size);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen /* @UNSAFE */
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen /* \r\n -> \n
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen \r<anything> -> \r<anything>
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen \r\r\n -> \r\n */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen dest = stream->pos;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen pending_cr = cstream->pending_cr;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen for (i = 0; i < size && dest < stream->buffer_size; ) {
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen if (data[i] == '\r') {
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen if (pending_cr) {
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen /* \r\r */
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen stream->w_buffer[dest++] = '\r';
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen } else {
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen pending_cr = TRUE;
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen }
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen i++;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen } else if (data[i] == '\n') {
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen /* [\r]\n */
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen pending_cr = FALSE;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen stream->w_buffer[dest++] = '\n';
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen i++;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen } else if (pending_cr) {
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen /* \r<anything> */
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen pending_cr = FALSE;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen stream->w_buffer[dest++] = '\r';
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen } else {
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen /* copy everything until the next \r */
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen max = I_MIN(size - i, stream->buffer_size - dest);
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen p = memchr(data + i, '\r', max);
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen if (p != NULL)
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen max = p - (data+i);
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen memcpy(stream->w_buffer + dest, data + i, max);
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen dest += max;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen i += max;
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen i_assert(i <= size);
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen i_assert(dest <= stream->buffer_size);
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen cstream->pending_cr = pending_cr;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen i_stream_skip(stream->parent, i);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5478609e31a7665ee108ded988a309673f221aa1Timo Sirainen ret = dest - stream->pos;
553667c748977991590854426255e1c34a615f24Timo Sirainen if (ret == 0) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen i_assert(cstream->pending_cr && size == 1);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen return i_stream_crlf_read_lf(stream);
553667c748977991590854426255e1c34a615f24Timo Sirainen }
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen i_assert(ret > 0);
5478609e31a7665ee108ded988a309673f221aa1Timo Sirainen stream->pos = dest;
5478609e31a7665ee108ded988a309673f221aa1Timo Sirainen return ret;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen}
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstatic struct istream *
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Siraineni_stream_create_crlf_full(struct istream *input, bool crlf)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen{
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen struct crlf_istream *cstream;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen cstream = i_new(struct crlf_istream, 1);
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen cstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen cstream->istream.read = crlf ? i_stream_crlf_read_crlf :
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen i_stream_crlf_read_lf;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen cstream->istream.istream.readable_fd = FALSE;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen cstream->istream.istream.blocking = input->blocking;
bf3fb941cc1deb06786449b89c77d9a56a07c251Timo Sirainen cstream->istream.istream.seekable = FALSE;
9511a40d933181045343110c8101b75887062aaeTimo Sirainen return i_stream_create(&cstream->istream, input,
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen i_stream_get_fd(input), 0);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen}
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstruct istream *i_stream_create_crlf(struct istream *input)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen{
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen return i_stream_create_crlf_full(input, TRUE);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen}
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstruct istream *i_stream_create_lf(struct istream *input)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen{
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen return i_stream_create_crlf_full(input, FALSE);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen}