diff --git a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml index 5e578d02acb4a213ee90534b2a1e4611cef0683e..d42b6094b5ff48a1b0cffa89ff50544852111014 100644 --- a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml +++ b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml @@ -1969,6 +1969,33 @@ </arg> </signal> + <signal name="conversationMemberEvent" tp:name-for-bindings="conversationMemberEvent"> + <tp:added version="10.0.0"/> + <tp:docstring> + Notify clients when a member is invited/added/removed/banned + </tp:docstring> + <arg type="s" name="account_id"> + <tp:docstring> + Account id related + </tp:docstring> + </arg> + <arg type="s" name="conversation_id"> + <tp:docstring> + Conversation id + </tp:docstring> + </arg> + <arg type="s" name="member_uri"> + <tp:docstring> + The URI of the member added + </tp:docstring> + </arg> + <arg type="i" name="event"> + <tp:docstring> + event: 0 = add, 1 = joins, 2 = leave, 3 = banned + </tp:docstring> + </arg> + </signal> + <signal name="debugMessageReceived" tp:name-for-bindings="debugMessageReceived"> <tp:added version="5.2.0"/> <tp:docstring> diff --git a/bin/dbus/dbusclient.cpp b/bin/dbus/dbusclient.cpp index 5771092feb6be72ae8f56f1923825c344514da3e..adb3b3d81e3432c04724a715e93289f3dc38664c 100644 --- a/bin/dbus/dbusclient.cpp +++ b/bin/dbus/dbusclient.cpp @@ -301,6 +301,8 @@ DBusClient::initLibrary(int flags) bind(&DBusConfigurationManager::conversationReady, confM, _1, _2)), exportable_callback<ConversationSignal::ConversationRemoved>( bind(&DBusConfigurationManager::conversationRemoved, confM, _1, _2)), + exportable_callback<ConversationSignal::ConversationMemberEvent>( + bind(&DBusConfigurationManager::conversationMemberEvent, confM, _1, _2, _3, _4)), }; #ifdef ENABLE_VIDEO diff --git a/bin/jni/conversation.i b/bin/jni/conversation.i index 36fed2c044ece2d2cd28ee18cffdb951f4c97130..40b249a44dca21da1946c5c761f0c418c1ce99d8 100644 --- a/bin/jni/conversation.i +++ b/bin/jni/conversation.i @@ -30,6 +30,7 @@ public: virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){} virtual void conversationReady(const std::string& /*accountId*/, const std::string& /* conversationId */){} virtual void conversationRemoved(const std::string& /*accountId*/, const std::string& /* conversationId */){} + virtual void conversationMemberEvent(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* memberUri */, int /* event */){} }; %} @@ -64,4 +65,5 @@ public: virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){} virtual void conversationReady(const std::string& /*accountId*/, const std::string& /* conversationId */){} virtual void conversationRemoved(const std::string& /*accountId*/, const std::string& /* conversationId */){} + virtual void conversationMemberEvent(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* memberUri */, int /* event */){} }; \ No newline at end of file diff --git a/bin/jni/jni_interface.i b/bin/jni/jni_interface.i index f846733a60b44286cc0a6161541a7a60b1d9db3a..10953578e6c31e25355bf6d8f466dd6d7ec66970 100644 --- a/bin/jni/jni_interface.i +++ b/bin/jni/jni_interface.i @@ -318,7 +318,8 @@ void init(ConfigurationCallback* confM, Callback* callM, PresenceCallback* presM exportable_callback<ConversationSignal::MessageReceived>(bind(&ConversationCallback::messageReceived, convM, _1, _2, _3)), exportable_callback<ConversationSignal::ConversationRequestReceived>(bind(&ConversationCallback::conversationRequestReceived, convM, _1, _2, _3)), exportable_callback<ConversationSignal::ConversationReady>(bind(&ConversationCallback::conversationReady, convM, _1, _2)), - exportable_callback<ConversationSignal::ConversationRemoved>(bind(&ConversationCallback::conversationRemoved, convM, _1, _2)) + exportable_callback<ConversationSignal::ConversationRemoved>(bind(&ConversationCallback::conversationRemoved, convM, _1, _2)), + exportable_callback<ConversationSignal::ConversationMemberEvent>(bind(&ConversationCallback::conversationMemberEvent, convM, _1, _2, _3, _4)) }; if (!DRing::init(static_cast<DRing::InitFlag>(DRing::DRING_FLAG_DEBUG))) diff --git a/bin/nodejs/conversation.i b/bin/nodejs/conversation.i index 2123934d38cda4710a55b0d41f4542f0aa701203..04377ee7fe0adfc48d41d4781e4eb9066b44638e 100644 --- a/bin/nodejs/conversation.i +++ b/bin/nodejs/conversation.i @@ -30,6 +30,7 @@ public: virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){} virtual void conversationReady(const std::string& /*accountId*/, const std::string& /* conversationId */){} virtual void conversationRemoved(const std::string& /*accountId*/, const std::string& /* conversationId */){} + virtual void conversationMemberEvent(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* memberUri */, int /* event */){} }; %} @@ -77,4 +78,5 @@ public: virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){} virtual void conversationReady(const std::string& /*accountId*/, const std::string& /* conversationId */){} virtual void conversationRemoved(const std::string& /*accountId*/, const std::string& /* conversationId */){} + virtual void conversationMemberEvent(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* memberUri */, int /* event */){} }; \ No newline at end of file diff --git a/src/client/ring_signal.cpp b/src/client/ring_signal.cpp index a6e8a949669cd24e550079844c4ad40f1de7fdb0..5d612b01dc76f4228b50923224f4003a2fb400a5 100644 --- a/src/client/ring_signal.cpp +++ b/src/client/ring_signal.cpp @@ -133,6 +133,7 @@ getSignalHandlers() exported_callback<DRing::ConversationSignal::ConversationRequestReceived>(), exported_callback<DRing::ConversationSignal::ConversationReady>(), exported_callback<DRing::ConversationSignal::ConversationRemoved>(), + exported_callback<DRing::ConversationSignal::ConversationMemberEvent>(), }; return handlers; diff --git a/src/dring/conversation_interface.h b/src/dring/conversation_interface.h index ed7927e0cd6769c7415d5cb8e41ab3f166aaf3d5..5bda2bed21ddac94219da4205f658fcf114ed1b7 100644 --- a/src/dring/conversation_interface.h +++ b/src/dring/conversation_interface.h @@ -59,9 +59,9 @@ DRING_PUBLIC void sendMessage(const std::string& accountId, const std::string& message, const std::string& parent); DRING_PUBLIC uint32_t loadConversationMessages(const std::string& accountId, - const std::string& conversationId, - const std::string& fromMessage, - size_t n); + const std::string& conversationId, + const std::string& fromMessage, + size_t n); struct DRING_PUBLIC ConversationSignal { @@ -99,6 +99,14 @@ struct DRING_PUBLIC ConversationSignal using cb_type = void(const std::string& /*accountId*/, const std::string& /* conversationId */); }; + struct DRING_PUBLIC ConversationMemberEvent + { + constexpr static const char* name = "ConversationMemberEvent"; + using cb_type = void(const std::string& /*accountId*/, + const std::string& /* conversationId */, + const std::string& /* memberUri */, + int /* event 0 = add, 1 = joins, 2 = leave, 3 = banned */); + }; }; } // namespace DRing diff --git a/src/gittransport.cpp b/src/gittransport.cpp index 473e6053354cee760a74c5b07dd094d7a2c4f93b..5d5eb42b9c7af8b7a94a97fd7e90d4c9bc08546f 100644 --- a/src/gittransport.cpp +++ b/src/gittransport.cpp @@ -120,7 +120,7 @@ P2PStreamWrite(git_smart_subtransport_stream* stream, const char* buffer, size_t } std::error_code ec; auto written = fs->socket->write(reinterpret_cast<const unsigned char*>(buffer), len, ec); - if (written < 0) { + if (ec) { giterr_set_str(GITERR_NET, ec.message().c_str()); return -1; } diff --git a/src/ice_transport.cpp b/src/ice_transport.cpp index 93dec33835cd021c1ef66da04a7d2c9eac4aa920..302db1b3ec7a5f9fd37502bd1a153d543b39531e 100644 --- a/src/ice_transport.cpp +++ b/src/ice_transport.cpp @@ -942,7 +942,7 @@ void IceTransport::Impl::setDefaultRemoteAddress(unsigned comp_id, const IpAddr& addr) { // Component ID must be valid. - assert(comp_id < component_count_); + assert(static_cast<unsigned>(comp_id) < component_count_); iceDefaultRemoteAddr_[comp_id] = addr; // The port does not matter. Set it 0 to avoid confusion. @@ -953,7 +953,7 @@ const IpAddr& IceTransport::Impl::getDefaultRemoteAddress(unsigned comp_id) const { // Component ID must be valid. - assert(comp_id < component_count_); + assert(static_cast<unsigned>(comp_id) < component_count_); return iceDefaultRemoteAddr_[comp_id]; } diff --git a/src/jamidht/channeled_transfers.cpp b/src/jamidht/channeled_transfers.cpp index ccf022ce696b9dd821ce831fad223c4aee0f8695..461ec4f5d08c675ba350b2d5b5ae952b12c9979a 100644 --- a/src/jamidht/channeled_transfers.cpp +++ b/src/jamidht/channeled_transfers.cpp @@ -31,8 +31,8 @@ namespace jami { ChanneledOutgoingTransfer::ChanneledOutgoingTransfer(const std::shared_ptr<ChannelSocket>& channel, OnStateChangedCb&& cb) - : channel_(channel) - , stateChangedCb_(cb) + : stateChangedCb_(cb) + , channel_(channel) {} ChanneledOutgoingTransfer::~ChanneledOutgoingTransfer() diff --git a/src/jamidht/connectionmanager.cpp b/src/jamidht/connectionmanager.cpp index 6d6484d62a1fedef2a58504ab907f6a6aab645e3..0abc7872185125d8e3a8ef678458173477e94fd6 100644 --- a/src/jamidht/connectionmanager.cpp +++ b/src/jamidht/connectionmanager.cpp @@ -797,7 +797,7 @@ ConnectionManager::Impl::onRequestOnNegoDone(const PeerConnectionRequest& req) void ConnectionManager::Impl::onDhtPeerRequest(const PeerConnectionRequest& req, - const std::shared_ptr<dht::crypto::Certificate>& cert) + const std::shared_ptr<dht::crypto::Certificate>& /*cert*/) { auto deviceId = req.from.toString(); JAMI_INFO() << account << "New connection requested by " << deviceId.c_str(); diff --git a/src/jamidht/conversationrepository.cpp b/src/jamidht/conversationrepository.cpp index 05e000c91b56bc56dda7e530ca52fc09d3275bf6..94abeebc22a9de180d6203b0bc9c6d70bda49de1 100644 --- a/src/jamidht/conversationrepository.cpp +++ b/src/jamidht/conversationrepository.cpp @@ -427,8 +427,7 @@ ConversationRepository::Impl::createMergeCommit(git_index* index, const std::str return false; } GitAnnotatedCommit annotated {annotated_ptr, git_annotated_commit_free}; - if (git_commit_lookup(&parent, repository_.get(), git_annotated_commit_id(annotated.get())) - < 0) { + if (git_commit_lookup(&parent, repository_.get(), git_annotated_commit_id(annotated.get())) < 0) { JAMI_ERR("Couldn't lookup commit %s", wanted_ref.c_str()); return false; } @@ -486,12 +485,7 @@ ConversationRepository::Impl::createMergeCommit(git_index* index, const std::str JAMI_INFO("New merge commit added with id: %s", commit_str); // Move commit to main branch git_reference* ref_ptr = nullptr; - if (git_reference_create(&ref_ptr, - repository_.get(), - "refs/heads/main", - &commit_oid, - true, - nullptr) + if (git_reference_create(&ref_ptr, repository_.get(), "refs/heads/main", &commit_oid, true, nullptr) < 0) { JAMI_WARN("Could not move commit to main"); } @@ -523,12 +517,7 @@ ConversationRepository::Impl::mergeFastforward(const git_oid* target_oid, int is const auto* symbolic_ref = git_reference_symbolic_target(head_ref.get()); // Create our main reference on the target OID - if (git_reference_create(&target_ref_ptr, - repository_.get(), - symbolic_ref, - target_oid, - 0, - nullptr) + if (git_reference_create(&target_ref_ptr, repository_.get(), symbolic_ref, target_oid, 0, nullptr) < 0) { JAMI_ERR("failed to create main reference"); return false; @@ -1405,11 +1394,7 @@ ConversationRepository::Impl::log(const std::string& from, const std::string& to cc->author = std::move(author); cc->parents = std::move(parents); git_buf signature = {}, signed_data = {}; - if (git_commit_extract_signature(&signature, - &signed_data, - repository_.get(), - &oid, - "signature") + if (git_commit_extract_signature(&signature, &signed_data, repository_.get(), &oid, "signature") < 0) { JAMI_WARN("Could not extract signature for commit %s", id.c_str()); } else { @@ -1482,7 +1467,6 @@ ConversationRepository::Impl::getInitialMembers() const return {}; } auto commit = firstCommit[0]; - auto authorDevice = commit.author.email; auto cert = tls::CertificateStore::instance().getCertificate(authorDevice); if (!cert && cert->issuer) { @@ -1890,12 +1874,7 @@ ConversationRepository::amend(const std::string& id, const std::string& msg) // Move commit to main branch git_reference* ref_ptr = nullptr; - if (git_reference_create(&ref_ptr, - pimpl_->repository_.get(), - "refs/heads/main", - &commit_id, - true, - nullptr) + if (git_reference_create(&ref_ptr, pimpl_->repository_.get(), "refs/heads/main", &commit_id, true, nullptr) < 0) { JAMI_WARN("Could not move commit to main"); } @@ -1931,10 +1910,7 @@ ConversationRepository::fetch(const std::string& remoteDeviceId) JAMI_ERR("Couldn't lookup for remote %s", remoteDeviceId.c_str()); return false; } - if (git_remote_create(&remote_ptr, - pimpl_->repository_.get(), - remoteDeviceId.c_str(), - channelName.c_str()) + if (git_remote_create(&remote_ptr, pimpl_->repository_.get(), remoteDeviceId.c_str(), channelName.c_str()) < 0) { JAMI_ERR("Could not create remote for repository for conversation %s", pimpl_->id_.c_str()); @@ -2078,8 +2054,7 @@ ConversationRepository::merge(const std::string& merge_id) git_merge_analysis_t analysis; git_merge_preference_t preference; const git_annotated_commit* const_annotated = annotated.get(); - if (git_merge_analysis(&analysis, &preference, pimpl_->repository_.get(), &const_annotated, 1) - < 0) { + if (git_merge_analysis(&analysis, &preference, pimpl_->repository_.get(), &const_annotated, 1) < 0) { JAMI_ERR("Merge operation aborted: repository analysis failed"); return false; } @@ -2116,8 +2091,7 @@ ConversationRepository::merge(const std::string& merge_id) return false; } - if (git_merge(pimpl_->repository_.get(), &const_annotated, 1, &merge_opts, &checkout_opts) - < 0) { + if (git_merge(pimpl_->repository_.get(), &const_annotated, 1, &merge_opts, &checkout_opts) < 0) { const git_error* err = giterr_last(); if (err) JAMI_ERR("Git merge failed: %s", err->message); diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index e77c5a78cd30ffd1d0da350e41100efe5dbb5d9b..a56372339bbe08c761ea838000793f7628ba7952 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -2580,13 +2580,27 @@ JamiAccount::doRegister_() return; } - if (!isConversation(conversationId)) { - JAMI_WARN("[Account %s] Git server requested, but for a non existing " - "conversation (%s)", - getAccountID().c_str(), - conversationId.c_str()); - return; + { + // Check if pull from banned device + std::unique_lock<std::mutex> lk(conversationsMtx_); + auto conversation = conversations_.find(conversationId); + if (conversation == conversations_.end()) { + JAMI_WARN("[Account %s] Git server requested, but for a non existing " + "conversation (%s)", + getAccountID().c_str(), + conversationId.c_str()); + return; + } + if (conversation->second->isBanned(remoteDevice)) { + JAMI_WARN("[Account %s] %s is a banned device in conversation %s", + getAccountID().c_str(), + remoteDevice.c_str(), + conversationId.c_str()); + return; + } } + + if (gitSocket(deviceId.toString(), conversationId) == channel) { // The onConnectionReady is already used as client (for retrieving messages) // So it's not the server socket @@ -4187,6 +4201,8 @@ JamiAccount::addConversationMember(const std::string& conversationId, return; } auto message = messages.front(); + if (message.at("type") == "member") + shared->announceMemberMessage(conversationId, message); emitSignal<DRing::ConversationSignal::MessageReceived>(shared->getAccountID(), conversationId, message); if (sendRequest) shared->sendTextMessage(contactUri, it->second->generateInvitation()); @@ -4210,6 +4226,8 @@ JamiAccount::removeConversationMember(const std::string& conversationId, std::reverse(messages.begin(), messages.end()); // Announce new commits for (const auto& msg : messages) { + if (msg.at("type") == "member") + shared->announceMemberMessage(conversationId, msg); emitSignal<DRing::ConversationSignal::MessageReceived>(shared->getAccountID(), conversationId, msg); @@ -4249,6 +4267,8 @@ JamiAccount::sendMessage(const std::string& conversationId, auto conversation = conversations_.find(conversationId); if (conversation != conversations_.end() && conversation->second) { auto commitId = conversation->second->sendMessage(message, type, parent); + if (!announce) + return; if (!commitId.empty()) { conversation->second->loadMessages([w=weak(), conversationId] (auto&& messages) { auto shared = w.lock(); @@ -4336,16 +4356,18 @@ JamiAccount::fetchNewCommits(const std::string& peer, return; } - auto announceMessages = [w = weak(), conversationId](const std::vector<std::map<std::string, std::string>>& messages) { + auto announceMessages = [w = weak(), conversationId]( + const std::vector<std::map<std::string, std::string>>& messages) { auto shared = w.lock(); if (!shared) return; std::unique_lock<std::mutex> lk(shared->conversationsMtx_); auto conversation = shared->conversations_.find(conversationId); if (conversation != shared->conversations_.end() && conversation->second) { - // Do a diff between last message, and current new message when merged lk.unlock(); for (const auto& message : messages) { + if (message.at("type") == "member") + shared->announceMemberMessage(conversationId, message); JAMI_DBG("[Account %s] New message received for conversation %s with id %s", shared->getAccountID().c_str(), conversationId.c_str(), @@ -4929,7 +4951,6 @@ JamiAccount::cacheSyncConnection(std::shared_ptr<ChannelSocket>&& socket, std::lock_guard<std::mutex> lk(conversationsRequestsMtx_); conversationsRequests_.erase(convId); } - auto itConv = conversations_.find(convId); if (not removed) { if (!isConversation(convId)) { { @@ -5219,4 +5240,31 @@ JamiAccount::getOneToOneConversation(const std::string& uri) const return {}; } +void +JamiAccount::announceMemberMessage(const std::string& convId, + const std::map<std::string, std::string>& message) const +{ + if (message.at("type") == "member") { + if (message.find("uri") == message.end() || message.find("action") == message.end()) + return; + auto uri = message.at("uri"); + auto actionStr = message.at("action"); + auto action = 0; + if (actionStr == "add") + action = 0; + else if (actionStr == "join") + action = 1; + else if (actionStr == "remove") + action = 2; + else if (actionStr == "ban") + action = 3; + else + return; + + emitSignal<DRing::ConversationSignal::ConversationMemberEvent>(getAccountID(), + convId, + uri, + action); + } +} } // namespace jami diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h index 17a669568f7c90e4b634c0f9fb0aad96b8c94c5a..e4f1e7055dd54d788bf8ba1dc3068919862e0fbd 100644 --- a/src/jamidht/jamiaccount.h +++ b/src/jamidht/jamiaccount.h @@ -969,6 +969,9 @@ private: * @return the conversation id if found else empty */ std::string getOneToOneConversation(const std::string& uri) const; + + void announceMemberMessage(const std::string& convId, + const std::map<std::string, std::string>& message) const; }; static inline std::ostream& diff --git a/src/manager.cpp b/src/manager.cpp index ac4c0236bc92854b8ce07afb4586eb68bba84e1b..caee34f38291b27bf6e8d8353dc3839a6b6eb63f 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -856,7 +856,6 @@ Manager::init(const std::string& config_file) void Manager::finish() noexcept { - git_libgit2_shutdown(); bool expected = false; if (not pimpl_->finished_.compare_exchange_strong(expected, true)) return; @@ -911,6 +910,7 @@ Manager::finish() noexcept } pj_shutdown(); + git_libgit2_shutdown(); if (!pimpl_->ioContext_->stopped()) { pimpl_->ioContext_->reset(); // allow to finish diff --git a/test/unitTest/conversation/conversation.cpp b/test/unitTest/conversation/conversation.cpp index 4855318ddad22799a8030766bbf51b92b8c8a865..9cad8f1255e7adc5f4417d089778f76434f1c0ab 100644 --- a/test/unitTest/conversation/conversation.cpp +++ b/test/unitTest/conversation/conversation.cpp @@ -124,6 +124,7 @@ private: void testOneToOneFetchWithNewMemberRefused(); void testAddOfflineContactThenConnect(); void testDeclineTrustRequestDoNotGenerateAnother(); + void testConversationMemberEvent(); CPPUNIT_TEST_SUITE(ConversationTest); CPPUNIT_TEST(testCreateConversation); @@ -161,6 +162,8 @@ private: CPPUNIT_TEST(testOneToOneFetchWithNewMemberRefused); CPPUNIT_TEST(testAddOfflineContactThenConnect); CPPUNIT_TEST(testDeclineTrustRequestDoNotGenerateAnother); + CPPUNIT_TEST(testConversationMemberEvent); + CPPUNIT_TEST_SUITE_END(); }; CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ConversationTest, ConversationTest::name()); @@ -231,6 +234,7 @@ ConversationTest::setUp() void ConversationTest::tearDown() { + DRing::unregisterSignalHandlers(); auto currentAccSize = Manager::instance().getAccountList().size(); Manager::instance().removeAccount(aliceId, true); Manager::instance().removeAccount(bobId, true); @@ -448,11 +452,14 @@ ConversationTest::testRemoveConversationWithMember() [&](const std::string& accountId, const std::string& conversationId, std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { + auto itFind = message.find("type"); + if (itFind == message.end()) + return; + if (accountId == aliceId && conversationId == convId && itFind->second == "member") { memberMessageGenerated = true; cv.notify_one(); } else if (accountId == bobId && conversationId == convId - && message["type"] == "member") { + && itFind->second == "member") { bobSeeAliceRemoved = true; cv.notify_one(); } @@ -460,7 +467,8 @@ ConversationTest::testRemoveConversationWithMember() DRing::registerSignalHandlers(confHandlers); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - CPPUNIT_ASSERT(memberMessageGenerated); + CPPUNIT_ASSERT( + cv.wait_for(lk, std::chrono::seconds(30), [&]() { return memberMessageGenerated; })); // Assert that repository exists auto repoPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + aliceAccount->getAccountID() @@ -528,7 +536,8 @@ ConversationTest::testAddMember() })); DRing::registerSignalHandlers(confHandlers); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - CPPUNIT_ASSERT(memberMessageGenerated); + CPPUNIT_ASSERT( + cv.wait_for(lk, std::chrono::seconds(30), [&]() { return memberMessageGenerated; })); // Assert that repository exists auto repoPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + aliceAccount->getAccountID() + DIR_SEPARATOR_STR + "conversations" + DIR_SEPARATOR_STR + convId; @@ -709,12 +718,10 @@ ConversationTest::testSendMessage() auto convId = aliceAccount->startConversation(); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - cv.wait_for(lk, std::chrono::seconds(30)); - CPPUNIT_ASSERT(requestReceived); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); bobAccount->acceptConversationRequest(convId); - cv.wait_for(lk, std::chrono::seconds(30)); - CPPUNIT_ASSERT(conversationReady); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return conversationReady; })); // Assert that repository exists auto repoPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + bobAccount->getAccountID() @@ -847,8 +854,7 @@ ConversationTest::testGetRequests() auto convId = aliceAccount->startConversation(); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - cv.wait_for(lk, std::chrono::seconds(30)); - CPPUNIT_ASSERT(requestReceived); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); auto requests = bobAccount->getConversationRequests(); CPPUNIT_ASSERT(requests.size() == 1); @@ -880,8 +886,7 @@ ConversationTest::testDeclineRequest() auto convId = aliceAccount->startConversation(); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - cv.wait_for(lk, std::chrono::seconds(30)); - CPPUNIT_ASSERT(requestReceived); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); bobAccount->declineConversationRequest(convId); // Decline request @@ -1028,8 +1033,7 @@ ConversationTest::testPingPongMessages() DRing::registerSignalHandlers(confHandlers); auto convId = aliceAccount->startConversation(); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - cv.wait_for(lk, std::chrono::seconds(30)); - CPPUNIT_ASSERT(requestReceived); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); messageAliceReceived = 0; bobAccount->acceptConversationRequest(convId); CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(60), [&]() { @@ -1171,7 +1175,9 @@ ConversationTest::testBanDevice() })); DRing::registerSignalHandlers(confHandlers); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { + return requestReceived && memberMessageGenerated; + })); memberMessageGenerated = false; bobAccount->acceptConversationRequest(convId); CPPUNIT_ASSERT( @@ -1210,6 +1216,7 @@ ConversationTest::testBanDevice() // Now check that alice, has the only admin, can remove bob memberMessageGenerated = false; voteMessageGenerated = false; + bobGetMessage = false; auto members = aliceAccount->getConversationMembers(convId); CPPUNIT_ASSERT(members.size() == 2); aliceAccount->removeConversationMember(convId, bobDeviceId, true); @@ -1270,7 +1277,9 @@ ConversationTest::testMemberTryToRemoveAdmin() })); DRing::registerSignalHandlers(confHandlers); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { + return requestReceived && memberMessageGenerated; + })); memberMessageGenerated = false; bobAccount->acceptConversationRequest(convId); CPPUNIT_ASSERT( @@ -1329,7 +1338,9 @@ ConversationTest::testBannedMemberCannotSendMessage() })); DRing::registerSignalHandlers(confHandlers); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { + return requestReceived && memberMessageGenerated; + })); memberMessageGenerated = false; bobAccount->acceptConversationRequest(convId); CPPUNIT_ASSERT( @@ -1395,7 +1406,9 @@ ConversationTest::testAddBannedMember() })); DRing::registerSignalHandlers(confHandlers); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { + return requestReceived && memberMessageGenerated; + })); memberMessageGenerated = false; bobAccount->acceptConversationRequest(convId); CPPUNIT_ASSERT( @@ -1454,39 +1467,6 @@ ConversationTest::generateFakeVote(std::shared_ptr<JamiAccount> account, account->sendMessage(convId, "trigger the fake history to be pulled"); } -void -ConversationTest::addAll(std::shared_ptr<JamiAccount> account, const std::string& convId) -{ - auto repoPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + account->getAccountID() - + DIR_SEPARATOR_STR + "conversations" + DIR_SEPARATOR_STR + convId; - - git_repository* repo = nullptr; - if (git_repository_open(&repo, repoPath.c_str()) != 0) - return; - GitRepository rep = {std::move(repo), git_repository_free}; - - // git add -A - git_index* index_ptr = nullptr; - git_strarray array = {0}; - if (git_repository_index(&index_ptr, repo) < 0) - return; - GitIndex index {index_ptr, git_index_free}; - git_index_add_all(index.get(), &array, 0, nullptr, nullptr); - git_index_write(index.get()); -} - -void -ConversationTest::commit(std::shared_ptr<JamiAccount> account, - const std::string& convId, - Json::Value& message) -{ - ConversationRepository cr(account->weak(), convId); - - Json::StreamWriterBuilder wbuilder; - wbuilder["commentStyle"] = "None"; - wbuilder["indentation"] = ""; - cr.commitMessage(Json::writeString(wbuilder, message)); -} void ConversationTest::generateFakeInvite(std::shared_ptr<JamiAccount> account, @@ -1645,14 +1625,18 @@ ConversationTest::testMemberCannotBanOther() })); DRing::registerSignalHandlers(confHandlers); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { + return requestReceived && memberMessageGenerated; + })); memberMessageGenerated = false; bobAccount->acceptConversationRequest(convId); CPPUNIT_ASSERT( cv.wait_for(lk, std::chrono::seconds(30), [&]() { return memberMessageGenerated; })); requestReceived = false; CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, carlaUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { + return requestReceived && memberMessageGenerated; + })); memberMessageGenerated = false; messageBobReceived = false; carlaAccount->acceptConversationRequest(convId); @@ -1719,14 +1703,18 @@ ConversationTest::testCheckAdminFakeAVoteIsDetected() })); DRing::registerSignalHandlers(confHandlers); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { + return requestReceived && memberMessageGenerated; + })); memberMessageGenerated = false; bobAccount->acceptConversationRequest(convId); CPPUNIT_ASSERT( cv.wait_for(lk, std::chrono::seconds(30), [&]() { return memberMessageGenerated; })); requestReceived = false; CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, carlaUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { + return requestReceived && memberMessageGenerated; + })); memberMessageGenerated = false; messageBobReceived = false; carlaAccount->acceptConversationRequest(convId); @@ -1835,8 +1823,7 @@ ConversationTest::testPlainTextNoBadFile() auto convId = aliceAccount->startConversation(); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - cv.wait_for(lk, std::chrono::seconds(30)); - CPPUNIT_ASSERT(requestReceived); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); bobAccount->acceptConversationRequest(convId); cv.wait_for(lk, std::chrono::seconds(30)); @@ -1909,14 +1896,18 @@ ConversationTest::testVoteNoBadFile() })); DRing::registerSignalHandlers(confHandlers); CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { + return requestReceived && memberMessageGenerated; + })); memberMessageGenerated = false; bobAccount->acceptConversationRequest(convId); CPPUNIT_ASSERT( cv.wait_for(lk, std::chrono::seconds(30), [&]() { return memberMessageGenerated; })); requestReceived = false; CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, carlaUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { + return requestReceived && memberMessageGenerated; + })); memberMessageGenerated = false; messageBobReceived = false; carlaAccount->acceptConversationRequest(convId); @@ -2125,9 +2116,6 @@ ConversationTest::testAddContact() auto repoPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + aliceAccount->getAccountID() + DIR_SEPARATOR_STR + "conversations" + DIR_SEPARATOR_STR + convId; CPPUNIT_ASSERT(fileutils::isDirectory(repoPath)); - // Check created files - auto bobInvited = repoPath + DIR_SEPARATOR_STR + "invited" + DIR_SEPARATOR_STR + bobUri; - CPPUNIT_ASSERT(fileutils::isFile(bobInvited)); CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { @@ -2136,8 +2124,6 @@ ConversationTest::testAddContact() auto clonedPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + bobAccount->getAccountID() + DIR_SEPARATOR_STR + "conversations" + DIR_SEPARATOR_STR + convId; CPPUNIT_ASSERT(fileutils::isDirectory(clonedPath)); - bobInvited = clonedPath + DIR_SEPARATOR_STR + "invited" + DIR_SEPARATOR_STR + bobUri; - CPPUNIT_ASSERT(!fileutils::isFile(bobInvited)); auto bobMember = clonedPath + DIR_SEPARATOR_STR + "members" + DIR_SEPARATOR_STR + bobUri + ".crt"; CPPUNIT_ASSERT(fileutils::isFile(bobMember)); @@ -2503,6 +2489,68 @@ ConversationTest::testOneToOneFetchWithNewMemberRefused() CPPUNIT_ASSERT(!cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBob; })); } +void +ConversationTest::testConversationMemberEvent() +{ + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto bobUri = bobAccount->getUsername(); + auto convId = aliceAccount->startConversation(); + std::mutex mtx; + std::unique_lock<std::mutex> lk {mtx}; + std::condition_variable cv; + std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>> confHandlers; + bool conversationReady = false, requestReceived = false, memberAddGenerated = false; + confHandlers.insert( + DRing::exportable_callback<DRing::ConversationSignal::ConversationRequestReceived>( + [&](const std::string& /*accountId*/, + const std::string& /* conversationId */, + std::map<std::string, std::string> /*metadatas*/) { + requestReceived = true; + cv.notify_one(); + })); + confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::ConversationReady>( + [&](const std::string& accountId, const std::string& /* conversationId */) { + if (accountId == bobId) { + conversationReady = true; + cv.notify_one(); + } + })); + confHandlers.insert( + DRing::exportable_callback<DRing::ConversationSignal::ConversationMemberEvent>( + [&](const std::string& accountId, + const std::string& conversationId, + const std::string& uri, + int event) { + if (accountId == aliceId && conversationId == convId && uri == bobUri + && event == 0) { + memberAddGenerated = true; + } + cv.notify_one(); + })); + DRing::registerSignalHandlers(confHandlers); + CPPUNIT_ASSERT(aliceAccount->addConversationMember(convId, bobUri)); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return memberAddGenerated; })); + // Assert that repository exists + auto repoPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + aliceAccount->getAccountID() + + DIR_SEPARATOR_STR + "conversations" + DIR_SEPARATOR_STR + convId; + CPPUNIT_ASSERT(fileutils::isDirectory(repoPath)); + // Check created files + auto bobInvited = repoPath + DIR_SEPARATOR_STR + "invited" + DIR_SEPARATOR_STR + bobUri; + CPPUNIT_ASSERT(fileutils::isFile(bobInvited)); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + bobAccount->acceptConversationRequest(convId); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return conversationReady; })); + auto clonedPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + bobAccount->getAccountID() + + DIR_SEPARATOR_STR + "conversations" + DIR_SEPARATOR_STR + convId; + CPPUNIT_ASSERT(fileutils::isDirectory(clonedPath)); + bobInvited = clonedPath + DIR_SEPARATOR_STR + "invited" + DIR_SEPARATOR_STR + bobUri; + CPPUNIT_ASSERT(!fileutils::isFile(bobInvited)); + auto bobMember = clonedPath + DIR_SEPARATOR_STR + "members" + DIR_SEPARATOR_STR + bobUri + + ".crt"; + CPPUNIT_ASSERT(fileutils::isFile(bobMember)); +} + void ConversationTest::testAddOfflineContactThenConnect() {