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 ...@@ -33,6 +33,7 @@ struct _mu_body
33 { 33 {
34 void *owner; 34 void *owner;
35 mu_stream_t stream; 35 mu_stream_t stream;
36 mu_stream_t rawstream;
36 mu_stream_t fstream; 37 mu_stream_t fstream;
37 int flags; 38 int flags;
38 39
......
...@@ -47,7 +47,8 @@ struct _mu_message ...@@ -47,7 +47,8 @@ struct _mu_message
47 mu_body_t body; 47 mu_body_t body;
48 48
49 int flags; 49 int flags;
50 mu_stream_t stream; 50 mu_stream_t rawstream;
51 mu_stream_t outstream;
51 mu_attribute_t attribute; 52 mu_attribute_t attribute;
52 mu_monitor_t monitor; 53 mu_monitor_t monitor;
53 mu_mime_t mime; 54 mu_mime_t mime;
......
...@@ -43,8 +43,7 @@ mu_message_get_body (mu_message_t msg, mu_body_t *pbody) ...@@ -43,8 +43,7 @@ mu_message_get_body (mu_message_t msg, mu_body_t *pbody)
43 if (status != 0) 43 if (status != 0)
44 return status; 44 return status;
45 /* If a stream is already set, use it to create the body stream. */ 45 /* If a stream is already set, use it to create the body stream. */
46 /* FIXME: I'm not sure if the second condition is really needed */ 46 if (msg->rawstream)
47 if (msg->stream/* && (msg->flags & MESSAGE_INTERNAL_STREAM)*/)
48 { 47 {
49 mu_stream_t stream; 48 mu_stream_t stream;
50 int flags = 0; 49 int flags = 0;
...@@ -52,8 +51,8 @@ mu_message_get_body (mu_message_t msg, mu_body_t *pbody) ...@@ -52,8 +51,8 @@ mu_message_get_body (mu_message_t msg, mu_body_t *pbody)
52 /* FIXME: The actual mu_header_size cannot be used as offset, 51 /* FIXME: The actual mu_header_size cannot be used as offset,
53 because the headers might have been modified in between. */ 52 because the headers might have been modified in between. */
54 53
55 mu_stream_get_flags (msg->stream, &flags); 54 mu_stream_get_flags (msg->rawstream, &flags);
56 status = mu_streamref_create_abridged (&stream, msg->stream, 55 status = mu_streamref_create_abridged (&stream, msg->rawstream,
57 msg->orig_header_size, 0); 56 msg->orig_header_size, 0);
58 if (status) 57 if (status)
59 { 58 {
......
...@@ -109,17 +109,9 @@ _header_fill (mu_stream_t stream, char **pbuf, size_t *plen) ...@@ -109,17 +109,9 @@ _header_fill (mu_stream_t stream, char **pbuf, size_t *plen)
109 static int 109 static int
110 message_header_fill (void *data, char **pbuf, size_t *plen) 110 message_header_fill (void *data, char **pbuf, size_t *plen)
111 { 111 {
112 int status = 0;
113 mu_message_t msg = data; 112 mu_message_t msg = data;
114 mu_stream_t stream;
115 113
116 status = mu_message_get_streamref (msg, &stream); 114 return _header_fill (msg->rawstream, pbuf, plen);
117 if (status == 0)
118 {
119 status = _header_fill (stream, pbuf, plen);
120 mu_stream_destroy (&stream);
121 }
122 return status;
123 } 115 }
124 116
125 int 117 int
...@@ -136,7 +128,7 @@ mu_message_get_header (mu_message_t msg, mu_header_t *phdr) ...@@ -136,7 +128,7 @@ mu_message_get_header (mu_message_t msg, mu_header_t *phdr)
136 int status = mu_header_create (&header, NULL, 0); 128 int status = mu_header_create (&header, NULL, 0);
137 if (status != 0) 129 if (status != 0)
138 return status; 130 return status;
139 if (msg->stream) 131 if (msg->rawstream)
140 mu_header_set_fill (header, message_header_fill, msg); 132 mu_header_set_fill (header, message_header_fill, msg);
141 status = mu_header_size (header, &msg->orig_header_size); 133 status = mu_header_size (header, &msg->orig_header_size);
142 if (status) 134 if (status)
......
...@@ -54,28 +54,12 @@ _mu_message_free (mu_message_t msg) ...@@ -54,28 +54,12 @@ _mu_message_free (mu_message_t msg)
54 mu_observable_destroy (&msg->observable, msg); 54 mu_observable_destroy (&msg->observable, msg);
55 } 55 }
56 56
57 /* Envelope. */
58 if (msg->envelope)
59 mu_envelope_destroy (&msg->envelope, msg); 57 mu_envelope_destroy (&msg->envelope, msg);
60
61 /* Header. */
62 if (msg->header)
63 mu_header_destroy (&msg->header); 58 mu_header_destroy (&msg->header);
64
65 /* Body. */
66 if (msg->body)
67 mu_body_destroy (&msg->body, msg); 59 mu_body_destroy (&msg->body, msg);
68
69 /* Attribute. */
70 if (msg->attribute)
71 mu_attribute_destroy (&msg->attribute, msg); 60 mu_attribute_destroy (&msg->attribute, msg);
72 61 mu_stream_destroy (&msg->rawstream);
73 /* Stream. */ 62 mu_stream_destroy (&msg->outstream);
74 if (msg->stream)
75 mu_stream_destroy (&msg->stream);
76
77 /* Mime. */
78 if (msg->flags & MESSAGE_MIME_OWNER)
79 mu_mime_destroy (&msg->mime); 63 mu_mime_destroy (&msg->mime);
80 64
81 /* Loose the owner. */ 65 /* Loose the owner. */
......
...@@ -283,16 +283,41 @@ mu_message_set_stream (mu_message_t msg, mu_stream_t stream, void *owner) ...@@ -283,16 +283,41 @@ mu_message_set_stream (mu_message_t msg, mu_stream_t stream, void *owner)
283 return EINVAL; 283 return EINVAL;
284 if (msg->owner != owner) 284 if (msg->owner != owner)
285 return EACCES; 285 return EACCES;
286 if (msg->stream) 286 mu_stream_destroy (&msg->rawstream);
287 mu_stream_destroy (&msg->stream); 287 mu_stream_destroy (&msg->outstream);
288 msg->stream = stream; 288 msg->rawstream = stream;
289 msg->flags |= MESSAGE_MODIFIED; 289 msg->flags |= MESSAGE_MODIFIED;
290 msg->flags &= ~MESSAGE_INTERNAL_STREAM; 290 msg->flags &= ~MESSAGE_INTERNAL_STREAM;
291 return 0; 291 return 0;
292 } 292 }
293 293
294 static int 294 static int
295 _message_get_stream (mu_message_t msg, mu_stream_t *pstream, int ref) 295 mkoutstream (mu_message_t msg)
296 {
297 int status;
298 mu_header_t hdr;
299 mu_body_t body;
300
301 if (msg->outstream)
302 return 0;
303 /* FIXME: Kind of a kludge: make sure the message has header
304 and body initialized. */
305 status = mu_message_get_header (msg, &hdr);
306 if (status)
307 return status;
308 status = mu_message_get_body (msg, &body);
309 if (status)
310 return status;
311
312 status = _message_stream_create (&msg->outstream, msg, MU_STREAM_RDWR);
313 if (status == 0)
314 msg->flags |= MESSAGE_INTERNAL_STREAM;
315 return status;
316 }
317
318
319 int
320 mu_message_get_stream (mu_message_t msg, mu_stream_t *pstream)
296 { 321 {
297 int status; 322 int status;
298 323
...@@ -301,54 +326,67 @@ _message_get_stream (mu_message_t msg, mu_stream_t *pstream, int ref) ...@@ -301,54 +326,67 @@ _message_get_stream (mu_message_t msg, mu_stream_t *pstream, int ref)
301 if (pstream == NULL) 326 if (pstream == NULL)
302 return MU_ERR_OUT_PTR_NULL; 327 return MU_ERR_OUT_PTR_NULL;
303 328
304 if (msg->stream == NULL) 329 /* FIXME: Deprecation warning */
330
331 if (msg->rawstream == NULL)
305 { 332 {
306 if (msg->_get_stream) 333 if (msg->_get_stream)
307 { 334 {
308 status = msg->_get_stream (msg, &msg->stream); 335 status = msg->_get_stream (msg, &msg->rawstream);
309 if (status) 336 if (status)
310 return status; 337 return status;
311 } 338 }
312 else 339 else
313 { 340 {
314 mu_header_t hdr; 341 status = mkoutstream (msg);
315 mu_body_t body;
316
317 /* FIXME: Kind of a kludge: make sure the message has header
318 and body initialized. */
319 status = mu_message_get_header (msg, &hdr);
320 if (status) 342 if (status)
321 return status; 343 return status;
322 status = mu_message_get_body (msg, &body); 344 status = mu_streamref_create (&msg->rawstream, msg->outstream);
345 }
346 }
347 *pstream = msg->rawstream;
348 return 0;
349 }
350
351 int
352 mu_message_get_streamref (mu_message_t msg, mu_stream_t *pstream)
353 {
354 int status = 0;
355
356 if (msg == NULL)
357 return EINVAL;
358 if (pstream == NULL)
359 return MU_ERR_OUT_PTR_NULL;
360
361 if (!msg->rawstream)
362 {
363 if (msg->_get_stream)
364 {
365 status = msg->_get_stream (msg, &msg->rawstream);
323 if (status) 366 if (status)
324 return status; 367 return status;
325 368 status = mu_streamref_create (&msg->outstream, msg->rawstream);
326 status = _message_stream_create (&msg->stream, msg, MU_STREAM_RDWR); 369 }
370 else
371 {
372 status = mkoutstream (msg);
327 if (status) 373 if (status)
328 return status; 374 return status;
329 msg->flags |= MESSAGE_INTERNAL_STREAM; 375 status = mu_streamref_create (&msg->rawstream, msg->outstream);
330 } 376 }
331 } 377 }
332 378
333 if (!ref) 379 if (status)
380 return status;
381
382 if (!msg->outstream ||
383 (!(msg->flags & MESSAGE_INTERNAL_STREAM) &&
384 mu_message_is_modified (msg)))
334 { 385 {
335 *pstream = msg->stream; 386 mu_stream_destroy (&msg->outstream);
336 return 0; 387 status = mkoutstream (msg);
337 } 388 }
338 return mu_streamref_create (pstream, msg->stream); 389 return mu_streamref_create (pstream, msg->outstream);
339 }
340
341 int
342 mu_message_get_stream (mu_message_t msg, mu_stream_t *pstream)
343 {
344 /* FIXME: Deprecation warning */
345 return _message_get_stream (msg, pstream, 0);
346 }
347
348 int
349 mu_message_get_streamref (mu_message_t msg, mu_stream_t *pstream)
350 {
351 return _message_get_stream (msg, pstream, 1);
352 } 390 }
353 391
354 int 392 int
......
...@@ -38,7 +38,7 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac ...@@ -38,7 +38,7 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
38 ## Non-installable programs 38 ## Non-installable programs
39 ## -------------------------- ## 39 ## -------------------------- ##
40 40
41 INCLUDES = @MU_LIB_COMMON_INCLUDES@ 41 AM_CPPFLAGS = @MU_LIB_COMMON_INCLUDES@
42 noinst_PROGRAMS = \ 42 noinst_PROGRAMS = \
43 addr\ 43 addr\
44 cidr\ 44 cidr\
...@@ -55,6 +55,7 @@ noinst_PROGRAMS = \ ...@@ -55,6 +55,7 @@ noinst_PROGRAMS = \
55 mimehdr\ 55 mimehdr\
56 modtofsaf\ 56 modtofsaf\
57 msgset\ 57 msgset\
58 modmesg\
58 prop\ 59 prop\
59 scantime\ 60 scantime\
60 strftime\ 61 strftime\
...@@ -94,6 +95,10 @@ TESTSUITE_AT = \ ...@@ -94,6 +95,10 @@ TESTSUITE_AT = \
94 list.at\ 95 list.at\
95 mailcap.at\ 96 mailcap.at\
96 mimehdr.at\ 97 mimehdr.at\
98 modmesg00.at\
99 modmesg01.at\
100 modmesg02.at\
101 modmesg03.at\
97 modtofsaf.at\ 102 modtofsaf.at\
98 msgset.at\ 103 msgset.at\
99 prop.at\ 104 prop.at\
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2013 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <assert.h>
18 #include <mailutils/mailutils.h>
19
20 char *text = "From: root\n\
21 \n\
22 This is a test message.\n\
23 oo\n\
24 ";
25
26 int
27 main (int argc, char **argv)
28 {
29 int i;
30 char *p;
31 mu_message_t msg;
32 mu_stream_t stream = NULL;
33 mu_header_t hdr;
34 mu_body_t body;
35 char *buf = NULL;
36
37 mu_set_program_name (argv[0]);
38
39 mu_static_memory_stream_create (&stream, text, strlen (text));
40 assert (mu_stream_to_message (stream, &msg) == 0);
41 mu_stream_unref (stream);
42 assert (mu_message_get_header (msg, &hdr) == 0);
43 assert (mu_message_get_body (msg, &body) == 0);
44 assert (mu_body_get_streamref (body, &stream) == 0);
45 assert (mu_stream_seek (stream, 0, MU_SEEK_END, NULL) == 0);
46
47 for (i = 1; i < argc; i++)
48 {
49 if (strcmp (argv[i], "-h") == 0)
50 {
51 mu_printf ("usage: %s [-a HDR:VAL] [-t TEXT]\n", mu_program_name);
52 return 0;
53 }
54
55 if (strcmp (argv[i], "-a") == 0)
56 {
57 i++;
58 assert (argv[i] != NULL);
59 p = strchr (argv[i], ':');
60 assert (p != NULL);
61 *p++ = 0;
62 while (*p && mu_isspace (*p))
63 p++;
64 assert (mu_header_set_value (hdr, argv[i], p, 1) == 0);
65 }
66 else if (strcmp (argv[i], "-l") == 0)
67 {
68 mu_off_t off;
69 int whence = MU_SEEK_SET;
70
71 i++;
72 assert (argv[i] != NULL);
73 off = strtol (argv[i], &p, 10);
74 assert (*p == 0);
75 if (off < 0)
76 whence = MU_SEEK_END;
77 assert (mu_stream_seek (stream, off, whence, NULL) == 0);
78 }
79 else if (strcmp (argv[i], "-t") == 0)
80 {
81 size_t len;
82 i++;
83 assert (argv[i] != NULL);
84 len = strlen (argv[i]);
85 buf = realloc (buf, len + 1);
86 mu_wordsplit_c_unquote_copy (buf, argv[i], len);
87 assert (buf != NULL);
88 assert (mu_stream_write (stream, buf,
89 strlen (buf), NULL) == 0);
90 }
91 else
92 mu_error ("ignoring unknown argument %s", argv[i]);
93 }
94 mu_stream_unref (stream);
95
96 assert (mu_message_get_streamref (msg, &stream) == 0);
97 assert (mu_stream_copy (mu_strout, stream, 0, NULL) == 0);
98 mu_stream_unref (stream);
99
100 return 0;
101 }
102
103
1 # This file is part of GNU Mailutils. -*- Autotest -*-
2 # Copyright (C) 2011-2012 Free Software Foundation, Inc.
3 #
4 # GNU Mailutils is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3, or (at
7 # your option) any later version.
8 #
9 # GNU Mailutils is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
16
17 AT_SETUP([unmodified message])
18 AT_KEYWORDS([modmesg00])
19
20 AT_CHECK([modmesg],
21 [0],
22 [From: root
23
24 This is a test message.
25 oo
26 ])
27
28 AT_CLEANUP
29
1 # This file is part of GNU Mailutils. -*- Autotest -*-
2 # Copyright (C) 2011-2012 Free Software Foundation, Inc.
3 #
4 # GNU Mailutils is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3, or (at
7 # your option) any later version.
8 #
9 # GNU Mailutils is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
16
17 AT_SETUP([add headers])
18 AT_KEYWORDS([modmesg01])
19
20 AT_CHECK([modmesg -a To:gray@localhost -a Subject:test],
21 [0],
22 [Subject: test
23 To: gray@localhost
24 From: root
25
26 This is a test message.
27 oo
28 ])
29
30 AT_CLEANUP
31
1 # This file is part of GNU Mailutils. -*- Autotest -*-
2 # Copyright (C) 2011-2012 Free Software Foundation, Inc.
3 #
4 # GNU Mailutils is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3, or (at
7 # your option) any later version.
8 #
9 # GNU Mailutils is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
16
17 AT_SETUP([modify body])
18 AT_KEYWORDS([modmesg02])
19
20 AT_CHECK([modmesg -t "word\n"],
21 [0],
22 [From: root
23
24 This is a test message.
25 oo
26 word
27 ])
28
29 AT_CHECK([modmesg -l 0 -t "That"],
30 [0],
31 [From: root
32
33 That is a test message.
34 oo
35 ])
36
37 AT_CLEANUP
38
1 # This file is part of GNU Mailutils. -*- Autotest -*-
2 # Copyright (C) 2011-2012 Free Software Foundation, Inc.
3 #
4 # GNU Mailutils is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3, or (at
7 # your option) any later version.
8 #
9 # GNU Mailutils is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
16
17 AT_SETUP([modify headers and body])
18 AT_KEYWORDS([modmesg02])
19
20 AT_CHECK([modmesg -a To:gray@localhost -a Subject:test -l 0 -t "That"],
21 [0],
22 [Subject: test
23 To: gray@localhost
24 From: root
25
26 That is a test message.
27 oo
28 ])
29
30 AT_CLEANUP
31
...@@ -92,6 +92,12 @@ m4_include([debugspec.at]) ...@@ -92,6 +92,12 @@ m4_include([debugspec.at])
92 AT_BANNER([IMAP IO]) 92 AT_BANNER([IMAP IO])
93 m4_include([imapio.at]) 93 m4_include([imapio.at])
94 94
95 AT_BANNER(Message modification)
96 m4_include([modmesg00.at])
97 m4_include([modmesg01.at])
98 m4_include([modmesg02.at])
99 m4_include([modmesg03.at])
100
95 m4_include([scantime.at]) 101 m4_include([scantime.at])
96 m4_include([strftime.at]) 102 m4_include([strftime.at])
97 103
......
...@@ -2159,7 +2159,6 @@ finish_msg (struct compose_env *env, mu_message_t *msg) ...@@ -2159,7 +2159,6 @@ finish_msg (struct compose_env *env, mu_message_t *msg)
2159 free (p); 2159 free (p);
2160 } 2160 }
2161 mu_mime_add_part (env->mime, *msg); 2161 mu_mime_add_part (env->mime, *msg);
2162 mu_message_unref (*msg);
2163 *msg = NULL; 2162 *msg = NULL;
2164 } 2163 }
2165 2164
...@@ -2598,6 +2597,7 @@ mhn_edit (struct compose_env *env, int level) ...@@ -2598,6 +2597,7 @@ mhn_edit (struct compose_env *env, int level)
2598 2597
2599 mu_message_get_body (msg, &body); 2598 mu_message_get_body (msg, &body);
2600 mu_body_get_streamref (body, &output); 2599 mu_body_get_streamref (body, &output);
2600 mu_message_ref (msg);
2601 line_count = 0; 2601 line_count = 0;
2602 ascii_buf = 1; /* Suppose it is ascii */ 2602 ascii_buf = 1; /* Suppose it is ascii */
2603 env->subpart++; 2603 env->subpart++;
...@@ -2640,6 +2640,7 @@ mhn_edit (struct compose_env *env, int level) ...@@ -2640,6 +2640,7 @@ mhn_edit (struct compose_env *env, int level)
2640 /* Close and append the previous part */ 2640 /* Close and append the previous part */
2641 mu_stream_close (output); 2641 mu_stream_close (output);
2642 mu_stream_destroy (&output); 2642 mu_stream_destroy (&output);
2643 mu_message_unref (msg);
2643 finish_text_msg (env, &msg, ascii_buf); 2644 finish_text_msg (env, &msg, ascii_buf);
2644 } 2645 }
2645 2646
......