diff --git a/python/src/deltachat/chatting.py b/python/src/deltachat/chatting.py index 384a30524..9d3d73df1 100644 --- a/python/src/deltachat/chatting.py +++ b/python/src/deltachat/chatting.py @@ -1,6 +1,7 @@ """ chatting related objects: Contact, Chat, Message. """ import mimetypes +import os from . import props from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array from .capi import lib, ffi @@ -315,3 +316,46 @@ class Chat(object): return list(iter_array( dc_array, lambda id: Contact(self._dc_context, id)) ) + + def set_profile_image(self, img_path): + """Set group profile image. + + If the group is already promoted (any message was sent to the group), + all group members are informed by a special status message that is sent + automatically by this function. + :params img_path: path to image object + :raises ValueError: if profile image could not be set + :returns: None + """ + assert os.path.exists(img_path), img_path + p = as_dc_charpointer(img_path) + res = lib.dc_set_chat_profile_image(self._dc_context, self.id, p) + if res != 1: + raise ValueError("Setting Profile Image {!r} failed".format(p)) + + def remove_profile_image(self): + """remove group profile image. + + If the group is already promoted (any message was sent to the group), + all group members are informed by a special status message that is sent + automatically by this function. + :raises ValueError: if profile image could not be reset + :returns: None + """ + res = lib.dc_set_chat_profile_image(self._dc_context, self.id, ffi.NULL) + if res != 1: + raise ValueError("Removing Profile Image failed") + + def get_profile_image(self): + """Get group profile image. + + For groups, this is the image set by any group member using + dc_set_chat_profile_image(). For normal chats, this is the image + set by each remote user on their own using dc_set_config(context, + "selfavatar", image). + :returns: path to profile image, None if no profile image exists. + """ + dc_res = lib.dc_chat_get_profile_image(self._dc_chat) + if dc_res == ffi.NULL: + return None + return from_dc_charpointer(dc_res) diff --git a/python/tests/test_account.py b/python/tests/test_account.py index b5990cd97..3f11c46bc 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -147,6 +147,15 @@ class TestOfflineChat: qr = chat.get_join_qr() assert ac2.check_qr(qr).is_ask_verifygroup + def test_get_set_profile_image(self, ac1, data): + chat = ac1.create_group_chat(name="title1") + p = data.get_path("d.png") + chat.set_profile_image(p) + p2 = chat.get_profile_image() + assert open(p, "rb").read() == open(p2, "rb").read() + chat.remove_profile_image() + assert chat.get_profile_image() is None + def test_delete_and_send_fails(self, ac1, chat1): chat1.delete() ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") @@ -589,3 +598,44 @@ class TestOnlineAccount: ch = ac2.qr_join_chat(qr) assert ch.id >= 10 wait_securejoin_inviter_progress(ac1, 1000) + + def test_set_get_profile_image(self, acfactory, data, lp): + ac1 = acfactory.get_online_configuring_account() + ac2 = acfactory.get_online_configuring_account() + wait_configuration_progress(ac2, 1000) + wait_configuration_progress(ac1, 1000) + + lp.sec("create unpromoted group chat") + chat = ac1.create_group_chat("hello") + p = data.get_path("d.png") + + lp.sec("set profile image") + chat.set_profile_image(p) + ac1._evlogger.get_matching("DC_EVENT_CHAT_MODIFIED") + assert not chat.is_promoted() + + lp.sec("add ac2 to unpromoted group chat") + c2 = ac1.create_contact(email=ac2.get_config("addr")) + contact1 = chat.add_contact(c2) + assert not chat.is_promoted() + + lp.sec("ac2: add ac1 per chat") + c1 = ac2.create_contact(email=ac1.get_config("addr")) + ac2.create_chat_by_contact(c1) + ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") + + lp.sec("ac1: send a first message to ac2") + chat.send_text("hi") + assert chat.is_promoted() + + lp.sec("ac2: wait for receiving message from ac1") + ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG") + msg_in = ac2.get_message_by_id(ev[2]) + assert msg_in.text == "hi" + assert not msg_in.chat.is_deaddrop() + + lp.sec("ac2: create proper chat and read profile image") + chat2 = ac2.create_chat_by_message(msg_in) + p2 = chat2.get_profile_image() + assert p2 is not None + assert open(p2, "rb").read() == open(p, "rb").read() diff --git a/src/chat.rs b/src/chat.rs index 535edd31b..379af6810 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1654,8 +1654,6 @@ pub unsafe fn set_chat_profile_image( ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat ID"); let mut chat = Chat::load_from_db(context, chat_id)?; - let mut msg = dc_msg_new_untyped(context); - let new_image_rel; if real_group_exists(context, chat_id) { if !(is_contact_in_chat(context, chat_id, 1i32 as u32) == 1i32) { @@ -1668,48 +1666,39 @@ pub unsafe fn set_chat_profile_image( /* we should respect this - whatever we send to the group, it gets discarded anyway! */ bail!("Failed to set profile image"); } + let mut new_image_rel: String; if !new_image.as_ref().is_empty() { - let mut img = new_image.as_ref().to_string(); - if !dc_make_rel_and_copy(context, &mut img) { - bail!("Failed to set profile image"); + new_image_rel = new_image.as_ref().to_string(); + if !dc_make_rel_and_copy(context, &mut new_image_rel) { + bail!("Failed to get relative path for profile image"); } - new_image_rel = Some(img); } else { - new_image_rel = Some("".to_string()); + new_image_rel = "".to_string(); } - if let Some(ref new_image_rel) = new_image_rel { - chat.param.set(Param::ProfileImage, new_image_rel); - } + chat.param.set(Param::ProfileImage, &new_image_rel); if chat.update_param().is_ok() { + info!(context, 0, "after update_param"); if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { - msg.param.set_int(Param::Cmd, 3); - if let Some(ref new_image_rel) = new_image_rel { - msg.param.set(Param::Arg, new_image_rel); - } + let mut msg = dc_msg_new_untyped(context); + info!(context, 0, "setting params for groupimage change"); + msg.param.set_int(Param::Cmd, DC_CMD_GROUPIMAGE_CHANGED); + msg.param.set(Param::Arg, &new_image_rel); msg.type_0 = Viewtype::Text; msg.text = Some(context.stock_system_msg( - if new_image_rel.is_some() { - StockMessage::MsgGrpImgChanged - } else { + if new_image_rel.is_empty() { StockMessage::MsgGrpImgDeleted + } else { + StockMessage::MsgGrpImgChanged }, "", "", DC_CONTACT_ID_SELF, )); msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default(); - context.call_cb( - Event::MSGS_CHANGED, - chat_id as uintptr_t, - msg.id as uintptr_t, - ); + emit_event!(context, Event::MSGS_CHANGED, chat_id, msg.id); } - context.call_cb( - Event::CHAT_MODIFIED, - chat_id as uintptr_t, - 0i32 as uintptr_t, - ); + emit_event!(context, Event::CHAT_MODIFIED, chat_id, 0); return Ok(()); } } diff --git a/src/constants.rs b/src/constants.rs index 6a066351e..08bd41b1b 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -557,6 +557,6 @@ pub const DC_CMD_GROUPIMAGE_CHANGED: libc::c_int = 3; pub const DC_CMD_MEMBER_ADDED_TO_GROUP: libc::c_int = 4; pub const DC_CMD_MEMBER_REMOVED_FROM_GROUP: libc::c_int = 5; pub const DC_CMD_AUTOCRYPT_SETUP_MESSAGE: libc::c_int = 6; -const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7; +pub const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7; pub const DC_CMD_LOCATION_STREAMING_ENABLED: libc::c_int = 8; -const DC_CMD_LOCATION_ONLY: libc::c_int = 9; +pub const DC_CMD_LOCATION_ONLY: libc::c_int = 9; diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index e5611779e..32db17672 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -532,6 +532,10 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i /* build header etc. */ let command = factory.msg.param.get_int(Param::Cmd).unwrap_or_default(); + info!( + factory.context, + 0, "render_message found command {}", command + ); if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { mailimf_fields_add( imf_fields, @@ -548,7 +552,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i dc_encode_header_words(name.as_ptr()), ), ); - if command == 5 { + if command == DC_CMD_MEMBER_REMOVED_FROM_GROUP { let email_to_remove = factory .msg .param @@ -567,7 +571,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i ), ); } - } else if command == 4 { + } else if command == DC_CMD_MEMBER_ADDED_TO_GROUP { let msg = &factory.msg; do_gossip = 1; let email_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup(); @@ -599,7 +603,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i ), ); } - } else if command == 2 { + } else if command == DC_CMD_GROUPNAME_CHANGED { let msg = &factory.msg; let value_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup(); @@ -612,7 +616,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i value_to_add, ), ); - } else if command == 3 { + } else if command == DC_CMD_GROUPIMAGE_CHANGED { let msg = &factory.msg; grpimage = msg.param.get(Param::Arg); if grpimage.is_none() { @@ -626,7 +630,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i } } } - if command == 8 { + if command == DC_CMD_LOCATION_STREAMING_ENABLED { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -637,7 +641,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i ), ); } - if command == 6 { + if command == DC_CMD_AUTOCRYPT_SETUP_MESSAGE { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -650,7 +654,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i .stock_str(StockMessage::AcSetupMsgBody) .strdup(); } - if command == 7 { + if command == DC_CMD_SECUREJOIN_MESSAGE { let msg = &factory.msg; let step = msg.param.get(Param::Arg).unwrap_or_default().strdup(); if strlen(step) > 0 { @@ -725,7 +729,9 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i } } } + info!(factory.context, 0, "grpimage {:?}", grpimage); if let Some(grpimage) = grpimage { + info!(factory.context, 0, "setting group image"); let mut meta = dc_msg_new_untyped(factory.context); meta.type_0 = Viewtype::Image; meta.param.set(Param::File, grpimage); diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 9a2e5a448..602a1cfc0 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -1078,7 +1078,6 @@ unsafe fn create_or_lookup_group( } set_better_msg(mime_parser, &better_msg); - // search the grpid in the header let optional_field = dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-ID"); if !optional_field.is_null() { grpid = to_string((*optional_field).fld_value) @@ -1316,9 +1315,11 @@ unsafe fn create_or_lookup_group( } // execute group commands + info!(context, 0, "before exec group commands"); if !X_MrAddToGrp.is_null() || !X_MrRemoveFromGrp.is_null() { recreate_member_list = 1; } else if 0 != X_MrGrpNameChanged && !grpname.is_null() && strlen(grpname) < 200 { + info!(context, 0, "updating grpname for chat {}", chat_id); if sql::execute( context, &context.sql, @@ -1331,6 +1332,13 @@ unsafe fn create_or_lookup_group( } } if !X_MrGrpImageChanged.is_null() { + info!( + context, + 0, + "handling group image changed {} for chat {}", + as_str(X_MrGrpImageChanged), + chat_id + ); let mut ok = 0; let mut grpimage = ptr::null_mut(); if strcmp( @@ -1347,6 +1355,7 @@ unsafe fn create_or_lookup_group( .get(Param::File) .map(|s| s.strdup()) .unwrap_or_else(|| std::ptr::null_mut()); + info!(context, 0, "found image {:?}", grpimage); ok = 1 } }