From 38ea7dc357595a6c98ee8caf321db65eff9b6036 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 26 Apr 2018 11:06:05 +0200 Subject: [PATCH] Send delivery notifications when loading chat rooms from DB if needed. --- src/chat/chat-room/abstract-chat-room-p.h | 2 + src/chat/chat-room/chat-room-p.h | 1 + src/chat/chat-room/chat-room.cpp | 10 +++ src/chat/chat-room/proxy-chat-room-p.h | 4 + src/core/core-chat-room.cpp | 4 +- src/db/main-db.cpp | 37 +++++++++ src/db/main-db.h | 4 + tester/group_chat_tester.c | 98 +++++++++++++++++++++++ 8 files changed, 159 insertions(+), 1 deletion(-) diff --git a/src/chat/chat-room/abstract-chat-room-p.h b/src/chat/chat-room/abstract-chat-room-p.h index bd2ea703e..de9054965 100644 --- a/src/chat/chat-room/abstract-chat-room-p.h +++ b/src/chat/chat-room/abstract-chat-room-p.h @@ -47,6 +47,8 @@ public: virtual void addTransientEvent (const std::shared_ptr &eventLog) = 0; virtual void removeTransientEvent (const std::shared_ptr &eventLog) = 0; + virtual void sendDeliveryNotifications () = 0; + virtual void notifyChatMessageReceived (const std::shared_ptr &chatMessage) = 0; virtual void notifyUndecryptableChatMessageReceived (const std::shared_ptr &chatMessage) = 0; diff --git a/src/chat/chat-room/chat-room-p.h b/src/chat/chat-room/chat-room-p.h index dfd7c8fa9..e882891c9 100644 --- a/src/chat/chat-room/chat-room-p.h +++ b/src/chat/chat-room/chat-room-p.h @@ -69,6 +69,7 @@ public: void sendDeliveryErrorNotification (const std::shared_ptr &message, LinphoneReason reason); void sendDeliveryNotification (const std::shared_ptr &message); + void sendDeliveryNotifications () override; bool sendDisplayNotification (const std::shared_ptr &message); void notifyChatMessageReceived (const std::shared_ptr &chatMessage) override; diff --git a/src/chat/chat-room/chat-room.cpp b/src/chat/chat-room/chat-room.cpp index 69e1e3f94..50a48b567 100644 --- a/src/chat/chat-room/chat-room.cpp +++ b/src/chat/chat-room/chat-room.cpp @@ -153,6 +153,16 @@ void ChatRoomPrivate::sendDeliveryNotification (const shared_ptr &m imdnHandler->notifyDelivery(message); } +void ChatRoomPrivate::sendDeliveryNotifications () { + L_Q(); + LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(q->getCore()->getCCore()); + if (linphone_im_notif_policy_get_send_imdn_delivered(policy)) { + auto messages = q->getCore()->getPrivate()->mainDb->findChatMessagesToBeNotifiedAsDelivered(q->getChatRoomId()); + for (const auto message : messages) + imdnHandler->notifyDelivery(message); + } +} + bool ChatRoomPrivate::sendDisplayNotification (const shared_ptr &message) { L_Q(); LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(q->getCore()->getCCore()); diff --git a/src/chat/chat-room/proxy-chat-room-p.h b/src/chat/chat-room/proxy-chat-room-p.h index b10aa3d8a..fc6840b34 100644 --- a/src/chat/chat-room/proxy-chat-room-p.h +++ b/src/chat/chat-room/proxy-chat-room-p.h @@ -57,6 +57,10 @@ public: chatRoom->getPrivate()->removeTransientEvent(eventLog); } + inline void sendDeliveryNotifications () override { + chatRoom->getPrivate()->sendDeliveryNotifications(); + } + inline void notifyChatMessageReceived (const std::shared_ptr &chatMessage) override { chatRoom->getPrivate()->notifyChatMessageReceived(chatMessage); } diff --git a/src/core/core-chat-room.cpp b/src/core/core-chat-room.cpp index cac503c2c..fdd7eb5a5 100644 --- a/src/core/core-chat-room.cpp +++ b/src/core/core-chat-room.cpp @@ -179,8 +179,10 @@ void CorePrivate::insertChatRoomWithDb (const shared_ptr &chat void CorePrivate::loadChatRooms () { chatRooms.clear(); chatRoomsById.clear(); - for (auto &chatRoom : mainDb->getChatRooms()) + for (auto &chatRoom : mainDb->getChatRooms()) { insertChatRoom(chatRoom); + chatRoom->getPrivate()->sendDeliveryNotifications(); + } } void CorePrivate::replaceChatRoom (const shared_ptr &replacedChatRoom, const shared_ptr &newChatRoom) { diff --git a/src/db/main-db.cpp b/src/db/main-db.cpp index 2f104f576..dcb589a60 100644 --- a/src/db/main-db.cpp +++ b/src/db/main-db.cpp @@ -2148,6 +2148,43 @@ list> MainDb::findChatMessages ( }; } +list> MainDb::findChatMessagesToBeNotifiedAsDelivered ( + const ChatRoomId &chatRoomId +) const { + static const string query = Statements::get(Statements::SelectConferenceEvents) + + string(" AND direction = :direction AND state = :state AND delivery_notification_required <> 0"); + + DurationLogger durationLogger( + "Find chat messages to be notified as delivered: (peer=" + chatRoomId.getPeerAddress().asString() + + ", local=" + chatRoomId.getLocalAddress().asString() + ")." + ); + + return L_DB_TRANSACTION { + L_D(); + + shared_ptr chatRoom = d->findChatRoom(chatRoomId); + list> chatMessages; + if (!chatRoom) + return chatMessages; + + const long long &dbChatRoomId = d->selectChatRoomId(chatRoomId); + const int &state = int(ChatMessage::State::Delivered); + const int &direction = int(ChatMessage::Direction::Incoming); + soci::rowset rows = ( + d->dbSession.getBackendSession()->prepare << query, soci::use(dbChatRoomId), soci::use(direction), soci::use(state) + ); + for (const auto &row : rows) { + shared_ptr event = d->selectGenericConferenceEvent(chatRoom, row); + if (event) { + L_ASSERT(event->getType() == EventLog::Type::ConferenceChatMessage); + chatMessages.push_back(static_pointer_cast(event)->getChatMessage()); + } + } + + return chatMessages; + }; +} + list> MainDb::getHistory (const ChatRoomId &chatRoomId, int nLast, FilterMask mask) const { return getHistoryRange(chatRoomId, 0, nLast, mask); } diff --git a/src/db/main-db.h b/src/db/main-db.h index bb134ab50..e43cb9130 100644 --- a/src/db/main-db.h +++ b/src/db/main-db.h @@ -130,6 +130,10 @@ public: const std::string &imdnMessageId ) const; + std::list> findChatMessagesToBeNotifiedAsDelivered ( + const ChatRoomId &chatRoomId + ) const; + // --------------------------------------------------------------------------- // Conference events. // --------------------------------------------------------------------------- diff --git a/tester/group_chat_tester.c b/tester/group_chat_tester.c index 98e714a89..f183aa73c 100644 --- a/tester/group_chat_tester.c +++ b/tester/group_chat_tester.c @@ -3235,6 +3235,103 @@ static void aggregated_imdn_for_group_chat_room_read_while_offline (void) { aggregated_imdn_for_group_chat_room_base(TRUE); } +static void imdn_sent_from_db_state (void) { + LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); + LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); + LinphoneCoreManager *chloe = linphone_core_manager_create("chloe_rc"); + bctbx_list_t *coresManagerList = NULL; + bctbx_list_t *participantsAddresses = NULL; + coresManagerList = bctbx_list_append(coresManagerList, marie); + coresManagerList = bctbx_list_append(coresManagerList, pauline); + coresManagerList = bctbx_list_append(coresManagerList, chloe); + bctbx_list_t *coresList = init_core_for_conference(coresManagerList); + start_core_for_conference(coresManagerList); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_new(linphone_core_get_identity(pauline->lc))); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_new(linphone_core_get_identity(chloe->lc))); + stats initialMarieStats = marie->stat; + stats initialPaulineStats = pauline->stat; + stats initialChloeStats = chloe->stat; + time_t initialTime = ms_time(NULL); + + // Enable IMDN except for Marie + linphone_im_notif_policy_clear(linphone_core_get_im_notif_policy(marie->lc)); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline->lc)); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(chloe->lc)); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues"; + LinphoneChatRoom *marieCr = create_chat_room_client_side(coresList, marie, &initialMarieStats, participantsAddresses, initialSubject, -1); + LinphoneAddress *confAddr = linphone_address_clone(linphone_chat_room_get_conference_address(marieCr)); + + // 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, 2, FALSE); + + // Check that the chat room is correctly created on Chloe's side and that the participants are added + LinphoneChatRoom *chloeCr = check_creation_chat_room_client_side(coresList, chloe, &initialChloeStats, confAddr, initialSubject, 2, FALSE); + + // Chloe begins composing a message + const char *chloeTextMessage = "Hello"; + LinphoneChatMessage *chloeMessage = _send_message(chloeCr, chloeTextMessage); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageReceived, initialMarieStats.number_of_LinphoneMessageReceived + 1, 3000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageReceived, initialPaulineStats.number_of_LinphoneMessageReceived + 1, 3000)); + LinphoneChatMessage *marieLastMsg = marie->stat.last_received_chat_message; + if (!BC_ASSERT_PTR_NOT_NULL(marieLastMsg)) + goto end; + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(marieLastMsg), chloeTextMessage); + LinphoneAddress *chloeAddr = linphone_address_new(linphone_core_get_identity(chloe->lc)); + BC_ASSERT_TRUE(linphone_address_weak_equal(chloeAddr, linphone_chat_message_get_from_address(marieLastMsg))); + linphone_address_unref(chloeAddr); + + // Check that the message is not globally marked as delivered to user since Marie do not notify its delivery + BC_ASSERT_FALSE(wait_for_list(coresList, &chloe->stat.number_of_LinphoneMessageDeliveredToUser, initialChloeStats.number_of_LinphoneMessageDeliveredToUser + 1, 3000)); + + // Restart Marie's core with IMDN enabled so that delivery notification is sent when chat room is loaded from DB + coresList = bctbx_list_remove(coresList, marie->lc); + linphone_core_manager_reinit(marie); + bctbx_list_t *tmpCoresManagerList = bctbx_list_append(NULL, marie); + bctbx_list_t *tmpCoresList = init_core_for_conference(tmpCoresManagerList); + bctbx_list_free(tmpCoresManagerList); + coresList = bctbx_list_concat(coresList, tmpCoresList); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie->lc)); + linphone_core_manager_start(marie, TRUE); + char *marieIdentity = linphone_core_get_device_identity(marie->lc); + LinphoneAddress *marieAddr = linphone_address_new(marieIdentity); + bctbx_free(marieIdentity); + marieCr = linphone_core_find_chat_room(marie->lc, confAddr, marieAddr); + linphone_address_unref(marieAddr); + linphone_address_unref(confAddr); + + // Check that the message has been delivered to Marie and Pauline + BC_ASSERT_TRUE(wait_for_list(coresList, &chloe->stat.number_of_LinphoneMessageDeliveredToUser, initialChloeStats.number_of_LinphoneMessageDeliveredToUser + 1, 3000)); + BC_ASSERT_PTR_NULL(linphone_chat_message_get_participants_that_have_displayed(chloeMessage)); + bctbx_list_t *participantsThatReceivedChloeMessage = linphone_chat_message_get_participants_that_have_received(chloeMessage); + if (BC_ASSERT_PTR_NOT_NULL(participantsThatReceivedChloeMessage)) { + BC_ASSERT_EQUAL((int)bctbx_list_size(participantsThatReceivedChloeMessage), 2, int, "%d"); + for (bctbx_list_t *item = participantsThatReceivedChloeMessage; item; item = bctbx_list_next(item)) { + LinphoneParticipantImdnState *state = (LinphoneParticipantImdnState *)bctbx_list_get_data(item); + BC_ASSERT_GREATER(linphone_participant_imdn_state_get_state_change_time(state), initialTime, int, "%d"); + BC_ASSERT_EQUAL(linphone_participant_imdn_state_get_state(state), LinphoneChatMessageStateDeliveredToUser, int, "%d"); + BC_ASSERT_PTR_NOT_NULL(linphone_participant_imdn_state_get_participant(state)); + } + bctbx_list_free_with_data(participantsThatReceivedChloeMessage, (bctbx_list_free_func)linphone_participant_imdn_state_unref); + } + BC_ASSERT_PTR_NULL(linphone_chat_message_get_participants_that_have_not_received(chloeMessage)); + + linphone_chat_message_unref(chloeMessage); + +end: + // Clean db from chat room + linphone_core_manager_delete_chat_room(marie, marieCr, coresList); + linphone_core_manager_delete_chat_room(chloe, chloeCr, coresList); + 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); + linphone_core_manager_destroy(chloe); +} + 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"); @@ -3423,6 +3520,7 @@ test_t group_chat_tests[] = { TEST_NO_TAG("IMDN for group chat room", imdn_for_group_chat_room), TEST_NO_TAG("Aggregated IMDN for group chat room", aggregated_imdn_for_group_chat_room), TEST_NO_TAG("Aggregated IMDN for group chat room read while offline", aggregated_imdn_for_group_chat_room_read_while_offline), + TEST_ONE_TAG("IMDN sent from DB state", imdn_sent_from_db_state, "LeaksMemory"), TEST_NO_TAG("Find one to one chat room", find_one_to_one_chat_room), TEST_NO_TAG("New device after group chat room creation", group_chat_room_new_device_after_creation) };