istream-crlf.c revision bf3fb941cc1deb06786449b89c77d9a56a07c251
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2007-2008 Dovecot authors, see the included COPYING file */
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "lib.h"
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen#include "istream-internal.h"
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainen#include "istream-crlf.h"
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainenstruct crlf_istream {
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen struct istream_private istream;
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen unsigned int pending_cr:1;
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen unsigned int last_cr:1;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen};
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger
06ff2a72c39cb34cc6425f17fc82c5e93fef2018Timo Sirainenstatic void i_stream_crlf_destroy(struct iostream_private *stream)
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct crlf_istream *cstream = (struct crlf_istream *)stream;
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen
de76b960297406115cf6bae473f004c08174b16aTimo Sirainen i_free(cstream->istream.w_buffer);
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen i_stream_unref(&cstream->istream.parent);
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen}
c519de264df14a9d525e2604671c332590ce54e3Timo Sirainen
61530b48694398df42744204e35535dbe3f745c4Timo Sirainenstatic void
61530b48694398df42744204e35535dbe3f745c4Timo Siraineni_stream_crlf_set_max_buffer_size(struct iostream_private *stream,
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen size_t max_size)
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen{
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen struct crlf_istream *cstream = (struct crlf_istream *)stream;
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen cstream->istream.max_buffer_size = max_size;
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainen i_stream_set_max_buffer_size(cstream->istream.parent, max_size);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen}
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainenstatic int i_stream_crlf_read_common(struct crlf_istream *cstream)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen{
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen struct istream_private *stream = &cstream->istream;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen const unsigned char *data;
2b3b0df76184799317584b596af8df5afec3ebddTimo Sirainen size_t size;
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen ssize_t ret;
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainen
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainen data = i_stream_get_data(stream->parent, &size);
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen if (size == 0) {
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen ret = i_stream_read(stream->parent);
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen if (ret <= 0 && (ret != -2 || stream->skip == 0)) {
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen stream->istream.stream_errno =
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen stream->parent->stream_errno;
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen stream->istream.eof = stream->parent->eof;
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen return ret;
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen }
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen data = i_stream_get_data(stream->parent, &size);
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen i_assert(size != 0);
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen }
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen if (!i_stream_get_buffer_space(stream, size, NULL))
d244c6cadd5f077f5d0f1e00c3652d0108a2d908Timo Sirainen return -2;
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen return 1;
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen}
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainenstatic ssize_t i_stream_crlf_read_crlf(struct istream_private *stream)
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen{
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen struct crlf_istream *cstream = (struct crlf_istream *)stream;
67c25cb4af273aff7384d5028d459cc9afdf8712Timo Sirainen const unsigned char *data;
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen size_t i, dest, size;
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen ssize_t ret;
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen
d244c6cadd5f077f5d0f1e00c3652d0108a2d908Timo Sirainen ret = i_stream_crlf_read_common(cstream);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ret <= 0)
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen return ret;
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen
d244c6cadd5f077f5d0f1e00c3652d0108a2d908Timo Sirainen data = i_stream_get_data(stream->parent, &size);
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen /* @UNSAFE: add missing CRs */
baf1148108b7d9739626b47cc57298c36929586aTimo Sirainen dest = stream->pos;
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen for (i = 0; i < size && dest < stream->buffer_size; i++) {
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen if (data[i] == '\n') {
baf1148108b7d9739626b47cc57298c36929586aTimo Sirainen if (i == 0) {
baf1148108b7d9739626b47cc57298c36929586aTimo Sirainen if (!cstream->last_cr)
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen stream->w_buffer[dest++] = '\r';
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen } else {
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen if (data[i-1] != '\r')
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen stream->w_buffer[dest++] = '\r';
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen }
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen if (dest == stream->buffer_size)
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen break;
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen }
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen stream->w_buffer[dest++] = data[i];
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen }
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen cstream->last_cr = stream->w_buffer[dest-1] == '\r';
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen i_stream_skip(stream->parent, i);
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen ret = dest - stream->pos;
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen i_assert(ret != 0);
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen stream->pos = dest;
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen return ret;
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen}
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainenstatic ssize_t i_stream_crlf_read_lf(struct istream_private *stream)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen{
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct crlf_istream *cstream = (struct crlf_istream *)stream;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen const unsigned char *data;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen size_t i, dest, size;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen ssize_t ret;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen int diff;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen ret = i_stream_crlf_read_common(cstream);
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch if (ret <= 0)
cddfd1355db6b60c71d7ee3c0b4f23b3efcc9ad1Timo Sirainen return ret;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen data = i_stream_get_data(stream->parent, &size);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* @UNSAFE */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen dest = stream->pos;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (data[0] == '\n') {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen stream->w_buffer[dest++] = '\n';
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen cstream->pending_cr = FALSE;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen } else {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (cstream->pending_cr) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* CR without LF */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen stream->w_buffer[dest++] = '\r';
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (dest == stream->buffer_size) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen cstream->pending_cr = FALSE;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return 1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (data[0] != '\r')
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen stream->w_buffer[dest++] = data[0];
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen diff = -1;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen for (i = 1; i < size && dest < stream->buffer_size; i++) {
cddfd1355db6b60c71d7ee3c0b4f23b3efcc9ad1Timo Sirainen if (data[i] == '\r') {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (data[i-1] != '\r')
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen continue;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen } else if (data[i-1] == '\r' && data[i] != '\n') {
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen stream->w_buffer[dest++] = '\r';
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (dest == stream->buffer_size) {
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen diff = 0;
cddfd1355db6b60c71d7ee3c0b4f23b3efcc9ad1Timo Sirainen break;
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen }
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen }
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen stream->w_buffer[dest++] = data[i];
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen }
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen cstream->pending_cr = data[i+diff] == '\r';
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen i_stream_skip(stream->parent, i);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen ret = dest - stream->pos;
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen if (ret == 0) {
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen i_assert(cstream->pending_cr && size == 1);
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen return i_stream_crlf_read_lf(stream);
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen }
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen stream->pos = dest;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return ret;
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen}
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainenstatic void ATTR_NORETURN
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Siraineni_stream_crlf_seek(struct istream_private *stream ATTR_UNUSED,
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen uoff_t v_offset ATTR_UNUSED, bool mark ATTR_UNUSED)
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen{
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen i_panic("crlf-istream: seeking unsupported currently");
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen}
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainenstatic const struct stat *
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Siraineni_stream_crlf_stat(struct istream_private *stream, bool exact)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen{
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return i_stream_stat(stream->parent, exact);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen}
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainenstatic struct istream *
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Siraineni_stream_create_crlf_full(struct istream *input, bool crlf)
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen{
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen struct crlf_istream *cstream;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_ref(input);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch cstream = i_new(struct crlf_istream, 1);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen cstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen cstream->istream.iostream.destroy = i_stream_crlf_destroy;
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen cstream->istream.iostream.set_max_buffer_size =
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen i_stream_crlf_set_max_buffer_size;
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen cstream->istream.read = crlf ? i_stream_crlf_read_crlf :
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen i_stream_crlf_read_lf;
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen cstream->istream.seek = i_stream_crlf_seek;
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen cstream->istream.stat = i_stream_crlf_stat;
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen cstream->istream.istream.blocking = input->blocking;
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen cstream->istream.istream.seekable = FALSE;
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen return i_stream_create(&cstream->istream, input,
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen i_stream_get_fd(input));
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen}
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainenstruct istream *i_stream_create_crlf(struct istream *input)
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen{
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen return i_stream_create_crlf_full(input, TRUE);
4145aa5025b57ec64418e503c2a5a6bf5a02aec5Timo Sirainen}
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
905951e448e0d0f0778f43ce7673d0cac60b9b61Timo Sirainenstruct istream *i_stream_create_lf(struct istream *input)
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen{
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen return i_stream_create_crlf_full(input, FALSE);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen}
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen