introduce FieldDelimiter and InfoDelimiter options
... for windows fs compatibility. the maildir-specific InfoDelimiter inherits the global FieldDelimiter (which affects SyncState), based on the assumption that if the sync state is on a windows FS, the mailboxes certainly will be as well, while the inverse is not necessarily true (when running on unix, anyway). REFMAIL: <CA+m_8J1ynqAjHRJagvKt9sb31yz047Q7NH-ODRmHOKyfru8vtA@mail.gmail.com>
This commit is contained in:
parent
85fd5ceb54
commit
f377e7b696
2
NEWS
2
NEWS
|
@ -8,6 +8,8 @@ Notice: Tunnels are assumed to be secure and thus default to no SSL.
|
|||
|
||||
Support for SASL (flexible authentication) has been added.
|
||||
|
||||
Support for Windows file systems has been added.
|
||||
|
||||
[1.1.0]
|
||||
|
||||
Support for hierarchical mailboxes in Patterns.
|
||||
|
|
3
README
3
README
|
@ -59,9 +59,6 @@ isync executable still exists; it is a compatibility wrapper around mbsync.
|
|||
At some point, ``isync'' has successfully run on:
|
||||
Linux, Solaris 2.7, OpenBSD 2.8, FreeBSD 4.3.
|
||||
|
||||
Note that Cygwin cannot be reasonably supported due to restrictions
|
||||
of the Windows file system.
|
||||
|
||||
* Requirements
|
||||
|
||||
Berkley DB 4.2+
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
|
||||
extern int DFlags;
|
||||
extern int UseFSync;
|
||||
extern char FieldDelimiter;
|
||||
|
||||
extern int Pid;
|
||||
extern char Hostname[256];
|
||||
|
|
13
src/config.c
13
src/config.c
|
@ -467,6 +467,19 @@ load_config( const char *where, int pseudo )
|
|||
{
|
||||
UseFSync = parse_bool( &cfile );
|
||||
}
|
||||
else if (!strcasecmp( "FieldDelimiter", cfile.cmd ))
|
||||
{
|
||||
if (strlen( cfile.val ) != 1) {
|
||||
error( "%s:%d: Field delimiter must be exactly one character long\n", cfile.file, cfile.line );
|
||||
cfile.err = 1;
|
||||
} else {
|
||||
FieldDelimiter = cfile.val[0];
|
||||
if (!ispunct( FieldDelimiter )) {
|
||||
error( "%s:%d: Field delimiter must be a punctuation character\n", cfile.file, cfile.line );
|
||||
cfile.err = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!getopt_helper( &cfile, &gcops, &global_conf ))
|
||||
{
|
||||
error( "%s:%d: unknown section keyword '%s'\n",
|
||||
|
|
|
@ -57,6 +57,8 @@ typedef struct maildir_store_conf {
|
|||
#ifdef USE_DB
|
||||
int alt_map;
|
||||
#endif /* USE_DB */
|
||||
char info_delimiter;
|
||||
char *info_prefix, *info_stop; /* precalculated from info_delimiter */
|
||||
} maildir_store_conf_t;
|
||||
|
||||
typedef struct maildir_message {
|
||||
|
@ -84,14 +86,14 @@ static int MaildirCount;
|
|||
static const char Flags[] = { 'D', 'F', 'R', 'S', 'T' };
|
||||
|
||||
static unsigned char
|
||||
maildir_parse_flags( const char *base )
|
||||
maildir_parse_flags( const char *info_prefix, const char *base )
|
||||
{
|
||||
const char *s;
|
||||
unsigned i;
|
||||
unsigned char flags;
|
||||
|
||||
flags = 0;
|
||||
if ((s = strstr( base, ":2," )))
|
||||
if ((s = strstr( base, info_prefix )))
|
||||
for (s += 3, i = 0; i < as(Flags); i++)
|
||||
if (strchr( s, Flags[i] ))
|
||||
flags |= (1 << i);
|
||||
|
@ -413,9 +415,9 @@ maildir_validate( const char *box, int create, maildir_store_t *ctx )
|
|||
|
||||
#ifdef USE_DB
|
||||
static void
|
||||
make_key( DBT *tkey, char *name )
|
||||
make_key( const char *info_stop, DBT *tkey, char *name )
|
||||
{
|
||||
char *u = strpbrk( name, ":," );
|
||||
char *u = strpbrk( name, info_stop );
|
||||
tkey->data = name;
|
||||
tkey->size = u ? (size_t)(u - name) : strlen( name );
|
||||
}
|
||||
|
@ -439,7 +441,7 @@ maildir_set_uid( maildir_store_t *ctx, const char *name, int *uid )
|
|||
return DRV_BOX_BAD;
|
||||
}
|
||||
if (uid) {
|
||||
make_key( &key, (char *)name );
|
||||
make_key( ((maildir_store_conf_t *)ctx->gen.conf)->info_stop, &key, (char *)name );
|
||||
value.data = uid;
|
||||
value.size = sizeof(*uid);
|
||||
if ((ret = ctx->db->put( ctx->db, 0, &key, &value, 0 )))
|
||||
|
@ -615,6 +617,7 @@ maildir_compare( const void *l, const void *r )
|
|||
static int
|
||||
maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
|
||||
{
|
||||
maildir_store_conf_t *conf = (maildir_store_conf_t *)ctx->gen.conf;
|
||||
DIR *d;
|
||||
FILE *f;
|
||||
struct dirent *e;
|
||||
|
@ -694,7 +697,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
|
|||
ctx->gen.recent += i;
|
||||
#ifdef USE_DB
|
||||
if (ctx->db) {
|
||||
make_key( &key, e->d_name );
|
||||
make_key( conf->info_stop, &key, e->d_name );
|
||||
if ((ret = ctx->db->get( ctx->db, 0, &key, &value, 0 ))) {
|
||||
if (ret != DB_NOTFOUND) {
|
||||
ctx->db->err( ctx->db, ret, "Maildir error: db->get()" );
|
||||
|
@ -834,7 +837,7 @@ maildir_scan( maildir_store_t *ctx, msglist_t *msglist )
|
|||
if ((u = strstr( entry->base, ",U=" )))
|
||||
for (ru = u + 3; isdigit( (unsigned char)*ru ); ru++);
|
||||
else
|
||||
u = ru = strchr( entry->base, ':' );
|
||||
u = ru = strchr( entry->base, conf->info_delimiter );
|
||||
fnl = (u ?
|
||||
nfsnprintf( buf + bl, sizeof(buf) - bl, "%s/%.*s,U=%d%s", subdirs[entry->recent], (int)(u - entry->base), entry->base, uid, ru ) :
|
||||
nfsnprintf( buf + bl, sizeof(buf) - bl, "%s/%s,U=%d", subdirs[entry->recent], entry->base, uid ))
|
||||
|
@ -906,7 +909,7 @@ maildir_init_msg( maildir_store_t *ctx, maildir_message_t *msg, msg_t *entry )
|
|||
msg->gen.status |= M_RECENT;
|
||||
if (ctx->gen.opts & OPEN_FLAGS) {
|
||||
msg->gen.status |= M_FLAGS;
|
||||
msg->gen.flags = maildir_parse_flags( msg->base );
|
||||
msg->gen.flags = maildir_parse_flags( ((maildir_store_conf_t *)ctx->gen.conf)->info_prefix, msg->base );
|
||||
} else
|
||||
msg->gen.flags = 0;
|
||||
}
|
||||
|
@ -1177,16 +1180,16 @@ maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data,
|
|||
}
|
||||
close( fd );
|
||||
if (!(gmsg->status & M_FLAGS))
|
||||
data->flags = maildir_parse_flags( msg->base );
|
||||
data->flags = maildir_parse_flags( ((maildir_store_conf_t *)gctx->conf)->info_prefix, msg->base );
|
||||
cb( DRV_OK, aux );
|
||||
}
|
||||
|
||||
static int
|
||||
maildir_make_flags( int flags, char *buf )
|
||||
maildir_make_flags( char info_delimiter, int flags, char *buf )
|
||||
{
|
||||
unsigned i, d;
|
||||
|
||||
buf[0] = ':';
|
||||
buf[0] = info_delimiter;
|
||||
buf[1] = '2';
|
||||
buf[2] = ',';
|
||||
for (d = 3, i = 0; i < as(Flags); i++)
|
||||
|
@ -1231,7 +1234,7 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
|
|||
box = ctx->trash;
|
||||
}
|
||||
|
||||
maildir_make_flags( data->flags, fbuf );
|
||||
maildir_make_flags( ((maildir_store_conf_t *)gctx->conf)->info_delimiter, data->flags, fbuf );
|
||||
nfsnprintf( buf, sizeof(buf), "%s/tmp/%s%s", box, base, fbuf );
|
||||
if ((fd = open( buf, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) {
|
||||
if (errno != ENOENT || !to_trash) {
|
||||
|
@ -1302,6 +1305,7 @@ static void
|
|||
maildir_set_flags( store_t *gctx, message_t *gmsg, int uid ATTR_UNUSED, int add, int del,
|
||||
void (*cb)( int sts, void *aux ), void *aux )
|
||||
{
|
||||
maildir_store_conf_t *conf = (maildir_store_conf_t *)gctx->conf;
|
||||
maildir_store_t *ctx = (maildir_store_t *)gctx;
|
||||
maildir_message_t *msg = (maildir_message_t *)gmsg;
|
||||
char *s, *p;
|
||||
|
@ -1319,7 +1323,7 @@ maildir_set_flags( store_t *gctx, message_t *gmsg, int uid ATTR_UNUSED, int add,
|
|||
oob();
|
||||
memcpy( buf + bl, msg->base, ol + 1 );
|
||||
memcpy( nbuf + bl, msg->base, ol + 1 );
|
||||
if ((s = strstr( nbuf + bl, ":2," ))) {
|
||||
if ((s = strstr( nbuf + bl, conf->info_prefix ))) {
|
||||
s += 3;
|
||||
fl = ol - (s - (nbuf + bl));
|
||||
for (i = 0; i < as(Flags); i++) {
|
||||
|
@ -1337,7 +1341,7 @@ maildir_set_flags( store_t *gctx, message_t *gmsg, int uid ATTR_UNUSED, int add,
|
|||
}
|
||||
tl = ol + 3 + fl;
|
||||
} else {
|
||||
tl = ol + maildir_make_flags( msg->gen.flags, nbuf + bl + ol );
|
||||
tl = ol + maildir_make_flags( conf->info_delimiter, msg->gen.flags, nbuf + bl + ol );
|
||||
}
|
||||
if (!rename( buf, nbuf ))
|
||||
break;
|
||||
|
@ -1362,7 +1366,7 @@ maildir_purge_msg( maildir_store_t *ctx, const char *name )
|
|||
{
|
||||
int ret;
|
||||
|
||||
make_key( &key, (char *)name );
|
||||
make_key( ((maildir_store_conf_t *)ctx->gen.conf)->info_stop, &key, (char *)name );
|
||||
if ((ret = ctx->db->del( ctx->db, 0, &key, 0 ))) {
|
||||
ctx->db->err( ctx->db, ret, "Maildir error: db->del()" );
|
||||
return DRV_BOX_BAD;
|
||||
|
@ -1384,7 +1388,7 @@ maildir_trash_msg( store_t *gctx, message_t *gmsg,
|
|||
|
||||
for (;;) {
|
||||
nfsnprintf( buf, sizeof(buf), "%s/%s/%s", gctx->path, subdirs[gmsg->status & M_RECENT], msg->base );
|
||||
s = strstr( msg->base, ":2," );
|
||||
s = strstr( msg->base, ((maildir_store_conf_t *)gctx->conf)->info_prefix );
|
||||
nfsnprintf( nbuf, sizeof(nbuf), "%s/%s/%ld.%d_%d.%s%s", ctx->trash,
|
||||
subdirs[gmsg->status & M_RECENT], (long)time( 0 ), Pid, ++MaildirCount, Hostname, s ? s : "" );
|
||||
if (!rename( buf, nbuf ))
|
||||
|
@ -1484,6 +1488,7 @@ maildir_parse_store( conffile_t *cfg, store_conf_t **storep )
|
|||
if (strcasecmp( "MaildirStore", cfg->cmd ))
|
||||
return 0;
|
||||
store = nfcalloc( sizeof(*store) );
|
||||
store->info_delimiter = FieldDelimiter;
|
||||
store->gen.driver = &maildir_driver;
|
||||
store->gen.name = nfstrdup( cfg->val );
|
||||
|
||||
|
@ -1496,10 +1501,24 @@ maildir_parse_store( conffile_t *cfg, store_conf_t **storep )
|
|||
else if (!strcasecmp( "AltMap", cfg->cmd ))
|
||||
store->alt_map = parse_bool( cfg );
|
||||
#endif /* USE_DB */
|
||||
else
|
||||
else if (!strcasecmp( "InfoDelimiter", cfg->cmd )) {
|
||||
if (strlen( cfg->val ) != 1) {
|
||||
error( "%s:%d: Info delimiter must be exactly one character long\n", cfg->file, cfg->line );
|
||||
cfg->err = 1;
|
||||
continue;
|
||||
}
|
||||
store->info_delimiter = cfg->val[0];
|
||||
if (!ispunct( store->info_delimiter )) {
|
||||
error( "%s:%d: Info delimiter must be a punctuation character\n", cfg->file, cfg->line );
|
||||
cfg->err = 1;
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
parse_generic_store( &store->gen, cfg );
|
||||
if (!store->inbox)
|
||||
store->inbox = expand_strdup( "~/Maildir" );
|
||||
nfasprintf( &store->info_prefix, "%c2,", store->info_delimiter );
|
||||
nfasprintf( &store->info_stop, "%c,", store->info_delimiter );
|
||||
*storep = &store->gen;
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,11 @@
|
|||
|
||||
int DFlags;
|
||||
int UseFSync = 1;
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__CYGWIN__)
|
||||
char FieldDelimiter = ';';
|
||||
#else
|
||||
char FieldDelimiter = ':';
|
||||
#endif
|
||||
|
||||
int Pid; /* for maildir and imap */
|
||||
char Hostname[256]; /* for maildir */
|
||||
|
|
20
src/mbsync.1
20
src/mbsync.1
|
@ -229,6 +229,13 @@ The location of the \fBINBOX\fR. This is \fInot\fR relative to \fBPath\fR,
|
|||
but it is allowed to place the \fBINBOX\fR inside the \fBPath\fR.
|
||||
(Default: \fI~/Maildir\fR)
|
||||
..
|
||||
.TP
|
||||
\fBInfoDelimiter\fR \fIdelim\fR
|
||||
The character used to delimit the info field from a message's basename.
|
||||
The Maildir standard defines this to be the colon, but this is incompatible
|
||||
with DOS/Windows file systems.
|
||||
(Default: the value of \fBFieldDelimiter\fR)
|
||||
..
|
||||
.SS IMAP4 Accounts
|
||||
.TP
|
||||
\fBIMAPAccount\fR \fIname\fR
|
||||
|
@ -513,7 +520,8 @@ mailbox name to make up a complete path.
|
|||
.br
|
||||
This option can be used outside any section for a global effect. In this case
|
||||
the appended string is made up according to the pattern
|
||||
\fB:\fImaster\fB:\fImaster-box\fB_:\fIslave\fB:\fIslave-box\fR.
|
||||
\fB:\fImaster\fB:\fImaster-box\fB_:\fIslave\fB:\fIslave-box\fR
|
||||
(see also \fBFieldDelimiter\fR below).
|
||||
.br
|
||||
(Global default: \fI~/.mbsync/\fR).
|
||||
..
|
||||
|
@ -549,6 +557,16 @@ in particular modern systems like ext4, btrfs and xfs. The performance impact
|
|||
on older file systems may be disproportionate.
|
||||
(Default: \fIyes\fR)
|
||||
..
|
||||
.TP
|
||||
\fBFieldDelimiter\fR \fIdelim\fR
|
||||
The character to use to delimit fields in the string appended to a global
|
||||
\fBSyncState\fR.
|
||||
\fBmbsync\fR prefers to use the colon, but this is incompatible with
|
||||
DOS/Windows file systems.
|
||||
This option is meaningless for \fBSyncState\fR if the latter is \fB*\fR,
|
||||
obviously. However, it also determines the default of \fBInfoDelimiter\fR.
|
||||
(Global default: \fI;\fR on Windows, \fI:\fR everywhere else)
|
||||
..
|
||||
.SH RECOMMENDATIONS
|
||||
Make sure your IMAP server does not auto-expunge deleted messages - it is
|
||||
slow, and semantically somewhat questionable. Specifically, Gmail needs to
|
||||
|
|
|
@ -669,9 +669,10 @@ box_selected( int sts, void *aux )
|
|||
if (chan->sync_state)
|
||||
nfasprintf( &svars->dname, "%s%s", chan->sync_state, csname );
|
||||
else {
|
||||
char c = FieldDelimiter;
|
||||
cmname = clean_strdup( svars->box_name[M] );
|
||||
nfasprintf( &svars->dname, "%s:%s:%s_:%s:%s", global_conf.sync_state,
|
||||
chan->stores[M]->name, cmname, chan->stores[S]->name, csname );
|
||||
nfasprintf( &svars->dname, "%s%c%s%c%s_%c%s%c%s", global_conf.sync_state,
|
||||
c, chan->stores[M]->name, c, cmname, c, chan->stores[S]->name, c, csname );
|
||||
free( cmname );
|
||||
}
|
||||
free( csname );
|
||||
|
|
Loading…
Reference in New Issue
Block a user