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;
......
......@@ -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,28 +54,12 @@ _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_stream_destroy (&msg->rawstream);
mu_stream_destroy (&msg->outstream);
mu_mime_destroy (&msg->mime);
/* Loose the owner. */
......
......@@ -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);
}
......