From 4dfbf7afbb6f0359d902ff34cadfb6c6257b91cc Mon Sep 17 00:00:00 2001 From: Sylvain Berfini <sylvain.berfini@belledonne-communications.com> Date: Wed, 19 Mar 2025 09:57:45 +0100 Subject: [PATCH] Using same workaround that is used for calls but for SIP simple messages --- coreapi/tester_utils.cpp | 7 ++ coreapi/tester_utils.h | 3 + include/linphone/api/c-chat-room.h | 7 ++ src/c-wrapper/api/c-chat-room.cpp | 5 ++ src/chat/chat-room/basic-chat-room.cpp | 4 + src/chat/chat-room/basic-chat-room.h | 1 + src/conference/conference.h | 4 +- src/conference/session/call-session.cpp | 30 ++++---- src/core/core-chat-room.cpp | 60 +++++++++++++++ src/core/core.cpp | 2 +- src/core/core.h | 3 + src/db/main-db.cpp | 6 +- tester/message_tester.c | 99 +++++++++++++++++++++++++ 13 files changed, 210 insertions(+), 21 deletions(-) diff --git a/coreapi/tester_utils.cpp b/coreapi/tester_utils.cpp index defaec7abe..6ad6c04b4d 100644 --- a/coreapi/tester_utils.cpp +++ b/coreapi/tester_utils.cpp @@ -345,3 +345,10 @@ int linphone_core_get_number_of_duplicated_messages(const LinphoneCore *core) { void linphone_core_set_account_deletion_timeout(LinphoneCore *core, unsigned int seconds) { L_GET_CPP_PTR_FROM_C_OBJECT(core)->setAccountDeletionTimeout(seconds); } + +LinphoneChatRoom * +linphone_core_create_basic_chat_room(LinphoneCore *core, const char *localSipUri, const char *remoteSipUri) { + return L_GET_CPP_PTR_FROM_C_OBJECT(core) + ->getOrCreateBasicChatRoomFromUri(L_C_TO_STRING(localSipUri), L_C_TO_STRING(remoteSipUri)) + ->toC(); +} \ No newline at end of file diff --git a/coreapi/tester_utils.h b/coreapi/tester_utils.h index 0d51f65c27..941431ac47 100644 --- a/coreapi/tester_utils.h +++ b/coreapi/tester_utils.h @@ -450,6 +450,9 @@ LINPHONE_PUBLIC void linphone_core_enable_gruu_in_conference_address(LinphoneCor **/ LINPHONE_PUBLIC bool_t linphone_core_gruu_in_conference_address_enabled(const LinphoneCore *core); +LINPHONE_PUBLIC LinphoneChatRoom * +linphone_core_create_basic_chat_room(LinphoneCore *core, const char *localSipUri, const char *remoteSipUri); + #ifdef __cplusplus } #endif diff --git a/include/linphone/api/c-chat-room.h b/include/linphone/api/c-chat-room.h index 48e83898ae..8fa6848705 100644 --- a/include/linphone/api/c-chat-room.h +++ b/include/linphone/api/c-chat-room.h @@ -141,6 +141,13 @@ LINPHONE_PUBLIC const LinphoneAddress *linphone_chat_room_get_local_address(Linp */ LINPHONE_PUBLIC const char *linphone_chat_room_get_identifier(const LinphoneChatRoom *chat_room); +/** + * Returns the local account to which this chat room is related. + * @param chat_room The #LinphoneChatRoom object. @notnil + * @return the related #LinphoneAccount object if any, NULL otherwise. @maybenil + */ +LINPHONE_PUBLIC LinphoneAccount *linphone_chat_room_get_account(LinphoneChatRoom *chat_room); + /** * Used to receive a chat message when using async mechanism with IM enchat_roomyption engine * @param chat_room #LinphoneChatRoom object @notnil diff --git a/src/c-wrapper/api/c-chat-room.cpp b/src/c-wrapper/api/c-chat-room.cpp index 9b079762ab..ef7c248ac2 100644 --- a/src/c-wrapper/api/c-chat-room.cpp +++ b/src/c-wrapper/api/c-chat-room.cpp @@ -113,6 +113,11 @@ const char *linphone_chat_room_get_identifier(const LinphoneChatRoom *chat_room) return NULL; } +LinphoneAccount *linphone_chat_room_get_account(LinphoneChatRoom *chat_room) { + shared_ptr<Account> account = AbstractChatRoom::toCpp(chat_room)->getAccount(); + return (account) ? account->toC() : nullptr; +} + LinphoneChatMessage *linphone_chat_room_create_empty_message(LinphoneChatRoom *chat_room) { ChatRoomLogContextualizer logContextualizer(chat_room); shared_ptr<ChatMessage> cppPtr = AbstractChatRoom::toCpp(chat_room)->createChatMessage(); diff --git a/src/chat/chat-room/basic-chat-room.cpp b/src/chat/chat-room/basic-chat-room.cpp index 819abe5449..3008ca3497 100644 --- a/src/chat/chat-room/basic-chat-room.cpp +++ b/src/chat/chat-room/basic-chat-room.cpp @@ -116,6 +116,10 @@ const ConferenceId &BasicChatRoom::getConferenceId() const { return mConferenceId; } +void BasicChatRoom::setConferenceId(const ConferenceId &conferenceId) { + mConferenceId = conferenceId; +} + std::optional<std::reference_wrapper<const std::string>> BasicChatRoom::getIdentifier() const { if (mState == ConferenceInterface::State::Instantiated) { return std::nullopt; diff --git a/src/chat/chat-room/basic-chat-room.h b/src/chat/chat-room/basic-chat-room.h index b94259b7d3..bc4cb20cf7 100644 --- a/src/chat/chat-room/basic-chat-room.h +++ b/src/chat/chat-room/basic-chat-room.h @@ -44,6 +44,7 @@ public: bool isReadOnly() const override; const ConferenceId &getConferenceId() const override; + void setConferenceId(const ConferenceId &conferenceId); std::optional<std::reference_wrapper<const std::string>> getIdentifier() const override; void invalidateAccount() override; diff --git a/src/conference/conference.h b/src/conference/conference.h index 705a2b7f0b..e8a72aa550 100644 --- a/src/conference/conference.h +++ b/src/conference/conference.h @@ -355,6 +355,8 @@ public: void resetLastNotify(); + void setConferenceId(const ConferenceId &conferenceId); + protected: explicit Conference(const std::shared_ptr<Core> &core, std::shared_ptr<CallSessionListener> callSessionListener, @@ -433,8 +435,6 @@ protected: void incrementLastNotify(); void setLastNotify(unsigned int lastNotify); - void setConferenceId(const ConferenceId &conferenceId); - void setChatRoom(const std::shared_ptr<AbstractChatRoom> &chatRoom); std::unique_ptr<LogContextualizer> getLogContextualizer() override; diff --git a/src/conference/session/call-session.cpp b/src/conference/session/call-session.cpp index 82eef61361..d08b8cb6cf 100644 --- a/src/conference/session/call-session.cpp +++ b/src/conference/session/call-session.cpp @@ -1486,24 +1486,22 @@ void CallSession::assignAccount(const std::shared_ptr<Account> &account) { cAccount = linphone_core_lookup_account_by_conference_factory_strict(core, toAddr); } if (!cAccount) { - cAccount = linphone_core_lookup_account_by_identity_strict(core, toAddr); - if (!cAccount) { - string toUser = d->log->getToAddress()->getUsername(); - if (!toUser.empty()) { - cAccount = linphone_core_lookup_known_account_2(core, fromAddr, FALSE); - if (cAccount && - Account::toCpp(cAccount)->getAccountParams()->getIdentityAddress()->getUsername() == - toUser) { - // We have a match for the from domain and the to username. - // We may face an IPBPX that sets the To domain to our IP address, which is - // a terribly stupid idea. - lWarning() << "Detecting to header probably ill-choosen. Applying workaround to have this " - "call assigned to a known account."; - // We must "hack" the call-log so it is correctly reported for this Account. - d->log->setToAddress(Account::toCpp(cAccount)->getAccountParams()->getIdentityAddress()); - } + auto account = getCore()->findAccountByIdentityAddress(d->log->getToAddress()); + if (!account) { + account = getCore()->guessLocalAccountFromMalformedMessage(d->log->getToAddress(), + d->log->getFromAddress()); + if (account) { + // We have a match for the from domain and the to username. + // We may face an IPBPX that sets the To domain to our IP address, which is + // a terribly stupid idea. + lWarning() << "Applying workaround to have this call assigned to a known account."; + // We must "hack" the call-log so it is correctly reported for this Account. + d->log->setToAddress(account->getAccountParams()->getIdentityAddress()); } } + if (account) { + cAccount = account->toC(); + } } } else { cAccount = linphone_core_lookup_account_by_identity(core, fromAddr); diff --git a/src/core/core-chat-room.cpp b/src/core/core-chat-room.cpp index f81cbf1108..7035df4daa 100644 --- a/src/core/core-chat-room.cpp +++ b/src/core/core-chat-room.cpp @@ -929,6 +929,30 @@ LinphoneReason Core::onSipMessageReceived(SalOp *op, const SalMessage *sal_msg) ConferenceId conferenceId(std::move(peerAddress), std::move(localAddress), createConferenceIdParams()); shared_ptr<AbstractChatRoom> chatRoom = findChatRoom(conferenceId, false); if (chatRoom) { + bool isBasic = (chatRoom->getCurrentParams()->getChatParams()->getBackend() == ChatParams::Backend::Basic); + if (isBasic) { + auto localAccount = + guessLocalAccountFromMalformedMessage(conferenceId.getLocalAddress(), conferenceId.getPeerAddress()); + if (localAccount) { + // We have a match for the from domain and the to username. + // We may face an IPBPX that sets the To domain to our IP address, which is + // a terribly stupid idea. + lWarning() << "Applying workaround to have this existing chat room assigned to a known account."; + auto oldConfId = chatRoom->getConferenceId(); + conferenceId.setLocalAddress(localAccount->getAccountParams()->getIdentityAddress(), true); + + d->mainDb->updateChatRoomConferenceId(oldConfId, conferenceId); + + auto basicChatRoom = dynamic_pointer_cast<BasicChatRoom>(chatRoom); + basicChatRoom->setConferenceId(conferenceId); + + d->mChatRoomsById.erase(oldConfId); + d->mChatRoomsById[conferenceId] = chatRoom; + + updateChatRoomList(); + } + } + reason = handleChatMessagesAggregation(chatRoom, op, sal_msg); } else if (!linphone_core_conference_server_enabled(cCore)) { const char *session_mode = sal_custom_header_find(op->getRecvCustomHeaders(), "Session-mode"); @@ -937,6 +961,16 @@ LinphoneReason Core::onSipMessageReceived(SalOp *op, const SalMessage *sal_msg) lError() << "Message is received in the context of a client chatroom for which we have no context."; reason = LinphoneReasonNotAcceptable; } else { + auto localAccount = + guessLocalAccountFromMalformedMessage(conferenceId.getLocalAddress(), conferenceId.getPeerAddress()); + if (localAccount) { + // We have a match for the from domain and the to username. + // We may face an IPBPX that sets the To domain to our IP address, which is + // a terribly stupid idea. + lWarning() << "Applying workaround to have this chat room assigned to a known account."; + conferenceId.setLocalAddress(localAccount->getAccountParams()->getIdentityAddress(), true); + } + chatRoom = getOrCreateBasicChatRoom(conferenceId); if (chatRoom) { reason = handleChatMessagesAggregation(chatRoom, op, sal_msg); @@ -1030,4 +1064,30 @@ void Core::decrementRemainingDownloadFileCount() { } } +std::shared_ptr<Account> Core::guessLocalAccountFromMalformedMessage(const std::shared_ptr<Address> &localAddress, + const std::shared_ptr<Address> &peerAddress) { + if (!localAddress) return nullptr; + + auto account = findAccountByIdentityAddress(localAddress); + if (!account) { + string toUser = localAddress->getUsername(); + if (!toUser.empty() && peerAddress) { + auto localAddressWithPeerDomain = make_shared<Address>(*localAddress); + localAddressWithPeerDomain->setDomain(peerAddress->getDomain()); + account = lookupKnownAccount(localAddressWithPeerDomain, false); + if (account) { + // We have a match for the from domain and the to username. + // We may face an IPBPX that sets the To domain to our IP address, which is + // a terribly stupid idea. + lWarning() << "Detecting TO header probably ill-choosen."; + return account; + } else { + lWarning() << "Failed to find an account matching TO header"; + } + } + } + + return nullptr; +} + LINPHONE_END_NAMESPACE diff --git a/src/core/core.cpp b/src/core/core.cpp index cc7ac2dac2..ebc6665e2c 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -2867,7 +2867,7 @@ std::shared_ptr<Account> Core::findAccountByIdentityAddress(const std::shared_pt for (const auto &account : accounts) { const auto ¶ms = account->getAccountParams(); const auto &address = params->getIdentityAddress(); - if (identity->weakEqual(*address)) { + if (address && identity->weakEqual(address)) { found = account; break; } diff --git a/src/core/core.h b/src/core/core.h index 4792c87440..406d879bc8 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -474,6 +474,9 @@ private: const ConferenceId prepareConfereceIdForSearch(const ConferenceId &conferenceId) const; void clearProxyConfigList() const; + std::shared_ptr<Account> guessLocalAccountFromMalformedMessage(const std::shared_ptr<Address> &localAddress, + const std::shared_ptr<Address> &peerAddress); + std::list<std::string> plugins; #if defined(_WIN32) && !defined(_WIN32_WCE) std::list<HINSTANCE> loadedPlugins; diff --git a/src/db/main-db.cpp b/src/db/main-db.cpp index f978e0d5c5..0aeef0574a 100644 --- a/src/db/main-db.cpp +++ b/src/db/main-db.cpp @@ -6541,12 +6541,14 @@ void MainDb::updateChatRoomConferenceId(const ConferenceId oldConferenceId, cons L_D(); const long long &peerSipAddressId = d->insertSipAddress(newConferenceId.getPeerAddress()); + const long long &localSipAddressId = d->insertSipAddress(newConferenceId.getLocalAddress()); const long long &dbChatRoomId = d->selectChatRoomId(oldConferenceId); *d->dbSession.getBackendSession() << "UPDATE chat_room" - " SET peer_sip_address_id = :peerSipAddressId" + " SET peer_sip_address_id = :peerSipAddressId," + " local_sip_address_id = :localSipAddressId" " WHERE id = :chatRoomId", - soci::use(peerSipAddressId), soci::use(dbChatRoomId); + soci::use(peerSipAddressId), soci::use(localSipAddressId), soci::use(dbChatRoomId); tr.commit(); diff --git a/tester/message_tester.c b/tester/message_tester.c index 72cf5dbd83..84212a2ca6 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -4445,6 +4445,102 @@ static void baudot_text_message_voice_switch_voice_carryover_europe(void) { #endif /* HAVE_BAUDOT */ +static void crappy_to_header(bool_t existing_chat_room) { + const char *template = "MESSAGE sip:%s@192.168.0.24:9597 SIP/2.0\r\n" + "Via: SIP/2.0/TCP 10.0.0.1;rport;branch=z9hG4bKSg92gr72NvDUp\r\n" + "Route: <sip:%s@192.168.0.24:9597>;transport=tcp\r\n" + "Max-Forwards: 70\r\n" + "From: <sip:marcel@sip.example.org>;tag=yPe~pl-Rg\r\n" + "To: <sip:%s@192.168.0.24:9597>;transport=tcp\r\n" + "Call-ID: e1431428-4229-4744-9479-e432618ba7f9\r\n" + "CSeq: 95887387 MESSAGE\r\n" + "User-Agent: FreeSWITCH\r\n" + "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY, " + "PUBLISH, SUBSCRIBE\r\n" + "Supported: path, replaces\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 2\r\n" + "\r\n" + "Hi\r\n"; + + LinphoneCoreManager *laure = linphone_core_manager_new("laure_rc_udp"); + const char *laure_username = linphone_address_get_username(laure->identity); + + if (existing_chat_room) { + char *localSipUri = bctbx_strdup_printf("sip:%s@192.168.0.24", laure_username); + LinphoneChatRoom *existing_cr = + linphone_core_create_basic_chat_room(laure->lc, localSipUri, "sip:marcel@sip.example.org"); + BC_ASSERT_PTR_NOT_NULL(existing_cr); + if (existing_cr) { + const LinphoneAddress *local_address = linphone_chat_room_get_local_address(existing_cr); + BC_ASSERT_STRING_EQUAL(linphone_address_get_username(local_address), laure_username); + BC_ASSERT_STRING_EQUAL(linphone_address_get_domain(local_address), "192.168.0.24"); + + const LinphoneAddress *peer_address = linphone_chat_room_get_peer_address(existing_cr); + BC_ASSERT_STRING_EQUAL(linphone_address_get_username(peer_address), "marcel"); + BC_ASSERT_STRING_EQUAL(linphone_address_get_domain(peer_address), "sip.example.org"); + } + bctbx_free(localSipUri); + } + + char *message = bctbx_strdup_printf(template, laure_username, laure_username, laure_username); + LinphoneTransports *tp = linphone_core_get_transports_used(laure->lc); + BC_ASSERT_TRUE(liblinphone_tester_send_data(message, strlen(message), "127.0.0.1", + linphone_transports_get_udp_port(tp), SOCK_DGRAM) > 0); + linphone_transports_unref(tp); + bctbx_free(message); + + BC_ASSERT_TRUE(wait_for(laure->lc, NULL, &laure->stat.number_of_LinphoneMessageReceived, 1)); + LinphoneChatMessage *received_message = laure->stat.last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(received_message); + if (received_message != NULL) { + LinphoneChatRoom *chat_room = linphone_chat_message_get_chat_room(received_message); + BC_ASSERT_PTR_NOT_NULL(chat_room); + if (chat_room) { + const LinphoneAddress *local_address = linphone_chat_room_get_local_address(chat_room); + BC_ASSERT_STRING_EQUAL(linphone_address_get_username(local_address), laure_username); + BC_ASSERT_STRING_EQUAL(linphone_address_get_domain(local_address), "sip.example.org"); + + const LinphoneAddress *peer_address = linphone_chat_room_get_peer_address(chat_room); + BC_ASSERT_STRING_EQUAL(linphone_address_get_username(peer_address), "marcel"); + BC_ASSERT_STRING_EQUAL(linphone_address_get_domain(peer_address), "sip.example.org"); + + LinphoneAccount *account = linphone_chat_room_get_account(chat_room); + BC_ASSERT_PTR_NOT_NULL(account); + if (account) { + const LinphoneAddress *identity_address = + linphone_account_params_get_identity_address(linphone_account_get_params(account)); + BC_ASSERT_STRING_EQUAL(linphone_address_get_username(identity_address), laure_username); + BC_ASSERT_STRING_EQUAL(linphone_address_get_domain(identity_address), "sip.example.org"); + + const bctbx_list_t *chat_rooms = linphone_account_get_chat_rooms(account); + BC_ASSERT_PTR_NOT_NULL(chat_rooms); + if (chat_rooms) { + BC_ASSERT_EQUAL((int)bctbx_list_size(chat_rooms), 1, int, "%d"); + LinphoneChatRoom *first_chat_room = (LinphoneChatRoom *)bctbx_list_get_data(chat_rooms); + LinphoneChatMessage *last_message = linphone_chat_room_get_last_message_in_history(first_chat_room); + BC_ASSERT_PTR_NOT_NULL(last_message); + if (last_message) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text_content(last_message), + linphone_chat_message_get_text_content(received_message)); + linphone_chat_message_unref(last_message); + } + } + } + } + } + + linphone_core_manager_destroy(laure); +} + +void text_message_crappy_to_header_existing_chat_room(void) { + crappy_to_header(TRUE); +} + +void text_message_crappy_to_header(void) { + crappy_to_header(FALSE); +} + test_t message_tests[] = { TEST_NO_TAG("File transfer content", file_transfer_content), TEST_NO_TAG("Message with 2 attachments", message_with_two_attachments), @@ -4479,6 +4575,9 @@ test_t message_tests[] = { text_message_in_call_chat_room_from_denied_text_offer_when_room_exists), TEST_NO_TAG("Text message duplication", text_message_duplication), TEST_NO_TAG("Text message auto resent after failure", text_message_auto_resent_after_failure), + TEST_NO_TAG("Text message with crappy TO header", text_message_crappy_to_header), + TEST_NO_TAG("Text message with crappy TO header from existing chat room", + text_message_crappy_to_header_existing_chat_room), TEST_NO_TAG("Transfer message", transfer_message), TEST_NO_TAG("Transfer message 2", transfer_message_2), TEST_NO_TAG("Transfer message 3", transfer_message_3), -- GitLab