diff --git a/src/jamidht/conversationrepository.cpp b/src/jamidht/conversationrepository.cpp index edd4adc03bcf67c39aeaeb1c8661f46c2d99314f..983fe1aec536bb0eeee138cea1614c2b70d62d81 100644 --- a/src/jamidht/conversationrepository.cpp +++ b/src/jamidht/conversationrepository.cpp @@ -948,11 +948,13 @@ ConversationRepository::Impl::checkVote(const std::string& userDevice, JAMI_ERR("Detected vote for self: %s", votedUri.c_str()); return false; } - // file in members or admin or invited - auto invitedFile = fmt::format("invited/{}", votedUri); - if (!memberCertificate(votedUri, treeOld) && !fileAtTree(invitedFile, treeOld)) { - JAMI_ERR("No member file found for vote: %s", votedUri.c_str()); - return false; + if (voteType == "ban") { + // file in members or admin or invited + auto invitedFile = fmt::format("invited/{}", votedUri); + if (!memberCertificate(votedUri, treeOld) && !fileAtTree(invitedFile, treeOld)) { + JAMI_ERR("No member file found for vote: %s", votedUri.c_str()); + return false; + } } } else { // Check not current device @@ -1067,14 +1069,17 @@ ConversationRepository::Impl::checkValidJoins(const std::string& userDevice, return false; // Check no other files changed auto changedFiles = ConversationRepository::changedFiles(diffStats(commitId, parentId)); - std::size_t wantedChanged = 3; - if (changedFiles.size() != wantedChanged) - return false; - auto invitedFile = fmt::format("invited/{}", uriMember); auto membersFile = fmt::format("members/{}.crt", uriMember); auto deviceFile = fmt::format("devices/{}.crt", userDevice); + for (auto& file : changedFiles) { + if (file != invitedFile && file != membersFile && file != deviceFile) { + JAMI_ERR("Unwanted file %s found", file.c_str()); + return false; + } + } + // Retrieve tree for commits auto repo = repository(); assert(repo); @@ -1108,10 +1113,6 @@ ConversationRepository::Impl::checkValidJoins(const std::string& userDevice, JAMI_ERR("%s devices not found", userUri.c_str()); return false; } - if (fileAtTree(deviceFile, treeOld)) { - JAMI_ERR("%s devices found too soon", userUri.c_str()); - return false; - } return true; } diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index 5e8ae81136c64221cf89b94dc5f45daf80e006fe..f00fc995de6713d70d6f8dd7da80a2416c451d5e 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -4377,7 +4377,10 @@ JamiAccount::askForProfile(const std::string& conversationId, if (!connectionManager_) return; - auto channelName = DATA_TRANSFER_URI + conversationId + "/profile/" + memberUri + ".vcf"; + auto channelName = fmt::format("{}{}/profile/{}.vcf", + DATA_TRANSFER_URI, + conversationId, + memberUri); // We can avoid to negotiate new sessions, as the file notif // probably come from an online device or last connected device. connectionManager_->connectDevice( diff --git a/test/unitTest/conversation/conversationMembersEvent.cpp b/test/unitTest/conversation/conversationMembersEvent.cpp index 12e69db0ea7830860738f2f1829db18323619c9e..9c4ad3845a8c6c602023abd05b0e107ba107ed1d 100644 --- a/test/unitTest/conversation/conversationMembersEvent.cpp +++ b/test/unitTest/conversation/conversationMembersEvent.cpp @@ -84,6 +84,7 @@ public: void testAvoidTwoOneToOne(); void testAvoidTwoOneToOneMultiDevices(); void testRemoveRequestBannedMultiDevices(); + void testBanUnbanMultiDevice(); std::string aliceId; std::string bobId; @@ -121,6 +122,7 @@ private: CPPUNIT_TEST(testAvoidTwoOneToOne); CPPUNIT_TEST(testAvoidTwoOneToOneMultiDevices); CPPUNIT_TEST(testRemoveRequestBannedMultiDevices); + CPPUNIT_TEST(testBanUnbanMultiDevice); CPPUNIT_TEST_SUITE_END(); }; @@ -2270,6 +2272,123 @@ ConversationMembersEventTest::testRemoveRequestBannedMultiDevices() CPPUNIT_ASSERT(DRing::getConversationRequests(bob2Id).size() == 0); } +void +ConversationMembersEventTest::testBanUnbanMultiDevice() +{ + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto bobUri = bobAccount->getUsername(); + auto aliceUri = aliceAccount->getUsername(); + + auto convId = DRing::startConversation(aliceId); + + std::mutex mtx; + std::unique_lock<std::mutex> lk {mtx}; + std::condition_variable cv; + std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>> confHandlers; + auto requestReceived = false, requestReceivedBob2 = false; + confHandlers.insert( + DRing::exportable_callback<DRing::ConversationSignal::ConversationRequestReceived>( + [&](const std::string& accountId, + const std::string& conversationId, + std::map<std::string, std::string> /*metadatas*/) { + if (accountId == bobId) + requestReceived = true; + else if (accountId == bob2Id) + requestReceivedBob2 = true; + cv.notify_one(); + })); + auto bob2Started = false; + confHandlers.insert( + DRing::exportable_callback<DRing::ConfigurationSignal::VolatileDetailsChanged>( + [&](const std::string& accountId, const std::map<std::string, std::string>& details) { + if (accountId == bob2Id) { + auto daemonStatus = details.at( + DRing::Account::VolatileProperties::DEVICE_ANNOUNCED); + if (daemonStatus == "true") + bob2Started = true; + } + cv.notify_one(); + })); + auto bob2ContactRemoved = false; + confHandlers.insert(DRing::exportable_callback<DRing::ConfigurationSignal::ContactRemoved>( + [&](const std::string& accountId, const std::string& uri, bool banned) { + if (accountId == bob2Id && uri == aliceUri && banned) { + bob2ContactRemoved = true; + } + cv.notify_one(); + })); + auto memberMessageGenerated = false, voteMessageGenerated = false; + confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::MessageReceived>( + [&](const std::string& accountId, + const std::string& conversationId, + std::map<std::string, std::string> message) { + auto itFind = message.find("type"); + if (itFind == message.end()) + return; + if (accountId == aliceId && conversationId == convId) { + if (itFind->second == "member") + memberMessageGenerated = true; + if (itFind->second == "vote") + voteMessageGenerated = true; + } + cv.notify_one(); + })); + auto conversationReadyBob = false, conversationReadyBob2 = false; + confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::ConversationReady>( + [&](const std::string& accountId, const std::string& conversationId) { + if (accountId == bobId && conversationId == convId) { + conversationReadyBob = true; + } else if (accountId == bob2Id && conversationId == convId) { + conversationReadyBob2 = true; + } + cv.notify_one(); + })); + DRing::registerSignalHandlers(confHandlers); + + // Bob creates a second device + auto bobArchive = std::filesystem::current_path().string() + "/bob.gz"; + std::remove(bobArchive.c_str()); + bobAccount->exportArchive(bobArchive); + std::map<std::string, std::string> details = DRing::getAccountTemplate("RING"); + details[ConfProperties::TYPE] = "RING"; + details[ConfProperties::DISPLAYNAME] = "BOB2"; + details[ConfProperties::ALIAS] = "BOB2"; + details[ConfProperties::UPNP_ENABLED] = "true"; + details[ConfProperties::ARCHIVE_PASSWORD] = ""; + details[ConfProperties::ARCHIVE_PIN] = ""; + details[ConfProperties::ARCHIVE_PATH] = bobArchive; + bob2Id = Manager::instance().addAccount(details); + + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Started; })); + + // Alice adds bob + requestReceived = false; + DRing::addConversationMember(aliceId, convId, bobUri); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { + return memberMessageGenerated && requestReceived && requestReceivedBob2; + })); + + // Alice kick Bob while invited + memberMessageGenerated = false; + voteMessageGenerated = false; + DRing::removeConversationMember(aliceId, convId, bobUri); + CPPUNIT_ASSERT( + cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + + // Alice re-add Bob while invited + memberMessageGenerated = false; + voteMessageGenerated = false; + DRing::addConversationMember(aliceId, convId, bobUri); + CPPUNIT_ASSERT( + cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + + // bob accepts + DRing::acceptConversationRequest(bobId, convId); + CPPUNIT_ASSERT( + cv.wait_for(lk, 30s, [&]() { return conversationReadyBob && conversationReadyBob2; })); +} + } // namespace test } // namespace jami