introduce ability to flatten the hierarchy of Stores

This commit is contained in:
Oswald Buddenhagen 2012-08-18 13:58:14 +02:00
parent 2585dd3324
commit dfd7516b9a
5 changed files with 55 additions and 5 deletions

View File

@ -430,7 +430,18 @@ parse_generic_store( store_conf_t *store, conffile_t *cfg, int *err )
store->max_size = parse_size( cfg ); store->max_size = parse_size( cfg );
else if (!strcasecmp( "MapInbox", cfg->cmd )) else if (!strcasecmp( "MapInbox", cfg->cmd ))
store->map_inbox = nfstrdup( cfg->val ); store->map_inbox = nfstrdup( cfg->val );
else { else if (!strcasecmp( "Flatten", cfg->cmd )) {
int sl = strlen( cfg->val );
if (sl != 1) {
error( "%s:%d: malformed flattened hierarchy delimiter\n", cfg->file, cfg->line );
*err = 1;
} else if (cfg->val[0] == '/') {
error( "%s:%d: flattened hierarchy delimiter cannot be the canonical delimiter '/'\n", cfg->file, cfg->line );
*err = 1;
} else {
store->flat_delim = cfg->val[0];
}
} else {
error( "%s:%d: unknown keyword '%s'\n", cfg->file, cfg->line, cfg->cmd ); error( "%s:%d: unknown keyword '%s'\n", cfg->file, cfg->line, cfg->cmd );
*err = 1; *err = 1;
} }

View File

