istream-crlf.c revision 5478609e31a7665ee108ded988a309673f221aa1
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2007 Dovecot authors, see the included COPYING file */
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi#include "lib.h"
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi#include "istream-internal.h"
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi#include "istream-crlf.h"
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomistruct crlf_istream {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi struct istream_private istream;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi struct istream *input;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi char last_char;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi unsigned int crlf:1;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi};
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomistatic void i_stream_crlf_destroy(struct iostream_private *stream)
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi{
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi struct crlf_istream *cstream = (struct crlf_istream *)stream;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi i_stream_unref(&cstream->input);
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi}
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomistatic void
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomii_stream_crlf_set_max_buffer_size(struct iostream_private *stream,
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi size_t max_size)
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi{
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi struct crlf_istream *cstream = (struct crlf_istream *)stream;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi i_stream_set_max_buffer_size(cstream->input, max_size);
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi}
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomistatic ssize_t i_stream_crlf_read(struct istream_private *stream)
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi{
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi struct crlf_istream *cstream = (struct crlf_istream *)stream;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi const unsigned char *data;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi size_t i, dest, size;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi ssize_t ret;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi data = i_stream_get_data(cstream->input, &size);
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (size <= stream->pos) {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi ret = i_stream_read(cstream->input);
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (ret <= 0 && (ret != -2 || stream->skip == 0)) {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi stream->istream.stream_errno =
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi cstream->input->stream_errno;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi stream->istream.eof = cstream->input->eof;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi return ret;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi data = i_stream_get_data(cstream->input, &size);
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi i_assert(size != 0);
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (size > stream->buffer_size - stream->pos) {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (stream->skip > 0) {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi /* remove the unused bytes from beginning of buffer */
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi i_stream_compress(stream);
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi } else if (stream->max_buffer_size == 0 ||
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi stream->buffer_size < stream->max_buffer_size) {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi /* buffer is full - grow it */
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE);
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (stream->pos == stream->buffer_size)
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi return -2;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi /* @UNSAFE */
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi dest = stream->pos;
c34bdea0d736047ba7bbc835baf4126ea78d5da7Aki Tuomi if (data[0] == '\n')
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi i = 0;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi else {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (cstream->last_char == '\r') {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi /* CR without LF */
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi stream->w_buffer[dest++] = '\r';
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (dest == stream->buffer_size) {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi cstream->last_char = 0;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi return 1;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (data[0] != '\r')
c34bdea0d736047ba7bbc835baf4126ea78d5da7Aki Tuomi stream->w_buffer[dest++] = data[0];
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi i = 1;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi cstream->last_char = data[size-1];
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi for (; i < size && dest < stream->buffer_size; i++) {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (data[i] <= '\r') {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (data[i] == '\n') {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (cstream->crlf) {
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (dest + 1 == stream->buffer_size)
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi break;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi stream->w_buffer[dest++] = '\r';
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi stream->w_buffer[dest++] = '\n';
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi continue;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (data[i] == '\r' && data[i-1] != '\r')
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi continue;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (data[i-1] == '\r') {
c31803dcbdf3d4e3b836134a2a04ee2cd5251ce5Aki Tuomi /* CR without LF */
c31803dcbdf3d4e3b836134a2a04ee2cd5251ce5Aki Tuomi stream->w_buffer[dest++] = '\r';
c31803dcbdf3d4e3b836134a2a04ee2cd5251ce5Aki Tuomi if (dest == stream->buffer_size) {
c31803dcbdf3d4e3b836134a2a04ee2cd5251ce5Aki Tuomi cstream->last_char = 0;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi break;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi if (data[i] == '\r')
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi continue;
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi stream->w_buffer[dest++] = data[i];
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi }
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi i_stream_skip(cstream->input, i);
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi
f7060b8499ed07205734cf7af94ad5405fa687b5Aki Tuomi ret = dest - stream->pos;
i_assert(ret > 0);
stream->pos = dest;
return ret;
}
static void ATTR_NORETURN
i_stream_crlf_seek(struct istream_private *stream ATTR_UNUSED,
uoff_t v_offset ATTR_UNUSED, bool mark ATTR_UNUSED)
{
i_panic("crlf-istream: seeking unsupported currently");
}
static const struct stat *
i_stream_crlf_stat(struct istream_private *stream, bool exact)
{
struct crlf_istream *cstream = (struct crlf_istream *)stream;
return i_stream_stat(cstream->input, exact);
}
static struct istream *
i_stream_create_crlf_full(struct istream *input, bool crlf)
{
struct crlf_istream *cstream;
i_stream_ref(input);
cstream = i_new(struct crlf_istream, 1);
cstream->input = input;
cstream->crlf = crlf;
cstream->istream.iostream.destroy = i_stream_crlf_destroy;
cstream->istream.iostream.set_max_buffer_size =
i_stream_crlf_set_max_buffer_size;
cstream->istream.read = i_stream_crlf_read;
cstream->istream.seek = i_stream_crlf_seek;
cstream->istream.stat = i_stream_crlf_stat;
cstream->istream.istream.blocking = input->blocking;
cstream->istream.istream.seekable = input->seekable;
return i_stream_create(&cstream->istream, i_stream_get_fd(input), 0);
}
struct istream *i_stream_create_crlf(struct istream *input)
{
return i_stream_create_crlf_full(input, TRUE);
}
struct istream *i_stream_create_lf(struct istream *input)
{
return i_stream_create_crlf_full(input, FALSE);
}