diff --git a/src/account/account-params.cpp b/src/account/account-params.cpp index 51c21e753f3afb315229c904b51fe8c898fd6a07..6b602b2e1969ad1241fdc7f73e8eb1949d05b718 100644 --- a/src/account/account-params.cpp +++ b/src/account/account-params.cpp @@ -949,7 +949,7 @@ LinphonePrivacyMask AccountParams::getPrivacy() const { return mPrivacy; } -std::shared_ptr<const Address> AccountParams::getIdentityAddress() const { +std::shared_ptr<Address> AccountParams::getIdentityAddress() const { return mIdentityAddress; } diff --git a/src/account/account-params.h b/src/account/account-params.h index b34453d5aae61a08ed981e52ffe9a8f995e36d55..fb14d4bdd5ba2e16b2b9b59baa264b1f31a398a7 100644 --- a/src/account/account-params.h +++ b/src/account/account-params.h @@ -145,7 +145,7 @@ public: const std::list<std::string> getRoutesString() const; const bctbx_list_t *getRoutesCString() const; LinphonePrivacyMask getPrivacy() const; - std::shared_ptr<const Address> getIdentityAddress() const; + std::shared_ptr<Address> getIdentityAddress() const; LinphoneAVPFMode getAvpfMode() const; std::shared_ptr<NatPolicy> getNatPolicy() const; PushNotificationConfig *getPushNotificationConfig() const; diff --git a/src/chat/chat-message/chat-message.cpp b/src/chat/chat-message/chat-message.cpp index 36b002711cd653a27477af3bce82a78800740032..dd7b2c500fe890cab10b87927f1d7edf22373e02 100644 --- a/src/chat/chat-message/chat-message.cpp +++ b/src/chat/chat-message/chat-message.cpp @@ -800,28 +800,49 @@ std::string ChatMessagePrivate::createFakeFileTransferFromUrl(const std::string } void ChatMessagePrivate::setChatRoom(const shared_ptr<AbstractChatRoom> &chatRoom) { + L_Q(); mChatRoom = chatRoom; const ConferenceId &conferenceId = chatRoom->getConferenceId(); const auto &account = chatRoom->getAccount(); - // If an account is attached to a chatroom, use its contact address otherwise use the local address of the - // conference ID. Note that the conference ID's local address may not have the "gr" parameter hence this may lead to - // issues such as IMDN not received + const bool isBasicChatRoom = + (chatRoom->getCurrentParams()->getChatParams()->getBackend() == ChatParams::Backend::Basic); + // If an account is attached to a chatroom, use its contact or the identity address otherwise use the local address + // of the conference ID. For Flexisip based chatrooms, it is paramount to use a contact address as the From header. + // RFC3428 forbids adding a Contact header to MESSAGE requests, therefore the From header is the only way a + // conference server knows which device sent the request and can be forwarded to the other tchat members as well as + // other devices of the same participant. std::shared_ptr<Address> localAddress = nullptr; - if (account && account->getContactAddress()) { - localAddress = account->getContactAddress()->clone()->toSharedPtr(); - } else { - lInfo() << "It looks that chatroom " << chatRoom << " with ID " << conferenceId - << " has no account associated to or the contact address is not available yet, setting conference ID's " - "local address as message local address"; - localAddress = conferenceId.getLocalAddress()->clone()->toSharedPtr(); + if (account) { + if (isBasicChatRoom) { + localAddress = account->getAccountParams()->getIdentityAddress(); + lInfo() << *chatRoom << " with ID " << conferenceId + << " is a basic chatroom therefore set the local address of message [" << q + << "] to the account identity address " << *localAddress; + } else { + localAddress = account->getContactAddress(); + if (localAddress) { + lInfo() << *chatRoom << " with ID " << conferenceId + << " is not a basic chatroom therefore set the local address of message [" << q + << "] to the account contact address " << *localAddress; + } + } + } + if (!localAddress) { + lInfo() << *chatRoom << " with ID " << conferenceId + << " has no account associated to or the contact or the identity address is not available yet, setting " + "conference ID's local address as message local address"; + localAddress = conferenceId.getLocalAddress(); } - auto peerAddress = conferenceId.getPeerAddress()->clone()->toSharedPtr(); - const bool isBasicChatRoom = - (chatRoom->getCurrentParams()->getChatParams()->getBackend() == ChatParams::Backend::Basic); - if (isBasicChatRoom && localAddress) { - localAddress = Address::create(localAddress->getUriWithoutGruu()); + if (localAddress) { + if (isBasicChatRoom) { + localAddress = Address::create(localAddress->getUriWithoutGruu()); + } else { + localAddress = localAddress->clone()->toSharedPtr(); + } } + + auto peerAddress = conferenceId.getPeerAddress()->clone()->toSharedPtr(); if (direction == ChatMessage::Direction::Outgoing) { fromAddress = localAddress; toAddress = peerAddress; diff --git a/src/chat/chat-room/abstract-chat-room.h b/src/chat/chat-room/abstract-chat-room.h index 2e762b32b21decdbf36993d4bb0dd6b1672df36b..dda0d01ddf704bc1a247da1814a2f0a0fa6d3baa 100644 --- a/src/chat/chat-room/abstract-chat-room.h +++ b/src/chat/chat-room/abstract-chat-room.h @@ -287,7 +287,7 @@ std::ostream &operator<<(std::ostream &lhs, AbstractChatRoom::EphemeralMode e); inline std::ostream &operator<<(std::ostream &str, const AbstractChatRoom &chatRoom) { const auto &conferenceAddress = chatRoom.getConferenceAddress(); - str << "Conference [" << &chatRoom << "] (" + str << "ChatRoom [" << &chatRoom << "] (" << (conferenceAddress ? conferenceAddress->toString() : std::string("sip:")) << ")"; return str; } diff --git a/src/chat/modifier/cpim-chat-message-modifier.cpp b/src/chat/modifier/cpim-chat-message-modifier.cpp index d135445de005fd52eac8ca39d6230f1d1ce6304a..88d21220d7e7c2a5bb060078c3693a36e6098b7a 100644 --- a/src/chat/modifier/cpim-chat-message-modifier.cpp +++ b/src/chat/modifier/cpim-chat-message-modifier.cpp @@ -59,11 +59,29 @@ const string imdnDispositionNotificationHeader = "Disposition-Notification"; ChatMessageModifier::Result CpimChatMessageModifier::encode(const shared_ptr<ChatMessage> &message, BCTBX_UNUSED(int &errorCode)) { Cpim::Message cpimMessage; - - cpimMessage.addMessageHeader( - Cpim::FromHeader(cpimAddressUri(message->getFromAddress()), cpimAddressDisplayName(message->getFromAddress()))); - cpimMessage.addMessageHeader( - Cpim::ToHeader(cpimAddressUri(message->getToAddress()), cpimAddressDisplayName(message->getToAddress()))); + shared_ptr<AbstractChatRoom> chatRoom = message->getChatRoom(); + const auto &account = chatRoom->getAccount(); + if (!account) { + lWarning() << "Unable to encode CPIM because the account attached to the message is unknown"; + return ChatMessageModifier::Result::Error; + } + const bool isBasicChatRoom = + (chatRoom->getCurrentParams()->getChatParams()->getBackend() == ChatParams::Backend::Basic); + std::shared_ptr<Address> localDevice; + // For non-basic tchat, prefer using the account contact address in case the message from address is the account's + // identity address. It will help the server to correctly dispatch requests to the other participants and sender + // devices + if (!isBasicChatRoom) { + localDevice = account->getContactAddress(); + } + if (!localDevice) { + localDevice = message->getFromAddress(); + } + localDevice = localDevice->clone()->toSharedPtr(); + localDevice->setDisplayName(""); + cpimMessage.addMessageHeader(Cpim::FromHeader(cpimAddressUri(localDevice), cpimAddressDisplayName(localDevice))); + const auto &to = message->getToAddress(); + cpimMessage.addMessageHeader(Cpim::ToHeader(cpimAddressUri(to), cpimAddressDisplayName(to))); cpimMessage.addMessageHeader(Cpim::DateTimeHeader(message->getTime())); bool linphoneNamespaceHeaderSet = false; @@ -313,7 +331,8 @@ CpimChatMessageModifier::createMinimalCpimContentForLimeMessage(const shared_ptr const auto &localDevice = accountContactAddress ? accountContactAddress : chatRoom->getConferenceId().getLocalAddress(); if (!localDevice) { - lWarning() << "Unable to create CPIM because account [" << account << "] has not registered yet and the chatroom local address is unknown"; + lWarning() << "Unable to create CPIM because " << *account + << " has not registered yet and the chatroom local address is unknown"; return Content::create(); } Cpim::Message cpimMessage; diff --git a/tester/group_chat_tester.c b/tester/group_chat_tester.c index 3f40af194500d328639baed96334d672dbd76d34..74bb37d691870f8d092987f8143e36afa423f87c 100644 --- a/tester/group_chat_tester.c +++ b/tester/group_chat_tester.c @@ -1021,7 +1021,7 @@ static void group_chat_room_creation_core_restart(void) { uuid = bctbx_strdup(linphone_config_get_string(linphone_core_get_config(marie->lc), "misc", "uuid", NULL)); } - ms_message("Restart %s'c core", linphone_core_get_identity(marie->lc)); + ms_message("Restart %s's core", linphone_core_get_identity(marie->lc)); initialMarieStats = marie->stat; coresList = bctbx_list_remove(coresList, marie->lc); linphone_core_manager_reinit(marie); @@ -6187,6 +6187,126 @@ end: linphone_core_manager_destroy(chloe); } +static void basic_chat_room_with_cpim_base(bool_t use_gruu) { + LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); + LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); + bctbx_list_t *coresManagerList = NULL; + coresManagerList = bctbx_list_append(coresManagerList, marie); + coresManagerList = bctbx_list_append(coresManagerList, pauline); + if (!use_gruu) { + linphone_core_remove_supported_tag(marie->lc, "gruu"); + } + bctbx_list_t *coresList = init_core_for_conference(coresManagerList); + start_core_for_conference(coresManagerList); + + // Enable CPIM + LinphoneAccount *marie_account = linphone_core_get_default_account(marie->lc); + const LinphoneAccountParams *marie_account_params = linphone_account_get_params(marie_account); + LinphoneAccountParams *cloned_marie_account_params = linphone_account_params_clone(marie_account_params); + linphone_account_params_enable_cpim_in_basic_chat_room(cloned_marie_account_params, TRUE); + linphone_account_set_params(marie_account, cloned_marie_account_params); + linphone_account_params_unref(cloned_marie_account_params); + + LinphoneAccount *pauline_account = linphone_core_get_default_account(pauline->lc); + const LinphoneAccountParams *pauline_account_params = linphone_account_get_params(pauline_account); + LinphoneAccountParams *cloned_pauline_account_params = linphone_account_params_clone(pauline_account_params); + linphone_account_params_enable_cpim_in_basic_chat_room(cloned_pauline_account_params, TRUE); + linphone_account_set_params(pauline_account, cloned_pauline_account_params); + linphone_account_params_unref(cloned_pauline_account_params); + + LinphoneAddress *pauline_contact_address = linphone_account_get_contact_address(pauline_account); + + bctbx_list_t *participantsAddresses = NULL; + participantsAddresses = bctbx_list_append(participantsAddresses, pauline_contact_address); + LinphoneConferenceParams *marie_params = linphone_core_create_conference_params(marie->lc); + linphone_conference_params_enable_chat(marie_params, TRUE); + linphone_conference_params_enable_group(marie_params, FALSE); + LinphoneChatParams *marie_chat_params = linphone_conference_params_get_chat_params(marie_params); + linphone_chat_params_set_backend(marie_chat_params, LinphoneChatRoomBackendBasic); + LinphoneChatRoom *marieCr = + linphone_core_create_chat_room_7(marie->lc, marie_params, marie->identity, participantsAddresses); + bctbx_list_free(participantsAddresses); + participantsAddresses = NULL; + linphone_chat_room_params_unref(marie_params); + BC_ASSERT_PTR_NOT_NULL(marieCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneChatRoomStateCreated, 1, + liblinphone_tester_sip_timeout)); + + // Marie sends a message in a basic chatroom + const char *marie_text = "This is a basic chat room"; + if (marieCr) { + const LinphoneAddress *peer_address = linphone_chat_room_get_peer_address(marieCr); + BC_ASSERT_TRUE(linphone_address_has_uri_param(peer_address, "gr")); + LinphoneChatMessage *sent_msg = _send_message(marieCr, marie_text); + BC_ASSERT_TRUE( + wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageSent, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneChatRoomStateCreated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageReceived, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageDelivered, 1, + liblinphone_tester_sip_timeout)); + linphone_chat_message_unref(sent_msg); + } + + LinphoneConferenceParams *pauline_params = linphone_core_create_conference_params(pauline->lc); + linphone_conference_params_enable_chat(pauline_params, TRUE); + linphone_conference_params_enable_group(pauline_params, FALSE); + LinphoneChatParams *pauline_chat_params = linphone_conference_params_get_chat_params(pauline_params); + linphone_chat_params_set_backend(pauline_chat_params, LinphoneChatRoomBackendBasic); + const LinphoneAddress *pauline_identity_address = + linphone_account_params_get_identity_address(linphone_account_get_params(pauline_account)); + LinphoneChatRoom *paulineCr = + linphone_core_search_chat_room_2(pauline->lc, pauline_params, pauline_identity_address, NULL, NULL); + BC_ASSERT_PTR_NOT_NULL(paulineCr); + linphone_chat_room_params_unref(pauline_params); + + const char *pauline_text = "Yes Madam"; + if (paulineCr) { + LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(paulineCr); + if (BC_ASSERT_PTR_NOT_NULL(msg)) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(msg), marie_text); + linphone_chat_message_unref(msg); + } + const LinphoneAddress *local_address = linphone_chat_room_get_local_address(paulineCr); + BC_ASSERT_TRUE(linphone_address_has_uri_param(local_address, "gr")); + LinphoneChatMessage *sent_msg = _send_message(paulineCr, pauline_text); + BC_ASSERT_TRUE( + wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageSent, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &marie->stat.number_of_LinphoneChatRoomStateCreated, 2, 2000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageReceived, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageDelivered, 1, + liblinphone_tester_sip_timeout)); + linphone_chat_message_unref(sent_msg); + } + + if (marieCr) { + LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(marieCr); + if (BC_ASSERT_PTR_NOT_NULL(msg)) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(msg), pauline_text); + linphone_chat_message_unref(msg); + } + linphone_core_manager_delete_chat_room(marie, marieCr, coresList); + linphone_chat_room_unref(marieCr); + } + if (paulineCr) linphone_core_manager_delete_chat_room(pauline, paulineCr, coresList); + + bctbx_list_free(coresList); + bctbx_list_free(coresManagerList); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + +static void basic_chat_room_with_cpim_gruu(void) { + basic_chat_room_with_cpim_base(TRUE); +} + +static void basic_chat_room_with_cpim_without_gruu(void) { + basic_chat_room_with_cpim_base(FALSE); +} + static void find_one_to_one_chat_room(void) { LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); @@ -9495,6 +9615,8 @@ test_t group_chat2_tests[] = { TEST_ONE_TAG("IMDN sent from DB state", imdn_sent_from_db_state, "LeaksMemory"), TEST_NO_TAG("IMDN updated for group chat room with one participant offline", imdn_updated_for_group_chat_room_with_one_participant_offline), + TEST_NO_TAG("Basic chat room with CPIM and GRUU", basic_chat_room_with_cpim_gruu), + TEST_NO_TAG("Basic chat room with CPIM and without GRUU", basic_chat_room_with_cpim_without_gruu), TEST_NO_TAG("Find one-to-one chat room", find_one_to_one_chat_room), TEST_NO_TAG("Exhumed one-to-one chat room 1", exhume_one_to_one_chat_room_1), TEST_NO_TAG("Exhumed one-to-one chat room 2", exhume_one_to_one_chat_room_2),