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. Changed default config & state locations to follow the XDG basedir spec.
The old locations remain supported. 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] [1.4.0]
The 'isync' compatibility wrapper was removed. The 'isync' compatibility wrapper was removed.

View File

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

View File

@ -18,6 +18,7 @@ typedef struct {
int line; int line;
int err; int err;
int ms_warn; int ms_warn;
int path_len;
char *cmd, *val, *rest; char *cmd, *val, *rest;
} conffile_t; } conffile_t;
@ -26,7 +27,7 @@ extern char FieldDelimiter;
#define ARG_OPTIONAL 0 #define ARG_OPTIONAL 0
#define ARG_REQUIRED 1 #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 ); 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 ))); } while ((arg = get_arg( cfg, ARG_OPTIONAL, NULL )));
#ifdef HAVE_LIBSSL #ifdef HAVE_LIBSSL
} else if (!strcasecmp( "CertificateFile", cfg->cmd )) { } 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 )) { if (access( server->sconf.cert_file, R_OK )) {
sys_error( "%s:%d: CertificateFile '%s'", sys_error( "%s:%d: CertificateFile '%s'",
cfg->file, cfg->line, server->sconf.cert_file ); 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 )) { } else if (!strcasecmp( "SystemCertificates", cfg->cmd )) {
server->sconf.system_certs = parse_bool( cfg ); server->sconf.system_certs = parse_bool( cfg );
} else if (!strcasecmp( "ClientCertificate", cfg->cmd )) { } 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 )) { if (access( server->sconf.client_certfile, R_OK )) {
sys_error( "%s:%d: ClientCertificate '%s'", sys_error( "%s:%d: ClientCertificate '%s'",
cfg->file, cfg->line, server->sconf.client_certfile ); cfg->file, cfg->line, server->sconf.client_certfile );
cfg->err = 1; cfg->err = 1;
} }
} else if (!strcasecmp( "ClientKey", cfg->cmd )) { } 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 )) { if (access( server->sconf.client_keyfile, R_OK )) {
sys_error( "%s:%d: ClientKey '%s'", sys_error( "%s:%d: ClientKey '%s'",
cfg->file, cfg->line, server->sconf.client_keyfile ); 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) { while (getcline( cfg ) && cfg->cmd) {
if (!strcasecmp( "Inbox", 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 )) { } else if (!strcasecmp( "Path", cfg->cmd )) {
store->path = expand_strdup( cfg->val ); store->path = expand_strdup( cfg->val, cfg );
#ifdef USE_DB #ifdef USE_DB
} else if (!strcasecmp( "AltMap", cfg->cmd )) { } else if (!strcasecmp( "AltMap", cfg->cmd )) {
store->alt_map = parse_bool( cfg ); store->alt_map = parse_bool( cfg );
@ -1892,7 +1892,7 @@ maildir_parse_store( conffile_t *cfg, store_conf_t **storep )
} }
} }
if (!store->inbox) if (!store->inbox)
store->inbox = expand_strdup( "~/Maildir" ); store->inbox = expand_strdup( "~/Maildir", NULL );
if (store->sub_style == SUB_MAILDIRPP && store->path) { if (store->sub_style == SUB_MAILDIRPP && store->path) {
error( "Maildir store '%s': Setting Path is incompatible with 'SubFolders Maildir++'\n", store->name ); error( "Maildir store '%s': Setting Path is incompatible with 'SubFolders Maildir++'\n", store->name );
cfg->err = 1; 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. All keywords (including those used as arguments) are case-insensitive.
Bash-like home directory expansion using the tilde (\fB~\fR) is supported Bash-like home directory expansion using the tilde (\fB~\fR) is supported
in all options which represent local paths. 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. 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 Sections begin with a section-starting keyword and are terminated by an empty
line or end of file. line or end of file.
@ -205,7 +207,8 @@ yet all messages are archived.
(Default: \fBno\fR) (Default: \fBno\fR)
. .
.SS Maildir Stores .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 .P
As \fBmbsync\fR needs UIDs, but no standardized UID storage scheme exists for 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. Maildir, \fBmbsync\fR supports two schemes, each with its pros and cons.

View File

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

View File

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