Make things work. Next: Tests, make sure that primary transport is published

This commit is contained in:
Hocuri
2026-03-16 14:41:35 +01:00
parent acffad37ef
commit 57529407e3
6 changed files with 85 additions and 27 deletions

View File

@@ -6784,8 +6784,8 @@ void dc_event_unref(dc_event_t* event);
* UI should update the list. * UI should update the list.
* *
* The event is emitted when the transports are modified on another device * 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` * using the JSON-RPC calls `add_or_update_transport`, `add_transport_from_qr`, `delete_transport`,
* or `set_config(configured_addr)`. * `set_transport_unpublished` or `set_config(configured_addr)`.
*/ */
#define DC_EVENT_TRANSPORTS_MODIFIED 2600 #define DC_EVENT_TRANSPORTS_MODIFIED 2600

View File

@@ -528,6 +528,7 @@ impl CommandApi {
/// from a server encoded in a QR code. /// from a server encoded in a QR code.
/// - [Self::list_transports()] to get a list of all configured transports. /// - [Self::list_transports()] to get a list of all configured transports.
/// - [Self::delete_transport()] to remove a transport. /// - [Self::delete_transport()] to remove a transport.
/// - [Self::set_transport_unpublished()] to set whether contacts see this transport.
async fn add_or_update_transport( async fn add_or_update_transport(
&self, &self,
account_id: u32, account_id: u32,
@@ -571,8 +572,16 @@ impl CommandApi {
ctx.delete_transport(&addr).await ctx.delete_transport(&addr).await
} }
// TODO not sure if that's a good API design, /// Change whether the transport is unpublished.
// but I'm also not sure about an alternative - Adding to EnteredLoginParam? ///
/// 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( async fn set_transport_unpublished(
&self, &self,
account_id: u32, account_id: u32,
@@ -580,19 +589,14 @@ impl CommandApi {
unpublished: bool, unpublished: bool,
) -> Result<()> { ) -> Result<()> {
let ctx = self.get_context(account_id).await?; 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, /// Check whether the transport is unpublished.
// but I'm also not sure about an alternative - Adding to EnteredLoginParam? /// See [`Self::set_transport_unpublished`] / `setTransportUnpublished` for details.
async fn get_transport_unpublished( async fn is_transport_unpublished(&self, account_id: u32, addr: String) -> Result<bool> {
&self,
account_id: u32,
addr: String,
unpublished: bool,
) -> Result<()> {
let ctx = self.get_context(account_id).await?; 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. /// Signal an ongoing process to stop.

View File

@@ -110,6 +110,7 @@ impl Context {
/// from a server encoded in a QR code. /// from a server encoded in a QR code.
/// - [Self::list_transports()] to get a list of all configured transports. /// - [Self::list_transports()] to get a list of all configured transports.
/// - [Self::delete_transport()] to remove a transport. /// - [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<()> { pub async fn add_or_update_transport(&self, param: &mut EnteredLoginParam) -> Result<()> {
self.stop_io().await; self.stop_io().await;
let result = self.add_transport_inner(param).await; let result = self.add_transport_inner(param).await;
@@ -261,6 +262,38 @@ impl Context {
Ok(()) 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<bool> {
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<()> { async fn inner_configure(&self, param: &EnteredLoginParam) -> Result<()> {
info!(self, "Configure ..."); info!(self, "Configure ...");
@@ -627,7 +660,7 @@ async fn configure(ctx: &Context, param: &EnteredLoginParam) -> Result<Option<&'
let provider = configured_param.provider; let provider = configured_param.provider;
configured_param configured_param
.clone() .clone()
.save_to_transports_table(ctx, param, time()) .save_to_transports_table(ctx, param, time(), true)
.await?; .await?;
send_sync_transports(ctx).await?; send_sync_transports(ctx).await?;

View File

@@ -2343,10 +2343,12 @@ ALTER TABLE contacts ADD COLUMN name_normalized TEXT;
.await?; .await?;
} }
// Add a `published` flag to transports. // Add an `is_published` flag to transports.
// Unpublished transports are not advertised to contacts, // Unpublished transports are not advertised to contacts,
// and self-sent messages are not sent there. // and self-sent messages are not sent there,
// The default is published, but when updating, // 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, // existing secondary transports are set to unpublished,
// so that an existing transport address doesn't suddenly get spammed with a lot of messages. // so that an existing transport address doesn't suddenly get spammed with a lot of messages.
inc_and_check(&mut migration_version, 149)?; inc_and_check(&mut migration_version, 149)?;

View File

@@ -65,6 +65,10 @@ pub(crate) struct TransportData {
/// Timestamp of when the transport was last time (re)configured. /// Timestamp of when the transport was last time (re)configured.
pub(crate) timestamp: i64, pub(crate) timestamp: i64,
/// Whether the transport is published.
/// See [`Context::set_transport_unpublished`] for details.
pub(crate) is_published: bool,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]

View File

@@ -561,8 +561,16 @@ impl ConfiguredLoginParam {
context: &Context, context: &Context,
entered_param: &EnteredLoginParam, entered_param: &EnteredLoginParam,
timestamp: i64, timestamp: i64,
is_published: bool,
) -> Result<()> { ) -> Result<()> {
save_transport(context, entered_param, &self.into(), timestamp).await?; save_transport(
context,
entered_param,
&self.into(),
timestamp,
is_published,
)
.await?;
Ok(()) Ok(())
} }
@@ -628,6 +636,7 @@ pub(crate) async fn save_transport(
entered_param: &EnteredLoginParam, entered_param: &EnteredLoginParam,
configured: &ConfiguredLoginParamJson, configured: &ConfiguredLoginParamJson,
add_timestamp: i64, add_timestamp: i64,
is_published: bool,
) -> Result<bool> { ) -> Result<bool> {
let addr = addr_normalize(&configured.addr); let addr = addr_normalize(&configured.addr);
let configured_addr = context.get_config(Config::ConfiguredAddr).await?; let configured_addr = context.get_config(Config::ConfiguredAddr).await?;
@@ -635,20 +644,23 @@ pub(crate) async fn save_transport(
let mut modified = context let mut modified = context
.sql .sql
.execute( .execute(
"INSERT INTO transports (addr, entered_param, configured_param, add_timestamp) "INSERT INTO transports (addr, entered_param, configured_param, add_timestamp, is_published)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?, ?, ?)
ON CONFLICT (addr) ON CONFLICT (addr)
DO UPDATE SET entered_param=excluded.entered_param, DO UPDATE SET entered_param=excluded.entered_param,
configured_param=excluded.configured_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 WHERE entered_param != excluded.entered_param
OR configured_param != excluded.configured_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, &addr,
serde_json::to_string(entered_param)?, serde_json::to_string(entered_param)?,
serde_json::to_string(configured)?, serde_json::to_string(configured)?,
add_timestamp, add_timestamp,
is_published,
), ),
) )
.await? .await?
@@ -685,7 +697,7 @@ pub(crate) async fn send_sync_transports(context: &Context) -> Result<()> {
let transports = context let transports = context
.sql .sql
.query_map_vec( .query_map_vec(
"SELECT entered_param, configured_param, add_timestamp "SELECT entered_param, configured_param, add_timestamp, is_published
FROM transports WHERE id>1", FROM transports WHERE id>1",
(), (),
|row| { |row| {
@@ -694,10 +706,12 @@ pub(crate) async fn send_sync_transports(context: &Context) -> Result<()> {
let configured_json: String = row.get(1)?; let configured_json: String = row.get(1)?;
let configured: ConfiguredLoginParamJson = serde_json::from_str(&configured_json)?; let configured: ConfiguredLoginParamJson = serde_json::from_str(&configured_json)?;
let timestamp: i64 = row.get(2)?; let timestamp: i64 = row.get(2)?;
let is_published: bool = row.get(3)?;
Ok(TransportData { Ok(TransportData {
configured, configured,
entered, entered,
timestamp, timestamp,
is_published,
}) })
}, },
) )
@@ -736,9 +750,10 @@ pub(crate) async fn sync_transports(
configured, configured,
entered, entered,
timestamp, timestamp,
is_published,
} in transports } in transports
{ {
modified |= save_transport(context, entered, configured, *timestamp).await?; modified |= save_transport(context, entered, configured, *timestamp, *is_published).await?;
} }
context context
@@ -843,7 +858,7 @@ mod tests {
param param
.clone() .clone()
.save_to_transports_table(&t, &EnteredLoginParam::default(), time()) .save_to_transports_table(&t, &EnteredLoginParam::default(), time(), true)
.await?; .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}"#; 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!( assert_eq!(
@@ -1061,7 +1076,7 @@ mod tests {
certificate_checks: ConfiguredCertificateChecks::Automatic, certificate_checks: ConfiguredCertificateChecks::Automatic,
oauth2: false, oauth2: false,
} }
.save_to_transports_table(&t, &EnteredLoginParam::default(), time()) .save_to_transports_table(&t, &EnteredLoginParam::default(), time(), true)
.await?; .await?;
let (_transport_id, loaded) = ConfiguredLoginParam::load(&t).await?.unwrap(); let (_transport_id, loaded) = ConfiguredLoginParam::load(&t).await?.unwrap();