Mercurial > dovecot > core-2.2
view src/plugins/mail-filter/ostream-ext-filter.c @ 17130:add8c00fb3cc
Updated copyright notices to include year 2014.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 04 Feb 2014 16:23:22 -0500 |
parents | ba855eac00db |
children | 3009a1a6f6d5 |
line wrap: on
line source
/* Copyright (c) 2013-2014 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "net.h" #include "eacces-error.h" #include "istream.h" #include "ostream-private.h" #include "ostream-ext-filter.h" #include <unistd.h> struct mail_filter_ostream { struct ostream_private ostream; int fd; struct istream *ext_in; struct ostream *ext_out; bool flushed; }; static void o_stream_mail_filter_close(struct iostream_private *stream, bool close_parent) { struct mail_filter_ostream *mstream = (struct mail_filter_ostream *)stream; if (mstream->ext_in != NULL) i_stream_destroy(&mstream->ext_in); if (mstream->ext_out != NULL) o_stream_destroy(&mstream->ext_out); if (mstream->fd != -1) { if (close(mstream->fd) < 0) i_error("ext-filter: close() failed: %m"); mstream->fd = -1; } if (close_parent) o_stream_close(mstream->ostream.parent); } static ssize_t o_stream_mail_filter_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct mail_filter_ostream *mstream = (struct mail_filter_ostream *)stream; ssize_t ret; if (mstream->ext_out == NULL) { /* connect failed */ mstream->ostream.ostream.stream_errno = EIO; return -1; } /* send the data to the filter */ ret = o_stream_sendv(mstream->ext_out, iov, iov_count); if (ret < 0) { stream->ostream.stream_errno = mstream->ext_out->stream_errno; return -1; } stream->ostream.offset += ret; return ret; } static int o_stream_mail_filter_flush(struct ostream_private *stream) { struct mail_filter_ostream *mstream = (struct mail_filter_ostream *)stream; const unsigned char *data; size_t size; ssize_t ret; if (mstream->ext_out == NULL) { /* connect failed */ return -1; } if (mstream->flushed) return 0; if (shutdown(mstream->fd, SHUT_WR) < 0) i_error("ext-filter: shutdown() failed: %m"); while ((ret = i_stream_read_data(mstream->ext_in, &data, &size, 0)) > 0) { ret = o_stream_send(stream->parent, data, size); if (ret != (ssize_t)size) { i_assert(ret < 0); o_stream_copy_error_from_parent(stream); return -1; } i_stream_skip(mstream->ext_in, size); } i_assert(ret == -1); if (!i_stream_have_bytes_left(mstream->ext_in) && mstream->ext_in->v_offset == 0) { /* EOF without any input -> assume the script is repoting failure. pretty ugly way, but currently there's no error reporting channel. */ stream->ostream.stream_errno = EIO; return -1; } if (mstream->ext_in->stream_errno != 0) { stream->ostream.stream_errno = mstream->ext_in->stream_errno; return -1; } ret = o_stream_flush(stream->parent); if (ret < 0) o_stream_copy_error_from_parent(stream); else mstream->flushed = TRUE; return ret; } static int filter_connect(struct mail_filter_ostream *mstream, const char *socket_path, const char *args) { const char **argv; string_t *str; int fd; argv = t_strsplit(args, " "); if ((fd = net_connect_unix_with_retries(socket_path, 1000)) < 0) { if (errno == EACCES) { i_error("ext-filter: %s", eacces_error_get("net_connect_unix", socket_path)); } else { i_error("ext-filter: net_connect_unix(%s) failed: %m", socket_path); } return -1; } net_set_nonblock(fd, FALSE); mstream->fd = fd; mstream->ext_in = i_stream_create_fd(fd, IO_BLOCK_SIZE, FALSE); mstream->ext_out = o_stream_create_fd(fd, 0, FALSE); str = t_str_new(256); str_append(str, "VERSION\tscript\t3\t0\nnoreply\n"); for (; *argv != NULL; argv++) { str_append(str, *argv); str_append_c(str, '\n'); } str_append_c(str, '\n'); o_stream_send(mstream->ext_out, str_data(str), str_len(str)); return 0; } struct ostream * o_stream_create_ext_filter(struct ostream *output, const char *socket_path, const char *args) { struct mail_filter_ostream *mstream; mstream = i_new(struct mail_filter_ostream, 1); mstream->fd = -1; mstream->ostream.iostream.close = o_stream_mail_filter_close; mstream->ostream.sendv = o_stream_mail_filter_sendv; mstream->ostream.flush = o_stream_mail_filter_flush; (void)filter_connect(mstream, socket_path, args); return o_stream_create(&mstream->ostream, output, mstream->fd); }