re-interpret relative local paths in config file

this makes config+data file "sets" relocatable, which is useful for
testing.

this is technically a gratuitous backwards incompatible behavior
change, but to the degree that anyone uses relative paths at all, they
almost certainly rely on PWD being set up such that they won't see a
difference.
This commit is contained in:
Oswald Buddenhagen 2022-05-30 23:04:52 +02:00
parent cf13630a00
commit d3f118be79
8 changed files with 39 additions and 19 deletions

3
NEWS
View File

@ -3,6 +3,9 @@
Changed default config & state locations to follow the XDG basedir spec.
The old locations remain supported.
The reference point for relative local paths in the configuration file
is now the file's containing directory.
[1.4.0]
The 'isync' compatibility wrapper was removed.

View File

@ -23,7 +23,7 @@ char FieldDelimiter = ':';
static store_conf_t *stores;
char *
expand_strdup( const char *s )
expand_strdup( const char *s, const conffile_t *cfile )
{
struct passwd *pw;
const char *p, *q;
@ -51,6 +51,9 @@ expand_strdup( const char *s )
}
nfasprintf( &r, "%s%s", q, p ? p : "" );
return r;
} else if (*s != '/') {
nfasprintf( &r, "%.*s%s", cfile->path_len, cfile->file, s );
return r;
} else {
return nfstrdup( s );
}
@ -220,7 +223,7 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
} while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL )));
conf->ops[F] |= XOP_HAVE_TYPE;
} else if (!strcasecmp( "SyncState", cfile->cmd )) {
conf->sync_state = expand_strdup( cfile->val );
conf->sync_state = !strcmp( cfile->val, "*" ) ? "*" : expand_strdup( cfile->val, cfile );
} else if (!strcasecmp( "CopyArrivalDate", cfile->cmd )) {
conf->use_internal_date = parse_bool( cfile );
} else if (!strcasecmp( "MaxMessages", cfile->cmd )) {
@ -354,24 +357,34 @@ load_config( const char *where )
char buf[1024];
if (!where) {
int path_len, path_len2;
const char *config_home = getenv( "XDG_CONFIG_HOME" );
if (config_home)
nfsnprintf( path, sizeof(path), "%s/isyncrc", config_home );
nfsnprintf( path, sizeof(path), "%s/%nisyncrc", config_home, &path_len );
else
nfsnprintf( path, sizeof(path), "%s/.config/isyncrc", Home );
nfsnprintf( path2, sizeof(path2), "%s/.mbsyncrc", Home );
nfsnprintf( path, sizeof(path), "%s/.config/%nisyncrc", Home, &path_len );
nfsnprintf( path2, sizeof(path2), "%s/%n.mbsyncrc", Home, &path_len2 );
struct stat st;
int ex = !lstat( path, &st );
int ex2 = !lstat( path2, &st );
if (ex2 && !ex) {
cfile.file = path2;
cfile.path_len = path_len2;
} else {
if (ex && ex2)
warn( "Both %s and %s exist; using the former.\n", path, path2 );
cfile.file = path;
cfile.path_len = path_len;
}
} else {
cfile.file = where;
const char *sl = strrchr( where, '/' );
if (!sl) {
nfsnprintf( path, sizeof(path), "./%n%s", &cfile.path_len, where );
cfile.file = path;
} else {
cfile.path_len = sl - where + 1;
cfile.file = where;
}
}
info( "Reading configuration file %s\n", cfile.file );

View File

@ -18,6 +18,7 @@ typedef struct {
int line;
int err;
int ms_warn;
int path_len;
char *cmd, *val, *rest;
} conffile_t;
@ -26,7 +27,7 @@ extern char FieldDelimiter;
#define ARG_OPTIONAL 0
#define ARG_REQUIRED 1
char *expand_strdup( const char *s );
char *expand_strdup( const char *s, const conffile_t *cfile );
char *get_arg( conffile_t *cfile, int required, int *comment );

View File

@ -3590,7 +3590,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
} while ((arg = get_arg( cfg, ARG_OPTIONAL, NULL )));
#ifdef HAVE_LIBSSL
} else if (!strcasecmp( "CertificateFile", cfg->cmd )) {
server->sconf.cert_file = expand_strdup( cfg->val );
server->sconf.cert_file = expand_strdup( cfg->val, cfg );
if (access( server->sconf.cert_file, R_OK )) {
sys_error( "%s:%d: CertificateFile '%s'",
cfg->file, cfg->line, server->sconf.cert_file );
@ -3599,14 +3599,14 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
} else if (!strcasecmp( "SystemCertificates", cfg->cmd )) {
server->sconf.system_certs = parse_bool( cfg );
} else if (!strcasecmp( "ClientCertificate", cfg->cmd )) {
server->sconf.client_certfile = expand_strdup( cfg->val );
server->sconf.client_certfile = expand_strdup( cfg->val, cfg );
if (access( server->sconf.client_certfile, R_OK )) {
sys_error( "%s:%d: ClientCertificate '%s'",
cfg->file, cfg->line, server->sconf.client_certfile );
cfg->err = 1;
}
} else if (!strcasecmp( "ClientKey", cfg->cmd )) {
server->sconf.client_keyfile = expand_strdup( cfg->val );
server->sconf.client_keyfile = expand_strdup( cfg->val, cfg );
if (access( server->sconf.client_keyfile, R_OK )) {
sys_error( "%s:%d: ClientKey '%s'",
cfg->file, cfg->line, server->sconf.client_keyfile );

View File

@ -1857,9 +1857,9 @@ maildir_parse_store( conffile_t *cfg, store_conf_t **storep )
while (getcline( cfg ) && cfg->cmd) {
if (!strcasecmp( "Inbox", cfg->cmd )) {
store->inbox = expand_strdup( cfg->val );
store->inbox = expand_strdup( cfg->val, cfg );
} else if (!strcasecmp( "Path", cfg->cmd )) {
store->path = expand_strdup( cfg->val );
store->path = expand_strdup( cfg->val, cfg );
#ifdef USE_DB
} else if (!strcasecmp( "AltMap", cfg->cmd )) {
store->alt_map = parse_bool( cfg );
@ -1892,7 +1892,7 @@ maildir_parse_store( conffile_t *cfg, store_conf_t **storep )
}
}
if (!store->inbox)
store->inbox = expand_strdup( "~/Maildir" );
store->inbox = expand_strdup( "~/Maildir", NULL );
if (store->sub_style == SUB_MAILDIRPP && store->path) {
error( "Maildir store '%s': Setting Path is incompatible with 'SubFolders Maildir++'\n", store->name );
cfg->err = 1;

View File

@ -105,6 +105,8 @@ and literal double quotes and backslashes (\fB\\\fR) must be backslash-escaped.
All keywords (including those used as arguments) are case-insensitive.
Bash-like home directory expansion using the tilde (\fB~\fR) is supported
in all options which represent local paths.
The reference point for relative local paths is the configuration file's
containing directory.
There are a few global options, the others apply to particular sections.
Sections begin with a section-starting keyword and are terminated by an empty
line or end of file.
@ -205,7 +207,8 @@ yet all messages are archived.
(Default: \fBno\fR)
.
.SS Maildir Stores
The reference point for relative \fBPath\fRs is the current working directory.
The reference point for relative \fBPath\fRs is the configuration file's
containing directory.
.P
As \fBmbsync\fR needs UIDs, but no standardized UID storage scheme exists for
Maildir, \fBmbsync\fR supports two schemes, each with its pros and cons.

View File

@ -289,12 +289,12 @@ sub writecfg($)
"FSync no
MaildirStore far
Path ./
Inbox ./far
Path \"\"
Inbox far
".$$sfx[0]."
MaildirStore near
Path ./
Inbox ./near
Path \"\"
Inbox near
".$$sfx[1]."
Channel test
Far :far:

View File

@ -35,7 +35,7 @@ typedef struct channel_conf {
const char *name;
store_conf_t *stores[2];
const char *boxes[2];
char *sync_state;
const char *sync_state;
string_list_t *patterns;
int ops[2];
int max_messages; // For near side only.