diff --git a/src/chat/chat-room/client-group-chat-room.cpp b/src/chat/chat-room/client-group-chat-room.cpp index 12e54d1ce0422c4a48fe7a6ffe3cb75d3f8e4a1f..86a0e390a6baa4c8c59918bcbe3d8886a9b94cd6 100644 --- a/src/chat/chat-room/client-group-chat-room.cpp +++ b/src/chat/chat-room/client-group-chat-room.cpp @@ -314,7 +314,11 @@ RemoteConference(core, me->getAddress(), nullptr) { getMe()->getPrivate()->addDevice(device->getAddress(), device->getName()); dConference->eventHandler->setConferenceId(conferenceId); - dConference->eventHandler->setLastNotify(lastNotifyId); + + bool_t forceFullState = linphone_config_get_bool(linphone_core_get_config(getCore()->getCCore()), "misc", "conference_event_package_force_full_state",FALSE ); + dConference->eventHandler->setLastNotify(forceFullState?0:lastNotifyId); + lInfo() << "Last notify set to [" << dConference->eventHandler->getLastNotify() << "] for conference [" << dConference << "]"; + if (!hasBeenLeft) getCore()->getPrivate()->remoteListEventHandler->addHandler(dConference->eventHandler.get()); } @@ -725,7 +729,10 @@ void ClientGroupChatRoom::onFirstNotifyReceived (const IdentityAddress &addr) { time(nullptr), d->conferenceId ); - d->addEvent(event); + + bool_t forceFullState = linphone_config_get_bool(linphone_core_get_config(getCore()->getCCore()), "misc", "conference_event_package_force_full_state",FALSE ); + if (!forceFullState) //to avoid this event to be repeated for each full state + d->addEvent(event); LinphoneChatRoom *cr = d->getCChatRoom(); _linphone_chat_room_notify_conference_joined(cr, L_GET_C_BACK_PTR(event)); @@ -907,7 +914,14 @@ void ClientGroupChatRoom::onParticipantDeviceRemoved (const shared_ptr<Conferenc void ClientGroupChatRoom::onParticipantsCleared () { L_D_T(RemoteConference, dConference); + //clear from db as well + for (const auto &participant : dConference->participants) { + getCore()->getPrivate()->mainDb->deleteChatRoomParticipant(getSharedFromThis(), participant->getAddress()); + for (const auto &device : participant->getPrivate()->getDevices()) + getCore()->getPrivate()->mainDb->deleteChatRoomParticipantDevice(getSharedFromThis(), device); + } dConference->participants.clear(); + } LINPHONE_END_NAMESPACE diff --git a/src/chat/encryption/lime-x3dh-encryption-engine.cpp b/src/chat/encryption/lime-x3dh-encryption-engine.cpp index 0682eb56c085304a7800adcc2ab410f7d9cf5af5..e40b5b702955933671d2a4337d248a2724001dad 100644 --- a/src/chat/encryption/lime-x3dh-encryption-engine.cpp +++ b/src/chat/encryption/lime-x3dh-encryption-engine.cpp @@ -371,10 +371,12 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processIncomingMessage ( // Discard incoming messages from unsafe peer devices lime::PeerDeviceStatus peerDeviceStatus = limeManager->get_peerDeviceStatus(senderDeviceId); - if (peerDeviceStatus == lime::PeerDeviceStatus::unsafe) { - lWarning() << "LIME X3DH discard incoming message from unsafe sender device " << senderDeviceId; - errorCode = 488; // Not Acceptable - return ChatMessageModifier::Result::Error; + if (linphone_config_get_int(linphone_core_get_config(chatRoom->getCore()->getCCore()), "lime", "allow_message_in_unsafe_chatroom", 0) == 0) { + if (peerDeviceStatus == lime::PeerDeviceStatus::unsafe) { + lWarning() << "LIME X3DH discard incoming message from unsafe sender device " << senderDeviceId; + errorCode = 488; // Not Acceptable + return ChatMessageModifier::Result::Error; + } } // ---------------------------------------------- HEADERS diff --git a/src/db/main-db.cpp b/src/db/main-db.cpp index e7a1708a743de4be5cf02365ae5888509aeacd5a..a2edf982b65aa4339e3c144af040bf06b7aedcfb 100644 --- a/src/db/main-db.cpp +++ b/src/db/main-db.cpp @@ -337,22 +337,22 @@ long long MainDbPrivate::insertChatRoom (const shared_ptr<AbstractChatRoom> &cha long long chatRoomId = selectChatRoomId(peerSipAddressId, localSipAddressId); if (chatRoomId >= 0) { // The chat room is already stored in DB, but still update the notify id that might have changed + lInfo() << "Update chat room in database: " << conferenceId << "."; *dbSession.getBackendSession() << "UPDATE chat_room SET last_notify_id = :lastNotifyId WHERE id = :chatRoomId", soci::use(notifyId), soci::use(chatRoomId); - return chatRoomId; - } - - lInfo() << "Insert new chat room in database: " << conferenceId << "."; - - const tm &creationTime = Utils::getTimeTAsTm(chatRoom->getCreationTime()); - const tm &lastUpdateTime = Utils::getTimeTAsTm(chatRoom->getLastUpdateTime()); - - // Remove capabilities like `Proxy`. - const int &capabilities = chatRoom->getCapabilities() & ~ChatRoom::CapabilitiesMask(ChatRoom::Capabilities::Proxy); - - const string &subject = chatRoom->getSubject(); - const int &flags = chatRoom->hasBeenLeft(); - *dbSession.getBackendSession() << "INSERT INTO chat_room (" + } else { + + lInfo() << "Insert new chat room in database: " << conferenceId << "."; + + const tm &creationTime = Utils::getTimeTAsTm(chatRoom->getCreationTime()); + const tm &lastUpdateTime = Utils::getTimeTAsTm(chatRoom->getLastUpdateTime()); + + // Remove capabilities like `Proxy`. + const int &capabilities = chatRoom->getCapabilities() & ~ChatRoom::CapabilitiesMask(ChatRoom::Capabilities::Proxy); + + const string &subject = chatRoom->getSubject(); + const int &flags = chatRoom->hasBeenLeft(); + *dbSession.getBackendSession() << "INSERT INTO chat_room (" " peer_sip_address_id, local_sip_address_id, creation_time," " last_update_time, capabilities, subject, flags, last_notify_id" ") VALUES (" @@ -361,9 +361,9 @@ long long MainDbPrivate::insertChatRoom (const shared_ptr<AbstractChatRoom> &cha ")", soci::use(peerSipAddressId), soci::use(localSipAddressId), soci::use(creationTime), soci::use(lastUpdateTime), soci::use(capabilities), soci::use(subject), soci::use(flags), soci::use(notifyId); - - chatRoomId = dbSession.getLastInsertId(); - + + chatRoomId = dbSession.getLastInsertId(); + } // Do not add 'me' when creating a server-group-chat-room. if (conferenceId.getLocalAddress() != conferenceId.getPeerAddress()) { shared_ptr<Participant> me = chatRoom->getMe(); @@ -2851,6 +2851,27 @@ void MainDb::updateChatRoomParticipantDevice ( }; } +void MainDb::deleteChatRoomParticipant ( + const std::shared_ptr<AbstractChatRoom> &chatRoom, + const IdentityAddress &participant +){ + L_D(); + const long long &dbChatRoomId = d->selectChatRoomId(chatRoom->getConferenceId()); + const long long &participantSipAddressId = d->selectSipAddressId(participant.asString()); + d->deleteChatRoomParticipant(dbChatRoomId, participantSipAddressId); +} + +void MainDb::deleteChatRoomParticipantDevice ( + const shared_ptr<AbstractChatRoom> &chatRoom, + const shared_ptr<ParticipantDevice> &device +) { + L_D(); + const long long &dbChatRoomId = d->selectChatRoomId(chatRoom->getConferenceId()); + const long long &participantSipAddressId = d->selectSipAddressId(device->getParticipant()->getAddress().asString()); + const long long &participantId = d->selectChatRoomParticipantId(dbChatRoomId, participantSipAddressId); + d->deleteChatRoomParticipantDevice(participantId, participantSipAddressId); +} + // ----------------------------------------------------------------------------- bool MainDb::import (Backend, const string ¶meters) { diff --git a/src/db/main-db.h b/src/db/main-db.h index 5e92de7fae56b0d97fd4241e1803d1d74e06ced0..7ce57d797d22f46b918e39cd025040b87193f0fe 100644 --- a/src/db/main-db.h +++ b/src/db/main-db.h @@ -189,6 +189,16 @@ public: const std::shared_ptr<ParticipantDevice> &device ); + void deleteChatRoomParticipant ( + const std::shared_ptr<AbstractChatRoom> &chatRoom, + const IdentityAddress &participant + ); + + void deleteChatRoomParticipantDevice ( + const std::shared_ptr<AbstractChatRoom> &chatRoom, + const std::shared_ptr<ParticipantDevice> &device + ); + // --------------------------------------------------------------------------- // Other. // --------------------------------------------------------------------------- diff --git a/tester/group_chat_tester.c b/tester/group_chat_tester.c index ebe18a0a259257a6bed89aec5fca84c74e66ad9e..2e3135746001af423dbff234597d6e0b13a92ca1 100644 --- a/tester/group_chat_tester.c +++ b/tester/group_chat_tester.c @@ -70,6 +70,13 @@ static void chat_room_participant_device_added (LinphoneChatRoom *cr, const Linp manager->stat.number_of_participant_devices_added++; } +static void chat_room_participant_device_removed (LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { + LinphoneCore *core = linphone_chat_room_get_core(cr); + LinphoneCoreManager *manager = (LinphoneCoreManager *)linphone_core_get_user_data(core); + manager->stat.number_of_participant_devices_removed++; +} + + static void chat_room_state_changed (LinphoneChatRoom *cr, LinphoneChatRoomState newState) { LinphoneCore *core = linphone_chat_room_get_core(cr); LinphoneCoreManager *manager = (LinphoneCoreManager *)linphone_core_get_user_data(core); @@ -153,6 +160,7 @@ void core_chat_room_state_changed (LinphoneCore *core, LinphoneChatRoom *cr, Lin linphone_chat_room_cbs_set_security_event(cbs, chat_room_security_event); linphone_chat_room_cbs_set_subject_changed(cbs, chat_room_subject_changed); linphone_chat_room_cbs_set_participant_device_added(cbs, chat_room_participant_device_added); + linphone_chat_room_cbs_set_participant_device_removed(cbs, chat_room_participant_device_removed); linphone_chat_room_cbs_set_undecryptable_message_received(cbs, undecryptable_message_received); linphone_chat_room_cbs_set_conference_joined(cbs, chat_room_conference_joined); linphone_chat_room_add_callbacks(cr, cbs); @@ -4815,6 +4823,8 @@ static void group_chat_lime_x3dh_reject_sas_before_message (void) { BC_ASSERT_EQUAL(linphone_core_get_zrtp_status(marie->lc, linphone_address_as_string_uri_only(paulineAddr)), LinphoneZrtpPeerStatusValid, int , "%d"); BC_ASSERT_EQUAL(linphone_core_get_zrtp_status(pauline->lc, linphone_address_as_string_uri_only(marieAddr)), LinphoneZrtpPeerStatusValid, int , "%d"); + + // Delete chatrooms linphone_core_manager_delete_chat_room(marie, marieCr, coresList); linphone_core_manager_delete_chat_room(pauline, paulineCr, coresList); @@ -6987,8 +6997,6 @@ static void participant_removed_then_added (void) { stats initialPauline1Stats = pauline1->stat; stats initialLaureStats = laure->stat; - - // Marie creates a new group chat room const char *initialSubject = "Colleagues"; LinphoneChatRoom *marie1Cr = create_chat_room_client_side(coresList, marie1, &initialMarie1Stats, participantsAddresses, initialSubject, FALSE); @@ -7053,6 +7061,133 @@ static void participant_removed_then_added (void) { linphone_core_manager_destroy(laure); } +static void group_chat_room_join_one_to_one_chat_room_with_a_new_device_not_notified(void) { + LinphoneCoreManager *marie1 = linphone_core_manager_create("marie_rc"); + LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); + bctbx_list_t *coresManagerList = NULL; + coresManagerList = bctbx_list_append(coresManagerList, marie1); + coresManagerList = bctbx_list_append(coresManagerList, pauline); + bctbx_list_t *coresList = init_core_for_conference(coresManagerList); + start_core_for_conference(coresManagerList); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_new(linphone_core_get_identity(pauline->lc))); + stats initialMarie1Stats = marie1->stat; + stats initialPaulineStats = pauline->stat; + + // Marie1 creates a new one-to-one chat room with Pauline + const char *initialSubject = "Pauline"; + LinphoneChatRoom *marie1Cr = create_chat_room_client_side(coresList, marie1, &initialMarie1Stats, participantsAddresses, initialSubject, FALSE); + BC_ASSERT_TRUE(linphone_chat_room_get_capabilities(marie1Cr) & LinphoneChatRoomCapabilitiesOneToOne); + + LinphoneAddress *confAddr = linphone_address_clone(linphone_chat_room_get_conference_address(marie1Cr)); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline, &initialPaulineStats, confAddr, initialSubject, 1, FALSE); + BC_ASSERT_TRUE(linphone_chat_room_get_capabilities(paulineCr) & LinphoneChatRoomCapabilitiesOneToOne); + + initialPaulineStats.number_of_participant_devices_added = pauline->stat.number_of_participant_devices_added; + LinphoneCoreManager *marie2 = linphone_core_manager_create("marie_rc"); + stats initialMarie2Stats = marie2->stat; + + bctbx_list_t *newCoresManagerList = bctbx_list_append(NULL, marie2); + bctbx_list_t *newCoresList = init_core_for_conference(newCoresManagerList); + start_core_for_conference(newCoresManagerList); + coresManagerList = bctbx_list_concat(coresManagerList, newCoresManagerList); + coresList = bctbx_list_concat(coresList, newCoresList); + + // Marie2 gets the one-to-one chat room with Pauline + LinphoneChatRoom *marie2Cr = check_creation_chat_room_client_side(coresList, marie2, &initialMarie2Stats, confAddr, initialSubject, 1, FALSE); + BC_ASSERT_TRUE(linphone_chat_room_get_capabilities(marie2Cr) & LinphoneChatRoomCapabilitiesOneToOne); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_participant_devices_added, initialPaulineStats.number_of_participant_devices_added + 1, 3000)); + + // Save pauline db + const char *uri = lp_config_get_string(linphone_core_get_config(pauline->lc), "storage", "uri", ""); + char *uriCopy = bc_tester_file("linphone_tester.db"); + BC_ASSERT_FALSE(liblinphone_tester_copy_file(uri, uriCopy)); + int initialPaulineEvent = linphone_chat_room_get_history_events_size(paulineCr); + // Simulate an uninstall of the application on Marie's side with unregistration (It should remove the device) + coresManagerList = bctbx_list_remove(coresManagerList, marie1); + coresList = bctbx_list_remove(coresList, marie1->lc); + initialPaulineStats = pauline->stat; + linphone_core_manager_destroy(marie1); + //force flexisip to publish on marie topic + linphone_core_refresh_registers(marie2->lc); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_participant_devices_removed, initialPaulineStats.number_of_participant_devices_removed + 1, 3000)); + + // Reset db at pauline side + pauline->database_path = uriCopy; + coresList = bctbx_list_remove(coresList, pauline->lc); + memset(&initialPaulineStats, 0, sizeof(initialPaulineStats)); + + + linphone_core_manager_reinit(pauline); + //force full state + linphone_config_set_bool(linphone_core_get_config(pauline->lc), "misc", "conference_event_package_force_full_state",TRUE); + bctbx_list_t *tmpCoresManagerList = bctbx_list_append(NULL, pauline); + bctbx_list_t *tmpCoresList = init_core_for_conference(tmpCoresManagerList); + bctbx_list_free(tmpCoresManagerList); + coresList = bctbx_list_concat(coresList, tmpCoresList); + linphone_core_manager_start(pauline, TRUE); + + //wait for first notify to be received by pauline + wait_for_list(coresList, NULL, 0, 1000); + + // Marie2 gets the one-to-one chat room with Pauline + paulineCr = check_has_chat_room_client_side(coresList, pauline, &initialPaulineStats, confAddr, initialSubject, 1, FALSE); + LinphoneAddress *marieAddress = linphone_address_new(linphone_core_get_identity(marie2->lc)); + LinphoneParticipant *marieParticipant = linphone_chat_room_find_participant(paulineCr, marieAddress); + BC_ASSERT_EQUAL(bctbx_list_size(linphone_participant_get_devices (marieParticipant)), 1, int, "%i"); + + //recheck after restart + coresList = bctbx_list_remove(coresList, pauline->lc); + linphone_core_manager_reinit(pauline); + linphone_config_set_bool(linphone_core_get_config(pauline->lc), "misc", "conference_event_package_force_full_state",FALSE); + tmpCoresManagerList = bctbx_list_append(NULL, pauline); + tmpCoresList = init_core_for_conference(tmpCoresManagerList); + bctbx_list_free(tmpCoresManagerList); + coresList = bctbx_list_concat(coresList, tmpCoresList); + linphone_core_manager_start(pauline, TRUE); + + + //wait for first notify to be received by pauline + wait_for_list(coresList, NULL, 0, 1000); + + // Marie2 gets the one-to-one chat room with Pauline + paulineCr = check_has_chat_room_client_side(coresList, pauline, &initialPaulineStats, confAddr, initialSubject, 1, FALSE); + marieParticipant = linphone_chat_room_find_participant(paulineCr, marieAddress); + BC_ASSERT_EQUAL(bctbx_list_size(linphone_participant_get_devices (marieParticipant)), 1, int, "%i"); + BC_ASSERT_EQUAL(linphone_chat_room_get_history_events_size(paulineCr), initialPaulineEvent, int, "%i"); + + //check if we can still communicate + // Marie1 sends a message + const char *textMessage = "Hello"; + LinphoneChatMessage *message = _send_message(marie2Cr, textMessage); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie2->stat.number_of_LinphoneMessageDelivered, initialMarie1Stats.number_of_LinphoneMessageDelivered + 1, 3000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageReceived, initialPaulineStats.number_of_LinphoneMessageReceived + 1, 3000)); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(pauline->stat.last_received_chat_message), textMessage); + linphone_chat_message_unref(message); + + // Pauline answers to the previous message + textMessage = "Hey. How are you?"; + message = _send_message(paulineCr, textMessage); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageDelivered, initialPaulineStats.number_of_LinphoneMessageDelivered + 1, 3000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie2->stat.number_of_LinphoneMessageReceived, initialMarie1Stats.number_of_LinphoneMessageReceived + 1, 3000)); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(marie2->stat.last_received_chat_message), textMessage); + linphone_chat_message_unref(message); + + // Clean db from chat room + + linphone_core_manager_delete_chat_room(marie2, marie2Cr, coresList); + linphone_core_manager_delete_chat_room(pauline, paulineCr, coresList); + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + bctbx_list_free(coresManagerList); + linphone_core_manager_destroy(marie2); + linphone_core_manager_destroy(pauline); +} + + test_t group_chat_tests[] = { TEST_NO_TAG("Group chat room creation server", group_chat_room_creation_server), TEST_ONE_TAG("Add participant", group_chat_room_add_participant, "LeaksMemory"), @@ -7134,7 +7269,8 @@ test_t group_chat_tests[] = { TEST_TWO_TAGS("Search friend result chat room participants", search_friend_chat_room_participants, "MagicSearch", "LeaksMemory"), TEST_ONE_TAG("Group chat room notify participant devices name", group_chat_room_participant_devices_name, "LeaksMemory" /* Core restarts */), TEST_NO_TAG("Add device in one to one chat room where other participant left", add_device_one_to_one_chat_room_other_left), - TEST_ONE_TAG("Participant removed then added", participant_removed_then_added, "LeaksMemory" /*due to core restart*/) + TEST_ONE_TAG("Participant removed then added", participant_removed_then_added, "LeaksMemory" /*due to core restart*/), + TEST_ONE_TAG("Check if participant device are removed", group_chat_room_join_one_to_one_chat_room_with_a_new_device_not_notified, "LeaksMemory" /*due to core restart*/) }; test_suite_t group_chat_test_suite = { diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 28087704af8226c4ffe1c2db46ec191fdb50638e..3de5775bccb809ccffaffe7b0195c5444c011a24 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -307,6 +307,7 @@ typedef struct _stats { int number_of_participants_removed; int number_of_subject_changed; int number_of_participant_devices_added; + int number_of_participant_devices_removed; int number_of_SecurityLevelDowngraded; int number_of_ParticipantMaxDeviceCountExceeded;