/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 1999-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 of the License, 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 this library. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #define BODY_MODIFIED 0x10000 struct _mu_body_stream { struct _mu_stream stream; mu_body_t body; mu_stream_t transport; }; /* Body stream. */ #define BODY_RDONLY 0 #define BODY_RDWR 1 static int init_tmp_stream (mu_body_t body) { int rc; mu_off_t off; rc = mu_stream_seek (body->data_stream, 0, MU_SEEK_CUR, &off); if (rc) return rc; rc = mu_stream_seek (body->data_stream, 0, MU_SEEK_SET, NULL); if (rc) return rc; rc = mu_stream_copy (body->temp_stream, body->data_stream, 0, NULL); if (rc) return rc; mu_stream_seek (body->data_stream, off, MU_SEEK_SET, NULL); return mu_stream_seek (body->temp_stream, off, MU_SEEK_SET, NULL); } static int body_get_transport (mu_body_t body, int mode, mu_stream_t *pstr) { if (!body->data_stream && body->_get_stream) { int status = body->_get_stream (body, &body->data_stream); if (status) return status; } if (mode == BODY_RDWR || !body->data_stream) { /* Create the temporary file. */ if (!body->temp_stream) { int rc; rc = mu_temp_stream_create (&body->temp_stream, 0); if (rc) return rc; mu_stream_set_buffer (body->temp_stream, mu_buffer_full, 0); if (body->data_stream) { rc = init_tmp_stream (body); if (rc) { mu_stream_destroy (&body->temp_stream); return rc; } } } body->flags |= BODY_MODIFIED; } *pstr = body->temp_stream ? body->temp_stream : body->data_stream; return 0; } static int bstr_close (struct _mu_stream *stream) { return 0; } void bstr_done (struct _mu_stream *stream) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; mu_stream_unref (str->transport); mu_body_unref (str->body); } static int bstr_seek (mu_stream_t stream, mu_off_t off, mu_off_t *presult) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; return mu_stream_seek (str->transport, off, MU_SEEK_SET, presult); } static int bstr_ioctl (mu_stream_t stream, int code, int opcode, void *ptr) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; return mu_stream_ioctl (str->transport, code, opcode, ptr); } static int bstr_read (mu_stream_t stream, char *buf, size_t size, size_t *pret) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; return mu_stream_read (str->transport, buf, size, pret); } static int bstr_write (mu_stream_t stream, const char *buf, size_t size, size_t *pret) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; if (!str->body->temp_stream) { int rc; mu_off_t off; mu_stream_t tmp, transport; rc = mu_stream_seek (str->transport, 0, MU_SEEK_CUR, &off); if (rc) return rc; rc = body_get_transport (str->body, BODY_RDWR, &tmp); if (rc) return rc; rc = mu_streamref_create (&transport, tmp); if (rc) return rc; mu_stream_destroy (&str->transport); str->transport = transport; rc = mu_stream_seek (str->transport, off, MU_SEEK_SET, NULL); if (rc) return rc; } return mu_stream_write (str->transport, buf, size, pret); } static int bstr_truncate (mu_stream_t stream, mu_off_t n) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; return mu_stream_truncate (str->transport, n); } static int bstr_size (mu_stream_t stream, mu_off_t *size) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; return mu_stream_size (str->transport, size); } static int bstr_flush (mu_stream_t stream) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; return mu_stream_flush (str->transport); } /* Default function for the body. */ static int body_get_lines (mu_body_t body, size_t *plines) { mu_stream_t transport, null; int status; mu_off_t off; mu_stream_stat_buffer stat; status = body_get_transport (body, BODY_RDONLY, &transport); if (status) return status; status = mu_stream_flush (transport); if (status) return status; status = mu_stream_seek (transport, 0, MU_SEEK_CUR, &off); if (status) return status; status = mu_stream_seek (transport, 0, MU_SEEK_SET, NULL); if (status) return status; status = mu_nullstream_create (&null, MU_STREAM_WRITE); if (status) return status; mu_stream_set_stat (null, MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUTLN), stat); status = mu_stream_copy (null, transport, 0, NULL); mu_stream_destroy (&null); mu_stream_seek (transport, off, MU_SEEK_SET, NULL); if (status == 0) *plines = stat[MU_STREAM_STAT_OUTLN]; return status; } static int body_get_size (mu_body_t body, size_t *psize) { mu_stream_t transport; mu_off_t off = 0; int rc; rc = body_get_transport (body, BODY_RDONLY, &transport); if (rc) return rc; rc = mu_stream_size (transport, &off); if (rc == 0) *psize = off; return 0; } static int body_stream_create (mu_body_t body, mu_stream_t *return_stream) { int rc; mu_stream_t stream, transport; struct _mu_body_stream *str; rc = body_get_transport (body, BODY_RDONLY, &stream); if (rc) return rc; rc = mu_streamref_create (&transport, stream); if (rc) return rc; str = (struct _mu_body_stream *) _mu_stream_create (sizeof (*str), MU_STREAM_RDWR|MU_STREAM_SEEK|_MU_STR_OPEN); if (!str) return ENOMEM; str->transport = transport; str->body = body; str->stream.ctl = bstr_ioctl; str->stream.read = bstr_read; str->stream.write = bstr_write; str->stream.truncate = bstr_truncate; str->stream.size = bstr_size; str->stream.seek = bstr_seek; str->stream.flush = bstr_flush; str->stream.close = bstr_close; str->stream.done = bstr_done; /* Override the defaults. */ body->_lines = body_get_lines; body->_size = body_get_size; mu_body_ref (body); *return_stream = (mu_stream_t) str; return 0; } int mu_body_get_streamref (mu_body_t body, mu_stream_t *pstream) { if (body == NULL) return EINVAL; if (pstream == NULL) return MU_ERR_OUT_PTR_NULL; return body_stream_create (body, pstream); } int mu_body_set_stream (mu_body_t body, mu_stream_t stream, void *owner) { if (body == NULL) return EINVAL; if (body->owner != owner) return EACCES; /* make sure we destroy the old one if it is owned by the body */ mu_stream_destroy (&body->temp_stream); mu_stream_destroy (&body->data_stream); body->data_stream = stream; body->flags |= BODY_MODIFIED; return 0; } int mu_body_set_get_stream (mu_body_t body, int (*_getstr) (mu_body_t, mu_stream_t *), void *owner) { if (body == NULL) return EINVAL; if (body->owner != owner) return EACCES; body->_get_stream = _getstr; return 0; } int mu_body_set_lines (mu_body_t body, int (*_lines) (mu_body_t, size_t *), void *owner) { if (body == NULL) return EINVAL; if (body->owner != owner) return EACCES; body->_lines = _lines; return 0; } int mu_body_lines (mu_body_t body, size_t *plines) { if (body == NULL) return EINVAL; if (plines == NULL) return MU_ERR_OUT_PTR_NULL; if (body->_lines) return body->_lines (body, plines); /* Fall back on the stream. */ return body_get_lines (body, plines); } int mu_body_size (mu_body_t body, size_t *psize) { int rc; mu_stream_t str; mu_off_t s; if (body == NULL) return EINVAL; if (psize == NULL) return MU_ERR_OUT_PTR_NULL; if (body->_size) return body->_size (body, psize); /* Fall back on the transport stream. */ rc = body_get_transport (body, BODY_RDONLY, &str); if (rc) return rc; rc = mu_stream_size (str, &s); *psize = s; return 0; } int mu_body_set_size (mu_body_t body, int (*_size)(mu_body_t, size_t*), void *owner) { if (body == NULL) return EINVAL; if (body->owner != owner) return EACCES; body->_size = _size; return 0; } int mu_body_create (mu_body_t *pbody, void *owner) { mu_body_t body; if (pbody == NULL) return MU_ERR_OUT_PTR_NULL; if (owner == NULL) return EINVAL; body = calloc (1, sizeof (*body)); if (body == NULL) return ENOMEM; body->owner = owner; mu_body_ref (body); *pbody = body; return 0; } static void _mu_body_free (mu_body_t body) { mu_stream_destroy (&body->data_stream); mu_stream_destroy (&body->temp_stream); free (body); } void mu_body_ref (mu_body_t body) { if (body) body->ref_count++; } void mu_body_unref (mu_body_t body) { if (body && --body->ref_count == 0) _mu_body_free (body); } void mu_body_destroy (mu_body_t *pbody, void *owner) { if (pbody && *pbody) { mu_body_t body = *pbody; if (body->owner == owner && --body->ref_count == 0) { _mu_body_free (body); *pbody = NULL; } } } void * mu_body_get_owner (mu_body_t body) { return (body) ? body->owner : NULL; } int mu_body_is_modified (mu_body_t body) { return (body) ? (body->flags & BODY_MODIFIED) : 0; } int mu_body_clear_modified (mu_body_t body) { if (body) body->flags &= ~BODY_MODIFIED; return 0; }