mirror of
https://github.com/chatmail/core.git
synced 2026-05-07 08:56:30 +03:00
feat: Don't update self-{avatar,status} from received messages (#7002)
The normal way of synchronizing self-avatar and -status nowadays is sync messages.
This commit is contained in:
@@ -278,7 +278,6 @@ async fn test_sync() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sync message mustn't be sent if self-{status,avatar} is changed by a self-sent message.
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_no_sync_on_self_sent_msg() -> Result<()> {
|
async fn test_no_sync_on_self_sent_msg() -> Result<()> {
|
||||||
let mut tcm = TestContextManager::new();
|
let mut tcm = TestContextManager::new();
|
||||||
@@ -288,7 +287,7 @@ async fn test_no_sync_on_self_sent_msg() -> Result<()> {
|
|||||||
a.set_config_bool(Config::SyncMsgs, true).await?;
|
a.set_config_bool(Config::SyncMsgs, true).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = "Synced via usual message";
|
let status = "Sent via usual message";
|
||||||
alice0.set_config(Config::Selfstatus, Some(status)).await?;
|
alice0.set_config(Config::Selfstatus, Some(status)).await?;
|
||||||
alice0.send_sync_msg().await?;
|
alice0.send_sync_msg().await?;
|
||||||
alice0.pop_sent_sync_msg().await;
|
alice0.pop_sent_sync_msg().await;
|
||||||
@@ -297,7 +296,7 @@ async fn test_no_sync_on_self_sent_msg() -> Result<()> {
|
|||||||
tcm.send_recv(alice0, alice1, "hi Alice!").await;
|
tcm.send_recv(alice0, alice1, "hi Alice!").await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
alice1.get_config(Config::Selfstatus).await?,
|
alice1.get_config(Config::Selfstatus).await?,
|
||||||
Some(status.to_string())
|
Some(status1.to_string())
|
||||||
);
|
);
|
||||||
sync(alice1, alice0).await;
|
sync(alice1, alice0).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -328,7 +327,7 @@ async fn test_no_sync_on_self_sent_msg() -> Result<()> {
|
|||||||
alice1
|
alice1
|
||||||
.get_config(Config::Selfavatar)
|
.get_config(Config::Selfavatar)
|
||||||
.await?
|
.await?
|
||||||
.filter(|path| path.ends_with(".png"))
|
.filter(|path| path.ends_with(".jpg"))
|
||||||
.is_some()
|
.is_some()
|
||||||
);
|
);
|
||||||
sync(alice1, alice0).await;
|
sync(alice1, alice0).await;
|
||||||
|
|||||||
@@ -383,20 +383,16 @@ async fn import_vcard_contact(context: &Context, contact: &VcardContact) -> Resu
|
|||||||
},
|
},
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
if let Some(path) = path {
|
if let Some(path) = path
|
||||||
// Currently this value doesn't matter as we don't import the contact of self.
|
&& let Err(e) = set_profile_image(context, id, &AvatarAction::Change(path)).await
|
||||||
let was_encrypted = false;
|
{
|
||||||
if let Err(e) =
|
warn!(
|
||||||
set_profile_image(context, id, &AvatarAction::Change(path), was_encrypted).await
|
context,
|
||||||
{
|
"import_vcard_contact: Could not set avatar for {}: {e:#}.", contact.addr
|
||||||
warn!(
|
);
|
||||||
context,
|
|
||||||
"import_vcard_contact: Could not set avatar for {}: {e:#}.", contact.addr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if let Some(biography) = &contact.biography
|
if let Some(biography) = &contact.biography
|
||||||
&& let Err(e) = set_status(context, id, biography.to_owned(), false, false).await
|
&& let Err(e) = set_status(context, id, biography.to_owned()).await
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
context,
|
context,
|
||||||
@@ -1824,25 +1820,19 @@ WHERE type=? AND id IN (
|
|||||||
/// The given profile image is expected to be already in the blob directory
|
/// The given profile image is expected to be already in the blob directory
|
||||||
/// as profile images can be set only by receiving messages, this should be always the case, however.
|
/// as profile images can be set only by receiving messages, this should be always the case, however.
|
||||||
///
|
///
|
||||||
/// For contact SELF, the image is not saved in the contact-database but as Config::Selfavatar;
|
/// For contact SELF, the image is not saved in the contact-database but as Config::Selfavatar.
|
||||||
/// this typically happens if we see message with our own profile image.
|
|
||||||
pub(crate) async fn set_profile_image(
|
pub(crate) async fn set_profile_image(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
contact_id: ContactId,
|
contact_id: ContactId,
|
||||||
profile_image: &AvatarAction,
|
profile_image: &AvatarAction,
|
||||||
was_encrypted: bool,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut contact = Contact::get_by_id(context, contact_id).await?;
|
let mut contact = Contact::get_by_id(context, contact_id).await?;
|
||||||
let changed = match profile_image {
|
let changed = match profile_image {
|
||||||
AvatarAction::Change(profile_image) => {
|
AvatarAction::Change(profile_image) => {
|
||||||
if contact_id == ContactId::SELF {
|
if contact_id == ContactId::SELF {
|
||||||
if was_encrypted {
|
context
|
||||||
context
|
.set_config_ex(Nosync, Config::Selfavatar, Some(profile_image))
|
||||||
.set_config_ex(Nosync, Config::Selfavatar, Some(profile_image))
|
.await?;
|
||||||
.await?;
|
|
||||||
} else {
|
|
||||||
info!(context, "Do not use unencrypted selfavatar.");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
contact.param.set(Param::ProfileImage, profile_image);
|
contact.param.set(Param::ProfileImage, profile_image);
|
||||||
}
|
}
|
||||||
@@ -1850,13 +1840,9 @@ pub(crate) async fn set_profile_image(
|
|||||||
}
|
}
|
||||||
AvatarAction::Delete => {
|
AvatarAction::Delete => {
|
||||||
if contact_id == ContactId::SELF {
|
if contact_id == ContactId::SELF {
|
||||||
if was_encrypted {
|
context
|
||||||
context
|
.set_config_ex(Nosync, Config::Selfavatar, None)
|
||||||
.set_config_ex(Nosync, Config::Selfavatar, None)
|
.await?;
|
||||||
.await?;
|
|
||||||
} else {
|
|
||||||
info!(context, "Do not use unencrypted selfavatar deletion.");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
contact.param.remove(Param::ProfileImage);
|
contact.param.remove(Param::ProfileImage);
|
||||||
}
|
}
|
||||||
@@ -1873,22 +1859,16 @@ pub(crate) async fn set_profile_image(
|
|||||||
|
|
||||||
/// Sets contact status.
|
/// Sets contact status.
|
||||||
///
|
///
|
||||||
/// For contact SELF, the status is not saved in the contact table, but as Config::Selfstatus. This
|
/// For contact SELF, the status is not saved in the contact table, but as Config::Selfstatus.
|
||||||
/// is only done if message is sent from Delta Chat and it is encrypted, to synchronize signature
|
|
||||||
/// between Delta Chat devices.
|
|
||||||
pub(crate) async fn set_status(
|
pub(crate) async fn set_status(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
contact_id: ContactId,
|
contact_id: ContactId,
|
||||||
status: String,
|
status: String,
|
||||||
encrypted: bool,
|
|
||||||
has_chat_version: bool,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if contact_id == ContactId::SELF {
|
if contact_id == ContactId::SELF {
|
||||||
if encrypted && has_chat_version {
|
context
|
||||||
context
|
.set_config_ex(Nosync, Config::Selfstatus, Some(&status))
|
||||||
.set_config_ex(Nosync, Config::Selfstatus, Some(&status))
|
.await?;
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let mut contact = Contact::get_by_id(context, contact_id).await?;
|
let mut contact = Contact::get_by_id(context, contact_id).await?;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use super::*;
|
|||||||
use crate::chat::{Chat, get_chat_contacts, send_text_msg};
|
use crate::chat::{Chat, get_chat_contacts, send_text_msg};
|
||||||
use crate::chatlist::Chatlist;
|
use crate::chatlist::Chatlist;
|
||||||
use crate::receive_imf::receive_imf;
|
use crate::receive_imf::receive_imf;
|
||||||
use crate::test_utils::{self, TestContext, TestContextManager, TimeShiftFalsePositiveNote};
|
use crate::test_utils::{self, TestContext, TestContextManager, TimeShiftFalsePositiveNote, sync};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_contact_id_values() {
|
fn test_contact_id_values() {
|
||||||
@@ -850,8 +850,7 @@ CCCB 5AA9 F6E1 141C 9431
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests that status is synchronized when sending encrypted BCC-self messages and not
|
/// Tests that self-status is not synchronized from outgoing messages.
|
||||||
/// synchronized when the message is not encrypted.
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_synchronize_status() -> Result<()> {
|
async fn test_synchronize_status() -> Result<()> {
|
||||||
let mut tcm = TestContextManager::new();
|
let mut tcm = TestContextManager::new();
|
||||||
@@ -870,21 +869,12 @@ async fn test_synchronize_status() -> Result<()> {
|
|||||||
.await?;
|
.await?;
|
||||||
let chat = alice1.create_email_chat(bob).await;
|
let chat = alice1.create_email_chat(bob).await;
|
||||||
|
|
||||||
// Alice sends a message to Bob from the first device.
|
// Alice sends an unencrypted message to Bob from the first device.
|
||||||
send_text_msg(alice1, chat.id, "Hello".to_string()).await?;
|
send_text_msg(alice1, chat.id, "Hello".to_string()).await?;
|
||||||
let sent_msg = alice1.pop_sent_msg().await;
|
let sent_msg = alice1.pop_sent_msg().await;
|
||||||
|
|
||||||
// Message is not encrypted.
|
|
||||||
let message = sent_msg.load_from_db().await;
|
|
||||||
assert!(!message.get_showpadlock());
|
|
||||||
|
|
||||||
// Alice's second devices receives a copy of outgoing message.
|
// Alice's second devices receives a copy of outgoing message.
|
||||||
alice2.recv_msg(&sent_msg).await;
|
alice2.recv_msg(&sent_msg).await;
|
||||||
|
|
||||||
// Bob receives message.
|
|
||||||
bob.recv_msg(&sent_msg).await;
|
|
||||||
|
|
||||||
// Message was not encrypted, so status is not copied.
|
|
||||||
assert_eq!(alice2.get_config(Config::Selfstatus).await?, default_status);
|
assert_eq!(alice2.get_config(Config::Selfstatus).await?, default_status);
|
||||||
|
|
||||||
// Alice sends encrypted message.
|
// Alice sends encrypted message.
|
||||||
@@ -892,17 +882,9 @@ async fn test_synchronize_status() -> Result<()> {
|
|||||||
send_text_msg(alice1, chat.id, "Hello".to_string()).await?;
|
send_text_msg(alice1, chat.id, "Hello".to_string()).await?;
|
||||||
let sent_msg = alice1.pop_sent_msg().await;
|
let sent_msg = alice1.pop_sent_msg().await;
|
||||||
|
|
||||||
// Second message is encrypted.
|
|
||||||
let message = sent_msg.load_from_db().await;
|
|
||||||
assert!(message.get_showpadlock());
|
|
||||||
|
|
||||||
// Alice's second devices receives a copy of second outgoing message.
|
// Alice's second devices receives a copy of second outgoing message.
|
||||||
alice2.recv_msg(&sent_msg).await;
|
alice2.recv_msg(&sent_msg).await;
|
||||||
|
assert_eq!(alice2.get_config(Config::Selfstatus).await?, default_status);
|
||||||
assert_eq!(
|
|
||||||
alice2.get_config(Config::Selfstatus).await?,
|
|
||||||
Some("New status".to_string())
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -915,9 +897,9 @@ async fn test_selfavatar_changed_event() -> Result<()> {
|
|||||||
// Alice has two devices.
|
// Alice has two devices.
|
||||||
let alice1 = &tcm.alice().await;
|
let alice1 = &tcm.alice().await;
|
||||||
let alice2 = &tcm.alice().await;
|
let alice2 = &tcm.alice().await;
|
||||||
|
for a in [alice1, alice2] {
|
||||||
// Bob has one device.
|
a.set_config_bool(Config::SyncMsgs, true).await?;
|
||||||
let bob = &tcm.bob().await;
|
}
|
||||||
|
|
||||||
assert_eq!(alice1.get_config(Config::Selfavatar).await?, None);
|
assert_eq!(alice1.get_config(Config::Selfavatar).await?, None);
|
||||||
|
|
||||||
@@ -933,17 +915,7 @@ async fn test_selfavatar_changed_event() -> Result<()> {
|
|||||||
.get_matching(|e| matches!(e, EventType::SelfavatarChanged))
|
.get_matching(|e| matches!(e, EventType::SelfavatarChanged))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Alice sends a message.
|
sync(alice1, alice2).await;
|
||||||
let alice1_chat_id = alice1.create_chat(bob).await.id;
|
|
||||||
send_text_msg(alice1, alice1_chat_id, "Hello".to_string()).await?;
|
|
||||||
let sent_msg = alice1.pop_sent_msg().await;
|
|
||||||
|
|
||||||
// The message is encrypted.
|
|
||||||
let message = sent_msg.load_from_db().await;
|
|
||||||
assert!(message.get_showpadlock());
|
|
||||||
|
|
||||||
// Alice's second device receives a copy of the outgoing message.
|
|
||||||
alice2.recv_msg(&sent_msg).await;
|
|
||||||
|
|
||||||
// Alice's second device applies the selfavatar.
|
// Alice's second device applies the selfavatar.
|
||||||
assert!(alice2.get_config(Config::Selfavatar).await?.is_some());
|
assert!(alice2.get_config(Config::Selfavatar).await?.is_some());
|
||||||
|
|||||||
@@ -991,42 +991,6 @@ Content-Disposition: reaction\n\
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Regression test for reaction resetting self-status.
|
|
||||||
///
|
|
||||||
/// Reactions do not contain the status,
|
|
||||||
/// but should not result in self-status being reset on other devices.
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
||||||
async fn test_reaction_status_multidevice() -> Result<()> {
|
|
||||||
let mut tcm = TestContextManager::new();
|
|
||||||
let alice1 = tcm.alice().await;
|
|
||||||
let alice2 = tcm.alice().await;
|
|
||||||
|
|
||||||
alice1
|
|
||||||
.set_config(Config::Selfstatus, Some("New status"))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let alice2_msg = tcm.send_recv(&alice1, &alice2, "Hi!").await;
|
|
||||||
assert_eq!(
|
|
||||||
alice2.get_config(Config::Selfstatus).await?.as_deref(),
|
|
||||||
Some("New status")
|
|
||||||
);
|
|
||||||
|
|
||||||
// Alice reacts to own message from second device,
|
|
||||||
// first device receives rection.
|
|
||||||
{
|
|
||||||
send_reaction(&alice2, alice2_msg.id, "👍").await?;
|
|
||||||
let msg = alice2.pop_sent_msg().await;
|
|
||||||
alice1.recv_msg_hidden(&msg).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the status is still the same.
|
|
||||||
assert_eq!(
|
|
||||||
alice1.get_config(Config::Selfstatus).await?.as_deref(),
|
|
||||||
Some("New status")
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_send_reaction_multidevice() -> Result<()> {
|
async fn test_send_reaction_multidevice() -> Result<()> {
|
||||||
let mut tcm = TestContextManager::new();
|
let mut tcm = TestContextManager::new();
|
||||||
|
|||||||
@@ -885,13 +885,11 @@ pub(crate) async fn receive_imf_inner(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(avatar_action) = &mime_parser.user_avatar
|
if let Some(avatar_action) = &mime_parser.user_avatar
|
||||||
&& from_id != ContactId::UNDEFINED
|
&& !matches!(from_id, ContactId::UNDEFINED | ContactId::SELF)
|
||||||
&& context
|
&& context
|
||||||
.update_contacts_timestamp(from_id, Param::AvatarTimestamp, mime_parser.timestamp_sent)
|
.update_contacts_timestamp(from_id, Param::AvatarTimestamp, mime_parser.timestamp_sent)
|
||||||
.await?
|
.await?
|
||||||
&& let Err(err) =
|
&& let Err(err) = contact::set_profile_image(context, from_id, avatar_action).await
|
||||||
contact::set_profile_image(context, from_id, avatar_action, mime_parser.was_encrypted())
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
warn!(context, "receive_imf cannot update profile image: {err:#}.");
|
warn!(context, "receive_imf cannot update profile image: {err:#}.");
|
||||||
};
|
};
|
||||||
@@ -899,18 +897,11 @@ pub(crate) async fn receive_imf_inner(
|
|||||||
// Ignore footers from mailinglists as they are often created or modified by the mailinglist software.
|
// Ignore footers from mailinglists as they are often created or modified by the mailinglist software.
|
||||||
if let Some(footer) = &mime_parser.footer
|
if let Some(footer) = &mime_parser.footer
|
||||||
&& !mime_parser.is_mailinglist_message()
|
&& !mime_parser.is_mailinglist_message()
|
||||||
&& from_id != ContactId::UNDEFINED
|
&& !matches!(from_id, ContactId::UNDEFINED | ContactId::SELF)
|
||||||
&& context
|
&& context
|
||||||
.update_contacts_timestamp(from_id, Param::StatusTimestamp, mime_parser.timestamp_sent)
|
.update_contacts_timestamp(from_id, Param::StatusTimestamp, mime_parser.timestamp_sent)
|
||||||
.await?
|
.await?
|
||||||
&& let Err(err) = contact::set_status(
|
&& let Err(err) = contact::set_status(context, from_id, footer.to_string()).await
|
||||||
context,
|
|
||||||
from_id,
|
|
||||||
footer.to_string(),
|
|
||||||
mime_parser.was_encrypted(),
|
|
||||||
mime_parser.has_chat_version(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
warn!(context, "Cannot update contact status: {err:#}.");
|
warn!(context, "Cannot update contact status: {err:#}.");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user