maildir-uidlist.c revision 92888ef30960c30ccc9e030fe7eab5d4d04a7d1c
/* Copyright (C) 2003 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "buffer.h"
#include "hash.h"
#include "istream.h"
#include "str.h"
#include "file-dotlock.h"
#include "write-full.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <utime.h>
/* how many seconds to wait before overriding uidlist.lock */
#define UIDLIST_IS_LOCKED(uidlist) \
struct maildir_uidlist_rec {
char *filename;
};
struct maildir_uidlist {
struct index_mailbox *ibox;
char *fname;
int lock_fd;
struct hash_table *files;
unsigned int version;
unsigned int initial_read:1;
unsigned int initial_sync:1;
unsigned int file_missing:1;
};
struct maildir_uidlist_sync_ctx {
struct maildir_uidlist *uidlist;
struct hash_table *files;
unsigned int first_new_pos;
unsigned int new_files_count;
unsigned int partial:1;
unsigned int synced:1;
unsigned int locked:1;
unsigned int finished:1;
unsigned int failed:1;
};
struct maildir_uidlist_iter_ctx {
};
{
const char *path;
int fd;
if (UIDLIST_IS_LOCKED(uidlist))
return 1;
NULL, 0, 0, UIDLIST_LOCK_STALE_TIMEOUT,
if (fd == -1) {
return 0;
"file_dotlock_open(%s) failed: %m", path);
return -1;
}
return 1;
}
{
const char *path;
if (!UIDLIST_IS_LOCKED(uidlist))
return;
}
{
struct maildir_uidlist *uidlist;
return uidlist;
}
{
}
static void
{
if (uidlist->first_recent_uid == 0 ||
}
{
struct maildir_uidlist_rec *rec;
line++;
}
/* we already have this */
return 1;
}
/* invalid file */
return 0;
}
"UIDs not ordered in file %s (%u > %u)",
return 0;
}
"UID larger than next_uid in file %s (%u >= %u)",
return 0;
}
flags = 0;
while (*line != ' ') {
switch (*line) {
case 'N':
break;
}
line++;
}
} else {
/* old version, have to assume it's in new dir since we
don't know */
}
"Duplicate file in uidlist file %s: %s",
return 0;
}
return 1;
}
{
const struct maildir_uidlist_rec *const *rec_p;
const char *line;
if (uidlist->last_mtime != 0) {
return -1;
}
return 0;
}
/* unchanged */
return 1;
}
}
if (fd == -1) {
return -1;
}
return 0;
}
return -1;
}
pool_alloconly_create("uidlist record_pool",
}
/* get header */
/* broken file */
"Corrupted header in file %s (version = %u)",
ret = 0;
} else {
ret = 1;
ret = 0;
break;
}
}
}
if (ret != 0)
else {
uidlist->last_mtime = 0;
}
return ret;
}
static const struct maildir_uidlist_rec *
unsigned int *idx_r)
{
const struct maildir_uidlist_rec *const *rec_p;
if (!uidlist->initial_read) {
/* first time we need to read uidlist */
if (maildir_uidlist_update(uidlist) < 0)
return NULL;
}
idx = 0;
left_idx = 0;
else {
}
}
return NULL;
}
const char *
enum maildir_uidlist_rec_flag *flags_r)
{
const struct maildir_uidlist_rec *rec;
unsigned int idx;
return NULL;
}
{
return FALSE;
return FALSE;
(flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0);
return (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0;
}
{
const struct maildir_uidlist_rec *const *rec_p;
unsigned int idx;
if (!uidlist->initial_sync) {
/* we haven't synced yet, trust index */
const struct mail_index_header *hdr;
return 0;
return hdr->recent_messages_count;
}
/* all recent messages were in new/ dir, so even if we did only
a partial sync we should know all the recent messages. */
if (uidlist->first_recent_uid == 0)
return 0;
count++;
}
return count;
}
{
return uidlist->uid_validity;
}
{
}
{
}
const char *temp_path)
{
struct maildir_uidlist_iter_ctx *iter;
int ret = 0;
if (uidlist->uid_validity == 0)
/* flush buffer */
"write_full(%s) failed: %m", temp_path);
ret = -1;
break;
}
str_truncate(str, 0);
}
"N" : "-";
}
if (ret < 0)
return -1;
"write_full(%s) failed: %m", temp_path);
return -1;
}
/* uidlist's mtime must grow every time */
"utime(%s) failed: %m", temp_path);
return -1;
}
"fsync(%s) failed: %m", temp_path);
return -1;
}
return 0;
}
{
int ret;
if (ret == 0) {
FALSE) <= 0) {
"file_dotlock_replace(%s) failed: %m", db_path);
ret = -1;
} else {
}
} else {
}
if (ret < 0)
return ret;
}
int nonsynced)
{
struct maildir_uidlist_rec **rec_p;
if (nonsynced) {
for (i = 0; i < size; i++)
} else {
for (i = 0; i < size; i++)
}
}
struct maildir_uidlist_sync_ctx *
{
struct maildir_uidlist_sync_ctx *ctx;
if (partial) {
/* initially mark all nonsynced */
return ctx;
}
ctx->record_pool =
return ctx;
}
{
int ret;
/* first time reading the uidlist,
no locking yet */
return -1;
}
return 0;
}
/* lock and update uidlist to see if it's just been added */
if (ret <= 0) {
if (ret == 0) {
return -1;
}
return -1;
}
return -1;
}
return 1;
}
static int
const char *filename,
{
struct maildir_uidlist_rec *rec;
/* we'll update uidlist directly */
if (ctx->new_files_count == 0) {
ctx->first_new_pos =
sizeof(rec);
}
ctx->new_files_count++;
pool_alloconly_create("uidlist record_pool",
1024);
}
struct maildir_uidlist_rec, 1);
}
if ((flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
return 1;
}
const char *filename)
{
int ret;
return 0;
if (ret < 0)
if (ret == 0)
}
return 1;
}
const char *filename,
{
return -1;
MAILDIR_UIDLIST_REC_FLAG_MOVED)) == 0) {
/* possibly duplicate */
return 0;
}
} else {
else {
ctx->new_files_count++;
}
}
if ((flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
return 1;
}
{
/* we have to do numeric comparision, strcmp() will break when
there's different amount of digits (mostly the 999999999 ->
1000000000 change in Sep 9 2001) */
s1++;
}
s2++;
}
}
unsigned int first_new_pos)
{
struct maildir_uidlist_rec **rec_p;
unsigned int dest;
/* sort new files and assign UIDs for them */
sizeof(*rec_p), maildir_time_cmp);
}
}
{
}
{
struct maildir_uidlist_rec **rec_p;
/* buffer is unsorted, sort it by UID up to beginning of new messages */
if (ctx->new_files_count != 0) {
}
}
{
} else {
if (ctx->new_files_count != 0) {
ctx->first_new_pos);
}
}
}
{
return ret;
}
struct maildir_uidlist_iter_ctx *
{
struct maildir_uidlist_iter_ctx *ctx;
return ctx;
}
enum maildir_uidlist_rec_flag *flags_r,
const char **filename_r)
{
return 0;
return 1;
}
{
}