diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 9085a616..be66012c 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -45,6 +45,13 @@ in ''; default = "::1"; }; + global.conduit_compat = lib.mkOption { + type = types.bool; + description = '' + Whether to operate as a drop-in replacement for Conduit. + ''; + default = false; + }; global.database_path = lib.mkOption { type = types.nonEmptyStr; readOnly = true; @@ -54,7 +61,9 @@ in Note that this is read-only because this module makes use of systemd's `StateDirectory` option. ''; - default = "/var/lib/grapevine"; + default = if cfg.settings.global.conduit_compat + then "/var/lib/matrix-conduit" + else "/var/lib/grapevine"; }; global.port = lib.mkOption { type = types.port; @@ -104,12 +113,16 @@ in RestrictNamespaces = true; RestrictRealtime = true; StartLimitBurst = 5; - StateDirectory = "grapevine"; + StateDirectory = if cfg.settings.global.conduit_compat + then "matrix-conduit" + else "grapevine"; StateDirectoryMode = "0700"; SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" "~@privileged" ]; UMask = "077"; - User = "grapevine"; + User = if cfg.settings.global.conduit_compat + then "conduit" + else "grapevine"; }; }; }; diff --git a/src/config.rs b/src/config.rs index b42f5f57..2313b92f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,6 +16,8 @@ use self::proxy::ProxyConfig; #[allow(clippy::struct_excessive_bools)] #[derive(Clone, Debug, Deserialize)] pub(crate) struct Config { + #[serde(default = "false_fn")] + pub(crate) conduit_compat: bool, #[serde(default = "default_address")] pub(crate) address: IpAddr, #[serde(default = "default_port")] diff --git a/src/database.rs b/src/database.rs index 1410ca27..faddb9f9 100644 --- a/src/database.rs +++ b/src/database.rs @@ -176,7 +176,16 @@ impl KeyValueDatabase { fn check_db_setup(config: &Config) -> Result<()> { let path = Path::new(&config.database_path); - let sqlite_exists = path.join("grapevine.db").exists(); + let sqlite_exists = path + .join(format!( + "{}.db", + if config.conduit_compat { + "conduit" + } else { + "grapevine" + } + )) + .exists(); let rocksdb_exists = path.join("IDENTITY").exists(); let mut count = 0; @@ -401,9 +410,15 @@ impl KeyValueDatabase { // Matrix resource ownership is based on the server name; changing it // requires recreating the database from scratch. if services().users.count()? > 0 { - let grapevine_user = - UserId::parse_with_server_name("grapevine", services().globals.server_name()) - .expect("@grapevine:server_name is valid"); + let grapevine_user = UserId::parse_with_server_name( + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + services().globals.server_name(), + ) + .expect("admin bot username should be valid"); if !services().users.exists(&grapevine_user)? { error!( diff --git a/src/database/abstraction/sqlite.rs b/src/database/abstraction/sqlite.rs index a463cb61..495f6193 100644 --- a/src/database/abstraction/sqlite.rs +++ b/src/database/abstraction/sqlite.rs @@ -97,7 +97,14 @@ impl Engine { impl KeyValueDatabaseEngine for Arc { fn open(config: &Config) -> Result { - let path = Path::new(&config.database_path).join("grapevine.db"); + let path = Path::new(&config.database_path).join(format!( + "{}.db", + if config.conduit_compat { + "conduit" + } else { + "grapevine" + } + )); // calculates cache-size per permanent connection // 1. convert MB to KiB diff --git a/src/service/admin.rs b/src/service/admin.rs index 3d10be63..bdfc6a9c 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -212,9 +212,16 @@ impl Service { // TODO: Use futures when we have long admin commands //let mut futures = FuturesUnordered::new(); - let grapevine_user = - UserId::parse(format!("@grapevine:{}", services().globals.server_name())) - .expect("@grapevine:server_name is valid"); + let grapevine_user = UserId::parse(format!( + "@{}:{}", + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + services().globals.server_name() + )) + .expect("admin bot username should be valid"); if let Ok(Some(grapevine_room)) = services().admin.get_admin_room() { loop { @@ -568,7 +575,11 @@ impl Service { if !services().users.exists(&user_id)? || user_id == UserId::parse_with_server_name( - "grapevine", + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, services().globals.server_name(), ) .expect("grapevine user exists") @@ -866,9 +877,15 @@ impl Service { // Utility to turn clap's `--help` text to HTML. fn usage_to_html(text: &str, server_name: &ServerName) -> String { // Replace `@grapevine:servername:-subcmdname` with `@grapevine:servername: subcmdname` + let localpart = if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }; + let text = text.replace( - &format!("@grapevine:{server_name}:-"), - &format!("@grapevine:{server_name}: "), + &format!("@{localpart}:{server_name}:-"), + &format!("@{localpart}:{server_name}: "), ); // For the grapevine admin room, subcommands become main commands @@ -952,9 +969,16 @@ impl Service { let state_lock = mutex_state.lock().await; // Create a user for the server - let grapevine_user = - UserId::parse_with_server_name("grapevine", services().globals.server_name()) - .expect("@grapevine:server_name is valid"); + let grapevine_user = UserId::parse(format!( + "@{}:{}", + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + services().globals.server_name() + )) + .expect("admin bot username should be valid"); services().users.create(&grapevine_user, None)?; @@ -1218,9 +1242,15 @@ impl Service { let state_lock = mutex_state.lock().await; // Use the server user to grant the new admin's power level - let grapevine_user = - UserId::parse_with_server_name("grapevine", services().globals.server_name()) - .expect("@grapevine:server_name is valid"); + let grapevine_user = UserId::parse_with_server_name( + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + services().globals.server_name(), + ) + .expect("admin bot username should be valid"); // Invite and join the real user services() diff --git a/src/service/rooms/timeline.rs b/src/service/rooms/timeline.rs index 1c57369d..42b8d113 100644 --- a/src/service/rooms/timeline.rs +++ b/src/service/rooms/timeline.rs @@ -483,7 +483,15 @@ impl Service { .search .index_pdu(shortroomid, &pdu_id, &body)?; - let server_user = format!("@grapevine:{}", services().globals.server_name()); + let server_user = format!( + "@{}:{}", + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + services().globals.server_name() + ); let to_grapevine = body.starts_with(&format!("{server_user}: ")) || body.starts_with(&format!("{server_user} ")) @@ -822,7 +830,14 @@ impl Service { .filter(|v| v.starts_with('@')) .unwrap_or(sender.as_str()); let server_name = services().globals.server_name(); - let server_user = format!("@grapevine:{server_name}"); + let server_user = format!( + "@{}:{server_name}", + if services().globals.config.conduit_compat { + "conduit" + } else { + "grapevine" + }, + ); let content = serde_json::from_str::(pdu.content.get()) .map_err(|_| Error::bad_database("Invalid content in pdu."))?;