@ -142,6 +142,7 @@ typedef struct store_conf {
const char *trash; const char *trash;
unsigned max_size; /* off_t is overkill */ unsigned max_size; /* off_t is overkill */
unsigned trash_remote_new:1, trash_only_new:1; unsigned trash_remote_new:1, trash_only_new:1;
char flat_delim;
} store_conf_t; } store_conf_t;
typedef struct string_list { typedef struct string_list {
@ -216,7 +217,8 @@ typedef struct store {
void *bad_callback_aux; void *bad_callback_aux;
/* currently open mailbox */ /* currently open mailbox */
const char *name; /* foreign! maybe preset? */ const char *orig_name; /* foreign! maybe preset? */
char *name; /* foreign! maybe preset? */
char *path; /* own */ char *path; /* own */
message_t *msgs; /* own */ message_t *msgs; /* own */
int uidvalidity; int uidvalidity;

View File

@ -718,12 +718,21 @@ static void
store_listed( int sts, void *aux ) store_listed( int sts, void *aux )
{ {
MVARS(aux) MVARS(aux)
string_list_t *box;
switch (sts) { switch (sts) {
case DRV_CANCELED: case DRV_CANCELED:
return; return;
case DRV_OK: case DRV_OK:
mvars->ctx[t]->listed = 1; mvars->ctx[t]->listed = 1;
if (mvars->ctx[t]->conf->flat_delim) {
for (box = mvars->ctx[t]->boxes; box; box = box->next) {
if (map_name( box->string, mvars->ctx[t]->conf->flat_delim, '/' ) < 0) {
error( "Error: flattened mailbox name '%s' contains canonical hierarchy delimiter\n", box->string );
mvars->ret = mvars->skip = 1;
}
}
}
if (mvars->ctx[t]->conf->map_inbox) if (mvars->ctx[t]->conf->map_inbox)
add_string_list( &mvars->ctx[t]->boxes, mvars->ctx[t]->conf->map_inbox ); add_string_list( &mvars->ctx[t]->boxes, mvars->ctx[t]->conf->map_inbox );
break; break;

View File

@ -150,6 +150,15 @@ Channels section.
This virtual mailbox does not support subfolders. This virtual mailbox does not support subfolders.
.. ..
.TP .TP
\fBFlatten\fR \fIdelim\fR
Flatten the hierarchy within this Store by substituting the canonical
hierarchy delimiter \fB/\fR with \fIdelim\fR.
This can be useful when the MUA used to access the Store provides
suboptimal handling of hierarchical mailboxes, as is the case with
\fBMutt\fR.
A common choice for the delimiter is \fB.\fR.
..
.TP
\fBTrash\fR \fImailbox\fR \fBTrash\fR \fImailbox\fR
Specifies a mailbox (relative to \fBPath\fR) to copy deleted messages to Specifies a mailbox (relative to \fBPath\fR) to copy deleted messages to
prior to expunging. See \fBINHERENT PROBLEMS\fR below. prior to expunging. See \fBINHERENT PROBLEMS\fR below.
@ -318,6 +327,9 @@ This option is meaningless if a \fBPath\fR was specified.
\fBPathDelimiter\fR \fIdelim\fR \fBPathDelimiter\fR \fIdelim\fR
Specify the server's hierarchy delimiter character. Specify the server's hierarchy delimiter character.
(Default: taken from the server's first "personal" NAMESPACE) (Default: taken from the server's first "personal" NAMESPACE)
.br
Do \fBNOT\fR abuse this to re-interpret the hierarchy.
Use \fBFlatten\fR instead.
.. ..
.SS Channels .SS Channels
.TP .TP

View File

@ -467,6 +467,7 @@ stats( sync_vars_t *svars )
static void sync_bail( sync_vars_t *svars ); static void sync_bail( sync_vars_t *svars );
static void sync_bail1( sync_vars_t *svars ); static void sync_bail1( sync_vars_t *svars );
static void sync_bail2( sync_vars_t *svars ); static void sync_bail2( sync_vars_t *svars );
static void sync_bail3( sync_vars_t *svars );
static void cancel_done( void *aux ); static void cancel_done( void *aux );
static void static void
@ -605,9 +606,16 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan,
svars->srecadd = &svars->srecs; svars->srecadd = &svars->srecs;
for (t = 0; t < 2; t++) { for (t = 0; t < 2; t++) {
ctx[t]->name = ctx[t]->orig_name =
(!names[t] || (ctx[t]->conf->map_inbox && !strcmp( ctx[t]->conf->map_inbox, names[t] ))) ? (!names[t] || (ctx[t]->conf->map_inbox && !strcmp( ctx[t]->conf->map_inbox, names[t] ))) ?
"INBOX" : names[t]; "INBOX" : names[t];
ctx[t]->name = nfstrdup( ctx[t]->orig_name );
if (ctx[t]->conf->flat_delim && map_name( ctx[t]->name, '/', ctx[t]->conf->flat_delim ) < 0) {
error( "Error: canonical mailbox name '%s' contains flattened hierarchy delimiter\n", ctx[t]->name );
svars->ret = SYNC_FAIL;
sync_bail3( svars );
return;
}
ctx[t]->uidvalidity = -1; ctx[t]->uidvalidity = -1;
set_bad_callback( ctx[t], store_bad, AUX ); set_bad_callback( ctx[t], store_bad, AUX );
svars->drv[t] = ctx[t]->conf->driver; svars->drv[t] = ctx[t]->conf->driver;
@ -615,7 +623,7 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan,
/* Both boxes must be fully set up at this point, so that error exit paths /* Both boxes must be fully set up at this point, so that error exit paths
* don't run into uninitialized variables. */ * don't run into uninitialized variables. */
for (t = 0; t < 2; t++) { for (t = 0; t < 2; t++) {
info( "Selecting %s %s...\n", str_ms[t], ctx[t]->name ); info( "Selecting %s %s...\n", str_ms[t], ctx[t]->orig_name );
DRIVER_CALL(select( ctx[t], (chan->ops[t] & OP_CREATE) != 0, box_selected, AUX )); DRIVER_CALL(select( ctx[t], (chan->ops[t] & OP_CREATE) != 0, box_selected, AUX ));
} }
} }
@ -696,7 +704,7 @@ box_selected( int sts, void *aux )
} }
if (fcntl( svars->lfd, F_SETLK, &lck )) { if (fcntl( svars->lfd, F_SETLK, &lck )) {
error( "Error: channel :%s:%s-:%s:%s is locked\n", error( "Error: channel :%s:%s-:%s:%s is locked\n",
chan->stores[M]->name, ctx[M]->name, chan->stores[S]->name, ctx[S]->name ); chan->stores[M]->name, ctx[M]->orig_name, chan->stores[S]->name, ctx[S]->orig_name );
svars->ret = SYNC_FAIL; svars->ret = SYNC_FAIL;
sync_bail1( svars ); sync_bail1( svars );
return; return;
@ -1721,6 +1729,14 @@ sync_bail2( sync_vars_t *svars )
free( svars->jname ); free( svars->jname );
free( svars->dname ); free( svars->dname );
flushn(); flushn();
sync_bail3( svars );
}
static void
sync_bail3( sync_vars_t *svars )
{
free( svars->ctx[M]->name );
free( svars->ctx[S]->name );
sync_deref( svars ); sync_deref( svars );
} }