1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2005-2021 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 /* MH burst command */
18
19 #include <mh.h>
20
21 static char prog_doc[] = N_("Explode digests into messages");
22 static char args_doc[] = N_("[MSGLIST]");
23
24 /* Command line switches */
25 int inplace;
26 int quiet;
27 int verbose;
28 int recursive;
29 int eb_min_length = 1; /* Minimal length of encapsulation boundary */
30
31 #define VERBOSE(c) do { if (verbose) { printf c; putchar ('\n'); } } while (0)
32
33 static struct mu_option options[] = {
34 { "inplace", 0, NULL, MU_OPTION_DEFAULT,
35 N_("replace the source message with the table of contents, insert extracted messages after it"),
36 mu_c_bool, &inplace },
37 { "quiet", 0, NULL, MU_OPTION_DEFAULT,
38 N_("be quiet about the messages that are not in digest format"),
39 mu_c_bool, &quiet },
40 { "verbose", 0, NULL, MU_OPTION_DEFAULT,
41 N_("verbosely list the actions taken"),
42 mu_c_bool, &verbose },
43 { "recursive", 0, NULL, MU_OPTION_DEFAULT,
44 N_("recursively expand MIME messages"),
45 mu_c_bool, &recursive },
46 { "length", 0, N_("NUM"), MU_OPTION_DEFAULT,
47 N_("set minimal length of digest encapsulation boundary (default 1)"),
48 mu_c_int, &eb_min_length },
49 MU_OPTION_END
50 };
51
52 /* General-purpose data structures */
53 struct burst_map
54 {
55 int mime; /* Is mime? */
56 size_t msgno; /* Number of the original message */
57 /* Following numbers refer to tmpbox */
58 size_t first; /* Number of the first bursted message */
59 size_t count; /* Number of bursted messages */
60 };
61
62
63 /* Global data */
64 struct burst_map map; /* Currently built map */
65 struct burst_map *burst_map; /* Finished burst map */
66 size_t burst_count; /* Number of items in burst_map */
67 mu_mailbox_t tmpbox; /* Temporary mailbox */
68 mu_opool_t pool; /* Object pool for building burst_map, etc. */
69
70 static int burst_or_copy (mu_message_t msg, int recursive, int copy);
71
72
73 /* MIME messages */
74 int
burst_mime(mu_message_t msg)75 burst_mime (mu_message_t msg)
76 {
77 size_t i, nparts;
78 int rc;
79
80 rc = mu_message_get_num_parts (msg, &nparts);
81 if (rc)
82 {
83 mu_diag_funcall (MU_DIAG_ERR, "mu_message_get_num_parts", NULL, rc);
84 return rc;
85 }
86
87 for (i = 1; i <= nparts; i++)
88 {
89 mu_message_t mpart;
90 if (mu_message_get_part (msg, i, &mpart) == 0)
91 {
92 if (!map.first)
93 mu_mailbox_uidnext (tmpbox, &map.first);
94 burst_or_copy (mpart, recursive, 1);
95 }
96 }
97 return 0;
98 }
99
100
101 /* Digest messages */
102
103 /* Bursting FSA states accoring to RFC 934:
104
105 S1 :: "-" S3
106 | CRLF {CRLF} S1
107 | c {c} S2
108
109 S2 :: CRLF {CRLF} S1
110 | c {c} S2
111
112 S3 :: " " S2
113 | c S4 ;; the bursting agent should consider the current
114 ;; message ended.
115
116 S4 :: CRLF S5
117 | c S4
118
119 S5 :: CRLF S5
120 | c {c} S2 ;; The bursting agent should consider a new
121 ;; message started
122 */
123
124 #define S1 1
125 #define S2 2
126 #define S3 3
127 #define S4 4
128 #define S5 5
129
130 /* Negative state means no write */
131 int transtab[5][256] = {
132 /* S1 */ { S2, S2, S2, S2, S2, S2, S2, S2,
133 S2, S2, S1, S2, S2, S2, S2, S2,
134 S2, S2, S2, S2, S2, S2, S2, S2,
135 S2, S2, S2, S2, S2, S2, S2, S2,
136 S2, S2, S2, S2, S2, S2, S2, S2,
137 S2, S2, S2, S2, S2, -S3, S2, S2,
138 S2, S2, S2, S2, S2, S2, S2, S2,
139 S2, S2, S2, S2, S2, S2, S2, S2,
140 S2, S2, S2, S2, S2, S2, S2, S2,
141 S2, S2, S2, S2, S2, S2, S2, S2,
142 S2, S2, S2, S2, S2, S2, S2, S2,
143 S2, S2, S2, S2, S2, S2, S2, S2,
144 S2, S2, S2, S2, S2, S2, S2, S2,
145 S2, S2, S2, S2, S2, S2, S2, S2,
146 S2, S2, S2, S2, S2, S2, S2, S2,
147 S2, S2, S2, S2, S2, S2, S2, S2,
148 S2, S2, S2, S2, S2, S2, S2, S2,
149 S2, S2, S2, S2, S2, S2, S2, S2,
150 S2, S2, S2, S2, S2, S2, S2, S2,
151 S2, S2, S2, S2, S2, S2, S2, S2,
152 S2, S2, S2, S2, S2, S2, S2, S2,
153 S2, S2, S2, S2, S2, S2, S2, S2,
154 S2, S2, S2, S2, S2, S2, S2, S2,
155 S2, S2, S2, S2, S2, S2, S2, S2,
156 S2, S2, S2, S2, S2, S2, S2, S2,
157 S2, S2, S2, S2, S2, S2, S2, S2,
158 S2, S2, S2, S2, S2, S2, S2, S2,
159 S2, S2, S2, S2, S2, S2, S2, S2,
160 S2, S2, S2, S2, S2, S2, S2, S2,
161 S2, S2, S2, S2, S2, S2, S2, S2,
162 S2, S2, S2, S2, S2, S2, S2, S2,
163 S2, S2, S2, S2, S2, S2, S2, S2 },
164 /* S2 */ { S2, S2, S2, S2, S2, S2, S2, S2,
165 S2, S2, S1, S2, S2, S2, S2, S2,
166 S2, S2, S2, S2, S2, S2, S2, S2,
167 S2, S2, S2, S2, S2, S2, S2, S2,
168 S2, S2, S2, S2, S2, S2, S2, S2,
169 S2, S2, S2, S2, S2, S2, S2, S2,
170 S2, S2, S2, S2, S2, S2, S2, S2,
171 S2, S2, S2, S2, S2, S2, S2, S2,
172 S2, S2, S2, S2, S2, S2, S2, S2,
173 S2, S2, S2, S2, S2, S2, S2, S2,
174 S2, S2, S2, S2, S2, S2, S2, S2,
175 S2, S2, S2, S2, S2, S2, S2, S2,
176 S2, S2, S2, S2, S2, S2, S2, S2,
177 S2, S2, S2, S2, S2, S2, S2, S2,
178 S2, S2, S2, S2, S2, S2, S2, S2,
179 S2, S2, S2, S2, S2, S2, S2, S2,
180 S2, S2, S2, S2, S2, S2, S2, S2,
181 S2, S2, S2, S2, S2, S2, S2, S2,
182 S2, S2, S2, S2, S2, S2, S2, S2,
183 S2, S2, S2, S2, S2, S2, S2, S2,
184 S2, S2, S2, S2, S2, S2, S2, S2,
185 S2, S2, S2, S2, S2, S2, S2, S2,
186 S2, S2, S2, S2, S2, S2, S2, S2,
187 S2, S2, S2, S2, S2, S2, S2, S2,
188 S2, S2, S2, S2, S2, S2, S2, S2,
189 S2, S2, S2, S2, S2, S2, S2, S2,
190 S2, S2, S2, S2, S2, S2, S2, S2,
191 S2, S2, S2, S2, S2, S2, S2, S2,
192 S2, S2, S2, S2, S2, S2, S2, S2,
193 S2, S2, S2, S2, S2, S2, S2, S2,
194 S2, S2, S2, S2, S2, S2, S2, S2,
195 S2, S2, S2, S2, S2, S2, S2, S2 },
196 /* S3 */ { -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
197 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
198 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
199 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
200 -S2, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
201 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
202 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
203 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
204 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
205 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
206 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
207 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
208 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
209 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
210 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
211 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
212 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
213 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
214 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
215 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
216 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
217 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
218 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
219 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
220 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
221 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
222 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
223 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
224 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
225 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
226 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
227 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4 },
228 /* S4 */ { -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
229 -S4, -S4, -S5, -S4, -S4, -S4, -S4, -S4,
230 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
231 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
232 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
233 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
234 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
235 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
236 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
237 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
238 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
239 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
240 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
241 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
242 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
243 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
244 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
245 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
246 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
247 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
248 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
249 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
250 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
251 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
252 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
253 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
254 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
255 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
256 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
257 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
258 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
259 -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4 },
260 /* S5 */ { S2, S2, S2, S2, S2, S2, S2, S2,
261 S2, S2, -S5, S2, S2, S2, S2, S2,
262 S2, S2, S2, S2, S2, S2, S2, S2,
263 S2, S2, S2, S2, S2, S2, S2, S2,
264 S2, S2, S2, S2, S2, S2, S2, S2,
265 S2, S2, S2, S2, S2, S2, S2, S2,
266 S2, S2, S2, S2, S2, S2, S2, S2,
267 S2, S2, S2, S2, S2, S2, S2, S2,
268 S2, S2, S2, S2, S2, S2, S2, S2,
269 S2, S2, S2, S2, S2, S2, S2, S2,
270 S2, S2, S2, S2, S2, S2, S2, S2,
271 S2, S2, S2, S2, S2, S2, S2, S2,
272 S2, S2, S2, S2, S2, S2, S2, S2,
273 S2, S2, S2, S2, S2, S2, S2, S2,
274 S2, S2, S2, S2, S2, S2, S2, S2,
275 S2, S2, S2, S2, S2, S2, S2, S2,
276 S2, S2, S2, S2, S2, S2, S2, S2,
277 S2, S2, S2, S2, S2, S2, S2, S2,
278 S2, S2, S2, S2, S2, S2, S2, S2,
279 S2, S2, S2, S2, S2, S2, S2, S2,
280 S2, S2, S2, S2, S2, S2, S2, S2,
281 S2, S2, S2, S2, S2, S2, S2, S2,
282 S2, S2, S2, S2, S2, S2, S2, S2,
283 S2, S2, S2, S2, S2, S2, S2, S2,
284 S2, S2, S2, S2, S2, S2, S2, S2,
285 S2, S2, S2, S2, S2, S2, S2, S2,
286 S2, S2, S2, S2, S2, S2, S2, S2,
287 S2, S2, S2, S2, S2, S2, S2, S2,
288 S2, S2, S2, S2, S2, S2, S2, S2,
289 S2, S2, S2, S2, S2, S2, S2, S2,
290 S2, S2, S2, S2, S2, S2, S2, S2,
291 S2, S2, S2, S2, S2, S2, S2, S2 }
292 };
293
294 #define F_FIRST 0x01 /* First part of the message (no EB seen so far) */
295 #define F_ENCAPS 0x02 /* Within encapsulated part */
296
297 struct burst_stream
298 {
299 mu_stream_t stream; /* Output stream */
300 int flags; /* See F_ flags above */
301 size_t msgno; /* Number of the current message */
302 size_t partno; /* Number of the part within the message */
303 };
304
305 static inline void
finish_stream(struct burst_stream * bs)306 finish_stream (struct burst_stream *bs)
307 {
308 if (bs->stream)
309 {
310 mu_message_t msg;
311
312 mu_stream_seek (bs->stream, 0, SEEK_SET, NULL);
313 msg = mh_stream_to_message (bs->stream);
314 if (!map.first)
315 mu_mailbox_uidnext (tmpbox, &map.first);
316 burst_or_copy (msg, recursive, 1);
317 mu_message_destroy (&msg, mu_message_get_owner (msg));
318 bs->stream = 0;
319
320 bs->partno++;
321 bs->flags &= ~F_FIRST;
322 }
323 }
324
325 static inline void
flush_stream(struct burst_stream * bs,char * buf,size_t size)326 flush_stream (struct burst_stream *bs, char *buf, size_t size)
327 {
328 int rc;
329
330 if (size == 0)
331 return;
332 if (!bs->stream)
333 {
334 if ((rc = mu_temp_stream_create (&bs->stream, 0)))
335 {
336 mu_error (_("Cannot open temporary file: %s"),
337 mu_strerror (rc));
338 exit (1);
339 }
340 mu_stream_printf (bs->stream, "X-Burst-Part: %lu %lu %02x\n",
341 (unsigned long) bs->msgno,
342 (unsigned long) bs->partno, bs->flags);
343 if (!bs->flags)
344 mu_stream_write (bs->stream, "\n", 1, NULL);
345
346 if (verbose && !inplace)
347 {
348 size_t nextuid;
349 mu_mailbox_uidnext (tmpbox, &nextuid);
350 printf (_("message %lu of digest %lu becomes message %lu\n"),
351 (unsigned long) bs->partno,
352 (unsigned long) bs->msgno,
353 (unsigned long) nextuid);
354 }
355 }
356 rc = mu_stream_write (bs->stream, buf, size, NULL);
357 if (rc)
358 {
359 mu_error (_("error writing temporary stream: %s"),
360 mu_strerror (rc));
361 exit (1); /* FIXME: better error handling please */
362 }
363 }
364
365 /* Burst an RFC 934 digest. Return 0 if OK, 1 if the message is not
366 a valid digest.
367 FIXME: On errors, cleanup and return -1.
368 */
369 int
burst_digest(mu_message_t msg)370 burst_digest (mu_message_t msg)
371 {
372 mu_stream_t is;
373 unsigned char c;
374 size_t n;
375 int state = S1;
376 int eb_length = 0;
377 struct burst_stream bs;
378 int result = 0;
379
380 bs.stream = NULL;
381 bs.flags = F_FIRST;
382 bs.partno = 1;
383 mh_message_number (msg, &bs.msgno);
384
385 mu_message_get_streamref (msg, &is);
386 while (mu_stream_read (is, &c, 1, &n) == 0 && n == 1)
387 {
388 int newstate = transtab[state - 1][c];
389 int silent = 0;
390
391 if (newstate < 0)
392 {
393 newstate = -newstate;
394 silent = 1;
395 }
396
397 if (state == S1)
398 {
399 /* GNU extension: check if we have seen enough dashes to
400 constitute a valid encapsulation boundary. */
401 if (newstate == S3)
402 {
403 eb_length++;
404 if (eb_length < eb_min_length)
405 continue; /* Ignore state change */
406 if (eb_min_length > 1)
407 {
408 newstate = S4;
409 finish_stream (&bs);
410 bs.flags ^= F_ENCAPS;
411 }
412 }
413 else
414 for (; eb_length; eb_length--)
415 flush_stream (&bs, "-", 1);
416 eb_length = 0;
417 }
418 else if (state == S5 && newstate == S2)
419 {
420 /* As the automaton traverses from state S5 to S2, the
421 bursting agent should consider a new message started
422 and output the first character. */
423 finish_stream (&bs);
424 }
425 else if (state == S3 && newstate == S4)
426 {
427 /* As the automaton traverses from state S3 to S4, the
428 bursting agent should consider the current message ended. */
429 finish_stream (&bs);
430 bs.flags ^= F_ENCAPS;
431 }
432 state = newstate;
433 if (!silent)
434 flush_stream (&bs, (char*)&c, 1);
435 }
436 mu_stream_destroy (&is);
437
438 if (bs.flags == F_FIRST)
439 {
440 mu_stream_destroy (&bs.stream);
441 result = 1;
442 }
443 else if (bs.stream)
444 {
445 mu_off_t size = 0;
446 mu_stream_size (bs.stream, &size);
447 if (size)
448 finish_stream (&bs);
449 else
450 mu_stream_destroy (&bs.stream);
451 }
452 return result;
453 }
454
455
456 int
burst_or_copy(mu_message_t msg,int recursive,int copy)457 burst_or_copy (mu_message_t msg, int recursive, int copy)
458 {
459 if (recursive)
460 {
461 int mime = 0;
462 mu_message_is_multipart (msg, &mime);
463
464 if (mime)
465 {
466 if (!map.first)
467 map.mime = 1;
468 return burst_mime (msg);
469 }
470 else if (burst_digest (msg) == 0)
471 return 0;
472 }
473
474 if (copy)
475 {
476 int rc;
477
478 if (map.mime)
479 {
480 mu_header_t hdr;
481 char *value = NULL;
482
483 mu_message_get_header (msg, &hdr);
484 if (mu_header_aget_value (hdr, MU_HEADER_CONTENT_TYPE, &value) == 0
485 && memcmp (value, "message/rfc822", 14) == 0)
486 {
487 mu_stream_t str;
488 mu_body_t body;
489
490 mu_message_get_body (msg, &body);
491 mu_body_get_streamref (body, &str);
492 msg = mh_stream_to_message (str);
493 }
494 free (value);
495 }
496
497 /* FIXME:
498 if (verbose && !inplace)
499 printf(_("message %lu of digest %lu becomes message %s"),
500 (unsigned long) (j+1),
501 (unsigned long) burst_map[i].msgno, to));
502 */
503
504 rc = mu_mailbox_append_message (tmpbox, msg);
505 if (rc)
506 {
507 mu_error (_("cannot append message: %s"), mu_strerror (rc));
508 exit (1);
509 }
510 map.count++;
511 return 0;
512 }
513
514 return 1;
515 }
516
517 int
burst(size_t num,mu_message_t msg,void * data)518 burst (size_t num, mu_message_t msg, void *data)
519 {
520 memset (&map, 0, sizeof (map));
521 mh_message_number (msg, &map.msgno);
522
523 if (burst_or_copy (msg, 1, 0) == 0)
524 {
525 VERBOSE((ngettext ("%s message exploded from digest %s",
526 "%s messages exploded from digest %s",
527 (unsigned long) map.count),
528 mu_umaxtostr (0, map.count),
529 mu_umaxtostr (1, num)));
530 if (inplace)
531 {
532 mu_opool_append (pool, &map, sizeof map);
533 burst_count++;
534 }
535 }
536 else if (!quiet)
537 mu_error (_("message %s not in digest format"), mu_umaxtostr (0, num));
538 return 0;
539 }
540
541
542 /* Inplace handling */
543 struct rename_env
544 {
545 size_t lastuid;
546 size_t idx;
547 };
548
549
550 static int
_rename(size_t msgno,void * data)551 _rename (size_t msgno, void *data)
552 {
553 struct rename_env *rp = data;
554
555 if (msgno == burst_map[rp->idx].msgno)
556 {
557 rp->lastuid -= burst_map[rp->idx].count;
558 burst_map[rp->idx].msgno = rp->lastuid;
559 rp->idx--;
560 }
561
562 if (msgno != rp->lastuid)
563 {
564 const char *from;
565 const char *to;
566
567 from = mu_umaxtostr (0, msgno);
568 to = mu_umaxtostr (1, rp->lastuid);
569 --rp->lastuid;
570
571 VERBOSE((_("message %s becomes message %s"), from, to));
572
573 if (rename (from, to))
574 {
575 mu_error (_("error renaming %s to %s: %s"),
576 from, to, mu_strerror (errno));
577 exit (1);
578 }
579 }
580 return 0;
581 }
582
583 void
burst_rename(mu_msgset_t ms,size_t lastuid)584 burst_rename (mu_msgset_t ms, size_t lastuid)
585 {
586 struct rename_env renv;
587
588 VERBOSE ((_("Renaming messages")));
589 renv.lastuid = lastuid;
590 renv.idx = burst_count - 1;
591 mu_msgset_foreach_dir_msguid (ms, 1, _rename, &renv);
592 }
593
594 void
msg_copy(size_t num,const char * file)595 msg_copy (size_t num, const char *file)
596 {
597 mu_message_t msg;
598 mu_attribute_t attr = NULL;
599 mu_stream_t istream, ostream;
600 int rc;
601
602 if ((rc = mu_file_stream_create (&ostream,
603 file,
604 MU_STREAM_WRITE|MU_STREAM_CREAT)))
605 {
606 mu_error (_("Cannot open output file `%s': %s"),
607 file, mu_strerror (rc));
608 exit (1);
609 }
610
611 mu_mailbox_get_message (tmpbox, num, &msg);
612 mu_message_get_streamref (msg, &istream);
613 rc = mu_stream_copy (ostream, istream, 0, NULL);
614 if (rc)
615 {
616 mu_error (_("copy stream error: %s"), mu_strerror (rc));
617 exit (1);
618 }
619
620 mu_stream_destroy (&istream);
621
622 mu_stream_close (ostream);
623 mu_stream_destroy (&ostream);
624
625 /* Mark message as deleted */
626 mu_message_get_attribute (msg, &attr);
627 mu_attribute_set_deleted (attr);
628 }
629
630 void
finalize_inplace(size_t lastuid)631 finalize_inplace (size_t lastuid)
632 {
633 size_t i;
634
635 VERBOSE ((_("Moving bursted out messages in place")));
636
637 for (i = 0; i < burst_count; i++)
638 {
639 size_t j;
640
641 /* FIXME: toc handling */
642 for (j = 0; j < burst_map[i].count; j++)
643 {
644 const char *to = mu_umaxtostr (0, burst_map[i].msgno + 1 + j);
645 VERBOSE((_("message %s of digest %s becomes message %s"),
646 mu_umaxtostr (1, j + 1),
647 mu_umaxtostr (2, burst_map[i].msgno), to));
648 msg_copy (burst_map[i].first + j, to);
649 }
650 }
651 }
652
653 int
main(int argc,char ** argv)654 main (int argc, char **argv)
655 {
656 int rc;
657 mu_mailbox_t mbox;
658 mu_msgset_t msgset;
659 const char *tempfolder = NULL;
660
661 mh_getopt (&argc, &argv, options, MH_GETOPT_DEFAULT_FOLDER,
662 args_doc, prog_doc, NULL);
663 if (!tempfolder)
664 tempfolder = mh_global_profile_get ("Temp-Folder", ".temp");
665 if (eb_min_length == 0)
666 eb_min_length = 1;
667
668 VERBOSE ((_("Opening folder `%s'"), mh_current_folder ()));
669 mbox = mh_open_folder (mh_current_folder (), MU_STREAM_RDWR);
670 mh_msgset_parse (&msgset, mbox, argc, argv, "cur");
671
672 if (inplace)
673 {
674 size_t i, count;
675
676 VERBOSE ((_("Opening temporary folder `%s'"), tempfolder));
677 tmpbox = mh_open_folder (tempfolder, MU_STREAM_RDWR|MU_STREAM_CREAT);
678 VERBOSE ((_("Cleaning up temporary folder")));
679 mu_mailbox_messages_count (tmpbox, &count);
680 for (i = 1; i <= count; i++)
681 {
682 mu_attribute_t attr = NULL;
683 mu_message_t msg = NULL;
684 mu_mailbox_get_message (tmpbox, i, &msg);
685 mu_message_get_attribute (msg, &attr);
686 mu_attribute_set_deleted (attr);
687 }
688 mu_mailbox_expunge (tmpbox);
689 mu_opool_create (&pool, MU_OPOOL_ENOMEMABRT);
690 }
691 else
692 tmpbox = mbox;
693
694 rc = mu_msgset_foreach_message (msgset, burst, NULL);
695 if (rc)
696 return rc;
697
698 if (inplace && burst_count)
699 {
700 mu_url_t dst_url = NULL;
701 size_t i, next_uid, last_uid;
702 mu_msgset_t ms;
703 size_t count;
704 const char *dir;
705
706 burst_map = mu_opool_finish (pool, NULL);
707
708 mu_mailbox_uidnext (mbox, &next_uid);
709 for (i = 0, last_uid = next_uid-1; i < burst_count; i++)
710 last_uid += burst_map[i].count;
711 VERBOSE ((_("Estimated last UID: %s"), mu_umaxtostr (0, last_uid)));
712
713 rc = mu_msgset_create (&ms, mbox, MU_MSGSET_NUM);
714 if (rc)
715 {
716 mu_diag_funcall (MU_DIAG_ERROR, "mu_msgset_create", NULL, rc);
717 exit (1);
718 }
719 mu_mailbox_messages_count (mbox, &count);
720 mu_msgset_add_range (ms, burst_map[0].msgno, count, MU_MSGSET_NUM);
721
722 mu_mailbox_get_url (mbox, &dst_url);
723 mu_url_sget_path (dst_url, &dir);
724 VERBOSE ((_("changing to `%s'"), dir));
725 if (chdir (dir))
726 {
727 mu_error (_("cannot change to `%s': %s"), dir, mu_strerror (errno));
728 exit (1);
729 }
730 mu_mailbox_close (mbox);
731
732 burst_rename (ms, last_uid);
733 mu_msgset_free (ms);
734
735 finalize_inplace (last_uid);
736
737 VERBOSE ((_("Expunging temporary folder")));
738 mu_mailbox_expunge (tmpbox);
739 mu_mailbox_close (tmpbox);
740 mu_mailbox_destroy (&tmpbox);
741 }
742 else
743 mu_mailbox_close (mbox);
744
745 mu_mailbox_destroy (&mbox);
746 VERBOSE ((_("Finished bursting")));
747 return rc;
748 }
749
750
751
752