diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c54f2b5c..7759d5990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ add the new recipients as group members #3781 - Remove `pytest-async` plugin #3846 - Only send the message about ephemeral timer change if the chat is promoted #3847 +- Use relative paths in `accounts.toml` #3838 ### API-Changes diff --git a/deltachat-rpc-client/src/deltachat_rpc_client/rpc.py b/deltachat-rpc-client/src/deltachat_rpc_client/rpc.py index 78474aa8b..8a407308d 100644 --- a/deltachat-rpc-client/src/deltachat_rpc_client/rpc.py +++ b/deltachat-rpc-client/src/deltachat_rpc_client/rpc.py @@ -14,9 +14,7 @@ class Rpc: if accounts_dir: kwargs["env"] = { **kwargs.get("env", os.environ), - "DC_ACCOUNTS_PATH": os.path.abspath( - os.path.expanduser(str(accounts_dir)) - ), + "DC_ACCOUNTS_PATH": str(accounts_dir), } self._kwargs = kwargs diff --git a/src/accounts.rs b/src/accounts.rs index 511f9f5b5..2380de04e 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -64,7 +64,7 @@ impl Accounts { let events = Events::new(); let stockstrings = StockStrings::new(); let accounts = config - .load_accounts(&events, &stockstrings) + .load_accounts(&events, &stockstrings, &dir) .await .context("failed to load accounts")?; @@ -107,10 +107,11 @@ impl Accounts { /// /// Returns account ID. pub async fn add_account(&mut self) -> Result { - let account_config = self.config.new_account(&self.dir).await?; + let account_config = self.config.new_account().await?; + let dbfile = account_config.dbfile(&self.dir); let ctx = Context::new( - &account_config.dbfile(), + &dbfile, account_config.id, self.events.clone(), self.stockstrings.clone(), @@ -123,10 +124,10 @@ impl Accounts { /// Adds a new closed account. pub async fn add_closed_account(&mut self) -> Result { - let account_config = self.config.new_account(&self.dir).await?; + let account_config = self.config.new_account().await?; let ctx = Context::new_closed( - &account_config.dbfile(), + &account_config.dbfile(&self.dir), account_config.id, self.events.clone(), self.stockstrings.clone(), @@ -147,6 +148,8 @@ impl Accounts { drop(ctx); if let Some(cfg) = self.config.get_account(id) { + let account_path = self.dir.join(cfg.dir); + // Spend up to 1 minute trying to remove the files. // Files may remain locked up to 30 seconds due to r2d2 bug: // https://github.com/sfackler/r2d2/issues/99 @@ -154,7 +157,7 @@ impl Accounts { loop { counter += 1; - if let Err(err) = fs::remove_dir_all(&cfg.dir) + if let Err(err) = fs::remove_dir_all(&account_path) .await .context("failed to remove account data") { @@ -187,16 +190,16 @@ impl Accounts { // create new account let account_config = self .config - .new_account(&self.dir) + .new_account() .await .context("failed to create new account")?; - let new_dbfile = account_config.dbfile(); + let new_dbfile = account_config.dbfile(&self.dir); let new_blobdir = Context::derive_blobdir(&new_dbfile); let new_walfile = Context::derive_walfile(&new_dbfile); let res = { - fs::create_dir_all(&account_config.dir) + fs::create_dir_all(self.dir.join(&account_config.dir)) .await .context("failed to create dir")?; fs::rename(&dbfile, &new_dbfile) @@ -358,8 +361,17 @@ impl Config { /// Read a configuration from the given file into memory. pub async fn from_file(file: PathBuf) -> Result { + let dir = file.parent().context("can't get config file directory")?; let bytes = fs::read(&file).await.context("failed to read file")?; - let inner: InnerConfig = toml::from_slice(&bytes).context("failed to parse config")?; + let mut inner: InnerConfig = toml::from_slice(&bytes).context("failed to parse config")?; + + // Previous versions of the core stored absolute paths in account config. + // Convert them to relative paths. + for account in &mut inner.accounts { + if let Ok(new_dir) = account.dir.strip_prefix(dir) { + account.dir = new_dir.to_path_buf(); + } + } Ok(Config { file, inner }) } @@ -372,12 +384,13 @@ impl Config { &self, events: &Events, stockstrings: &StockStrings, + dir: &Path, ) -> Result> { let mut accounts = BTreeMap::new(); for account_config in &self.inner.accounts { let ctx = Context::new( - &account_config.dbfile(), + &account_config.dbfile(dir), account_config.id, events.clone(), stockstrings.clone(), @@ -386,7 +399,7 @@ impl Config { .with_context(|| { format!( "failed to create context from file {:?}", - account_config.dbfile() + account_config.dbfile(dir) ) })?; @@ -396,12 +409,12 @@ impl Config { Ok(accounts) } - /// Create a new account in the given root directory. - async fn new_account(&mut self, dir: &Path) -> Result { + /// Creates a new account in the account manager directory. + async fn new_account(&mut self) -> Result { let id = { let id = self.inner.next_id; let uuid = Uuid::new_v4(); - let target_dir = dir.join(uuid.to_string()); + let target_dir = PathBuf::from(uuid.to_string()); self.inner.accounts.push(AccountConfig { id, @@ -473,14 +486,16 @@ struct AccountConfig { /// Unique id. pub id: u32, /// Root directory for all data for this account. + /// + /// The path is relative to the account manager directory. pub dir: std::path::PathBuf, pub uuid: Uuid, } impl AccountConfig { /// Get the canoncial dbfile name for this configuration. - pub fn dbfile(&self) -> std::path::PathBuf { - self.dir.join(DB_NAME) + pub fn dbfile(&self, accounts_dir: &Path) -> std::path::PathBuf { + accounts_dir.join(&self.dir).join(DB_NAME) } }