feat: Move primary transport checked update into "remove transports" transaction

This guarantees that the primary transport is never removed while being in use. At least this way
it's obvious that there are no such corner cases.
This commit is contained in:
iequidoo
2026-01-15 03:36:01 -03:00
parent c3a6e48882
commit 2fd88890e6
3 changed files with 51 additions and 45 deletions

View File

@@ -812,46 +812,12 @@ pub(crate) async fn receive_imf_inner(
if from_id == ContactId::SELF {
if mime_parser.was_encrypted() {
context
.execute_sync_items(sync_items, mime_parser.timestamp_sent)
.execute_sync_items(
sync_items,
mime_parser.timestamp_sent,
&mime_parser.from.addr,
)
.await;
// Receiving encrypted message from self updates primary transport.
let from_addr = &mime_parser.from.addr;
let transport_changed = context
.sql
.transaction(|transaction| {
let transport_exists = transaction.query_row(
"SELECT COUNT(*) FROM transports WHERE addr=?",
(from_addr,),
|row| {
let count: i64 = row.get(0)?;
Ok(count > 0)
},
)?;
let transport_changed = if transport_exists {
transaction.execute(
"
UPDATE config SET value=? WHERE keyname='configured_addr' AND value!=?1
",
(from_addr,),
)? > 0
} else {
warn!(
context,
"Received sync message from unknown address {from_addr:?}."
);
false
};
Ok(transport_changed)
})
.await?;
if transport_changed {
info!(context, "Primary transport changed to {from_addr:?}.");
context.sql.uncache_raw_config("configured_addr").await;
context.emit_event(EventType::TransportsModified);
}
} else {
warn!(context, "Sync items are not encrypted.");
}

View File

@@ -303,7 +303,12 @@ impl Context {
/// If an error is returned, the caller shall not try over because some sync items could be
/// already executed. Sync items are considered independent and executed in the given order but
/// regardless of whether executing of the previous items succeeded.
pub(crate) async fn execute_sync_items(&self, items: &SyncItems, timestamp_sent: i64) {
pub(crate) async fn execute_sync_items(
&self,
items: &SyncItems,
timestamp_sent: i64,
from: &str,
) {
info!(self, "executing {} sync item(s)", items.items.len());
for item in &items.items {
// Limit the timestamp to ensure it is not in the future.
@@ -323,7 +328,7 @@ impl Context {
SyncData::Transports {
transports,
removed_transports,
} => sync_transports(self, transports, removed_transports).await,
} => sync_transports(self, from, transports, removed_transports).await,
},
SyncDataOrUnknown::Unknown(data) => {
warn!(self, "Ignored unknown sync item: {data}.");
@@ -632,7 +637,12 @@ mod tests {
.to_string(),
)
?;
t.execute_sync_items(&sync_items, timestamp_sent).await;
t.execute_sync_items(
&sync_items,
timestamp_sent,
&t.get_config(Config::Addr).await?.unwrap(),
)
.await;
assert!(
Contact::lookup_id_by_addr(&t, "bob@example.net", Origin::Unknown)

View File

@@ -19,6 +19,7 @@ use crate::configure::server_params::{ServerParams, expand_param_vector};
use crate::constants::{DC_LP_AUTH_FLAGS, DC_LP_AUTH_OAUTH2};
use crate::context::Context;
use crate::events::EventType;
use crate::log::warn;
use crate::login_param::EnteredLoginParam;
use crate::net::load_connection_timestamp;
use crate::provider::{Protocol, Provider, Socket, UsernamePattern, get_provider_by_id};
@@ -724,6 +725,7 @@ pub(crate) async fn send_sync_transports(context: &Context) -> Result<()> {
/// Process received data for transport synchronization.
pub(crate) async fn sync_transports(
context: &Context,
from_addr: &str,
transports: &[TransportData],
removed_transports: &[RemovedTransportData],
) -> Result<()> {
@@ -737,7 +739,7 @@ pub(crate) async fn sync_transports(
modified |= save_transport(context, entered, configured, *timestamp).await?;
}
context
let primary_changed = context
.sql
.transaction(|transaction| {
for RemovedTransportData { addr, timestamp } in removed_transports {
@@ -755,13 +757,41 @@ pub(crate) async fn sync_transports(
(addr, timestamp),
)?;
}
Ok(())
let transport_exists = transaction.query_row(
"SELECT COUNT(*) FROM transports WHERE addr=?",
(from_addr,),
|row| {
let count: i64 = row.get(0)?;
Ok(count > 0)
},
)?;
let primary_changed = if transport_exists {
transaction.execute(
"
UPDATE config SET value=? WHERE keyname='configured_addr' AND value!=?1
",
(from_addr,),
)? > 0
} else {
warn!(
context,
"Received sync message from unknown address {from_addr:?}."
);
false
};
Ok(primary_changed)
})
.await?;
if modified {
if modified || primary_changed {
context.emit_event(EventType::TransportsModified);
}
if primary_changed {
info!(context, "Primary transport changed to {from_addr:?}.");
context.sql.uncache_raw_config("configured_addr").await;
}
Ok(())
}