diff --git a/src/init.rs b/src/init.rs index 3424246..c2cf603 100644 --- a/src/init.rs +++ b/src/init.rs @@ -51,6 +51,9 @@ pub enum InitError { #[error("Error restoring existing session: {0}")] RestoreSession(RestoreSessionError), + #[error("Error purging existing database: {0}")] + PurgeDatabase(std::io::Error), + #[error("Whoami sanity check failed due to an invalid access token. You may need to delete all persisted data (session and database) and start fresh")] WhoAmISanityCheckFailed, @@ -115,6 +118,21 @@ pub async fn init(init_config: &InitConfig) -> Result { }); perform_whoami_sanity_check(&client).await?; + } else { + // No session file. Let's make sure the database directory is empty too, so we can start a new session cleanly. + + if persistence_manager.has_existing_db_state_file() { + tracing::warn!( + "Found an existing database state file ({}), but no session file ({}). This may happen when a previous initialization attempt failed mid-way or if the session file was deleted subsequently. The only way to recover is to start fresh. Doing that now..", + persistence_manager.db_state_file_path().to_string_lossy(), + persistence_manager.session_file_path().to_string_lossy(), + ); + + persistence_manager.purge_database() + .map_err(InitError::PurgeDatabase)?; + + tracing::info!("The old database has been purged successfully"); + } } let client_state = if let Some(client_state) = client_state { diff --git a/src/persistence.rs b/src/persistence.rs index fbe964b..e94204e 100644 --- a/src/persistence.rs +++ b/src/persistence.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use tokio::fs; use thiserror::Error; @@ -41,8 +43,41 @@ impl Manager { } } + pub(crate) fn session_file_path(&self) -> PathBuf { + self.config.session_file_path.clone() + } + + pub(crate) fn db_state_file_path(&self) -> PathBuf { + self.config.db_dir_path.join("matrix-sdk-state.sqlite3") + } + pub(crate) fn has_existing_session(&self) -> bool { - self.config.session_file_path.exists() + self.session_file_path().exists() + } + + pub(crate) fn has_existing_db_state_file(&self) -> bool { + self.db_state_file_path().exists() + } + + pub(crate) fn purge_database(&self) -> Result<(), std::io::Error> { + let base_path = self.config.db_dir_path.clone(); + + for entry in std::fs::read_dir(base_path)? { + let entry = entry?; + let path = entry.path(); + if !path.is_file() { + continue; + } + + // Out of precaution, we'll only be deleting *.sqlite3 files + if !path.extension().map_or(false, |ext| ext == "sqlite3") { + continue; + } + + std::fs::remove_file(path)?; + } + + Ok(()) } pub(crate) async fn read_full_session(&self) -> Result {