diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index abc3f0ef0..759ad65ff 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -6784,8 +6784,8 @@ void dc_event_unref(dc_event_t* event); * UI should update the list. * * The event is emitted when the transports are modified on another device - * using the JSON-RPC calls `add_or_update_transport`, `add_transport_from_qr`, `delete_transport` - * or `set_config(configured_addr)`. + * using the JSON-RPC calls `add_or_update_transport`, `add_transport_from_qr`, `delete_transport`, + * `set_transport_unpublished` or `set_config(configured_addr)`. */ #define DC_EVENT_TRANSPORTS_MODIFIED 2600 diff --git a/deltachat-jsonrpc/src/api.rs b/deltachat-jsonrpc/src/api.rs index a1cc8699a..a07df3530 100644 --- a/deltachat-jsonrpc/src/api.rs +++ b/deltachat-jsonrpc/src/api.rs @@ -528,6 +528,7 @@ impl CommandApi { /// from a server encoded in a QR code. /// - [Self::list_transports()] to get a list of all configured transports. /// - [Self::delete_transport()] to remove a transport. + /// - [Self::set_transport_unpublished()] to set whether contacts see this transport. async fn add_or_update_transport( &self, account_id: u32, @@ -571,8 +572,16 @@ impl CommandApi { ctx.delete_transport(&addr).await } - // TODO not sure if that's a good API design, - // but I'm also not sure about an alternative - Adding to EnteredLoginParam? + /// Change whether the transport is unpublished. + /// + /// Unpublished transports are not advertised to contacts, + /// and self-sent messages are not sent there, + /// so that we don't cause extra messages to the corresponding inbox, + /// but can still receive messages from contacts who don't know the new relay addresses yet. + /// + /// The default is true, but when updating, + /// existing secondary transports are set to unpublished, + /// so that an existing transport address doesn't suddenly get spammed with a lot of messages. async fn set_transport_unpublished( &self, account_id: u32, @@ -580,19 +589,14 @@ impl CommandApi { unpublished: bool, ) -> Result<()> { let ctx = self.get_context(account_id).await?; - ctx.set_transport_unpublished(addr, unpublished).await + ctx.set_transport_unpublished(&addr, unpublished).await } - // TODO not sure if that's a good API design, - // but I'm also not sure about an alternative - Adding to EnteredLoginParam? - async fn get_transport_unpublished( - &self, - account_id: u32, - addr: String, - unpublished: bool, - ) -> Result<()> { + /// Check whether the transport is unpublished. + /// See [`Self::set_transport_unpublished`] / `setTransportUnpublished` for details. + async fn is_transport_unpublished(&self, account_id: u32, addr: String) -> Result { let ctx = self.get_context(account_id).await?; - ctx.get_transport_unpublished(addr, unpublished).await + ctx.is_transport_unpublished(&addr).await } /// Signal an ongoing process to stop. diff --git a/src/configure.rs b/src/configure.rs index 099f1ecac..582ae2349 100644 --- a/src/configure.rs +++ b/src/configure.rs @@ -110,6 +110,7 @@ impl Context { /// from a server encoded in a QR code. /// - [Self::list_transports()] to get a list of all configured transports. /// - [Self::delete_transport()] to remove a transport. + /// - [Self::set_transport_unpublished()] to set whether contacts see this transport. pub async fn add_or_update_transport(&self, param: &mut EnteredLoginParam) -> Result<()> { self.stop_io().await; let result = self.add_transport_inner(param).await; @@ -261,6 +262,38 @@ impl Context { Ok(()) } + /// Change whether the transport is unpublished. + /// + /// Unpublished transports are not advertised to contacts, + /// and self-sent messages are not sent there, + /// so that we don't cause extra messages to the corresponding inbox, + /// but can still receive messages from contacts who don't know the new relay addresses yet. + /// + /// The default is true, but when updating, + /// existing secondary transports are set to unpublished, + /// so that an existing transport address doesn't suddenly get spammed with a lot of messages. + pub async fn set_transport_unpublished(&self, addr: &str, unpublished: bool) -> Result<()> { + self.sql + .execute( + "UPDATE transports SET is_published=? WHERE addr=?", + (!unpublished, addr), + ) + .await?; + send_sync_transports(self).await?; + Ok(()) + } + + /// Check whether the transport is unpublished. + /// See [`Self::set_transport_unpublished`] for details. + pub async fn is_transport_unpublished(&self, addr: &str) -> Result { + let published: bool = self + .sql + .query_get_value("SELECT is_published FROM transports WHERE addr=?", (addr,)) + .await? + .context("is_published is not supposed to be NULL")?; + Ok(!published) + } + async fn inner_configure(&self, param: &EnteredLoginParam) -> Result<()> { info!(self, "Configure ..."); @@ -627,7 +660,7 @@ async fn configure(ctx: &Context, param: &EnteredLoginParam) -> Result Result<()> { - save_transport(context, entered_param, &self.into(), timestamp).await?; + save_transport( + context, + entered_param, + &self.into(), + timestamp, + is_published, + ) + .await?; Ok(()) } @@ -628,6 +636,7 @@ pub(crate) async fn save_transport( entered_param: &EnteredLoginParam, configured: &ConfiguredLoginParamJson, add_timestamp: i64, + is_published: bool, ) -> Result { let addr = addr_normalize(&configured.addr); let configured_addr = context.get_config(Config::ConfiguredAddr).await?; @@ -635,20 +644,23 @@ pub(crate) async fn save_transport( let mut modified = context .sql .execute( - "INSERT INTO transports (addr, entered_param, configured_param, add_timestamp) - VALUES (?, ?, ?, ?) + "INSERT INTO transports (addr, entered_param, configured_param, add_timestamp, is_published) + VALUES (?, ?, ?, ?, ?) ON CONFLICT (addr) DO UPDATE SET entered_param=excluded.entered_param, configured_param=excluded.configured_param, - add_timestamp=excluded.add_timestamp + add_timestamp=excluded.add_timestamp, + is_published=excluded.is_published, WHERE entered_param != excluded.entered_param OR configured_param != excluded.configured_param - OR add_timestamp < excluded.add_timestamp", + OR add_timestamp < excluded.add_timestamp + OR is_published != excluded.is_published", ( &addr, serde_json::to_string(entered_param)?, serde_json::to_string(configured)?, add_timestamp, + is_published, ), ) .await? @@ -685,7 +697,7 @@ pub(crate) async fn send_sync_transports(context: &Context) -> Result<()> { let transports = context .sql .query_map_vec( - "SELECT entered_param, configured_param, add_timestamp + "SELECT entered_param, configured_param, add_timestamp, is_published FROM transports WHERE id>1", (), |row| { @@ -694,10 +706,12 @@ pub(crate) async fn send_sync_transports(context: &Context) -> Result<()> { let configured_json: String = row.get(1)?; let configured: ConfiguredLoginParamJson = serde_json::from_str(&configured_json)?; let timestamp: i64 = row.get(2)?; + let is_published: bool = row.get(3)?; Ok(TransportData { configured, entered, timestamp, + is_published, }) }, ) @@ -736,9 +750,10 @@ pub(crate) async fn sync_transports( configured, entered, timestamp, + is_published, } in transports { - modified |= save_transport(context, entered, configured, *timestamp).await?; + modified |= save_transport(context, entered, configured, *timestamp, *is_published).await?; } context @@ -843,7 +858,7 @@ mod tests { param .clone() - .save_to_transports_table(&t, &EnteredLoginParam::default(), time()) + .save_to_transports_table(&t, &EnteredLoginParam::default(), time(), true) .await?; let expected_param = r#"{"addr":"alice@example.org","imap":[{"connection":{"host":"imap.example.com","port":123,"security":"Starttls"},"user":"alice"}],"imap_user":"","imap_password":"foo","smtp":[{"connection":{"host":"smtp.example.com","port":456,"security":"Tls"},"user":"alice@example.org"}],"smtp_user":"","smtp_password":"bar","provider_id":null,"certificate_checks":"Strict","oauth2":false}"#; assert_eq!( @@ -1061,7 +1076,7 @@ mod tests { certificate_checks: ConfiguredCertificateChecks::Automatic, oauth2: false, } - .save_to_transports_table(&t, &EnteredLoginParam::default(), time()) + .save_to_transports_table(&t, &EnteredLoginParam::default(), time(), true) .await?; let (_transport_id, loaded) = ConfiguredLoginParam::load(&t).await?.unwrap();