/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2005-2021 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 . */
/* MH burst command */
#include
static char prog_doc[] = N_("Explode digests into messages");
static char args_doc[] = N_("[MSGLIST]");
/* Command line switches */
int inplace;
int quiet;
int verbose;
int recursive;
int eb_min_length = 1; /* Minimal length of encapsulation boundary */
#define VERBOSE(c) do { if (verbose) { printf c; putchar ('\n'); } } while (0)
static struct mu_option options[] = {
{ "inplace", 0, NULL, MU_OPTION_DEFAULT,
N_("replace the source message with the table of contents, insert extracted messages after it"),
mu_c_bool, &inplace },
{ "quiet", 0, NULL, MU_OPTION_DEFAULT,
N_("be quiet about the messages that are not in digest format"),
mu_c_bool, &quiet },
{ "verbose", 0, NULL, MU_OPTION_DEFAULT,
N_("verbosely list the actions taken"),
mu_c_bool, &verbose },
{ "recursive", 0, NULL, MU_OPTION_DEFAULT,
N_("recursively expand MIME messages"),
mu_c_bool, &recursive },
{ "length", 0, N_("NUM"), MU_OPTION_DEFAULT,
N_("set minimal length of digest encapsulation boundary (default 1)"),
mu_c_int, &eb_min_length },
MU_OPTION_END
};
/* General-purpose data structures */
struct burst_map
{
int mime; /* Is mime? */
size_t msgno; /* Number of the original message */
/* Following numbers refer to tmpbox */
size_t first; /* Number of the first bursted message */
size_t count; /* Number of bursted messages */
};
/* Global data */
struct burst_map map; /* Currently built map */
struct burst_map *burst_map; /* Finished burst map */
size_t burst_count; /* Number of items in burst_map */
mu_mailbox_t tmpbox; /* Temporary mailbox */
mu_opool_t pool; /* Object pool for building burst_map, etc. */
static int burst_or_copy (mu_message_t msg, int recursive, int copy);
/* MIME messages */
int
burst_mime (mu_message_t msg)
{
size_t i, nparts;
int rc;
rc = mu_message_get_num_parts (msg, &nparts);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERR, "mu_message_get_num_parts", NULL, rc);
return rc;
}
for (i = 1; i <= nparts; i++)
{
mu_message_t mpart;
if (mu_message_get_part (msg, i, &mpart) == 0)
{
if (!map.first)
mu_mailbox_uidnext (tmpbox, &map.first);
burst_or_copy (mpart, recursive, 1);
}
}
return 0;
}
/* Digest messages */
/* Bursting FSA states accoring to RFC 934:
S1 :: "-" S3
| CRLF {CRLF} S1
| c {c} S2
S2 :: CRLF {CRLF} S1
| c {c} S2
S3 :: " " S2
| c S4 ;; the bursting agent should consider the current
;; message ended.
S4 :: CRLF S5
| c S4
S5 :: CRLF S5
| c {c} S2 ;; The bursting agent should consider a new
;; message started
*/
#define S1 1
#define S2 2
#define S3 3
#define S4 4
#define S5 5
/* Negative state means no write */
int transtab[5][256] = {
/* S1 */ { S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S1, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, -S3, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2 },
/* S2 */ { S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S1, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2 },
/* S3 */ { -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S2, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4 },
/* S4 */ { -S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S5, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4,
-S4, -S4, -S4, -S4, -S4, -S4, -S4, -S4 },
/* S5 */ { S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, -S5, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2,
S2, S2, S2, S2, S2, S2, S2, S2 }
};
#define F_FIRST 0x01 /* First part of the message (no EB seen so far) */
#define F_ENCAPS 0x02 /* Within encapsulated part */
struct burst_stream
{
mu_stream_t stream; /* Output stream */
int flags; /* See F_ flags above */
size_t msgno; /* Number of the current message */
size_t partno; /* Number of the part within the message */
};
static inline void
finish_stream (struct burst_stream *bs)
{
if (bs->stream)
{
mu_message_t msg;
mu_stream_seek (bs->stream, 0, SEEK_SET, NULL);
msg = mh_stream_to_message (bs->stream);
if (!map.first)
mu_mailbox_uidnext (tmpbox, &map.first);
burst_or_copy (msg, recursive, 1);
mu_message_destroy (&msg, mu_message_get_owner (msg));
bs->stream = 0;
bs->partno++;
bs->flags &= ~F_FIRST;
}
}
static inline void
flush_stream (struct burst_stream *bs, char *buf, size_t size)
{
int rc;
if (size == 0)
return;
if (!bs->stream)
{
if ((rc = mu_temp_stream_create (&bs->stream, 0)))
{
mu_error (_("Cannot open temporary file: %s"),
mu_strerror (rc));
exit (1);
}
mu_stream_printf (bs->stream, "X-Burst-Part: %lu %lu %02x\n",
(unsigned long) bs->msgno,
(unsigned long) bs->partno, bs->flags);
if (!bs->flags)
mu_stream_write (bs->stream, "\n", 1, NULL);
if (verbose && !inplace)
{
size_t nextuid;
mu_mailbox_uidnext (tmpbox, &nextuid);
printf (_("message %lu of digest %lu becomes message %lu\n"),
(unsigned long) bs->partno,
(unsigned long) bs->msgno,
(unsigned long) nextuid);
}
}
rc = mu_stream_write (bs->stream, buf, size, NULL);
if (rc)
{
mu_error (_("error writing temporary stream: %s"),
mu_strerror (rc));
exit (1); /* FIXME: better error handling please */
}
}
/* Burst an RFC 934 digest. Return 0 if OK, 1 if the message is not
a valid digest.
FIXME: On errors, cleanup and return -1.
*/
int
burst_digest (mu_message_t msg)
{
mu_stream_t is;
unsigned char c;
size_t n;
int state = S1;
int eb_length = 0;
struct burst_stream bs;
int result = 0;
bs.stream = NULL;
bs.flags = F_FIRST;
bs.partno = 1;
mh_message_number (msg, &bs.msgno);
mu_message_get_streamref (msg, &is);
while (mu_stream_read (is, &c, 1, &n) == 0 && n == 1)
{
int newstate = transtab[state - 1][c];
int silent = 0;
if (newstate < 0)
{
newstate = -newstate;
silent = 1;
}
if (state == S1)
{
/* GNU extension: check if we have seen enough dashes to
constitute a valid encapsulation boundary. */
if (newstate == S3)
{
eb_length++;
if (eb_length < eb_min_length)
continue; /* Ignore state change */
if (eb_min_length > 1)
{
newstate = S4;
finish_stream (&bs);
bs.flags ^= F_ENCAPS;
}
}
else
for (; eb_length; eb_length--)
flush_stream (&bs, "-", 1);
eb_length = 0;
}
else if (state == S5 && newstate == S2)
{
/* As the automaton traverses from state S5 to S2, the
bursting agent should consider a new message started
and output the first character. */
finish_stream (&bs);
}
else if (state == S3 && newstate == S4)
{
/* As the automaton traverses from state S3 to S4, the
bursting agent should consider the current message ended. */
finish_stream (&bs);
bs.flags ^= F_ENCAPS;
}
state = newstate;
if (!silent)
flush_stream (&bs, (char*)&c, 1);
}
mu_stream_destroy (&is);
if (bs.flags == F_FIRST)
{
mu_stream_destroy (&bs.stream);
result = 1;
}
else if (bs.stream)
{
mu_off_t size = 0;
mu_stream_size (bs.stream, &size);
if (size)
finish_stream (&bs);
else
mu_stream_destroy (&bs.stream);
}
return result;
}
int
burst_or_copy (mu_message_t msg, int recursive, int copy)
{
if (recursive)
{
int mime = 0;
mu_message_is_multipart (msg, &mime);
if (mime)
{
if (!map.first)
map.mime = 1;
return burst_mime (msg);
}
else if (burst_digest (msg) == 0)
return 0;
}
if (copy)
{
int rc;
if (map.mime)
{
mu_header_t hdr;
char *value = NULL;
mu_message_get_header (msg, &hdr);
if (mu_header_aget_value (hdr, MU_HEADER_CONTENT_TYPE, &value) == 0
&& memcmp (value, "message/rfc822", 14) == 0)
{
mu_stream_t str;
mu_body_t body;
mu_message_get_body (msg, &body);
mu_body_get_streamref (body, &str);
msg = mh_stream_to_message (str);
}
free (value);
}
/* FIXME:
if (verbose && !inplace)
printf(_("message %lu of digest %lu becomes message %s"),
(unsigned long) (j+1),
(unsigned long) burst_map[i].msgno, to));
*/
rc = mu_mailbox_append_message (tmpbox, msg);
if (rc)
{
mu_error (_("cannot append message: %s"), mu_strerror (rc));
exit (1);
}
map.count++;
return 0;
}
return 1;
}
int
burst (size_t num, mu_message_t msg, void *data)
{
memset (&map, 0, sizeof (map));
mh_message_number (msg, &map.msgno);
if (burst_or_copy (msg, 1, 0) == 0)
{
VERBOSE((ngettext ("%s message exploded from digest %s",
"%s messages exploded from digest %s",
(unsigned long) map.count),
mu_umaxtostr (0, map.count),
mu_umaxtostr (1, num)));
if (inplace)
{
mu_opool_append (pool, &map, sizeof map);
burst_count++;
}
}
else if (!quiet)
mu_error (_("message %s not in digest format"), mu_umaxtostr (0, num));
return 0;
}
/* Inplace handling */
struct rename_env
{
size_t lastuid;
size_t idx;
};
static int
_rename (size_t msgno, void *data)
{
struct rename_env *rp = data;
if (msgno == burst_map[rp->idx].msgno)
{
rp->lastuid -= burst_map[rp->idx].count;
burst_map[rp->idx].msgno = rp->lastuid;
rp->idx--;
}
if (msgno != rp->lastuid)
{
const char *from;
const char *to;
from = mu_umaxtostr (0, msgno);
to = mu_umaxtostr (1, rp->lastuid);
--rp->lastuid;
VERBOSE((_("message %s becomes message %s"), from, to));
if (rename (from, to))
{
mu_error (_("error renaming %s to %s: %s"),
from, to, mu_strerror (errno));
exit (1);
}
}
return 0;
}
void
burst_rename (mu_msgset_t ms, size_t lastuid)
{
struct rename_env renv;
VERBOSE ((_("Renaming messages")));
renv.lastuid = lastuid;
renv.idx = burst_count - 1;
mu_msgset_foreach_dir_msguid (ms, 1, _rename, &renv);
}
void
msg_copy (size_t num, const char *file)
{
mu_message_t msg;
mu_attribute_t attr = NULL;
mu_stream_t istream, ostream;
int rc;
if ((rc = mu_file_stream_create (&ostream,
file,
MU_STREAM_WRITE|MU_STREAM_CREAT)))
{
mu_error (_("Cannot open output file `%s': %s"),
file, mu_strerror (rc));
exit (1);
}
mu_mailbox_get_message (tmpbox, num, &msg);
mu_message_get_streamref (msg, &istream);
rc = mu_stream_copy (ostream, istream, 0, NULL);
if (rc)
{
mu_error (_("copy stream error: %s"), mu_strerror (rc));
exit (1);
}
mu_stream_destroy (&istream);
mu_stream_close (ostream);
mu_stream_destroy (&ostream);
/* Mark message as deleted */
mu_message_get_attribute (msg, &attr);
mu_attribute_set_deleted (attr);
}
void
finalize_inplace (size_t lastuid)
{
size_t i;
VERBOSE ((_("Moving bursted out messages in place")));
for (i = 0; i < burst_count; i++)
{
size_t j;
/* FIXME: toc handling */
for (j = 0; j < burst_map[i].count; j++)
{
const char *to = mu_umaxtostr (0, burst_map[i].msgno + 1 + j);
VERBOSE((_("message %s of digest %s becomes message %s"),
mu_umaxtostr (1, j + 1),
mu_umaxtostr (2, burst_map[i].msgno), to));
msg_copy (burst_map[i].first + j, to);
}
}
}
int
main (int argc, char **argv)
{
int rc;
mu_mailbox_t mbox;
mu_msgset_t msgset;
const char *tempfolder = NULL;
mh_getopt (&argc, &argv, options, MH_GETOPT_DEFAULT_FOLDER,
args_doc, prog_doc, NULL);
if (!tempfolder)
tempfolder = mh_global_profile_get ("Temp-Folder", ".temp");
if (eb_min_length == 0)
eb_min_length = 1;
VERBOSE ((_("Opening folder `%s'"), mh_current_folder ()));
mbox = mh_open_folder (mh_current_folder (), MU_STREAM_RDWR);
mh_msgset_parse (&msgset, mbox, argc, argv, "cur");
if (inplace)
{
size_t i, count;
VERBOSE ((_("Opening temporary folder `%s'"), tempfolder));
tmpbox = mh_open_folder (tempfolder, MU_STREAM_RDWR|MU_STREAM_CREAT);
VERBOSE ((_("Cleaning up temporary folder")));
mu_mailbox_messages_count (tmpbox, &count);
for (i = 1; i <= count; i++)
{
mu_attribute_t attr = NULL;
mu_message_t msg = NULL;
mu_mailbox_get_message (tmpbox, i, &msg);
mu_message_get_attribute (msg, &attr);
mu_attribute_set_deleted (attr);
}
mu_mailbox_expunge (tmpbox);
mu_opool_create (&pool, MU_OPOOL_ENOMEMABRT);
}
else
tmpbox = mbox;
rc = mu_msgset_foreach_message (msgset, burst, NULL);
if (rc)
return rc;
if (inplace && burst_count)
{
mu_url_t dst_url = NULL;
size_t i, next_uid, last_uid;
mu_msgset_t ms;
size_t count;
const char *dir;
burst_map = mu_opool_finish (pool, NULL);
mu_mailbox_uidnext (mbox, &next_uid);
for (i = 0, last_uid = next_uid-1; i < burst_count; i++)
last_uid += burst_map[i].count;
VERBOSE ((_("Estimated last UID: %s"), mu_umaxtostr (0, last_uid)));
rc = mu_msgset_create (&ms, mbox, MU_MSGSET_NUM);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_msgset_create", NULL, rc);
exit (1);
}
mu_mailbox_messages_count (mbox, &count);
mu_msgset_add_range (ms, burst_map[0].msgno, count, MU_MSGSET_NUM);
mu_mailbox_get_url (mbox, &dst_url);
mu_url_sget_path (dst_url, &dir);
VERBOSE ((_("changing to `%s'"), dir));
if (chdir (dir))
{
mu_error (_("cannot change to `%s': %s"), dir, mu_strerror (errno));
exit (1);
}
mu_mailbox_close (mbox);
burst_rename (ms, last_uid);
mu_msgset_free (ms);
finalize_inplace (last_uid);
VERBOSE ((_("Expunging temporary folder")));
mu_mailbox_expunge (tmpbox);
mu_mailbox_close (tmpbox);
mu_mailbox_destroy (&tmpbox);
}
else
mu_mailbox_close (mbox);
mu_mailbox_destroy (&mbox);
VERBOSE ((_("Finished bursting")));
return rc;
}