/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 2010-2021 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with GNU Mailutils. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include static int _iostream_read (struct _mu_stream *str, char *buf, size_t bufsize, size_t *pnread) { struct _mu_iostream *sp = (struct _mu_iostream *)str; int rc = mu_stream_read (sp->transport[_MU_STREAM_INPUT], buf, bufsize, pnread); if (rc) sp->last_err_str = _MU_STREAM_INPUT; return rc; } static int _iostream_write (struct _mu_stream *str, const char *buf, size_t bufsize, size_t *pnwrite) { struct _mu_iostream *sp = (struct _mu_iostream *)str; int rc = mu_stream_write (sp->transport[_MU_STREAM_OUTPUT], buf, bufsize, pnwrite); if (rc) sp->last_err_str = _MU_STREAM_OUTPUT; return rc; } static int _iostream_flush (struct _mu_stream *str) { struct _mu_iostream *sp = (struct _mu_iostream *)str; int rc = mu_stream_flush (sp->transport[_MU_STREAM_INPUT]); if (rc) { sp->last_err_str = _MU_STREAM_INPUT; return rc; } if (sp->transport[_MU_STREAM_INPUT] != sp->transport[_MU_STREAM_OUTPUT]) { rc = mu_stream_flush (sp->transport[_MU_STREAM_OUTPUT]); if (rc) sp->last_err_str = _MU_STREAM_OUTPUT; } return rc; } static int _iostream_open (struct _mu_stream *str) { struct _mu_iostream *sp = (struct _mu_iostream *)str; int rc; rc = mu_stream_open (sp->transport[_MU_STREAM_INPUT]); if (rc) { sp->last_err_str = _MU_STREAM_INPUT; return rc; } if (sp->transport[_MU_STREAM_INPUT] != sp->transport[_MU_STREAM_OUTPUT]) { rc = mu_stream_open (sp->transport[_MU_STREAM_OUTPUT]); if (rc) { sp->last_err_str = _MU_STREAM_OUTPUT; mu_stream_close (sp->transport[_MU_STREAM_INPUT]); } } return rc; } static int _iostream_close (struct _mu_stream *str) { struct _mu_iostream *sp = (struct _mu_iostream *)str; mu_stream_close (sp->transport[_MU_STREAM_INPUT]); mu_stream_close (sp->transport[_MU_STREAM_OUTPUT]); return 0; } static void _iostream_done (struct _mu_stream *str) { struct _mu_iostream *sp = (struct _mu_iostream *)str; mu_stream_unref (sp->transport[_MU_STREAM_INPUT]); mu_stream_unref (sp->transport[_MU_STREAM_OUTPUT]); } static int _iostream_ctl (struct _mu_stream *str, int code, int opcode, void *arg) { struct _mu_iostream *sp = (struct _mu_iostream *)str; switch (code) { case MU_IOCTL_TRANSPORT: if (!arg) return EINVAL; else { mu_transport_t *ptrans = arg; switch (opcode) { case MU_IOCTL_OP_GET: ptrans[0] = (mu_transport_t) sp->transport[_MU_STREAM_INPUT]; ptrans[1] = (mu_transport_t) sp->transport[_MU_STREAM_OUTPUT]; break; case MU_IOCTL_OP_SET: ptrans = arg; sp->transport[_MU_STREAM_INPUT] = (mu_stream_t) ptrans[0]; sp->transport[_MU_STREAM_OUTPUT] = (mu_stream_t) ptrans[1]; break; default: return EINVAL; } } break; case MU_IOCTL_SUBSTREAM: case MU_IOCTL_TOPSTREAM: if (!arg) return EINVAL; else { mu_stream_t *pstr = arg; switch (opcode) { case MU_IOCTL_OP_GET: pstr[0] = sp->transport[0]; mu_stream_ref (pstr[0]); pstr[1] = sp->transport[1]; mu_stream_ref (pstr[1]); break; case MU_IOCTL_OP_SET: mu_stream_unref (sp->transport[0]); sp->transport[0] = pstr[0]; mu_stream_ref (sp->transport[0]); mu_stream_unref (sp->transport[1]); sp->transport[1] = pstr[1]; mu_stream_ref (sp->transport[1]); break; default: return EINVAL; } } break; case MU_IOCTL_TRANSPORT_BUFFER: if (!arg) return EINVAL; else { struct mu_buffer_query *qp = arg; if (!MU_TRANSPORT_VALID_TYPE (qp->type) || !sp->transport[qp->type]) return EINVAL; return mu_stream_ioctl (sp->transport[qp->type], code, opcode, arg); } case MU_IOCTL_TCPSTREAM: return mu_stream_ioctl (sp->transport[0], code, opcode, arg); default: return ENOSYS; } return 0; } static int _iostream_wait (struct _mu_stream *str, int *pflags, struct timeval *tvp) { struct _mu_iostream *sp = (struct _mu_iostream *)str; int rc = EINVAL; if (*pflags == MU_STREAM_READY_RD) { rc = mu_stream_wait (sp->transport[_MU_STREAM_INPUT], pflags, tvp); if (rc) sp->last_err_str = _MU_STREAM_INPUT; } else if (*pflags == MU_STREAM_READY_WR) { rc = mu_stream_wait (sp->transport[_MU_STREAM_OUTPUT], pflags, tvp); if (rc) sp->last_err_str = _MU_STREAM_OUTPUT; } return rc; } static int _iostream_shutdown (struct _mu_stream *str, int how) { struct _mu_iostream *sp = (struct _mu_iostream *)str; int rc = EINVAL; switch (how) { case MU_STREAM_READ: rc = mu_stream_shutdown (sp->transport[_MU_STREAM_INPUT], how); if (rc) sp->last_err_str = _MU_STREAM_INPUT; break; case MU_STREAM_WRITE: rc = mu_stream_shutdown (sp->transport[_MU_STREAM_OUTPUT], how); if (rc) sp->last_err_str = _MU_STREAM_OUTPUT; } return rc; } static const char * _iostream_error_string (struct _mu_stream *str, int rc) { struct _mu_iostream *sp = (struct _mu_iostream *)str; mu_stream_t transport = sp->transport[sp->last_err_str]; if (transport) return mu_stream_strerror (transport, rc); return mu_strerror (rc); } int mu_iostream_create (mu_stream_t *pref, mu_stream_t in, mu_stream_t out) { struct _mu_iostream *sp; sp = (struct _mu_iostream *) _mu_stream_create (sizeof (*sp), MU_STREAM_READ | MU_STREAM_WRITE); if (!sp) return ENOMEM; sp->stream.flags |= _MU_STR_OPEN; sp->stream.read = _iostream_read; sp->stream.write = _iostream_write; sp->stream.flush = _iostream_flush; sp->stream.open = _iostream_open; sp->stream.close = _iostream_close; sp->stream.done = _iostream_done; sp->stream.ctl = _iostream_ctl; sp->stream.wait = _iostream_wait; sp->stream.shutdown = _iostream_shutdown; sp->stream.error_string = _iostream_error_string; mu_stream_ref (in); sp->transport[_MU_STREAM_INPUT] = in; mu_stream_ref (out); sp->transport[_MU_STREAM_OUTPUT] = out; mu_stream_set_buffer ((mu_stream_t) sp, mu_buffer_line, 0); *pref = (mu_stream_t) sp; return 0; }