Skip to content

Commit 5630c6e

Browse files
committed
fix: Add Chat-Group-Name-Timestamp header and use it to update group names (#6412)
Add "Chat-Group-Name-Timestamp" message header and use the last-write-wins logic when updating group names (similar to group member timestamps). Note that if the "Chat-Group-Name-Changed" header is absent though, we don't add a system message (`MsgGrpNameChangedBy`) because we don't want to blame anyone.
1 parent 9f67d0f commit 5630c6e

File tree

5 files changed

+93
-14
lines changed

5 files changed

+93
-14
lines changed

src/headerdef.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ pub enum HeaderDef {
5757
ChatGroupId,
5858
ChatGroupName,
5959
ChatGroupNameChanged,
60+
ChatGroupNameTimestamp,
6061
ChatVerified,
6162
ChatGroupAvatar,
6263
ChatUserAvatar,

src/mimefactory.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1149,7 +1149,7 @@ impl MimeFactory {
11491149
let Loaded::Message { chat, msg } = &self.loaded else {
11501150
bail!("Attempt to render MDN as a message");
11511151
};
1152-
let chat = chat.clone();
1152+
let mut chat = chat.clone();
11531153
let msg = msg.clone();
11541154
let command = msg.param.get_cmd();
11551155
let mut placeholdertext = None;
@@ -1181,6 +1181,23 @@ impl MimeFactory {
11811181
"Chat-Group-Name",
11821182
mail_builder::headers::text::Text::new(chat.name.to_string()).into(),
11831183
));
1184+
if chat.param.get_i64(Param::GroupNameTimestamp).is_none()
1185+
|| command == SystemMessage::GroupNameChanged
1186+
{
1187+
chat.param
1188+
.set_i64(Param::GroupNameTimestamp, self.timestamp);
1189+
chat.update_param(context).await?;
1190+
}
1191+
headers.push((
1192+
"Chat-Group-Name-Timestamp",
1193+
mail_builder::headers::text::Text::new(
1194+
chat.param
1195+
.get_i64(Param::GroupNameTimestamp)
1196+
.unwrap_or(0)
1197+
.to_string(),
1198+
)
1199+
.into(),
1200+
));
11841201

11851202
match command {
11861203
SystemMessage::MemberRemovedFromGroup => {

src/mimeparser.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ impl MimeMessage {
445445
HeaderDef::ChatGroupId,
446446
HeaderDef::ChatGroupName,
447447
HeaderDef::ChatGroupNameChanged,
448+
HeaderDef::ChatGroupNameTimestamp,
448449
HeaderDef::ChatGroupAvatar,
449450
HeaderDef::ChatGroupMemberRemoved,
450451
HeaderDef::ChatGroupMemberAdded,

src/receive_imf.rs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2104,14 +2104,16 @@ async fn create_group(
21042104
// serialization/deserialization #3650" issue. DC itself never creates group names with
21052105
// leading/trailing whitespace.
21062106
.trim();
2107+
let mut param = Params::new();
2108+
param.set_i64(Param::GroupNameTimestamp, mime_parser.timestamp_sent);
21072109
let new_chat_id = ChatId::create_multiuser_record(
21082110
context,
21092111
Chattype::Group,
21102112
grpid,
21112113
grpname,
21122114
create_blocked,
21132115
create_protected,
2114-
None,
2116+
Some(param.to_string()),
21152117
mime_parser.timestamp_sent,
21162118
)
21172119
.await
@@ -2332,9 +2334,18 @@ async fn apply_group_changes(
23322334
}
23332335

23342336
better_msg = Some(stock_str::msg_add_member_local(context, added_addr, from_id).await);
2335-
} else if let Some(old_name) = mime_parser
2337+
}
2338+
2339+
let group_name_timestamp = mime_parser
2340+
.get_header(HeaderDef::ChatGroupNameTimestamp)
2341+
.and_then(|s| s.parse::<i64>().ok());
2342+
if let Some(old_name) = mime_parser
23362343
.get_header(HeaderDef::ChatGroupNameChanged)
23372344
.map(|s| s.trim())
2345+
.or(match group_name_timestamp {
2346+
Some(_) => Some(chat.name.as_str()),
2347+
None => None,
2348+
})
23382349
{
23392350
if let Some(grpname) = mime_parser
23402351
.get_header(HeaderDef::ChatGroupName)
@@ -2343,13 +2354,15 @@ async fn apply_group_changes(
23432354
{
23442355
let grpname = &sanitize_single_line(grpname);
23452356
let old_name = &sanitize_single_line(old_name);
2346-
if chat_id
2347-
.update_timestamp(
2348-
context,
2349-
Param::GroupNameTimestamp,
2350-
mime_parser.timestamp_sent,
2351-
)
2352-
.await?
2357+
2358+
let chat_group_name_timestamp = chat.param.get_i64(Param::GroupNameTimestamp);
2359+
let group_name_timestamp = group_name_timestamp.unwrap_or(mime_parser.timestamp_sent);
2360+
// NB: For old chats (created before "Chat-Group-Name-Timestamp" header introduction)
2361+
// this may cause an extra name update and `ChatModified` event, but that's fine.
2362+
if chat_group_name_timestamp < Some(group_name_timestamp)
2363+
&& chat_id
2364+
.update_timestamp(context, Param::GroupNameTimestamp, group_name_timestamp)
2365+
.await?
23532366
{
23542367
info!(context, "Updating grpname for chat {chat_id}.");
23552368
context
@@ -2358,10 +2371,18 @@ async fn apply_group_changes(
23582371
.await?;
23592372
send_event_chat_modified = true;
23602373
}
2361-
2362-
better_msg = Some(stock_str::msg_grp_name(context, old_name, grpname, from_id).await);
2374+
if mime_parser
2375+
.get_header(HeaderDef::ChatGroupNameChanged)
2376+
.is_some()
2377+
{
2378+
better_msg.get_or_insert(
2379+
stock_str::msg_grp_name(context, old_name, grpname, from_id).await,
2380+
);
2381+
}
23632382
}
2364-
} else if let Some(value) = mime_parser.get_header(HeaderDef::ChatContent) {
2383+
}
2384+
2385+
if let (Some(value), None) = (mime_parser.get_header(HeaderDef::ChatContent), &better_msg) {
23652386
if value == "group-avatar-changed" {
23662387
if let Some(avatar_action) = &mime_parser.group_avatar {
23672388
// this is just an explicit message containing the group-avatar,
@@ -2837,14 +2858,16 @@ async fn create_adhoc_group(
28372858
return Ok(None);
28382859
}
28392860

2861+
let mut param = Params::new();
2862+
param.set_i64(Param::GroupNameTimestamp, mime_parser.timestamp_sent);
28402863
let new_chat_id: ChatId = ChatId::create_multiuser_record(
28412864
context,
28422865
Chattype::Group,
28432866
"", // Ad hoc groups have no ID.
28442867
grpname,
28452868
create_blocked,
28462869
ProtectionStatus::Unprotected,
2847-
None,
2870+
Some(param.to_string()),
28482871
mime_parser.timestamp_sent,
28492872
)
28502873
.await?;

src/receive_imf/receive_imf_tests.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5425,6 +5425,43 @@ Hello!"
54255425
Ok(())
54265426
}
54275427

5428+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
5429+
async fn test_rename_chat_on_missing_message() -> Result<()> {
5430+
let alice = TestContext::new_alice().await;
5431+
let bob = TestContext::new_bob().await;
5432+
let chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "Group").await?;
5433+
add_to_chat_contacts_table(
5434+
&alice,
5435+
time(),
5436+
chat_id,
5437+
&[Contact::create(&alice, "bob", "[email protected]").await?],
5438+
)
5439+
.await?;
5440+
send_text_msg(&alice, chat_id, "populate".to_string()).await?;
5441+
let bob_chat_id = bob.recv_msg(&alice.pop_sent_msg().await).await.chat_id;
5442+
bob_chat_id.accept(&bob).await?;
5443+
5444+
// Bob changes the group name.
5445+
// TODO: If Bob does this too fast, it's not guaranteed that his group name wins because "Date"
5446+
// of his message may be less than one of Alice's message, and setting "Group-Name-Timestamp" to
5447+
// a value greater than "Date" doesn't look as a good solution. This should be fixed by
5448+
// adjusting "Date" on the replier side (limited to a few seconds).
5449+
SystemTime::shift(Duration::from_secs(3600));
5450+
chat::set_chat_name(&bob, bob_chat_id, "Renamed").await?;
5451+
bob.pop_sent_msg().await;
5452+
5453+
// Bob adds a new member.
5454+
let bob_orange = Contact::create(&bob, "orange", "[email protected]").await?;
5455+
add_contact_to_chat(&bob, bob_chat_id, bob_orange).await?;
5456+
let add_msg = bob.pop_sent_msg().await;
5457+
5458+
// Alice only receives the member addition.
5459+
alice.recv_msg(&add_msg).await;
5460+
let chat = Chat::load_from_db(&alice, chat_id).await?;
5461+
assert_eq!(chat.get_name(), "Renamed");
5462+
Ok(())
5463+
}
5464+
54285465
/// Tests that creating a group
54295466
/// is preferred over assigning message to existing
54305467
/// chat based on `In-Reply-To` and `References`.

0 commit comments

Comments
 (0)