/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2010, 2011 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 static int _body_flush (mu_stream_t); static int _body_read (mu_stream_t, char *, size_t, size_t *); static int _body_truncate (mu_stream_t, mu_off_t); static int _body_size (mu_stream_t, mu_off_t *); static int _body_write (mu_stream_t, const char *, size_t, size_t *); static int _body_ioctl (mu_stream_t, int, int, void *); static int _body_seek (mu_stream_t, mu_off_t, mu_off_t *); static const char *_body_error_string (mu_stream_t, int); /* Our own defaults for the body. */ static int _body_get_size (mu_body_t, size_t *); static int _body_get_lines (mu_body_t, size_t *); static int _body_get_size0 (mu_stream_t, size_t *); static int _body_get_lines0 (mu_stream_t, size_t *); 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; *pbody = body; return 0; } void mu_body_destroy (mu_body_t *pbody, void *owner) { if (pbody && *pbody) { mu_body_t body = *pbody; if (body->owner == owner) { if (body->stream) mu_stream_destroy (&body->stream); if (body->fstream) { mu_stream_close (body->fstream); mu_stream_destroy (&body->fstream); } free (body); } *pbody = NULL; } } void * mu_body_get_owner (mu_body_t body) { return (body) ? body->owner : NULL; } /* FIXME: not implemented. */ int mu_body_is_modified (mu_body_t body) { return (body) ? (body->flags & BODY_MODIFIED) : 0; } /* FIXME: not implemented. */ int mu_body_clear_modified (mu_body_t body) { if (body) body->flags &= ~BODY_MODIFIED; return 0; } struct _mu_body_stream { struct _mu_stream stream; mu_body_t body; }; static int _body_get_stream (mu_body_t body, mu_stream_t *pstream, int ref) { if (body == NULL) return EINVAL; if (pstream == NULL) return MU_ERR_OUT_PTR_NULL; if (body->stream == NULL) { if (body->_get_stream) { int status = body->_get_stream (body, &body->stream); if (status) return status; } else { int status; struct _mu_body_stream *str = (struct _mu_body_stream *) _mu_stream_create (sizeof (*str), MU_STREAM_RDWR|MU_STREAM_SEEK|_MU_STR_OPEN); if (!str) return ENOMEM; /* Create the temporary file. */ status = mu_temp_file_stream_create (&body->fstream, NULL, 0); if (status != 0) return status; mu_stream_set_buffer (body->fstream, mu_buffer_full, 0); str->stream.ctl = _body_ioctl; str->stream.read = _body_read; str->stream.write = _body_write; str->stream.truncate = _body_truncate; str->stream.size = _body_size; str->stream.seek = _body_seek; str->stream.flush = _body_flush; str->body = body; body->stream = (mu_stream_t) str; /* Override the defaults. */ body->_lines = _body_get_lines; body->_size = _body_get_size; } } if (!ref) { *pstream = body->stream; return 0; } return mu_streamref_create (pstream, body->stream); } int mu_body_get_stream (mu_body_t body, mu_stream_t *pstream) { /* FIXME: Deprecation warning */ return _body_get_stream (body, pstream, 0); } int mu_body_get_streamref (mu_body_t body, mu_stream_t *pstream) { return _body_get_stream (body, pstream, 1); } 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->stream); body->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 (body->_lines) return body->_lines (body, plines); /* Fall on the stream. */ if (body->stream) return _body_get_lines0 (body->stream, plines); if (plines) *plines = 0; return 0; } int mu_body_size (mu_body_t body, size_t *psize) { if (body == NULL) return EINVAL; if (body->_size) return body->_size (body, psize); /* Fall on the stream. */ if (body->stream) return _body_get_size0 (body->stream, psize); if (psize) *psize = 0; 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; } /* Stub function for the body stream. */ static int _body_seek (mu_stream_t stream, mu_off_t off, mu_off_t *presult) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; mu_body_t body = str->body; return mu_stream_seek (body->fstream, off, MU_SEEK_SET, presult); } static const char * _body_error_string (mu_stream_t stream, int rc) { /* FIXME: How to know if rc was returned by a body->stream? */ return NULL; } static int _body_ioctl (mu_stream_t stream, int code, int opcode, void *ptr) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; mu_body_t body = str->body; return mu_stream_ioctl (body->fstream, code, opcode, ptr); } static int _body_read (mu_stream_t stream, char *buf, size_t size, size_t *pret) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; mu_body_t body = str->body; return mu_stream_read (body->fstream, buf, size, pret); } static int _body_write (mu_stream_t stream, const char *buf, size_t size, size_t *pret) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; mu_body_t body = str->body; return mu_stream_write (body->fstream, buf, size, pret); } static int _body_truncate (mu_stream_t stream, mu_off_t n) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; mu_body_t body = str->body; return mu_stream_truncate (body->fstream, n); } static int _body_size (mu_stream_t stream, mu_off_t *size) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; mu_body_t body = str->body; return mu_stream_size (body->fstream, size); } static int _body_flush (mu_stream_t stream) { struct _mu_body_stream *str = (struct _mu_body_stream*) stream; mu_body_t body = str->body; return mu_stream_flush (body->fstream); } /* Default function for the body. */ static int _body_get_lines (mu_body_t body, size_t *plines) { return _body_get_lines0 (body->fstream, plines); } static int _body_get_size (mu_body_t body, size_t *psize) { return _body_get_size0 (body->fstream, psize); } static int _body_get_size0 (mu_stream_t stream, size_t *psize) { mu_off_t off = 0; int status = mu_stream_size (stream, &off); if (psize) *psize = off; return status; } static int _body_get_lines0 (mu_stream_t stream, size_t *plines) { int status = mu_stream_flush (stream); size_t lines = 0; if (status == 0) { char buf[128]; size_t n = 0; status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL); if (status) return status; while ((status = mu_stream_readline (stream, buf, sizeof buf, &n)) == 0 && n > 0) { if (buf[n - 1] == '\n') lines++; } } if (plines) *plines = lines; return status; }