mbox-rewrite.c revision 0ffdbd0c6da308c91b76c1166b0af5cdb6a8ea92
/* Copyright (C) 2002 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "file-set-size.h"
#include "str.h"
#include "write-full.h"
#include "mbox-index.h"
#include "mbox-lock.h"
#include "mail-index-util.h"
#include "mail-custom-flags.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
struct mbox_rewrite_context {
unsigned int msg_flags;
const char **custom_flags;
unsigned int uid_validity;
unsigned int uid_last;
char *x_keywords;
unsigned int ximapbase_found:1;
unsigned int xuid_found:1;
unsigned int status_found:1;
unsigned int xstatus_found:1;
unsigned int content_length_found:1;
};
/* Remove dirty flag from all messages */
{
struct mail_index_record *rec;
}
}
{
int failed;
/* fsck should have noticed it.. */
} else {
}
return !failed;
}
{
const char *str;
int i;
return FALSE;
for (i = 0; i < MAIL_CUSTOM_FLAGS_COUNT; i++) {
return FALSE;
ctx->custom_flags[i]) < 0)
return FALSE;
}
}
return FALSE;
return TRUE;
}
{
const char *str;
return FALSE;
return TRUE;
}
int force_filler)
{
unsigned int field;
int i;
/* nothing to do, and not enough extra space to write the
filler. Do it only if there's space for "X-Keywords: \n" */
return TRUE;
}
return FALSE;
return FALSE;
ctx->custom_flags[i]) < 0)
return FALSE;
}
}
if (x_keywords != NULL) {
/* X-Keywords that aren't custom flags */
return FALSE;
return FALSE;
}
/* fill the rest with spaces. -1 for \n */
char buf[1024];
return FALSE;
}
return FALSE;
}
return FALSE;
return TRUE;
}
const char *status)
{
const char *str;
return FALSE;
return FALSE;
return TRUE;
}
const char *x_status)
{
const char *str;
/* X-Status field */
return TRUE;
return FALSE;
return FALSE;
return TRUE;
}
{
return FALSE;
return TRUE;
}
const char *list)
{
/* @UNSAFE: leave only unknown flags, very likely none */
char *ret, *p;
size_t i;
for (i = 0; i < value_len; i++) {
*p++ = value[i];
}
if (ret == p)
return NULL;
*p = '\0';
return ret;
}
{
if (index < 0) {
/* not found, keep it */
}
}
struct mbox_rewrite_context *ctx)
{
}
struct message_header_line *hdr)
{
const char *str;
case 5:
if (ctx->xuid_found)
return TRUE;
return mbox_write_xuid(ctx);
}
break;
case 6:
if (ctx->status_found)
return TRUE;
return TRUE;
}
}
break;
case 8:
if (ctx->xstatus_found)
return TRUE;
return TRUE;
}
}
break;
case 10:
return TRUE;
return TRUE;
}
return TRUE;
return TRUE;
return mbox_write_ximapbase(ctx);
}
break;
case 14:
if (ctx->content_length_found)
return TRUE;
return mbox_write_content_length(ctx);
}
break;
}
/* save this header */
}
if (!hdr->no_newline)
}
}
{
/* We need to update fields that define message flags. Standard fields
are stored in Status and X-Status. For custom flags we use
uw-imapd compatible format, by first listing them in first message's
X-IMAPbase field and actually defining them in X-Keywords field.
Format of X-IMAPbase is: <UID validity> <last used UID> <flag names>
We don't want to sync our UIDs with the mbox file, so the UID
validity is always kept different from our internal UID validity.
Last used UID is also not updated, and set to 0 initially.
*/
struct mbox_rewrite_context ctx;
struct message_header_parser_ctx *hdr_ctx;
struct message_header_line *hdr;
struct message_size hdr_parsed_size;
int force_filler;
/* fsck should have noticed it.. */
return FALSE;
}
t_push();
/* parse the header, write the fields we don't want to change */
t_push();
t_pop();
}
/* append the flag fields */
/* write X-IMAPbase header to first message */
(void)mbox_write_ximapbase(&ctx);
}
if (!ctx.status_found)
if (!ctx.xstatus_found)
if (!ctx.xuid_found)
(void)mbox_write_xuid(&ctx);
if (!ctx.content_length_found)
(void)mbox_write_content_length(&ctx);
/* write the x-keywords header last so it can fill the extra space
with spaces. -1 is for ending \n. */
t_pop();
/* empty line ends headers */
return TRUE;
}
{
int ret;
/* first grow the file to wanted size, to make sure we don't run out
of disk space */
return -1;
}
return -1;
}
}
return -1;
}
t_push();
if (ret < 0) {
}
t_pop();
return ret;
}
{
return TRUE;
if (o_stream_flush(output) < 0) {
return FALSE;
}
/* POSSIBLE DATA LOSS HERE. We're writing to the mbox file,
so if we get killed here before finished, we'll lose some
bytes. I can't really think of any way to fix this,
rename() is problematic too especially because of file
locking issues (new mail could be lost).
Usually we're moving the data by just a few bytes, so
the data loss should never be more than those few bytes..
If we moved more, we could have written the file from end
to beginning in blocks (it'd be a bit slow to do it in
blocks of ~1-10 bytes which is the usual case, so we don't
bother).
Also, we might as well be shrinking the file, in which
case we can't lose data. */
return FALSE;
/* All ok. Just make sure the timestamps of index and
mbox differ, so index will be updated at next sync */
if (o_stream_seek(output, 0) < 0) {
return FALSE;
}
return TRUE;
}
#define INDEX_DIRTY_FLAGS \
{
/* Write messages beginning from the first dirty one to temp file,
then copy it over the mbox file. This may create data loss if
interrupted (see below). This rewriting relies quite a lot on
struct mail_index_record *rec;
const char *path;
unsigned int seq;
if (!no_locking) {
return FALSE;
}
if (!no_locking) {
return FALSE;
}
if (!rewrite) {
/* no need to rewrite */
return TRUE;
}
do {
if (!no_locking) {
break;
NULL))
break;
}
break;
/* fsck() figured out there's no dirty messages
after all */
break;
}
if (tmp_fd == -1)
break;
} while (0);
if (!rewrite) {
if (!no_locking) {
}
return !failed;
}
/* need to update X-IMAPbase in first message */
dirty_found = TRUE;
} else {
dirty_found = FALSE;
}
dirty_offset = 0;
/* note: we can't use data_stack_pool with output stream because it's
being written to inside t_push() .. t_pop() calls */
/* get offset to beginning of mail headers */
/* fsck should have fixed it */
break;
}
"Invalid message offset");
break;
}
"Invalid message size");
break;
}
}
if (!dirty_found &&
/* first dirty message */
dirty_found = TRUE;
}
if (dirty_found) {
/* write the From-line */
break;
}
/* write header, updating flag fields */
break;
}
/* no need to write more, flush */
break;
}
dirty_found = FALSE;
} else {
/* write body */
break;
}
}
}
seq++;
}
if (!failed && dirty_found) {
/* end with \n */
}
}
if (!failed && dirty_found) {
else {
/* we may have shrinked the file */
}
}
}
if (!failed)
if (!no_locking) {
}
return !failed;
}