istream-metawrap.c revision d28179fd78550a58be44dcb1e3e830ab7d33172d
1008N/A/* Copyright (c) 2007-2016 Dovecot authors, see the included COPYING file */
1008N/A
1008N/A#include "lib.h"
1008N/A#include "istream-private.h"
1008N/A#include "istream-metawrap.h"
1008N/A
1008N/Astruct metawrap_istream {
1008N/A struct istream_private istream;
1008N/A metawrap_callback_t *callback;
1008N/A void *context;
1008N/A
1008N/A uoff_t start_offset, pending_seek;
1008N/A bool in_metadata;
1008N/A};
1008N/A
1008N/Astatic int metadata_header_read(struct metawrap_istream *mstream)
1008N/A{
1008N/A char *line, *p;
1008N/A
1008N/A while ((line = i_stream_read_next_line(mstream->istream.parent)) != NULL) {
1008N/A if (*line == '\0') {
1008N/A mstream->callback(NULL, NULL, mstream->context);
1008N/A return 1;
1008N/A }
4129N/A p = strchr(line, ':');
1008N/A if (p == NULL) {
1008N/A io_stream_set_error(&mstream->istream.iostream,
2371N/A "Metadata header line is missing ':'");
1008N/A mstream->istream.istream.stream_errno = EINVAL;
1008N/A return -1;
2371N/A }
1008N/A *p++ = '\0';
1008N/A mstream->callback(line, p, mstream->context);
1008N/A }
1008N/A if (mstream->istream.parent->eof) {
2371N/A if (mstream->istream.parent->stream_errno != 0) {
2371N/A mstream->istream.istream.stream_errno =
1008N/A mstream->istream.parent->stream_errno;
2371N/A } else {
1008N/A io_stream_set_error(&mstream->istream.iostream,
4129N/A "Metadata header is missing ending line");
1008N/A mstream->istream.istream.stream_errno = EINVAL;
2371N/A return -1;
1008N/A }
1008N/A mstream->istream.istream.eof = TRUE;
4129N/A return -1;
4129N/A }
1008N/A i_assert(!mstream->istream.parent->blocking);
1008N/A return 0;
2371N/A}
2371N/A
1008N/Astatic ssize_t i_stream_metawrap_read(struct istream_private *stream)
1008N/A{
1008N/A struct metawrap_istream *mstream = (struct metawrap_istream *)stream;
2371N/A int ret;
1008N/A
2371N/A i_stream_seek(stream->parent, mstream->start_offset +
1008N/A stream->istream.v_offset);
1008N/A
1008N/A if (mstream->in_metadata) {
1008N/A ret = metadata_header_read(mstream);
3065N/A i_assert(stream->istream.v_offset == 0);
1008N/A mstream->start_offset = stream->parent->v_offset;
1008N/A if (ret <= 0)
1008N/A return ret;
1008N/A /* this stream is kind of silently skipping over the metadata */
1008N/A stream->start_offset += mstream->start_offset;
1008N/A mstream->in_metadata = FALSE;
1008N/A if (mstream->pending_seek != 0) {
1008N/A i_stream_seek(&stream->istream, mstream->pending_seek);
2371N/A return i_stream_read(&stream->istream);
2371N/A }
2371N/A }
2371N/A /* after metadata header it's all just passthrough */
2371N/A return i_stream_read_copy_from_parent(&stream->istream);
2371N/A}
2371N/A
1008N/Astatic void
1008N/Ai_stream_metawrap_seek(struct istream_private *stream,
1008N/A uoff_t v_offset, bool mark ATTR_UNUSED)
1008N/A{
4129N/A struct metawrap_istream *mstream = (struct metawrap_istream *)stream;
4129N/A
4129N/A if (!mstream->in_metadata) {
2371N/A /* already read through metadata. we can skip directly. */
3065N/A stream->istream.v_offset = v_offset;
2371N/A mstream->pending_seek = 0;
1008N/A } else {
1008N/A /* we need to read through the metadata first */
1008N/A mstream->pending_seek = v_offset;
2371N/A stream->istream.v_offset = 0;
2371N/A }
1008N/A stream->skip = stream->pos = 0;
1008N/A}
1008N/A
1008N/Astatic int i_stream_metawrap_stat(struct istream_private *stream, bool exact)
1008N/A{
1008N/A struct metawrap_istream *mstream = (struct metawrap_istream *)stream;
1008N/A const struct stat *st;
1008N/A int ret;
1008N/A
1008N/A if (i_stream_stat(stream->parent, exact, &st) < 0) {
1008N/A stream->istream.stream_errno = stream->parent->stream_errno;
1008N/A return -1;
1008N/A }
1008N/A stream->statbuf = *st;
1929N/A
1008N/A if (mstream->in_metadata) {
3065N/A ret = i_stream_read(&stream->istream);
1008N/A if (ret < 0 && stream->istream.stream_errno != 0)
1008N/A return -1;
1008N/A if (ret == 0) {
4129N/A stream->statbuf.st_size = -1;
1008N/A return 0;
2371N/A }
1008N/A }
1008N/A i_assert((uoff_t)stream->statbuf.st_size >= mstream->start_offset);
1008N/A stream->statbuf.st_size -= mstream->start_offset;
1008N/A return 0;
1008N/A}
1008N/A
1008N/Astruct istream *
1008N/Ai_stream_create_metawrap(struct istream *input,
1008N/A metawrap_callback_t *callback, void *context)
1008N/A{
1008N/A struct metawrap_istream *mstream;
1008N/A
1008N/A mstream = i_new(struct metawrap_istream, 1);
1008N/A mstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
1008N/A
1929N/A mstream->istream.read = i_stream_metawrap_read;
1008N/A mstream->istream.seek = i_stream_metawrap_seek;
3065N/A mstream->istream.stat = input->seekable ? i_stream_metawrap_stat : NULL;
1008N/A
1008N/A /* we can't set abs_start_offset early enough so that it would get
1008N/A passed to our child istreams. */
4129N/A mstream->istream.istream.readable_fd = FALSE;
4129N/A mstream->istream.istream.blocking = input->blocking;
4129N/A mstream->istream.istream.seekable = input->seekable;
4129N/A mstream->in_metadata = TRUE;
4129N/A mstream->callback = callback;
4129N/A mstream->context = context;
4129N/A return i_stream_create(&mstream->istream, input,
4129N/A i_stream_get_fd(input));
4129N/A}
4129N/A