Przeglądaj źródła

introduce ability to flatten the hierarchy of Stores

Oswald Buddenhagen 13 lat temu
rodzic
commit
dfd7516b9a
5 zmienionych plików z 55 dodań i 5 usunięć
  1. 12 1
      src/config.c
  2. 3 1
      src/isync.h
  3. 9 0
      src/main.c
  4. 12 0
      src/mbsync.1
  5. 19 3
      src/sync.c

+ 12 - 1
src/config.c

@@ -430,7 +430,18 @@ parse_generic_store( store_conf_t *store, conffile_t *cfg, int *err )
 		store->max_size = parse_size( cfg );
 	else if (!strcasecmp( "MapInbox", cfg->cmd ))
 		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 );
 		*err = 1;
 	}

+ 3 - 1
src/isync.h

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

+ 9 - 0
src/main.c

@@ -718,12 +718,21 @@ static void
 store_listed( int sts, void *aux )
 {
 	MVARS(aux)
+	string_list_t *box;
 
 	switch (sts) {
 	case DRV_CANCELED:
 		return;
 	case DRV_OK:
 		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)
 			add_string_list( &mvars->ctx[t]->boxes, mvars->ctx[t]->conf->map_inbox );
 		break;

+ 12 - 0
src/mbsync.1

@@ -150,6 +150,15 @@ Channels section.
 This virtual mailbox does not support subfolders.
 ..
 .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
 Specifies a mailbox (relative to \fBPath\fR) to copy deleted messages to
 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
 Specify the server's hierarchy delimiter character.
 (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
 .TP

+ 19 - 3
src/sync.c

@@ -467,6 +467,7 @@ stats( sync_vars_t *svars )
 static void sync_bail( sync_vars_t *svars );
 static void sync_bail1( 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
@@ -605,9 +606,16 @@ sync_boxes( store_t *ctx[], const char *names[], channel_conf_t *chan,
 	svars->srecadd = &svars->srecs;
 
 	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] ))) ?
 				"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;
 		set_bad_callback( ctx[t], store_bad, AUX );
 		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
 	 * don't run into uninitialized variables. */
 	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 ));
 	}
 }
@@ -696,7 +704,7 @@ box_selected( int sts, void *aux )
 	}
 	if (fcntl( svars->lfd, F_SETLK, &lck )) {
 		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;
 		sync_bail1( svars );
 		return;
@@ -1721,6 +1729,14 @@ sync_bail2( sync_vars_t *svars )
 	free( svars->jname );
 	free( svars->dname );
 	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 );
 }