Commit efa51304 efa513048a74fca2e3ab1b16cc927f8331347f75 by Sergey Poznyakoff

Make sure modifications to a message or its parts become visible in the message stream.

* include/mailutils/sys/body.h (_mu_body)<rawstream>: New member.
* include/mailutils/sys/message.h (_mu_message)<rawstream>: New member.
* libmailutils/mailbox/body.c: Rewrite. Return the specially crafted
"body-stream" for user I/O operations. Writes to that stream trigger
creation of a temporary stream (write-through mode) which will be used
for subsequent I/O.
* libmailutils/mailbox/msgbody.c (mu_message_get_body): Use msg->rawstream.
* libmailutils/mailbox/msgheader.c (message_header_fill): Do not
call mu_message_get_streamref, as that can result in endless recursion.
Use msg->rawstream instead.
* libmailutils/mailbox/msgref.c (_mu_message_free): Destroy both
rawstream and outstream.
* libmailutils/mailbox/msgstream.c (mu_message_set_stream): Set
rawstream.
(mu_message_get_stream): Create outstream as needed.
(mu_message_get_streamref): If the message has been modified,
create the outstream and return it to the caller. This way, the
modifications become visible via the stream interface.
* libmailutils/tests/Makefile.am: Add new testcases.
* libmailutils/tests/testsuite.at: Likewise.
* libmailutils/tests/modmesg.c: New file.
* libmailutils/tests/modmesg00.at: New file.
* libmailutils/tests/modmesg01.at: New file.
* libmailutils/tests/modmesg02.at: New file.
* libmailutils/tests/modmesg03.at: New file.
* mh/mhn.c: Fix message reference counting.
1 parent 3a6ea349
......@@ -33,6 +33,7 @@ struct _mu_body
{
void *owner;
mu_stream_t stream;
mu_stream_t rawstream;
mu_stream_t fstream;
int flags;
......
......@@ -47,7 +47,8 @@ struct _mu_message
mu_body_t body;
int flags;
mu_stream_t stream;
mu_stream_t rawstream;
mu_stream_t outstream;
mu_attribute_t attribute;
mu_monitor_t monitor;
mu_mime_t mime;
......
......@@ -35,139 +35,296 @@
#include <mailutils/sys/stream.h>
#include <mailutils/sys/body.h>
#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 *);
#define BODY_MODIFIED 0x10000
int
mu_body_create (mu_body_t *pbody, void *owner)
static int body_get_stream (mu_body_t body, mu_stream_t *pstream, int ref);
struct _mu_body_stream
{
struct _mu_stream stream;
mu_body_t body;
};
if (pbody == NULL)
return MU_ERR_OUT_PTR_NULL;
if (owner == NULL)
return EINVAL;
/* Body stream. */
#define BODY_RDONLY 0
#define BODY_RDWR 1
body = calloc (1, sizeof (*body));
if (body == NULL)
return ENOMEM;
static int
init_tmp_stream (mu_body_t body)
{
int rc;
mu_off_t off;
body->owner = owner;
*pbody = body;
return 0;
rc = mu_stream_seek (body->rawstream, 0, MU_SEEK_CUR, &off);
if (rc)
return rc;
rc = mu_stream_seek (body->rawstream, 0, MU_SEEK_SET, NULL);
if (rc)
return rc;
rc = mu_stream_copy (body->fstream, body->rawstream, 0, NULL);
if (rc)
return rc;
mu_stream_seek (body->rawstream, off, MU_SEEK_SET, NULL);
return mu_stream_seek (body->fstream, off, MU_SEEK_SET, NULL);
}
void
mu_body_destroy (mu_body_t *pbody, void *owner)
static int
body_stream_transport (mu_stream_t stream, int mode, mu_stream_t *pstr)
{
if (pbody && *pbody)
struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
mu_body_t body = str->body;
if (!body->rawstream && body->_get_stream)
{
mu_body_t body = *pbody;
if (body->owner == owner)
int status = body->_get_stream (body, &body->rawstream);
if (status)
return status;
}
if (mode == BODY_RDWR || !body->rawstream)
{
/* Create the temporary file. */
if (!body->fstream)
{
if (body->stream)
mu_stream_destroy (&body->stream);
if (body->fstream)
int rc;
rc = mu_temp_file_stream_create (&body->fstream, NULL, 0);
if (rc)
return rc;
mu_stream_set_buffer (body->fstream, mu_buffer_full, 0);
if (body->rawstream)
{
mu_stream_close (body->fstream);
mu_stream_destroy (&body->fstream);
rc = init_tmp_stream (body);
if (rc)
{
mu_stream_destroy (&body->fstream);
return rc;
}
}
free (body);
}
*pbody = NULL;
body->flags |= BODY_MODIFIED;
}
*pstr = body->fstream ? body->fstream : body->rawstream;
return 0;
}
static int
bstr_close (struct _mu_stream *stream)
{
struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
mu_body_t body = str->body;
mu_stream_close (body->rawstream);
mu_stream_close (body->fstream);
return 0;
}
void *
mu_body_get_owner (mu_body_t body)
void
bstr_done (struct _mu_stream *stream)
{
return (body) ? body->owner : NULL;
struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
mu_body_t body = str->body;
mu_stream_destroy (&body->rawstream);
mu_stream_destroy (&body->fstream);
}
/* FIXME: not implemented. */
int
mu_body_is_modified (mu_body_t body)
static int
bstr_seek (mu_stream_t stream, mu_off_t off, mu_off_t *presult)
{
return (body) ? (body->flags & BODY_MODIFIED) : 0;
mu_stream_t transport;
int rc;
rc = body_stream_transport (stream, BODY_RDONLY, &transport);
if (rc)
return rc;
return mu_stream_seek (transport, off, MU_SEEK_SET, presult);
}
/* FIXME: not implemented. */
int
mu_body_clear_modified (mu_body_t body)
static int
bstr_ioctl (mu_stream_t stream, int code, int opcode, void *ptr)
{
if (body)
body->flags &= ~BODY_MODIFIED;
return 0;
mu_stream_t transport;
int rc;
/* FIXME: always RDONLY, is it? */
rc = body_stream_transport (stream, BODY_RDONLY, &transport);
if (rc)
return rc;
return mu_stream_ioctl (transport, code, opcode, ptr);
}
struct _mu_body_stream
static int
bstr_read (mu_stream_t stream, char *buf, size_t size, size_t *pret)
{
struct _mu_stream stream;
mu_body_t body;
};
mu_stream_t transport;
int rc;
rc = body_stream_transport (stream, BODY_RDONLY, &transport);
if (rc)
return rc;
return mu_stream_read (transport, buf, size, pret);
}
static int
_body_get_stream (mu_body_t body, mu_stream_t *pstream, int ref)
bstr_write (mu_stream_t stream, const char *buf, size_t size, size_t *pret)
{
if (body == NULL)
return EINVAL;
if (pstream == NULL)
return MU_ERR_OUT_PTR_NULL;
mu_stream_t transport;
int rc;
rc = body_stream_transport (stream, BODY_RDWR, &transport);
if (rc)
return rc;
return mu_stream_write (transport, buf, size, pret);
}
if (body->stream == NULL)
static int
bstr_truncate (mu_stream_t stream, mu_off_t n)
{
mu_stream_t transport;
int rc;
rc = body_stream_transport (stream, BODY_RDWR, &transport);
if (rc)
return rc;
return mu_stream_truncate (transport, n);
}
static int
bstr_size (mu_stream_t stream, mu_off_t *size)
{
mu_stream_t transport;
int rc;
rc = body_stream_transport (stream, BODY_RDONLY, &transport);
if (rc)
return rc;
return mu_stream_size (transport, size);
}
static int
bstr_flush (mu_stream_t stream)
{
mu_stream_t transport;
int rc;
rc = body_stream_transport (stream, BODY_RDONLY, &transport);
if (rc)
return rc;
return mu_stream_flush (transport);
}
/* Default function for the body. */
static int
bstr_get_lines (mu_body_t body, size_t *plines)
{
mu_stream_t stream, transport;
int status;
size_t lines = 0;
mu_off_t off;
status = body_get_stream (body, &stream, 0);
if (status)
return status;
status = body_stream_transport (body->stream, 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 == 0)
{
if (body->_get_stream)
char buf[128];
size_t n = 0;
status = mu_stream_seek (transport, 0, MU_SEEK_SET, NULL);
if (status)
return status;
while ((status = mu_stream_readline (transport, buf, sizeof buf,
&n)) == 0 && n > 0)
{
int status = body->_get_stream (body, &body->stream);
if (status)
return status;
if (buf[n - 1] == '\n')
lines++;
}
else
{
int status;
struct _mu_body_stream *str =
(struct _mu_body_stream *)
mu_stream_seek (transport, off, MU_SEEK_SET, NULL);
}
if (plines)
*plines = lines;
return status;
}
static int
bstr_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
bstr_get_size (mu_body_t body, size_t *psize)
{
mu_stream_t transport;
int rc;
rc = body_stream_transport (body->stream, BODY_RDONLY, &transport);
if (rc)
return rc;
return bstr_get_size0 (transport, psize);
}
static int
body_stream_create (mu_body_t body)
{
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;
if (!str)
return ENOMEM;
/* Create the temporary file. */
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;
str->body = body;
body->stream = (mu_stream_t) str;
/* Override the defaults. */
body->_lines = bstr_get_lines;
body->_size = bstr_get_size;
body->stream = (mu_stream_t) str;
return 0;
}
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;
}
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)
{
int status = body_stream_create (body);
if (status)
return status;
}
if (!ref)
......@@ -182,13 +339,13 @@ int
mu_body_get_stream (mu_body_t body, mu_stream_t *pstream)
{
/* FIXME: Deprecation warning */
return _body_get_stream (body, pstream, 0);
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);
return body_get_stream (body, pstream, 1);
}
int
......@@ -200,7 +357,8 @@ mu_body_set_stream (mu_body_t body, mu_stream_t stream, void *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;
mu_stream_destroy (&body->rawstream);
body->rawstream = stream;
body->flags |= BODY_MODIFIED;
return 0;
}
......@@ -238,11 +396,7 @@ mu_body_lines (mu_body_t body, size_t *plines)
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;
return bstr_get_lines (body, plines);
}
int
......@@ -254,14 +408,15 @@ mu_body_size (mu_body_t body, size_t *psize)
return body->_size (body, psize);
/* Fall on the stream. */
if (body->stream)
return _body_get_size0 (body->stream, psize);
return bstr_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)
mu_body_set_size (mu_body_t body, int (*_size)(mu_body_t, size_t*),
void *owner)
{
if (body == NULL)
return EINVAL;
......@@ -271,118 +426,61 @@ mu_body_set_size (mu_body_t body, int (*_size)(mu_body_t, size_t*) , void *owner
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)
int
mu_body_create (mu_body_t *pbody, void *owner)
{
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);
}
mu_body_t body;
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);
}
if (pbody == NULL)
return MU_ERR_OUT_PTR_NULL;
if (owner == NULL)
return EINVAL;
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);
}
body = calloc (1, sizeof (*body));
if (body == NULL)
return ENOMEM;
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);
body->owner = owner;
*pbody = body;
return 0;
}
static int
_body_flush (mu_stream_t stream)
void
mu_body_destroy (mu_body_t *pbody, void *owner)
{
struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
mu_body_t body = str->body;
return mu_stream_flush (body->fstream);
if (pbody && *pbody)
{
mu_body_t body = *pbody;
if (body->owner == owner)
{
mu_stream_destroy (&body->rawstream);
mu_stream_destroy (&body->stream);
free (body);
}
*pbody = NULL;
}
}
/* Default function for the body. */
static int
_body_get_lines (mu_body_t body, size_t *plines)
void *
mu_body_get_owner (mu_body_t body)
{
return _body_get_lines0 (body->fstream, plines);
return (body) ? body->owner : NULL;
}
static int
_body_get_size (mu_body_t body, size_t *psize)
int
mu_body_is_modified (mu_body_t body)
{
return _body_get_size0 (body->fstream, psize);
return (body) ? (body->flags & BODY_MODIFIED) : 0;
}
static int
_body_get_size0 (mu_stream_t stream, size_t *psize)
int
mu_body_clear_modified (mu_body_t body)
{
mu_off_t off = 0;
int status = mu_stream_size (stream, &off);
if (psize)
*psize = off;
return status;
if (body)
body->flags &= ~BODY_MODIFIED;
return 0;
}
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;
}
......
......@@ -43,8 +43,7 @@ mu_message_get_body (mu_message_t msg, mu_body_t *pbody)
if (status != 0)
return status;
/* If a stream is already set, use it to create the body stream. */
/* FIXME: I'm not sure if the second condition is really needed */
if (msg->stream/* && (msg->flags & MESSAGE_INTERNAL_STREAM)*/)
if (msg->rawstream)
{
mu_stream_t stream;
int flags = 0;
......@@ -52,8 +51,8 @@ mu_message_get_body (mu_message_t msg, mu_body_t *pbody)
/* FIXME: The actual mu_header_size cannot be used as offset,
because the headers might have been modified in between. */
mu_stream_get_flags (msg->stream, &flags);
status = mu_streamref_create_abridged (&stream, msg->stream,
mu_stream_get_flags (msg->rawstream, &flags);
status = mu_streamref_create_abridged (&stream, msg->rawstream,
msg->orig_header_size, 0);
if (status)
{
......
......@@ -109,17 +109,9 @@ _header_fill (mu_stream_t stream, char **pbuf, size_t *plen)
static int
message_header_fill (void *data, char **pbuf, size_t *plen)
{
int status = 0;
mu_message_t msg = data;
mu_stream_t stream;
status = mu_message_get_streamref (msg, &stream);
if (status == 0)
{
status = _header_fill (stream, pbuf, plen);
mu_stream_destroy (&stream);
}
return status;
return _header_fill (msg->rawstream, pbuf, plen);
}
int
......@@ -136,7 +128,7 @@ mu_message_get_header (mu_message_t msg, mu_header_t *phdr)
int status = mu_header_create (&header, NULL, 0);
if (status != 0)
return status;
if (msg->stream)
if (msg->rawstream)
mu_header_set_fill (header, message_header_fill, msg);
status = mu_header_size (header, &msg->orig_header_size);
if (status)
......
......@@ -54,29 +54,13 @@ _mu_message_free (mu_message_t msg)
mu_observable_destroy (&msg->observable, msg);
}
/* Envelope. */
if (msg->envelope)
mu_envelope_destroy (&msg->envelope, msg);
/* Header. */
if (msg->header)
mu_header_destroy (&msg->header);
/* Body. */
if (msg->body)
mu_body_destroy (&msg->body, msg);
/* Attribute. */
if (msg->attribute)
mu_attribute_destroy (&msg->attribute, msg);
/* Stream. */
if (msg->stream)
mu_stream_destroy (&msg->stream);
/* Mime. */
if (msg->flags & MESSAGE_MIME_OWNER)
mu_mime_destroy (&msg->mime);
mu_envelope_destroy (&msg->envelope, msg);
mu_header_destroy (&msg->header);
mu_body_destroy (&msg->body, msg);
mu_attribute_destroy (&msg->attribute, msg);
mu_stream_destroy (&msg->rawstream);
mu_stream_destroy (&msg->outstream);
mu_mime_destroy (&msg->mime);
/* Loose the owner. */
msg->owner = NULL;
......
......@@ -283,16 +283,41 @@ mu_message_set_stream (mu_message_t msg, mu_stream_t stream, void *owner)
return EINVAL;
if (msg->owner != owner)
return EACCES;
if (msg->stream)
mu_stream_destroy (&msg->stream);
msg->stream = stream;
mu_stream_destroy (&msg->rawstream);
mu_stream_destroy (&msg->outstream);
msg->rawstream = stream;
msg->flags |= MESSAGE_MODIFIED;
msg->flags &= ~MESSAGE_INTERNAL_STREAM;
return 0;
}
static int
_message_get_stream (mu_message_t msg, mu_stream_t *pstream, int ref)
mkoutstream (mu_message_t msg)
{
int status;
mu_header_t hdr;
mu_body_t body;
if (msg->outstream)
return 0;
/* FIXME: Kind of a kludge: make sure the message has header
and body initialized. */
status = mu_message_get_header (msg, &hdr);
if (status)
return status;
status = mu_message_get_body (msg, &body);
if (status)
return status;
status = _message_stream_create (&msg->outstream, msg, MU_STREAM_RDWR);
if (status == 0)
msg->flags |= MESSAGE_INTERNAL_STREAM;
return status;
}
int
mu_message_get_stream (mu_message_t msg, mu_stream_t *pstream)
{
int status;
......@@ -301,54 +326,67 @@ _message_get_stream (mu_message_t msg, mu_stream_t *pstream, int ref)
if (pstream == NULL)
return MU_ERR_OUT_PTR_NULL;
if (msg->stream == NULL)
/* FIXME: Deprecation warning */
if (msg->rawstream == NULL)
{
if (msg->_get_stream)
{
status = msg->_get_stream (msg, &msg->stream);
status = msg->_get_stream (msg, &msg->rawstream);
if (status)
return status;
}
else
{
mu_header_t hdr;
mu_body_t body;
/* FIXME: Kind of a kludge: make sure the message has header
and body initialized. */
status = mu_message_get_header (msg, &hdr);
status = mkoutstream (msg);
if (status)
return status;
status = mu_message_get_body (msg, &body);
status = mu_streamref_create (&msg->rawstream, msg->outstream);
}
}
*pstream = msg->rawstream;
return 0;
}
int
mu_message_get_streamref (mu_message_t msg, mu_stream_t *pstream)
{
int status = 0;
if (msg == NULL)
return EINVAL;
if (pstream == NULL)
return MU_ERR_OUT_PTR_NULL;
if (!msg->rawstream)
{
if (msg->_get_stream)
{
status = msg->_get_stream (msg, &msg->rawstream);
if (status)
return status;
status = _message_stream_create (&msg->stream, msg, MU_STREAM_RDWR);
status = mu_streamref_create (&msg->outstream, msg->rawstream);
}
else
{
status = mkoutstream (msg);
if (status)
return status;
msg->flags |= MESSAGE_INTERNAL_STREAM;
status = mu_streamref_create (&msg->rawstream, msg->outstream);
}
}
if (!ref)
if (status)
return status;
if (!msg->outstream ||
(!(msg->flags & MESSAGE_INTERNAL_STREAM) &&
mu_message_is_modified (msg)))
{
*pstream = msg->stream;
return 0;
mu_stream_destroy (&msg->outstream);
status = mkoutstream (msg);
}
return mu_streamref_create (pstream, msg->stream);
}
int
mu_message_get_stream (mu_message_t msg, mu_stream_t *pstream)
{
/* FIXME: Deprecation warning */
return _message_get_stream (msg, pstream, 0);
}
int
mu_message_get_streamref (mu_message_t msg, mu_stream_t *pstream)
{
return _message_get_stream (msg, pstream, 1);
return mu_streamref_create (pstream, msg->outstream);
}
int
......
......@@ -38,7 +38,7 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
## Non-installable programs
## -------------------------- ##
INCLUDES = @MU_LIB_COMMON_INCLUDES@
AM_CPPFLAGS = @MU_LIB_COMMON_INCLUDES@
noinst_PROGRAMS = \
addr\
cidr\
......@@ -55,6 +55,7 @@ noinst_PROGRAMS = \
mimehdr\
modtofsaf\
msgset\
modmesg\
prop\
scantime\
strftime\
......@@ -94,6 +95,10 @@ TESTSUITE_AT = \
list.at\
mailcap.at\
mimehdr.at\
modmesg00.at\
modmesg01.at\
modmesg02.at\
modmesg03.at\
modtofsaf.at\
msgset.at\
prop.at\
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2013 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Mailutils 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#include <assert.h>
#include <mailutils/mailutils.h>
char *text = "From: root\n\
\n\
This is a test message.\n\
oo\n\
";
int
main (int argc, char **argv)
{
int i;
char *p;
mu_message_t msg;
mu_stream_t stream = NULL;
mu_header_t hdr;
mu_body_t body;
char *buf = NULL;
mu_set_program_name (argv[0]);
mu_static_memory_stream_create (&stream, text, strlen (text));
assert (mu_stream_to_message (stream, &msg) == 0);
mu_stream_unref (stream);
assert (mu_message_get_header (msg, &hdr) == 0);
assert (mu_message_get_body (msg, &body) == 0);
assert (mu_body_get_streamref (body, &stream) == 0);
assert (mu_stream_seek (stream, 0, MU_SEEK_END, NULL) == 0);
for (i = 1; i < argc; i++)
{
if (strcmp (argv[i], "-h") == 0)
{
mu_printf ("usage: %s [-a HDR:VAL] [-t TEXT]\n", mu_program_name);
return 0;
}
if (strcmp (argv[i], "-a") == 0)
{
i++;
assert (argv[i] != NULL);
p = strchr (argv[i], ':');
assert (p != NULL);
*p++ = 0;
while (*p && mu_isspace (*p))
p++;
assert (mu_header_set_value (hdr, argv[i], p, 1) == 0);
}
else if (strcmp (argv[i], "-l") == 0)
{
mu_off_t off;
int whence = MU_SEEK_SET;
i++;
assert (argv[i] != NULL);
off = strtol (argv[i], &p, 10);
assert (*p == 0);
if (off < 0)
whence = MU_SEEK_END;
assert (mu_stream_seek (stream, off, whence, NULL) == 0);
}
else if (strcmp (argv[i], "-t") == 0)
{
size_t len;
i++;
assert (argv[i] != NULL);
len = strlen (argv[i]);
buf = realloc (buf, len + 1);
mu_wordsplit_c_unquote_copy (buf, argv[i], len);
assert (buf != NULL);
assert (mu_stream_write (stream, buf,
strlen (buf), NULL) == 0);
}
else
mu_error ("ignoring unknown argument %s", argv[i]);
}
mu_stream_unref (stream);
assert (mu_message_get_streamref (msg, &stream) == 0);
assert (mu_stream_copy (mu_strout, stream, 0, NULL) == 0);
mu_stream_unref (stream);
return 0;
}
# This file is part of GNU Mailutils. -*- Autotest -*-
# Copyright (C) 2011-2012 Free Software Foundation, Inc.
#
# GNU Mailutils is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3, or (at
# your option) any later version.
#
# GNU Mailutils 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
AT_SETUP([unmodified message])
AT_KEYWORDS([modmesg00])
AT_CHECK([modmesg],
[0],
[From: root
This is a test message.
oo
])
AT_CLEANUP
# This file is part of GNU Mailutils. -*- Autotest -*-
# Copyright (C) 2011-2012 Free Software Foundation, Inc.
#
# GNU Mailutils is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3, or (at
# your option) any later version.
#
# GNU Mailutils 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
AT_SETUP([add headers])
AT_KEYWORDS([modmesg01])
AT_CHECK([modmesg -a To:gray@localhost -a Subject:test],
[0],
[Subject: test
To: gray@localhost
From: root
This is a test message.
oo
])
AT_CLEANUP
# This file is part of GNU Mailutils. -*- Autotest -*-
# Copyright (C) 2011-2012 Free Software Foundation, Inc.
#
# GNU Mailutils is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3, or (at
# your option) any later version.
#
# GNU Mailutils 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
AT_SETUP([modify body])
AT_KEYWORDS([modmesg02])
AT_CHECK([modmesg -t "word\n"],
[0],
[From: root
This is a test message.
oo
word
])
AT_CHECK([modmesg -l 0 -t "That"],
[0],
[From: root
That is a test message.
oo
])
AT_CLEANUP
# This file is part of GNU Mailutils. -*- Autotest -*-
# Copyright (C) 2011-2012 Free Software Foundation, Inc.
#
# GNU Mailutils is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3, or (at
# your option) any later version.
#
# GNU Mailutils 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
AT_SETUP([modify headers and body])
AT_KEYWORDS([modmesg02])
AT_CHECK([modmesg -a To:gray@localhost -a Subject:test -l 0 -t "That"],
[0],
[Subject: test
To: gray@localhost
From: root
That is a test message.
oo
])
AT_CLEANUP
......@@ -92,6 +92,12 @@ m4_include([debugspec.at])
AT_BANNER([IMAP IO])
m4_include([imapio.at])
AT_BANNER(Message modification)
m4_include([modmesg00.at])
m4_include([modmesg01.at])
m4_include([modmesg02.at])
m4_include([modmesg03.at])
m4_include([scantime.at])
m4_include([strftime.at])
......
......@@ -2159,7 +2159,6 @@ finish_msg (struct compose_env *env, mu_message_t *msg)
free (p);
}
mu_mime_add_part (env->mime, *msg);
mu_message_unref (*msg);
*msg = NULL;
}
......@@ -2598,6 +2597,7 @@ mhn_edit (struct compose_env *env, int level)
mu_message_get_body (msg, &body);
mu_body_get_streamref (body, &output);
mu_message_ref (msg);
line_count = 0;
ascii_buf = 1; /* Suppose it is ascii */
env->subpart++;
......@@ -2640,6 +2640,7 @@ mhn_edit (struct compose_env *env, int level)
/* Close and append the previous part */
mu_stream_close (output);
mu_stream_destroy (&output);
mu_message_unref (msg);
finish_text_msg (env, &msg, ascii_buf);
}
......