istream-crlf.c revision da2aa032ccfa8e7e4a4380ef738014549f4d2c2d
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "lib.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "istream-internal.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "istream-crlf.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstruct crlf_istream {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen struct istream_private istream;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen unsigned int pending_cr:1;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen unsigned int 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;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen size_t size;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen ssize_t ret;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
a425baf62f30f4b62250227ff97b4b01c4b01affTimo Sirainen (void)i_stream_get_data(stream->parent, &size);
42dbeeb3462895b03e7633dbc59e8e191199734bTimo Sirainen if (size == 0) {
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen ret = i_stream_read(stream->parent);
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen if (ret <= 0 && (ret != -2 || stream->skip == 0)) {
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 }
a425baf62f30f4b62250227ff97b4b01c4b01affTimo Sirainen (void)i_stream_get_data(stream->parent, &size);
1a266561b099269bef75eee1a3742e61130ef780Timo Sirainen i_assert(size != 0);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
14175321ddb88619015866978c05a27786ca4814Timo Sirainen if (!i_stream_get_buffer_space(stream, size, NULL))
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;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen const unsigned char *data;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen size_t i, dest, size;
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
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen data = i_stream_get_data(stream->parent, &size);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen /* @UNSAFE: add missing CRs */
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen dest = stream->pos;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen for (i = 0; i < size && dest < stream->buffer_size; i++) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (data[i] == '\n') {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (i == 0) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (!cstream->last_cr)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen stream->w_buffer[dest++] = '\r';
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen } else {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (data[i-1] != '\r')
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen stream->w_buffer[dest++] = '\r';
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen }
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (dest == stream->buffer_size)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen break;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen }
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen stream->w_buffer[dest++] = data[i];
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen }
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen cstream->last_cr = stream->w_buffer[dest-1] == '\r';
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen i_stream_skip(stream->parent, i);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen ret = dest - stream->pos;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen i_assert(ret > 0);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen stream->pos = dest;
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;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen const unsigned char *data;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen size_t i, dest, size;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen ssize_t ret;
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen int diff;
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 */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen dest = stream->pos;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (data[0] == '\n') {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen stream->w_buffer[dest++] = '\n';
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen cstream->pending_cr = FALSE;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen } else {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (cstream->pending_cr) {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen /* CR without LF */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen stream->w_buffer[dest++] = '\r';
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (dest == stream->buffer_size) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen cstream->pending_cr = FALSE;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen return 1;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (data[0] != '\r')
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen stream->w_buffer[dest++] = data[0];
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen diff = -1;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen for (i = 1; i < size && dest < stream->buffer_size; i++) {
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen if (data[i] == '\r') {
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen if (data[i-1] != '\r')
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen continue;
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen } else if (data[i-1] == '\r' && data[i] != '\n') {
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen stream->w_buffer[dest++] = '\r';
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen if (dest == stream->buffer_size) {
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen diff = 0;
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen break;
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen }
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen }
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen stream->w_buffer[dest++] = data[i];
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen cstream->pending_cr = data[i+diff] == '\r';
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 const struct stat *
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Siraineni_stream_crlf_stat(struct istream_private *stream, bool exact)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen{
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen return i_stream_stat(stream->parent, exact);
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;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen cstream->istream.read = crlf ? i_stream_crlf_read_crlf :
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen i_stream_crlf_read_lf;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen cstream->istream.stat = i_stream_crlf_stat;
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,
9511a40d933181045343110c8101b75887062aaeTimo Sirainen i_stream_get_fd(input));
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}