deal sensibly with incomplete maildir directories

a directory is no mailbox unless it contains a cur/ subdir.
but if that one is present, create new/ and tmp/ if they are missing.

this makes it possible to resume interrupted maildir creations.
This commit is contained in:
Oswald Buddenhagen 2014-12-29 02:01:23 +01:00
parent 5f4e3b285e
commit 7489ff8613

View File

@ -68,7 +68,7 @@ typedef struct maildir_message {
typedef struct maildir_store { typedef struct maildir_store {
store_t gen; store_t gen;
int uvfd, uvok, nuid, fresh; int uvfd, uvok, nuid, fresh[3];
int minuid, maxuid, newuid, nexcs, *excs; int minuid, maxuid, newuid, nexcs, *excs;
char *trash; char *trash;
#ifdef USE_DB #ifdef USE_DB
@ -378,8 +378,12 @@ maildir_validate( const char *box, int create, maildir_store_t *ctx )
bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", box ); bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", box );
if (stat( buf, &st )) { if (stat( buf, &st )) {
if (errno == ENOENT) { if (errno != ENOENT) {
if (create) { sys_error( "Maildir error: cannot access mailbox '%s'", box );
return DRV_BOX_BAD;
}
if (!create)
return DRV_BOX_BAD;
p = memrchr( buf, '/', bl - 1 ); p = memrchr( buf, '/', bl - 1 );
if (*(p + 1) == '.') { if (*(p + 1) == '.') {
*p = 0; *p = 0;
@ -388,37 +392,34 @@ maildir_validate( const char *box, int create, maildir_store_t *ctx )
*p = '/'; *p = '/';
} }
if (mkdir( buf, 0700 )) { if (mkdir( buf, 0700 )) {
sys_error( "Maildir error: cannot create mailbox '%s'", buf ); sys_error( "Maildir error: cannot create mailbox '%s'", box );
maildir_invoke_bad_callback( &ctx->gen ); maildir_invoke_bad_callback( &ctx->gen );
return DRV_CANCELED; return DRV_CANCELED;
} }
} else if (!S_ISDIR(st.st_mode)) {
notdir:
error( "Maildir error: '%s' is no valid mailbox\n", box );
return DRV_BOX_BAD;
}
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
memcpy( buf + bl, subdirs[i], 4 ); memcpy( buf + bl, subdirs[i], 4 );
if (stat( buf, &st )) {
/* We always create new/ and tmp/ if they are missing. cur/ is the presence indicator. */
if (!i && !create)
return DRV_BOX_BAD;
if (mkdir( buf, 0700 )) { if (mkdir( buf, 0700 )) {
sys_error( "Maildir error: cannot create directory %s", buf ); sys_error( "Maildir error: cannot create directory %s", buf );
return DRV_BOX_BAD; return DRV_BOX_BAD;
} }
} ctx->fresh[i] = 1;
} else if (!S_ISDIR(st.st_mode)) {
goto notdir;
} else { } else {
error( "Maildir error: mailbox '%s' does not exist\n", buf ); if (i == 2) {
return DRV_BOX_BAD;
}
} else {
sys_error( "Maildir error: cannot access mailbox '%s'", buf );
return DRV_BOX_BAD;
}
ctx->fresh = 1;
} else {
for (i = 0; i < 3; i++) {
memcpy( buf + bl, subdirs[i], 4 );
if (stat( buf, &st ) || !S_ISDIR(st.st_mode)) {
error( "Maildir error: '%.*s' is no valid mailbox\n", bl, buf );
return DRV_BOX_BAD;
}
}
if ((ret = maildir_clear_tmp( buf, sizeof(buf), bl )) != DRV_OK) if ((ret = maildir_clear_tmp( buf, sizeof(buf), bl )) != DRV_OK)
return ret; return ret;
ctx->fresh = 0; }
}
} }
return DRV_OK; return DRV_OK;
} }
@ -676,7 +677,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
sys_error( "Maildir error: cannot stat %s", buf ); sys_error( "Maildir error: cannot stat %s", buf );
goto dfail; goto dfail;
} }
if (st.st_mtime == now && !(DFlags & ZERODELAY)) { if (st.st_mtime == now && !(DFlags & ZERODELAY) && !ctx->fresh[i]) {
/* If the modification happened during this second, we wouldn't be able to /* If the modification happened during this second, we wouldn't be able to
* tell if there were further modifications during this second. So wait. * tell if there were further modifications during this second. So wait.
* This has the nice side effect that we wait for "batches" of changes to * This has the nice side effect that we wait for "batches" of changes to
@ -948,6 +949,7 @@ maildir_select_box( store_t *gctx, const char *name )
#ifdef USE_DB #ifdef USE_DB
ctx->db = 0; ctx->db = 0;
#endif /* USE_DB */ #endif /* USE_DB */
ctx->fresh[0] = ctx->fresh[1] = 0;
if (starts_with( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == '/')) { if (starts_with( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == '/')) {
gctx->path = maildir_join_path( ((maildir_store_conf_t *)gctx->conf)->inbox, name + 5 ); gctx->path = maildir_join_path( ((maildir_store_conf_t *)gctx->conf)->inbox, name + 5 );
} else { } else {
@ -1088,11 +1090,6 @@ maildir_load_box( store_t *gctx, int minuid, int maxuid, int newuid, int *excs,
ctx->excs = nfrealloc( excs, nexcs * sizeof(int) ); ctx->excs = nfrealloc( excs, nexcs * sizeof(int) );
ctx->nexcs = nexcs; ctx->nexcs = nexcs;
if (ctx->fresh) {
ctx->gen.count = ctx->gen.recent = 0;
goto dontscan;
}
if (maildir_scan( ctx, &msglist ) != DRV_OK) { if (maildir_scan( ctx, &msglist ) != DRV_OK) {
cb( DRV_BOX_BAD, aux ); cb( DRV_BOX_BAD, aux );
return; return;
@ -1102,7 +1099,6 @@ maildir_load_box( store_t *gctx, int minuid, int maxuid, int newuid, int *excs,
maildir_app_msg( ctx, &msgapp, msglist.ents + i ); maildir_app_msg( ctx, &msgapp, msglist.ents + i );
maildir_free_scan( &msglist ); maildir_free_scan( &msglist );
dontscan:
cb( DRV_OK, aux ); cb( DRV_OK, aux );
} }
@ -1114,6 +1110,7 @@ maildir_rescan( maildir_store_t *ctx )
msglist_t msglist; msglist_t msglist;
int i; int i;
ctx->fresh[0] = ctx->fresh[1] = 0;
if (maildir_scan( ctx, &msglist ) != DRV_OK) if (maildir_scan( ctx, &msglist ) != DRV_OK)
return DRV_BOX_BAD; return DRV_BOX_BAD;
for (msgapp = &ctx->gen.msgs, i = 0; for (msgapp = &ctx->gen.msgs, i = 0;