diff --git a/src/jamidht/account_manager.cpp b/src/jamidht/account_manager.cpp index 85832053f26d6e35e67f894d3be3e0be0fe932d1..855c13bca0a910a90902c33797bd60d5a386e074 100644 --- a/src/jamidht/account_manager.cpp +++ b/src/jamidht/account_manager.cpp @@ -453,7 +453,7 @@ AccountManager::getContacts() const } void -AccountManager::setConversations(const std::vector<ConvInfo>& newConv) +AccountManager::setConversations(const std::map<std::string, ConvInfo>& newConv) { if (info_) { info_->conversations = newConv; @@ -466,11 +466,10 @@ AccountManager::setConversationMembers(const std::string& convId, const std::vector<std::string>& members) { if (info_) { - for (auto& ci : info_->conversations) { - if (ci.id == convId) { - ci.members = members; - saveConvInfos(); - } + auto convIt = info_->conversations.find(convId); + if (convIt != info_->conversations.end()) { + convIt->second.members = members; + saveConvInfos(); } } } @@ -489,7 +488,7 @@ void AccountManager::addConversation(const ConvInfo& info) { if (info_) { - info_->conversations.emplace_back(info); + info_->conversations[info.id] = info; saveConvInfos(); } } diff --git a/src/jamidht/account_manager.h b/src/jamidht/account_manager.h index 4376a2f16670857f22d7835e46f1469818714c4f..2638004510860c7f5e037fd53a20a9a06468213a 100644 --- a/src/jamidht/account_manager.h +++ b/src/jamidht/account_manager.h @@ -46,7 +46,7 @@ struct AccountInfo { dht::crypto::Identity identity; std::unique_ptr<ContactList> contacts; - std::vector<ConvInfo> conversations; + std::map<std::string, ConvInfo> conversations; std::map<std::string, ConversationRequest> conversationsRequests; std::string accountId; std::string deviceId; @@ -222,7 +222,7 @@ public: // Conversations void saveConvInfos() const; void saveConvRequests() const; - void setConversations(const std::vector<ConvInfo>& newConv); + void setConversations(const std::map<std::string, ConvInfo>& newConv); void setConversationMembers(const std::string& convId, const std::vector<std::string>& members); void addConversation(const ConvInfo& info); void setConversationsRequests(const std::map<std::string, ConversationRequest>& newConvReq); diff --git a/src/jamidht/accountarchive.cpp b/src/jamidht/accountarchive.cpp index 4c54fe611bb4ed613c8400a864a0a96ab818b656..da6dd72a44e0ed94ec0ee60041cf85c33fa842f2 100644 --- a/src/jamidht/accountarchive.cpp +++ b/src/jamidht/accountarchive.cpp @@ -75,7 +75,8 @@ AccountArchive::deserialize(const std::vector<uint8_t>& dat) } } else if (key.compare(Conf::CONVERSATIONS_KEY) == 0) { for (Json::ValueIterator citr = itr->begin(); citr != itr->end(); citr++) { - conversations.emplace_back(ConvInfo(*citr)); + auto ci = ConvInfo(*citr); + conversations[ci.id] = std::move(ci); } } else if (key.compare(Conf::CONVERSATIONS_REQUESTS_KEY) == 0) { for (Json::ValueIterator citr = itr->begin(); citr != itr->end(); citr++) { @@ -131,8 +132,8 @@ AccountArchive::serialize() const if (not conversations.empty()) { Json::Value& jsonConversations = root[Conf::CONVERSATIONS_KEY]; - for (const auto& c : conversations) { - jsonConversations.append(c.toJson()); + for (const auto& [key, c] : conversations) { + jsonConversations[key] = c.toJson(); } } diff --git a/src/jamidht/accountarchive.h b/src/jamidht/accountarchive.h index e03bd3c6b3956fda1f3ac8a6f27c1a5796717e34..f0654ecbb14328965b1c3a99276880610b46a7cf 100644 --- a/src/jamidht/accountarchive.h +++ b/src/jamidht/accountarchive.h @@ -51,7 +51,7 @@ struct AccountArchive std::map<dht::InfoHash, Contact> contacts; // Conversations - std::vector<ConvInfo> conversations; + std::map<std::string, ConvInfo> conversations; std::map<std::string, ConversationRequest> conversationsRequests; /** Account configuration */ diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index f53c003d183a9649b314176f7d25545b022308ca..25eb0ae4f9a8ece3e2f3c589550a8b2959ddeb65 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -109,7 +109,7 @@ using namespace std::placeholders; struct SyncMsg { jami::DeviceSync ds; - std::vector<jami::ConvInfo> c; + std::map<std::string, jami::ConvInfo> c; std::map<std::string, jami::ConversationRequest> cr; MSGPACK_DEFINE(ds, c, cr) }; @@ -1065,7 +1065,6 @@ JamiAccount::addDevice(const std::string& password) bool JamiAccount::exportArchive(const std::string& destinationPath, const std::string& password) { - saveConvInfos(); // Refresh members known if (auto manager = dynamic_cast<ArchiveAccountManager*>(accountManager_.get())) { return manager->exportArchive(destinationPath, password); } @@ -1125,7 +1124,7 @@ JamiAccount::loadAccount(const std::string& archive_password, JAMI_DBG("[Account %s] loading account", getAccountID().c_str()); AccountManager::OnChangeCallback callbacks { [this](const std::string& uri, bool confirmed) { - dht::ThreadPool::computation().run([id = getAccountID(), uri, confirmed] { + dht::ThreadPool::computation().run([this, id = getAccountID(), uri, confirmed] { emitSignal<DRing::ConfigurationSignal::ContactAdded>(id, uri, confirmed); }); }, @@ -1143,14 +1142,12 @@ JamiAccount::loadAccount(const std::string& archive_password, auto info = accountManager_->getInfo(); if (!info) return; - auto ci = info->conversations; - for (auto& info : ci) { - if (info.id == conversationId) { - JAMI_INFO("[Account %s] Received a request for a conversation " - "already handled. Ignore", - getAccountID().c_str()); - return; - } + auto itConv = info->conversations.find(conversationId); + if (itConv != info->conversations.end()) { + JAMI_INFO("[Account %s] Received a request for a conversation " + "already handled. Ignore", + getAccountID().c_str()); + return; } saveReq = true; if (accountManager_->getRequest(conversationId) != std::nullopt) { @@ -1995,15 +1992,15 @@ JamiAccount::syncConversations(const std::string& peer, const std::string& devic std::set<std::string> toClone; { if (auto infos = accountManager_->getInfo()) { - for (const auto& ci : infos->conversations) { - auto it = conversations_.find(ci.id); + for (const auto& [key, ci] : infos->conversations) { + auto it = conversations_.find(key); if (it != conversations_.end() && it->second) { if (it->second->isMember(peer, false)) - toFetch.emplace(ci.id); + toFetch.emplace(key); } else if (std::find(ci.members.begin(), ci.members.end(), peer) != ci.members.end()) { // In this case the conversation was never cloned (can be after an import) - toClone.emplace(ci.id); + toClone.emplace(key); } } } @@ -2242,7 +2239,6 @@ JamiAccount::doRegister_() auto isVCard = name.substr(0, 8) == "vcard://"; auto isDataTransfer = name.substr(0, 16) == "data-transfer://"; if (name.find("git://") == 0) { - // TODO return true; } else if (name == "sip") { return true; @@ -3108,13 +3104,25 @@ JamiAccount::getTrustRequests() const bool JamiAccount::acceptTrustRequest(const std::string& from, bool includeConversation) { - std::lock_guard<std::mutex> lock(configurationMutex_); + std::unique_lock<std::mutex> lock(configurationMutex_); if (accountManager_) { if (!accountManager_->acceptTrustRequest(from, includeConversation)) { // Note: unused for swarm // Typically the case where the trust request doesn't exists, only incoming DHT messages return accountManager_->addContact(from, true); } + + lock.unlock(); + auto details = getContactDetails(from); + auto it = details.find(DRing::Account::TrustRequest::CONVERSATIONID); + if (it != details.end() && !it->second.empty()) { + ConvInfo info; + info.id = it->second; + info.created = std::time(nullptr); + info.members.emplace_back(getUsername()); + info.members.emplace_back(from); + addNewConversation(info); + } return true; } JAMI_WARN("[Account %s] acceptTrustRequest: account not loaded", getAccountID().c_str()); @@ -3647,8 +3655,7 @@ JamiAccount::startConversation(ConversationMode mode, const std::string& otherMe info.members.emplace_back(getUsername()); if (!otherMember.empty()) info.members.emplace_back(otherMember); - accountManager_->addConversation(info); - saveConvInfos(); + addNewConversation(info); runOnMainThread([w = weak()]() { // Invite connected devices for the same user @@ -3708,6 +3715,15 @@ JamiAccount::acceptConversationRequest(const std::string& conversationId) }); }); accountManager_->rmConversationRequest(conversationId); + ConvInfo info; + info.id = conversationId; + info.created = std::time(nullptr); + info.members.emplace_back(getUsername()); + info.members.emplace_back(request->from); + runOnMainThread([w = weak(), info = std::move(info)] { + if (auto shared = w.lock()) + shared->addNewConversation(info); + }); syncWithConnected(); checkConversationsEvents(); } @@ -3754,23 +3770,6 @@ JamiAccount::handlePendingConversations() } if (conversation) { auto commitId = conversation->join(); - // TODO change convInfos to map<id, ConvInfo> - auto found = false; - for (const auto& ci : info->conversations) { - if (ci.id == conversationId) { - found = true; - break; - } - } - if (!found) { - ConvInfo info; - info.id = conversationId; - info.created = std::time(nullptr); - for (const auto& member : conversation->getMembers()) { - info.members.emplace_back(member.at("uri")); - } - shared->accountManager_->addConversation(info); - } { std::lock_guard<std::mutex> lk(shared->conversationsMtx_); shared->conversations_.emplace(conversationId, std::move(conversation)); @@ -3788,7 +3787,6 @@ JamiAccount::handlePendingConversations() } }); } - shared->saveConvInfos(); // Inform user that the conversation is ready emitSignal<DRing::ConversationSignal::ConversationReady>(shared->accountID_, conversationId); @@ -3838,23 +3836,17 @@ JamiAccount::removeConversation(const std::string& conversationId) if (!infos) return false; auto ci = infos->conversations; - for (auto& info : ci) { - if (info.id == conversationId) { - info.removed = std::time(nullptr); - // Sync now, because it can take some time to really removes the datas - runOnMainThread([w = weak(), hasMembers]() { - // Invite connected devices for the same user - auto shared = w.lock(); - if (!shared or !shared->accountManager_) - return; - - shared->saveConvInfos(); - // Send to connected devices - if (hasMembers) - shared->syncWithConnected(); - }); - break; - } + auto itConv = ci.find(conversationId); + if (itConv != ci.end()) { + itConv->second.removed = std::time(nullptr); + // Sync now, because it can take some time to really removes the datas + runOnMainThread([w = weak(), hasMembers]() { + // Invite connected devices for the same user + auto shared = w.lock(); + // Send to connected devices + if (shared && hasMembers) + shared->syncWithConnected(); + }); } accountManager_->setConversations(ci); auto commitId = it->second->leave(); @@ -3884,12 +3876,14 @@ std::vector<std::string> JamiAccount::getConversations() { std::vector<std::string> result; - std::lock_guard<std::mutex> lk(conversationsMtx_); - result.reserve(conversations_.size()); - for (const auto& [key, conv] : conversations_) { - if (conv->isRemoving()) - continue; - result.emplace_back(key); + if (auto info = accountManager_->getInfo()) { + std::lock_guard<std::mutex> lk(conversationsMtx_); + result.reserve(info->conversations.size()); + for (const auto& [key, conv] : info->conversations) { + if (conv.removed) + continue; + result.emplace_back(key); + } } return result; } @@ -3944,15 +3938,22 @@ JamiAccount::updateConversationInfos(const std::string& conversationId, std::map<std::string, std::string> JamiAccount::conversationInfos(const std::string& conversationId) const { - std::lock_guard<std::mutex> lk(conversationsMtx_); - // Add a new member in the conversation - auto it = conversations_.find(conversationId); - if (it == conversations_.end() or not it->second) { - JAMI_ERR("Conversation %s doesn't exist", conversationId.c_str()); - return {}; - } + if (auto info = accountManager_->getInfo()) { + std::lock_guard<std::mutex> lk(conversationsMtx_); + // Add a new member in the conversation + auto it = conversations_.find(conversationId); + if (it == conversations_.end() or not it->second) { + auto itConv = info->conversations.find(conversationId); + if (itConv == info->conversations.end()) { + JAMI_ERR("Conversation %s doesn't exist", conversationId.c_str()); + return {}; + } + return {{"syncing", "true"}}; + } - return it->second->infos(); + return it->second->infos(); + } + return {}; } std::vector<uint8_t> @@ -4060,10 +4061,23 @@ JamiAccount::removeConversationMember(const std::string& conversationId, std::vector<std::map<std::string, std::string>> JamiAccount::getConversationMembers(const std::string& conversationId) const { - std::lock_guard<std::mutex> lk(conversationsMtx_); + std::unique_lock<std::mutex> lk(conversationsMtx_); auto conversation = conversations_.find(conversationId); if (conversation != conversations_.end() && conversation->second) return conversation->second->getMembers(true); + + lk.unlock(); + if (auto info = accountManager_->getInfo()) { + auto convIt = info->conversations.find(conversationId); + if (convIt != info->conversations.end()) { + std::vector<std::map<std::string, std::string>> result; + result.reserve(convIt->second.members.size()); + for (const auto& uri : convIt->second.members) { + result.emplace_back(std::map<std::string, std::string> {{"uri", uri}}); + } + return result; + } + } return {}; } @@ -4168,13 +4182,12 @@ JamiAccount::onNewGitCommit(const std::string& peer, const std::string& conversationId, const std::string& commitId) { - auto infos = accountManager_->getInfo(); - if (!infos) + auto info = accountManager_->getInfo(); + if (!info) return; - for (auto& info : infos->conversations) - if (info.id == conversationId) - if (info.removed) // ignore new commits for removed conversation - return; + auto itConv = info->conversations.find(conversationId); + if (itConv != info->conversations.end() && itConv->second.removed) + return; // ignore new commits for removed conversation JAMI_DBG("[Account %s] on new commit notification from %s, for %s, commit %s", getAccountID().c_str(), peer.c_str(), @@ -4323,11 +4336,10 @@ JamiAccount::fetchNewCommits(const std::string& peer, auto infos = accountManager_->getInfo(); if (!infos) return; - for (const auto& ci : infos->conversations) { - if (ci.id == conversationId) { - cloneConversation(deviceId, peer, conversationId); - return; - } + auto itConv = infos->conversations.find(conversationId); + if (itConv != infos->conversations.end()) { + cloneConversation(deviceId, peer, itConv->first); + return; } JAMI_WARN("[Account %s] Could not find conversation %s, ask for an invite", getAccountID().c_str(), @@ -4876,22 +4888,18 @@ JamiAccount::cacheSyncConnection(std::shared_ptr<ChannelSocket>&& socket, manager->onSyncData(std::move(msg.ds), false); } - for (const auto& convInfo : msg.c) { + for (const auto& [key, convInfo] : msg.c) { auto convId = convInfo.id; auto removed = convInfo.removed; accountManager_->rmConversationRequest(convId); + auto info = accountManager_->getInfo(); if (not removed) { // If multi devices, it can detect a conversation that was already // removed, so just check if the convinfo contains a removed conv - auto wasRemoved = false; - for (auto& info : convInfos_) { - if (info.id == convId) { - wasRemoved = info.removed; - break; - } - } - if (!wasRemoved) - cloneConversation(deviceId, peerId, convId); + auto itConv = info->conversations.find(convId); + if (itConv != info->conversations.end() && itConv->second.removed) + continue; + cloneConversation(deviceId, peerId, convId); } else { { std::lock_guard<std::mutex> lk(conversationsMtx_); @@ -4905,18 +4913,17 @@ JamiAccount::cacheSyncConnection(std::shared_ptr<ChannelSocket>&& socket, auto infos = accountManager_->getInfo(); if (!infos) return len; - auto conversations = infos->conversations; - for (auto& info : conversations) { - if (info.id == convId) { - info.removed = std::time(nullptr); - if (convInfo.erased) { - info.erased = std::time(nullptr); - removeRepository(convId, false); - } - break; + auto ci = infos->conversations; + auto itConv = ci.find(convId); + if (itConv != ci.end()) { + itConv->second.removed = std::time(nullptr); + if (convInfo.erased) { + itConv->second.erased = std::time(nullptr); + removeRepository(convId, false); } + break; } - accountManager_->setConversations(std::move(conversations)); + accountManager_->setConversations(std::move(ci)); } } @@ -4942,7 +4949,6 @@ JamiAccount::cacheSyncConnection(std::shared_ptr<ChannelSocket>&& socket, convId, req.toMap()); } - saveConvInfos(); return len; }); sendProfile(device.toString()); @@ -5014,7 +5020,8 @@ JamiAccount::syncWithConnected() void JamiAccount::loadConvInfos() { - std::vector<ConvInfo> convInfos; + std::map<std::string, ConvInfo> convInfos; + std::vector<ConvInfo> oldCi; // TODO remove when public beta, just to avoid to break try { // read file auto file = fileutils::loadFile("convInfo", idPath_); @@ -5022,11 +5029,23 @@ JamiAccount::loadConvInfos() msgpack::object_handle oh = msgpack::unpack((const char*) file.data(), file.size()); oh.get().convert(convInfos); } catch (const std::exception& e) { - JAMI_WARN("[convInfo] error loading convInfo: %s", e.what()); - return; + // TODO remove when public beta, just to avoid to break + try { + // read file + auto file = fileutils::loadFile("convInfo", idPath_); + // load values + msgpack::object_handle oh = msgpack::unpack((const char*) file.data(), file.size()); + oh.get().convert(oldCi); + for (auto ci : oldCi) { + convInfos[ci.id] = ci; + } + } catch (const std::exception& e) { + JAMI_WARN("[convInfo] error loading convInfo: %s", e.what()); + return; + } } - for (auto& info : convInfos) { + for (auto& [key, info] : convInfos) { std::lock_guard<std::mutex> lk(conversationsMtx_); auto itConv = conversations_.find(info.id); if (itConv != conversations_.end() && info.removed) { @@ -5036,28 +5055,6 @@ JamiAccount::loadConvInfos() accountManager_->setConversations(convInfos); } -void -JamiAccount::saveConvInfos() const -{ - auto info = accountManager_->getInfo(); - if (!info) - return; - auto ci = info->conversations; - - // Update infos - // TODO avoid to do this for all conversations, just last updated if possible - for (auto& c : ci) { - c.members.clear(); - auto members = getConversationMembers(c.id); - for (const auto& member : members) { - auto uri = member.find("uri"); - if (uri != member.end()) { - c.members.emplace_back(uri->second); - } - } - } -} - void JamiAccount::loadConvRequests() { @@ -5134,18 +5131,14 @@ JamiAccount::removeRepository(const std::string& conversationId, bool sync, bool if (!sync or !info) return; auto ci = info->conversations; - for (auto& info : ci) { - if (info.id == conversationId) { - info.erased = std::time(nullptr); - runOnMainThread([w = weak()]() { - // Send to connected devices - if (auto shared = w.lock()) { - shared->saveConvInfos(); // will lock conversationsMtx_ - shared->syncWithConnected(); - } - }); - break; - } + auto convIt = ci.find(conversationId); + if (convIt != ci.end()) { + convIt->second.erased = std::time(nullptr); + runOnMainThread([w = weak()]() { + // Send to connected devices + if (auto shared = w.lock()) + shared->syncWithConnected(); + }); } accountManager_->setConversations(ci); } @@ -5198,6 +5191,14 @@ JamiAccount::getOneToOneConversation(const std::string& uri) const return {}; } +void +JamiAccount::addNewConversation(const ConvInfo& convInfo) +{ + std::lock_guard<std::mutex> lk(configurationMutex_); + if (accountManager_) + accountManager_->addConversation(convInfo); +} + void JamiAccount::addCallHistoryMessage(const std::string& uri, uint64_t duration_ms) { diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h index 11553aeaeb31a78883bd43ccf7dafd68bfbbd0c4..88a528769e9a01daa28471697f391d0d53e6e6aa 100644 --- a/src/jamidht/jamiaccount.h +++ b/src/jamidht/jamiaccount.h @@ -781,7 +781,6 @@ private: void generateDhParams(); void loadConvInfos(); - void saveConvInfos() const; void loadConvRequests(); @@ -827,7 +826,6 @@ private: std::lock_guard<std::mutex> lk(conversationsMtx_); return conversations_.find(convId) != conversations_.end(); } - mutable std::vector<ConvInfo> convInfos_; mutable std::mutex dhtValuesMtx_; bool dhtPublicInCalls_ {true}; @@ -1045,6 +1043,12 @@ private: */ std::string getOneToOneConversation(const std::string& uri) const; + /** + * Add a new ConvInfo + * @param id of the conversation + */ + void addNewConversation(const ConvInfo& convInfo); + std::atomic_bool deviceAnnounced_ {false}; //// File transfer diff --git a/test/unitTest/conversation/conversation.cpp b/test/unitTest/conversation/conversation.cpp index 8f574da3a5fecb6684b07297774c6e33c9f4eb3a..988963c543be22a1c50f269298f758ae1f10c105 100644 --- a/test/unitTest/conversation/conversation.cpp +++ b/test/unitTest/conversation/conversation.cpp @@ -153,6 +153,8 @@ private: void testUpdateProfileWithBadFile(); void testFetchProfileUnauthorized(); void testDoNotLoadIncorrectConversation(); + void testSyncingWhileAccepting(); + void testGetConversationsMembersWhileSyncing(); CPPUNIT_TEST_SUITE(ConversationTest); CPPUNIT_TEST(testCreateConversation); @@ -208,6 +210,8 @@ private: CPPUNIT_TEST(testUpdateProfileWithBadFile); CPPUNIT_TEST(testFetchProfileUnauthorized); CPPUNIT_TEST(testDoNotLoadIncorrectConversation); + CPPUNIT_TEST(testSyncingWhileAccepting); + CPPUNIT_TEST(testGetConversationsMembersWhileSyncing); CPPUNIT_TEST_SUITE_END(); }; @@ -4330,9 +4334,9 @@ ConversationTest::testDoNotLoadIncorrectConversation() auto uri = aliceAccount->getUsername(); auto convId = aliceAccount->startConversation(); - auto conversations = aliceAccount->getConversations(); - CPPUNIT_ASSERT(conversations.size() == 1); - CPPUNIT_ASSERT(conversations.front() == convId); + auto convInfos = aliceAccount->conversationInfos(convId); + CPPUNIT_ASSERT(convInfos.find("mode") != convInfos.end()); + CPPUNIT_ASSERT(convInfos.find("syncing") == convInfos.end()); Manager::instance().sendRegister(aliceId, false); auto repoGitPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + aliceAccount->getAccountID() @@ -4342,9 +4346,111 @@ ConversationTest::testDoNotLoadIncorrectConversation() aliceAccount->loadConversations(); // Refresh. This should detect the incorrect conversations. - // the conv should be detected as invalid and not added - conversations = aliceAccount->getConversations(); - CPPUNIT_ASSERT(conversations.size() == 0); + // the conv should be detected as invalid and added as syncing + convInfos = aliceAccount->conversationInfos(convId); + CPPUNIT_ASSERT(convInfos.find("mode") == convInfos.end()); + CPPUNIT_ASSERT(convInfos["syncing"] == "true"); +} + +void +ConversationTest::testSyncingWhileAccepting() +{ + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto bobUri = bobAccount->getUsername(); + auto aliceUri = aliceAccount->getUsername(); + 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; + std::string convId = ""; + confHandlers.insert(DRing::exportable_callback<DRing::ConfigurationSignal::IncomingTrustRequest>( + [&](const std::string& account_id, + const std::string& /*from*/, + const std::string& /*conversationId*/, + const std::vector<uint8_t>& /*payload*/, + time_t /*received*/) { + if (account_id == bobId) + requestReceived = true; + cv.notify_one(); + })); + confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::ConversationReady>( + [&](const std::string& accountId, const std::string& conversationId) { + if (accountId == aliceId) { + convId = conversationId; + } else if (accountId == bobId) { + conversationReady = true; + } + cv.notify_one(); + })); + DRing::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); + aliceAccount->sendTrustRequest(bobUri, {}); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + + Manager::instance().sendRegister(aliceId, false); // This avoid to sync immediately + CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); + + auto convInfos = bobAccount->conversationInfos(convId); + CPPUNIT_ASSERT(convInfos["syncing"] == "true"); + + Manager::instance().sendRegister(aliceId, true); // This avoid to sync immediately + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return conversationReady; })); + + convInfos = bobAccount->conversationInfos(convId); + CPPUNIT_ASSERT(convInfos.find("syncing") == convInfos.end()); +} + +void +ConversationTest::testGetConversationsMembersWhileSyncing() +{ + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto bobUri = bobAccount->getUsername(); + auto aliceUri = aliceAccount->getUsername(); + 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; + std::string convId = ""; + confHandlers.insert(DRing::exportable_callback<DRing::ConfigurationSignal::IncomingTrustRequest>( + [&](const std::string& account_id, + const std::string& /*from*/, + const std::string& /*conversationId*/, + const std::vector<uint8_t>& /*payload*/, + time_t /*received*/) { + if (account_id == bobId) + requestReceived = true; + cv.notify_one(); + })); + confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::ConversationReady>( + [&](const std::string& accountId, const std::string& conversationId) { + if (accountId == aliceId) { + convId = conversationId; + } else if (accountId == bobId) { + conversationReady = true; + } + cv.notify_one(); + })); + DRing::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); + aliceAccount->sendTrustRequest(bobUri, {}); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; })); + + Manager::instance().sendRegister(aliceId, false); // This avoid to sync immediately + CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); + + auto members = bobAccount->getConversationMembers(convId); + CPPUNIT_ASSERT(std::find_if(members.begin(), + members.end(), + [&](auto memberInfo) { return memberInfo["uri"] == aliceUri; }) + != members.end()); + CPPUNIT_ASSERT(std::find_if(members.begin(), + members.end(), + [&](auto memberInfo) { return memberInfo["uri"] == bobUri; }) + != members.end()); } } // namespace test