diff --git a/src/account.h b/src/account.h index d747bb65af78387c8530687dd53a7051c009cef2..56574850e94e5950a49630b9690580641ec1765e 100644 --- a/src/account.h +++ b/src/account.h @@ -390,6 +390,7 @@ public: // once the backward compatibility is no more required. bool isIceCompIdRfc5245Compliant() const { return iceCompIdRfc5245Compliant_; } void enableIceCompIdRfc5245Compliance(bool enable) { iceCompIdRfc5245Compliant_ = enable; } + void enableAutoLoadConversations(bool enable) { autoLoadConversations_ = enable; } std::shared_ptr<Call> getCall(const std::string& callId) const { @@ -486,12 +487,15 @@ protected: bool iceForMediaEnabled_ {true}; bool iceCompIdRfc5245Compliant_ {false}; + /** + * Auto load conversations when creatinf convModule() + */ + bool autoLoadConversations_ {true}; /** * private account codec searching functions */ - std::shared_ptr<SystemCodecInfo> searchCodecByName(const std::string& name, - MediaType mediaType); + std::shared_ptr<SystemCodecInfo> searchCodecByName(const std::string& name, MediaType mediaType); std::vector<unsigned> getAccountCodecInfoIdList(MediaType mediaType) const; void setAllCodecsActive(MediaType mediaType, bool active); void sortCodec(); diff --git a/src/client/configurationmanager.cpp b/src/client/configurationmanager.cpp index 5e6381794e7d9b203655b2f6e71364e120ada22d..d2a2179e10bd5c3b635dfcb70a784d2fb54d002d 100644 --- a/src/client/configurationmanager.cpp +++ b/src/client/configurationmanager.cpp @@ -242,6 +242,12 @@ setAccountActive(const std::string& accountId, bool enable, bool shutdownConnect jami::Manager::instance().setAccountActive(accountId, enable, shutdownConnections); } +void +loadAccountAndConversation(const std::string& accountID, const std::string& convID) +{ + jami::Manager::instance().loadAccountAndConversation(accountID, convID); +} + void sendRegister(const std::string& accountId, bool enable) { @@ -264,7 +270,6 @@ getPasswordKey(const std::string& accountID, const std::string& password) return {}; } - void registerAllAccounts() { @@ -343,9 +348,9 @@ exportOnRing(const std::string& accountId, const std::string& password) bool exportToFile(const std::string& accountId, - const std::string& destinationPath, - const std::string& scheme, - const std::string& password) + const std::string& destinationPath, + const std::string& scheme, + const std::string& password) { if (const auto account = jami::Manager::instance().getAccount<jami::JamiAccount>(accountId)) { return account->exportArchive(destinationPath, scheme, password); @@ -354,7 +359,10 @@ exportToFile(const std::string& accountId, } bool -revokeDevice(const std::string& accountId, const std::string& deviceId, const std::string& scheme, const std::string& password) +revokeDevice(const std::string& accountId, + const std::string& deviceId, + const std::string& scheme, + const std::string& password) { if (const auto account = jami::Manager::instance().getAccount<jami::JamiAccount>(accountId)) { return account->revokeDevice(deviceId, scheme, password); @@ -553,8 +561,7 @@ setCodecDetails(const std::string& accountId, JAMI_WARN("parameters for %s changed ", foundCodec->name.c_str()); if (auto call = jami::Manager::instance().getCurrentCall()) { if (call->getVideoCodec() == foundCodec) { - JAMI_WARN("%s running. Need to restart encoding", - foundCodec->name.c_str()); + JAMI_WARN("%s running. Need to restart encoding", foundCodec->name.c_str()); call->restartMediaSender(); } } @@ -1045,7 +1052,10 @@ searchUser(const std::string& account, const std::string& query) } bool -registerName(const std::string& account, const std::string& name, const std::string& scheme, const std::string& password) +registerName(const std::string& account, + const std::string& name, + const std::string& scheme, + const std::string& password) { #if HAVE_RINGNS if (auto acc = jami::Manager::instance().getAccount<JamiAccount>(account)) { diff --git a/src/jami/configurationmanager_interface.h b/src/jami/configurationmanager_interface.h index a88d2b08357faf8dadb4928f12a9dbfc58b9972b..7bb70e52881f2d122988d4ade9b4dd1b0d908cc3 100644 --- a/src/jami/configurationmanager_interface.h +++ b/src/jami/configurationmanager_interface.h @@ -61,6 +61,8 @@ LIBJAMI_PUBLIC void setAccountDetails(const std::string& accountId, LIBJAMI_PUBLIC void setAccountActive(const std::string& accountId, bool active, bool shutdownConnections = false); +LIBJAMI_PUBLIC void loadAccountAndConversation(const std::string& accountID, + const std::string& convID); LIBJAMI_PUBLIC std::map<std::string, std::string> getAccountTemplate(const std::string& accountType); LIBJAMI_PUBLIC std::string addAccount(const std::map<std::string, std::string>& details, const std::string& accountId = {}); @@ -84,7 +86,8 @@ LIBJAMI_PUBLIC bool changeAccountPassword(const std::string& accountId, const std::string& password_old, const std::string& password_new); LIBJAMI_PUBLIC bool isPasswordValid(const std::string& accountId, const std::string& password); -LIBJAMI_PUBLIC std::vector<uint8_t> getPasswordKey(const std::string& accountId, const std::string& password); +LIBJAMI_PUBLIC std::vector<uint8_t> getPasswordKey(const std::string& accountId, + const std::string& password); LIBJAMI_PUBLIC bool lookupName(const std::string& account, const std::string& nameserver, diff --git a/src/jami/jami.h b/src/jami/jami.h index 536ba75df4d55f1f8240744ce44600e68126d27b..f896c86fed08c544c4410f785837d18b1836ae1b 100644 --- a/src/jami/jami.h +++ b/src/jami/jami.h @@ -44,7 +44,8 @@ enum InitFlag { LIBJAMI_FLAG_NO_LOCAL_AUDIO = 1 << 6, LIBJAMI_FLAG_NO_LOCAL_VIDEO = 1 << 7, LIBJAMI_FLAG_NO_LOCAL_MEDIA = LIBJAMI_FLAG_NO_LOCAL_AUDIO | LIBJAMI_FLAG_NO_LOCAL_VIDEO, - LIBJAMI_FLAG_NO_AUTOSYNC = 1 << 8 + LIBJAMI_FLAG_NO_AUTOSYNC = 1 << 8, + LIBJAMI_FLAG_NO_AUTOLOAD = 1 << 9 // disable auto loading of accounts and conversations }; /** @@ -69,6 +70,7 @@ LIBJAMI_PUBLIC bool init(enum InitFlag flags) noexcept; * Start asynchronously daemon created by init(). * @returns true if daemon started successfully */ + LIBJAMI_PUBLIC bool start(const std::filesystem::path& config_file = {}) noexcept; /** @@ -185,12 +187,13 @@ private: // This is quite a ugly method used to transmit templated TFunc with their arguments in the // ioContext of the manager to avoid locks for signals. - template <typename TCallback> + template<typename TCallback> auto ioContextWrapper(TCallback&& fun) { - return [this, fun{std::move(fun)}](auto&&... args) -> decltype(fun(std::forward<decltype(args)>(args)...)) - { - post([fun{std::move(fun)}, forwardArgs=std::make_tuple(std::move(args)...)]() mutable { + return [this, fun {std::move(fun)}]( + auto&&... args) -> decltype(fun(std::forward<decltype(args)>(args)...)) { + post([fun {std::move(fun)}, + forwardArgs = std::make_tuple(std::move(args)...)]() mutable { std::apply(std::move(fun), std::move(forwardArgs)); }); }; @@ -255,8 +258,8 @@ exportable_callback(std::function<typename Ts::cb_type>&& func, template<typename Ts> std::pair<std::string, std::shared_ptr<CallbackWrapperBase>> exportable_serialized_callback(std::function<typename Ts::cb_type>&& func, - const char* file = CURRENT_FILENAME(), - uint32_t linum = CURRENT_LINE()) + const char* file = CURRENT_FILENAME(), + uint32_t linum = CURRENT_LINE()) { return std::make_pair((const std::string&) Ts::name, std::make_shared<SerializedCallbackWrapper<typename Ts::cb_type>>( diff --git a/src/jamidht/conversation_module.cpp b/src/jamidht/conversation_module.cpp index 8346b96f34b53d82b9521c1847fd568450d0f0be..678d31f993ef2b3fff6d158ee0c78c538abd3b4a 100644 --- a/src/jamidht/conversation_module.cpp +++ b/src/jamidht/conversation_module.cpp @@ -95,7 +95,8 @@ struct SyncedConversation pending.reset(); } - std::vector<std::map<std::string, std::string>> getMembers(bool includeLeft, bool includeBanned) const + std::vector<std::map<std::string, std::string>> getMembers(bool includeLeft, + bool includeBanned) const { // conversation mtx must be locked if (conversation) @@ -296,9 +297,7 @@ public: /** * @note convInfosMtx_ should be locked */ - void saveConvInfos() const { - ConversationModule::saveConvInfos(accountId_, convInfos_); - } + void saveConvInfos() const { ConversationModule::saveConvInfos(accountId_, convInfos_); } /** * @note conversationsRequestsMtx_ should be locked */ @@ -377,13 +376,18 @@ public: std::function<void(std::string, Conversation::BootstrapStatus)> bootstrapCbTest_; #endif - void fixStructures(std::shared_ptr<JamiAccount> account, const std::vector<std::tuple<std::string, std::string, std::string>>& updateContactConv, const std::set<std::string>& toRm); + void fixStructures( + std::shared_ptr<JamiAccount> account, + const std::vector<std::tuple<std::string, std::string, std::string>>& updateContactConv, + const std::set<std::string>& toRm); - void cloneConversationFrom(const std::shared_ptr<SyncedConversation> conv, const std::string& deviceId, const std::string& oldConvId = ""); + void cloneConversationFrom(const std::shared_ptr<SyncedConversation> conv, + const std::string& deviceId, + const std::string& oldConvId = ""); void bootstrap(const std::string& convId); void cloneConversationFrom(const std::string& conversationId, - const std::string& uri, - const std::string& oldConvId = ""); + const std::string& uri, + const std::string& oldConvId = ""); }; ConversationModule::Impl::Impl(std::weak_ptr<JamiAccount>&& account, @@ -498,8 +502,8 @@ ConversationModule::Impl::fetchNewCommits(const std::string& peer, // it means that the contact was removed but not banned. // If he wants a new conversation, they must removes/re-add the contact who declined. JAMI_WARNING("[Account {:s}] Received a commit for {}, but conversation is removed", - accountId_, - conversationId); + accountId_, + conversationId); return; } } @@ -508,7 +512,8 @@ ConversationModule::Impl::fetchNewCommits(const std::string& peer, std::lock_guard lk(conversationsRequestsMtx_); oldReq = getRequest(conversationId); if (oldReq != std::nullopt && oldReq->declined) { - JAMI_DEBUG("[Account {}] Received a request for a conversation already declined.", accountId_); + JAMI_DEBUG("[Account {}] Received a request for a conversation already declined.", + accountId_); return; } } @@ -886,10 +891,13 @@ ConversationModule::Impl::removeConversationImpl(SyncedConversation& conv) auto members = conv.getMembers(false, false); auto isSyncing = !conv.conversation; auto hasMembers = !isSyncing // If syncing there is no member to inform - && std::find_if(members.begin(), members.end(), [&](const auto& member) { - return member.at("uri") == username_; - }) != members.end() // We must be still a member - && members.size() != 1; // If there is only ourself + && std::find_if(members.begin(), + members.end(), + [&](const auto& member) { + return member.at("uri") == username_; + }) + != members.end() // We must be still a member + && members.size() != 1; // If there is only ourself conv.info.removed = std::time(nullptr); if (isSyncing) conv.info.erased = std::time(nullptr); @@ -1130,9 +1138,12 @@ ConversationModule::Impl::bootstrapCb(std::string convId) } void -ConversationModule::Impl::fixStructures(std::shared_ptr<JamiAccount> acc, const std::vector<std::tuple<std::string, std::string, std::string>>& updateContactConv, const std::set<std::string>& toRm) +ConversationModule::Impl::fixStructures( + std::shared_ptr<JamiAccount> acc, + const std::vector<std::tuple<std::string, std::string, std::string>>& updateContactConv, + const std::set<std::string>& toRm) { - for (const auto& [uri, oldConv, newConv]: updateContactConv) { + for (const auto& [uri, oldConv, newConv] : updateContactConv) { acc->updateConvForContact(uri, oldConv, newConv); } //////////////////////////////////////////////////////////////// @@ -1147,8 +1158,7 @@ ConversationModule::Impl::fixStructures(std::shared_ptr<JamiAccount> acc, const if (itConvId != request.end() && itConvFrom != request.end()) { // Check if requests exists or is declined. auto itReq = conversationsRequests_.find(itConvId->second); - auto declined = itReq == conversationsRequests_.end() - || itReq->second.declined; + auto declined = itReq == conversationsRequests_.end() || itReq->second.declined; if (declined) { JAMI_WARNING("Invalid trust request found: {:s}", itConvId->second); invalidPendingRequests.emplace_back(itConvFrom->second); @@ -1158,7 +1168,8 @@ ConversationModule::Impl::fixStructures(std::shared_ptr<JamiAccount> acc, const auto requestRemoved = false; for (auto it = conversationsRequests_.begin(); it != conversationsRequests_.end();) { if (it->second.from == username_) { - JAMI_WARNING("Detected request from ourself, this makes no sense. Remove {}", it->first); + JAMI_WARNING("Detected request from ourself, this makes no sense. Remove {}", + it->first); it = conversationsRequests_.erase(it); } else { ++it; @@ -1167,7 +1178,6 @@ ConversationModule::Impl::fixStructures(std::shared_ptr<JamiAccount> acc, const if (requestRemoved) { saveConvRequests(); } - } for (const auto& invalidPendingRequest : invalidPendingRequests) acc->discardTrustRequest(invalidPendingRequest); @@ -1181,7 +1191,9 @@ ConversationModule::Impl::fixStructures(std::shared_ptr<JamiAccount> acc, const } void -ConversationModule::Impl::cloneConversationFrom(const std::shared_ptr<SyncedConversation> conv, const std::string& deviceId, const std::string& oldConvId) +ConversationModule::Impl::cloneConversationFrom(const std::shared_ptr<SyncedConversation> conv, + const std::string& deviceId, + const std::string& oldConvId) { std::lock_guard lk(conv->mtx); const auto& conversationId = conv->info.id; @@ -1193,7 +1205,8 @@ ConversationModule::Impl::cloneConversationFrom(const std::shared_ptr<SyncedConv onNeedSocket_( conversationId, deviceId, - [sthis=shared_from_this(), conv, conversationId, oldConvId, deviceId](const auto& channel) { + [sthis = shared_from_this(), conv, conversationId, oldConvId, deviceId]( + const auto& channel) { auto acc = sthis->account_.lock(); std::lock_guard lk(conv->mtx); if (conv->pending && !conv->pending->ready) { @@ -1205,8 +1218,8 @@ ConversationModule::Impl::cloneConversationFrom(const std::shared_ptr<SyncedConv if (!conv->pending->cloning) { conv->pending->cloning = true; dht::ThreadPool::io().run([w = sthis->weak(), - conversationId, - deviceId = conv->pending->deviceId]() { + conversationId, + deviceId = conv->pending->deviceId]() { if (auto sthis = w.lock()) sthis->handlePendingConversation(conversationId, deviceId); }); @@ -1233,10 +1246,7 @@ ConversationModule::Impl::bootstrap(const std::string& convId) #ifdef LIBJAMI_TESTABLE conv->onBootstrapStatus(bootstrapCbTest_); #endif // LIBJAMI_TESTABLE - conv->bootstrap(std::bind(&ConversationModule::Impl::bootstrapCb, - this, - conv->id()), - kd); + conv->bootstrap(std::bind(&ConversationModule::Impl::bootstrapCb, this, conv->id()), kd); } }; std::vector<std::string> toClone; @@ -1271,7 +1281,9 @@ ConversationModule::Impl::bootstrap(const std::string& convId) } void -ConversationModule::Impl::cloneConversationFrom(const std::string& conversationId, const std::string& uri, const std::string& oldConvId) +ConversationModule::Impl::cloneConversationFrom(const std::string& conversationId, + const std::string& uri, + const std::string& oldConvId) { auto acc = account_.lock(); auto memberHash = dht::InfoHash(uri); @@ -1286,16 +1298,15 @@ ConversationModule::Impl::cloneConversationFrom(const std::string& conversationI conv->info.created = std::time(nullptr); conv->info.members.emplace(username_); conv->info.members.emplace(uri); - acc->forEachDevice( - memberHash, - [w = weak(), conv, conversationId, oldConvId]( - const std::shared_ptr<dht::crypto::PublicKey>& pk) { - auto sthis = w.lock(); - auto deviceId = pk->getLongId().toString(); - if (!sthis or deviceId == sthis->deviceId_) - return; - sthis->cloneConversationFrom(conv, deviceId, oldConvId); - }); + acc->forEachDevice(memberHash, + [w = weak(), conv, conversationId, oldConvId]( + const std::shared_ptr<dht::crypto::PublicKey>& pk) { + auto sthis = w.lock(); + auto deviceId = pk->getLongId().toString(); + if (!sthis or deviceId == sthis->deviceId_) + return; + sthis->cloneConversationFrom(conv, deviceId, oldConvId); + }); addConvInfo(conv->info); } @@ -1312,7 +1323,8 @@ ConversationModule::saveConvRequests( void ConversationModule::saveConvRequestsToPath( - const std::filesystem::path& path, const std::map<std::string, ConversationRequest>& conversationsRequests) + const std::filesystem::path& path, + const std::map<std::string, ConversationRequest>& conversationsRequests) { auto p = path / "convRequests"; std::lock_guard lock(dhtnet::fileutils::getFileLock(p)); @@ -1328,7 +1340,8 @@ ConversationModule::saveConvInfos(const std::string& accountId, const ConvInfoMa } void -ConversationModule::saveConvInfosToPath(const std::filesystem::path& path, const ConvInfoMap& conversations) +ConversationModule::saveConvInfosToPath(const std::filesystem::path& path, + const ConvInfoMap& conversations) { std::ofstream file(path / "convInfo", std::ios::trunc | std::ios::binary); msgpack::pack(file, conversations); @@ -1342,7 +1355,8 @@ ConversationModule::ConversationModule(std::weak_ptr<JamiAccount>&& account, NeedSocketCb&& onNeedSocket, NeedSocketCb&& onNeedSwarmSocket, UpdateConvReq&& updateConvReqCb, - OneToOneRecvCb&& oneToOneRecvCb) + OneToOneRecvCb&& oneToOneRecvCb, + bool autoLoadConversations) : pimpl_ {std::make_unique<Impl>(std::move(account), std::move(needsSyncingCb), std::move(sendMsgCb), @@ -1351,7 +1365,9 @@ ConversationModule::ConversationModule(std::weak_ptr<JamiAccount>&& account, std::move(updateConvReqCb), std::move(oneToOneRecvCb))} { - loadConversations(); + if (autoLoadConversations) { + loadConversations(); + } } #ifdef LIBJAMI_TESTABLE @@ -1377,13 +1393,15 @@ ConversationModule::loadConversations() auto conversationsRepositories = dhtnet::fileutils::readDirectory( fileutils::get_data_dir() / pimpl_->accountId_ / "conversations"); - auto contacts = acc->getContacts(true); // Avoid to lock configurationMtx while conv Mtx is locked + auto contacts = acc->getContacts( + true); // Avoid to lock configurationMtx while conv Mtx is locked std::unique_lock<std::mutex> lk(pimpl_->conversationsMtx_); std::unique_lock<std::mutex> ilk(pimpl_->convInfosMtx_); pimpl_->convInfos_ = convInfos(pimpl_->accountId_); pimpl_->conversations_.clear(); - struct Ctx { + struct Ctx + { std::mutex cvMtx; std::condition_variable cv; std::mutex toRmMtx; @@ -1420,9 +1438,11 @@ ConversationModule::loadConversations() // If we got a 1:1 conversation, but not in the contact details, it's rather a // duplicate or a weird state auto otherUri = *members.begin(); - auto itContact = std::find_if(ctx->contacts.cbegin(), ctx->contacts.cend(), [&](const auto& c) { - return c.at("id") == otherUri; - }); + auto itContact = std::find_if(ctx->contacts.cbegin(), + ctx->contacts.cend(), + [&](const auto& c) { + return c.at("id") == otherUri; + }); if (itContact == ctx->contacts.end()) { JAMI_WARNING("Contact {} not found", otherUri); std::lock_guard lkCv {ctx->cvMtx}; @@ -1438,22 +1458,25 @@ ConversationModule::loadConversations() if (convFromDetails.empty()) { if (isRemoved) { // If details is empty, contact is removed and not banned. - JAMI_ERROR("Conversation {} detected for {} and should be removed", repository, otherUri); + JAMI_ERROR("Conversation {} detected for {} and should be removed", + repository, + otherUri); std::lock_guard lkMtx {ctx->toRmMtx}; ctx->toRm.insert(repository); } else { JAMI_ERROR("No conversation detected for {} but one exists ({}). " - "Update details", - otherUri, - repository); + "Update details", + otherUri, + repository); std::lock_guard lkMtx {ctx->toRmMtx}; - ctx->updateContactConv.emplace_back(std::make_tuple(otherUri, convFromDetails, repository)); + ctx->updateContactConv.emplace_back( + std::make_tuple(otherUri, convFromDetails, repository)); } } else { JAMI_ERROR("Multiple conversation detected for {} but ({} & {})", - otherUri, - repository, - convFromDetails); + otherUri, + repository, + convFromDetails); std::lock_guard lkMtx {ctx->toRmMtx}; ctx->toRm.insert(repository); } @@ -1466,7 +1489,8 @@ ConversationModule::loadConversations() JAMI_ERROR("Missing conv info for {}. This is a bug!", repository); sconv->info.created = std::time(nullptr); sconv->info.members = std::move(members); - sconv->info.lastDisplayed = conv->infos()[ConversationMapKeys::LAST_DISPLAYED]; + sconv->info.lastDisplayed + = conv->infos()[ConversationMapKeys::LAST_DISPLAYED]; // convInfosMtx_ is already locked pimpl_->convInfos_[repository] = sconv->info; } else { @@ -1492,8 +1516,8 @@ ConversationModule::loadConversations() pimpl_->conversations_.emplace(repository, std::move(sconv)); } catch (const std::logic_error& e) { JAMI_WARNING("[Account {}] Conversations not loaded: {}", - pimpl_->accountId_, - e.what()); + pimpl_->accountId_, + e.what()); } std::lock_guard lkCv {ctx->cvMtx}; --ctx->convNb; @@ -1502,7 +1526,7 @@ ConversationModule::loadConversations() } std::unique_lock<std::mutex> lkCv {ctx->cvMtx}; - ctx->cv.wait(lkCv, [&] {return ctx->convNb.load() == 0;}); + ctx->cv.wait(lkCv, [&] { return ctx->convNb.load() == 0; }); // Prune any invalid conversations without members and // set the removed flag if needed @@ -1550,13 +1574,41 @@ ConversationModule::loadConversations() ilk.unlock(); lk.unlock(); - dht::ThreadPool::io().run( - [w = pimpl_->weak(), acc, updateContactConv = std::move(ctx->updateContactConv), toRm = std::move(ctx->toRm)]() { - // Will lock account manager - if (auto shared = w.lock()) - shared->fixStructures(acc, updateContactConv, toRm); - }); + dht::ThreadPool::io().run([w = pimpl_->weak(), + acc, + updateContactConv = std::move(ctx->updateContactConv), + toRm = std::move(ctx->toRm)]() { + // Will lock account manager + if (auto shared = w.lock()) + shared->fixStructures(acc, updateContactConv, toRm); + }); +} + +void +ConversationModule::loadSingleConversation(const std::string& convId) +{ + auto acc = pimpl_->account_.lock(); + if (!acc) + return; + JAMI_LOG("[Account {}] Start loading conversation {}", pimpl_->accountId_, convId); + std::unique_lock<std::mutex> lk(pimpl_->conversationsMtx_); + pimpl_->conversations_.clear(); + + try { + auto sconv = std::make_shared<SyncedConversation>(convId); + + auto conv = std::make_shared<Conversation>(acc, convId); + + conv->onNeedSocket(pimpl_->onNeedSwarmSocket_); + + sconv->conversation = conv; + pimpl_->conversations_.emplace(convId, std::move(sconv)); + } catch (const std::logic_error& e) { + JAMI_WARNING("[Account {}] Conversations not loaded: {}", pimpl_->accountId_, e.what()); + } + + lk.unlock(); } void @@ -1644,8 +1696,9 @@ ConversationModule::onTrustRequest(const std::string& uri, if (getOneToOneConversation(uri) != "") { // If there is already an active one to one conversation here, it's an active // contact and the contact will reclone this activeConv, so ignore the request - JAMI_WARNING("Contact is sending a request for a non active conversation. Ignore. They will " - "clone the old one"); + JAMI_WARNING( + "Contact is sending a request for a non active conversation. Ignore. They will " + "clone the old one"); return; } std::unique_lock<std::mutex> lk(pimpl_->conversationsRequestsMtx_); @@ -1669,8 +1722,8 @@ ConversationModule::onTrustRequest(const std::string& uri, pimpl_->needsSyncingCb_({}); } else { JAMI_DEBUG("[Account {}] Received a request for a conversation " - "already existing. Ignore", - pimpl_->accountId_); + "already existing. Ignore", + pimpl_->accountId_); } } @@ -1686,9 +1739,9 @@ ConversationModule::onConversationRequest(const std::string& from, const Json::V } std::unique_lock<std::mutex> lk(pimpl_->conversationsRequestsMtx_); JAMI_DEBUG("[Account {}] Receive a new conversation request for conversation {} from {}", - pimpl_->accountId_, - req.conversationId, - from); + pimpl_->accountId_, + req.conversationId, + from); auto convId = req.conversationId; // Already accepted request, do nothing @@ -1697,8 +1750,9 @@ ConversationModule::onConversationRequest(const std::string& from, const Json::V auto oldReq = pimpl_->getRequest(convId); if (oldReq != std::nullopt) { JAMI_DEBUG("[Account {}] Received a request for a conversation already existing. " - "Ignore. Declined: {}", - pimpl_->accountId_, static_cast<int>(oldReq->declined)); + "Ignore. Declined: {}", + pimpl_->accountId_, + static_cast<int>(oldReq->declined)); return; } @@ -1707,8 +1761,9 @@ ConversationModule::onConversationRequest(const std::string& from, const Json::V // Already a conversation with the contact. // If there is already an active one to one conversation here, it's an active // contact and the contact will reclone this activeConv, so ignore the request - JAMI_WARNING("Contact is sending a request for a non active conversation. Ignore. They will " - "clone the old one"); + JAMI_WARNING( + "Contact is sending a request for a non active conversation. Ignore. They will " + "clone the old one"); return; } @@ -1753,7 +1808,8 @@ ConversationModule::onNeedConversationRequest(const std::string& from, } void -ConversationModule::acceptConversationRequest(const std::string& conversationId, const std::string& deviceId) +ConversationModule::acceptConversationRequest(const std::string& conversationId, + const std::string& deviceId) { // For all conversation members, try to open a git channel with this conversation ID std::unique_lock<std::mutex> lkCr(pimpl_->conversationsRequestsMtx_); @@ -1767,7 +1823,9 @@ ConversationModule::acceptConversationRequest(const std::string& conversationId, pimpl_->cloneConversationFrom(conv, deviceId); } } - JAMI_WARNING("[Account {}] Request not found for conversation {}", pimpl_->accountId_, conversationId); + JAMI_WARNING("[Account {}] Request not found for conversation {}", + pimpl_->accountId_, + conversationId); return; } pimpl_->rmConversationRequest(conversationId); @@ -2061,13 +2119,13 @@ ConversationModule::loadSwarmUntil(const std::string& conversationId, options.to = toMessage; options.includeTo = true; conv->conversation->loadMessages2( - [accountId = pimpl_->accountId_, conversationId, id](auto&& messages) { - emitSignal<libjami::ConversationSignal::SwarmLoaded>(id, - accountId, - conversationId, - messages); - }, - options); + [accountId = pimpl_->accountId_, conversationId, id](auto&& messages) { + emitSignal<libjami::ConversationSignal::SwarmLoaded>(id, + accountId, + conversationId, + messages); + }, + options); return id; } } @@ -2165,7 +2223,8 @@ ConversationModule::onSyncData(const SyncMsg& msg, auto conv = pimpl_->startConversation(convInfo); std::unique_lock<std::mutex> lk(conv->mtx); // Skip outdated info - if (std::max(convInfo.created, convInfo.removed) < std::max(conv->info.created, conv->info.removed)) + if (std::max(convInfo.created, convInfo.removed) + < std::max(conv->info.created, conv->info.removed)) continue; if (not convInfo.isRemoved()) { // If multi devices, it can detect a conversation that was already @@ -2322,9 +2381,9 @@ ConversationModule::setFetched(const std::string& conversationId, void ConversationModule::fetchNewCommits(const std::string& peer, - const std::string& deviceId, - const std::string& conversationId, - const std::string& commitId) + const std::string& deviceId, + const std::string& conversationId, + const std::string& commitId) { pimpl_->fetchNewCommits(peer, deviceId, conversationId, commitId); } @@ -2544,7 +2603,8 @@ ConversationModule::isBanned(const std::string& convId, const std::string& uri) // If 1:1 we check the certificate status if (auto acc = pimpl_->account_.lock()) { if (auto am = acc->accountManager()) - return am->getCertificateStatus(uri) == dhtnet::tls::TrustStore::PermissionStatus::BANNED; + return am->getCertificateStatus(uri) + == dhtnet::tls::TrustStore::PermissionStatus::BANNED; } return true; } @@ -2584,9 +2644,7 @@ ConversationModule::removeContact(const std::string& uri, bool banned) }; auto removeConvInfo = [&](const auto& conv, const auto& members) { if ((isSelf && members.size() == 1) - || (!isSelf - && std::find(members.begin(), members.end(), uri) - != members.end())) { + || (!isSelf && std::find(members.begin(), members.end(), uri) != members.end())) { // Mark as removed conv->info.removed = std::time(nullptr); updateClient(conv->info.id); @@ -2899,8 +2957,7 @@ ConversationModule::convInfosFromPath(const std::filesystem::path& path) std::map<std::string, ConvInfo> convInfos; try { // read file - std::lock_guard lock( - dhtnet::fileutils::getFileLock(path / "convInfo")); + std::lock_guard lock(dhtnet::fileutils::getFileLock(path / "convInfo")); auto file = fileutils::loadFile("convInfo", path); // load values msgpack::unpacked result; @@ -2925,8 +2982,7 @@ ConversationModule::convRequestsFromPath(const std::filesystem::path& path) std::map<std::string, ConversationRequest> convRequests; try { // read file - std::lock_guard lock( - dhtnet::fileutils::getFileLock(path / "convRequests")); + std::lock_guard lock(dhtnet::fileutils::getFileLock(path / "convRequests")); auto file = fileutils::loadFile("convRequests", path); // load values msgpack::unpacked result; @@ -3003,11 +3059,11 @@ ConversationModule::shutdownConnections() { std::lock_guard lk(pimpl_->conversationsMtx_); conversations.reserve(pimpl_->conversations_.size()); - for (auto& [k, c]: pimpl_->conversations_) { + for (auto& [k, c] : pimpl_->conversations_) { conversations.emplace_back(c); } } - for (const auto& c: conversations) { + for (const auto& c : conversations) { std::lock_guard lkc(c->mtx); if (c->conversation) c->conversation->shutdownConnections(); @@ -3030,18 +3086,18 @@ ConversationModule::connectivityChanged() { std::lock_guard lk(pimpl_->conversationsMtx_); syncedConversations.reserve(pimpl_->conversations_.size()); - for (const auto& [k, c]: pimpl_->conversations_) { + for (const auto& [k, c] : pimpl_->conversations_) { syncedConversations.emplace_back(c); } } std::vector<std::shared_ptr<Conversation>> conversations; conversations.reserve(syncedConversations.size()); - for (const auto& c: syncedConversations) { + for (const auto& c : syncedConversations) { std::lock_guard lkc(c->mtx); if (c->conversation) conversations.emplace_back(c->conversation); } - for (const auto& conv: conversations) + for (const auto& conv : conversations) conv->connectivityChanged(); } } // namespace jami diff --git a/src/jamidht/conversation_module.h b/src/jamidht/conversation_module.h index eb8c3198334bf883a12733c41653d39e0efcfea0..10346e6dab023293623e5d781c46d85f5668867c 100644 --- a/src/jamidht/conversation_module.h +++ b/src/jamidht/conversation_module.h @@ -79,7 +79,8 @@ public: NeedSocketCb&& onNeedSocket, NeedSocketCb&& onNeedSwarmSocket, UpdateConvReq&& updateConvReqCb, - OneToOneRecvCb&& oneToOneRecvCb); + OneToOneRecvCb&& oneToOneRecvCb, + bool autoLoadConversations = true); ~ConversationModule() = default; /** @@ -87,6 +88,8 @@ public: */ void loadConversations(); + void loadSingleConversation(const std::string& convId); + #ifdef LIBJAMI_TESTABLE void onBootstrapStatus(const std::function<void(std::string, Conversation::BootstrapStatus)>& cb); #endif @@ -164,7 +167,8 @@ public: * @param convId * @param deviceId If a trust request is accepted from a device (can help to sync) */ - void acceptConversationRequest(const std::string& conversationId, const std::string& deviceId = ""); + void acceptConversationRequest(const std::string& conversationId, + const std::string& deviceId = ""); /** * Decline a conversation's request @@ -239,8 +243,8 @@ public: const std::string& fromMessage = "", size_t n = 0); uint32_t loadConversation(const std::string& conversationId, - const std::string& fromMessage = "", - size_t n = 0); + const std::string& fromMessage = "", + size_t n = 0); uint32_t loadConversationUntil(const std::string& conversationId, const std::string& fromMessage, const std::string& to); @@ -471,7 +475,8 @@ public: static std::map<std::string, ConvInfo> convInfos(const std::string& accountId); static std::map<std::string, ConvInfo> convInfosFromPath(const std::filesystem::path& path); static std::map<std::string, ConversationRequest> convRequests(const std::string& accountId); - static std::map<std::string, ConversationRequest> convRequestsFromPath(const std::filesystem::path& path); + static std::map<std::string, ConversationRequest> convRequestsFromPath( + const std::filesystem::path& path); void addConvInfo(const ConvInfo& info); /** @@ -486,7 +491,7 @@ public: * @return the related socket */ std::shared_ptr<dhtnet::ChannelSocket> gitSocket(std::string_view deviceId, - std::string_view convId) const; + std::string_view convId) const; void removeGitSocket(std::string_view deviceId, std::string_view convId); void addGitSocket(std::string_view deviceId, std::string_view convId, @@ -500,7 +505,8 @@ public: * @param conversationId * @param socket */ - void addSwarmChannel(const std::string& conversationId, std::shared_ptr<dhtnet::ChannelSocket> socket); + void addSwarmChannel(const std::string& conversationId, + std::shared_ptr<dhtnet::ChannelSocket> socket); /** * Triggers a bucket maintainance for DRTs */ diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index da90b36ede08ef497a29ed8158d8e0db869cd956..58c9de7ac876ac65139e7fc4a0a5b1404bb67e04 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -2301,7 +2301,8 @@ JamiAccount::convModule(bool noCreation) convId, {}); }); - }); + }, + autoLoadConversations_); } return convModule_.get(); } @@ -2332,6 +2333,13 @@ JamiAccount::onTextMessage(const std::string& id, } } +void +JamiAccount::loadConversation(const std::string& convId) +{ + if (auto cm = convModule(false)) + cm->loadSingleConversation(convId); +} + void JamiAccount::doUnregister(std::function<void(bool)> released_cb) { diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h index 78ec1d5bcec2514117924af98a6a275aceda9693..b1667056780a6e5a93d38129f64936780adbb693 100644 --- a/src/jamidht/jamiaccount.h +++ b/src/jamidht/jamiaccount.h @@ -261,17 +261,20 @@ public: const std::string& from, const std::string& deviceId, const std::map<std::string, std::string>& payloads) override; + void loadConversation(const std::string& convId); virtual bool isTlsEnabled() const override { return true; } bool isSrtpEnabled() const override { return true; } virtual bool getSrtpFallback() const override { return false; } - bool setCertificateStatus(const std::string& cert_id, dhtnet::tls::TrustStore::PermissionStatus status); + bool setCertificateStatus(const std::string& cert_id, + dhtnet::tls::TrustStore::PermissionStatus status); bool setCertificateStatus(const std::shared_ptr<crypto::Certificate>& cert, dhtnet::tls::TrustStore::PermissionStatus status, bool local = true); - std::vector<std::string> getCertificatesByStatus(dhtnet::tls::TrustStore::PermissionStatus status); + std::vector<std::string> getCertificatesByStatus( + dhtnet::tls::TrustStore::PermissionStatus status); bool findCertificate(const std::string& id); bool findCertificate( @@ -344,8 +347,12 @@ public: * doesn't have a password * @return if the archive was exported */ - bool exportArchive(const std::string& destinationPath, std::string_view scheme = {}, const std::string& password = {}); - bool revokeDevice(const std::string& device, std::string_view scheme = {}, const std::string& password = {}); + bool exportArchive(const std::string& destinationPath, + std::string_view scheme = {}, + const std::string& password = {}); + bool revokeDevice(const std::string& device, + std::string_view scheme = {}, + const std::string& password = {}); std::map<std::string, std::string> getKnownDevices() const; bool isPasswordValid(const std::string& password); @@ -361,7 +368,9 @@ public: #if HAVE_RINGNS void lookupName(const std::string& name); void lookupAddress(const std::string& address); - void registerName(const std::string& name, const std::string& scheme, const std::string& password); + void registerName(const std::string& name, + const std::string& scheme, + const std::string& password); #endif bool searchUser(const std::string& nameQuery); @@ -370,15 +379,9 @@ public: /// further calls bool isMessageTreated(std::string_view id); - std::shared_ptr<dht::DhtRunner> dht() - { - return dht_; - } + std::shared_ptr<dht::DhtRunner> dht() { return dht_; } - const dht::crypto::Identity& identity() const - { - return id_; - } + const dht::crypto::Identity& identity() const { return id_; } void forEachDevice(const dht::InfoHash& to, std::function<void(const std::shared_ptr<dht::crypto::PublicKey>&)>&& op, @@ -426,23 +429,15 @@ public: std::map<std::string, std::string> getNearbyPeers() const override; #ifdef LIBJAMI_TESTABLE - dhtnet::ConnectionManager& connectionManager() - { - return *connectionManager_; - } + dhtnet::ConnectionManager& connectionManager() { return *connectionManager_; } /** * Only used for tests, disable sha3sum verification for transfers. * @param newValue */ - void noSha3sumVerification(bool newValue) - { - noSha3sumVerification_ = newValue; - } + void noSha3sumVerification(bool newValue) { noSha3sumVerification_ = newValue; } - void publishPresence(bool newValue) { - publishPresence_ = newValue; - } + void publishPresence(bool newValue) { publishPresence_ = newValue; } #endif /** @@ -460,7 +455,8 @@ public: void monitor(); // conversationId optional - std::vector<std::map<std::string, std::string>> getConnectionList(const std::string& conversationId = ""); + std::vector<std::map<std::string, std::string>> getConnectionList( + const std::string& conversationId = ""); std::vector<std::map<std::string, std::string>> getChannelList(const std::string& connectionId); // File transfer @@ -534,14 +530,9 @@ public: std::filesystem::path profilePath() const; - const std::shared_ptr<AccountManager>& accountManager() { - return accountManager_; - } + const std::shared_ptr<AccountManager>& accountManager() { return accountManager_; } - bool sha3SumVerify() const - { - return !noSha3sumVerification_; - } + bool sha3SumVerify() const { return !noSha3sumVerification_; } /** * Change certificate's validity period @@ -550,7 +541,10 @@ public: * @param validity New validity * @note forceReloadAccount may be necessary to retrigger the migration */ - bool setValidity(std::string_view scheme, const std::string& pwd, const dht::InfoHash& id, int64_t validity); + bool setValidity(std::string_view scheme, + const std::string& pwd, + const dht::InfoHash& id, + int64_t validity); /** * Try to reload the account to force the identity to be updated */ @@ -576,10 +570,7 @@ public: * connected. This kind of node corresponds to devices with push notifications & proxy and are * stored in the mobile nodes */ - bool isMobile() const - { - return config().proxyEnabled and not config().deviceKey.empty(); - } + bool isMobile() const { return config().proxyEnabled and not config().deviceKey.empty(); } #ifdef LIBJAMI_TESTABLE std::map<Uri::Scheme, std::unique_ptr<ChannelHandlerInterface>>& channelHandlers() @@ -588,10 +579,7 @@ public: }; #endif - dhtnet::tls::CertificateStore& certStore() const - { - return *certStore_; - } + dhtnet::tls::CertificateStore& certStore() const { return *certStore_; } /** * Check if a Device is connected * @param deviceId @@ -723,7 +711,6 @@ private: std::map<dht::Value::Id, PendingMessage> sentMessages_; std::set<std::string, std::less<>> treatedMessages_ {}; - /* tracked buddies presence */ mutable std::mutex buddyInfoMtx; std::map<dht::InfoHash, BuddyInfo> trackedBuddies_; diff --git a/src/manager.cpp b/src/manager.cpp index 2a9caec4b181a338152776aa67a48609cefb3f7c..0e13bc2f8f39bb9cb93b03c3dfe46dfe2acd89fc 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -187,8 +187,7 @@ check_rename(const std::filesystem::path& old_dir, const std::filesystem::path& for (const auto& file_iterator : std::filesystem::directory_iterator(old_dir, ec)) { const auto& file_path = file_iterator.path(); auto new_path = new_dir / file_path.filename(); - if (file_iterator.is_directory() - and std::filesystem::is_directory(new_path)) { + if (file_iterator.is_directory() and std::filesystem::is_directory(new_path)) { check_rename(file_path, new_path); } else { JAMI_WARNING("Migrating {} to {}", old_dir, new_path); @@ -875,7 +874,13 @@ Manager::init(const std::filesystem::path& config_file, libjami::InitFlag flags) getRingBufferPool().getInternalAudioFormat().sampleFormat)); } } - registerAccounts(); + + if (libjami::LIBJAMI_FLAG_NO_AUTOLOAD & flags) { + JAMI_DBG("LIBJAMI_FLAG_NO_AUTOLOAD is set, accounts and conversations will not be loaded"); + return; + } else { + registerAccounts(); + } } void @@ -1408,9 +1413,7 @@ Manager::addParticipant(const std::string& accountId, bool Manager::addParticipant(Call& call, Conference& conference) { - JAMI_DEBUG("Add participant {} to conference {}", - call.getCallId(), - conference.getConfId()); + JAMI_DEBUG("Add participant {} to conference {}", call.getCallId(), conference.getConfId()); // store the current call id (it will change in offHoldCall or in answerCall) pimpl_->bindCallToConference(call, conference); @@ -1713,8 +1716,7 @@ Manager::addAudio(Call& call) auto medias = call.getAudioStreams(); for (const auto& media : medias) { JAMI_DEBUG("[call:{}] Attach audio", media.first); - getRingBufferPool().bindRingbuffers(media.first, - RingBufferPool::DEFAULT_ID); + getRingBufferPool().bindRingbuffers(media.first, RingBufferPool::DEFAULT_ID); } auto oldGuard = std::move(call.audioGuard); call.audioGuard = startAudioStream(AudioDeviceType::PLAYBACK); @@ -2577,9 +2579,9 @@ Manager::ManagerPimpl::processIncomingCall(const std::string& accountId, Call& i JAMI_WARNING("Incoming call {} has an empty media list", incomCallId); JAMI_DEBUG("Incoming call {} on account {} with {} media", - incomCallId, - accountId, - mediaList.size()); + incomCallId, + accountId, + mediaList.size()); emitSignal<libjami::CallSignal::IncomingCallWithMedia>(accountId, incomCallId, @@ -3048,6 +3050,27 @@ Manager::setAccountActive(const std::string& accountID, bool active, bool shutdo accountID, acc->getVolatileAccountDetails()); } +void +Manager::loadAccountAndConversation(const std::string& accountID, const std::string& convID) +{ + if (const auto a = getAccount(accountID)) { + a->enableAutoLoadConversations(false); + if (a->getRegistrationState() == RegistrationState::UNLOADED) { + a->loadConfig(); + } + a->setActive(true); + if (a->isUsable()) + a->doRegister(); + if (auto jamiAcc = std::dynamic_pointer_cast<JamiAccount>(a)) { + // load requests before loading conversation + if (auto convModule = jamiAcc->convModule()) { + convModule->reloadRequests(); + } + jamiAcc->loadConversation(convID); + } + } +} + std::shared_ptr<AudioLayer> Manager::getAudioDriver() { diff --git a/src/manager.h b/src/manager.h index 32b1aa99b4f727e95299ae55c95447a9504a0881..b06d0682b3b701e5be623a3f470ccf02107ba13f 100644 --- a/src/manager.h +++ b/src/manager.h @@ -57,7 +57,7 @@ class io_context; namespace dhtnet { class ChannelSocket; class IceTransportFactory; -} +} // namespace dhtnet namespace jami { namespace video { @@ -126,7 +126,7 @@ public: /** * Initialisation of thread (sound) and map. - * Init a new VoIPLink, audio codec and audio driver + * Init a new VoIPLink, audio codec and audio driver. */ void init(const std::filesystem::path& config_file, libjami::InitFlag flags); @@ -137,8 +137,10 @@ public: void monitor(bool continuous); - std::vector<std::map<std::string, std::string>> getConnectionList(const std::string& accountId, const std::string& conversationId); - std::vector<std::map<std::string, std::string>> getChannelList(const std::string& accountId, const std::string& connectionId); + std::vector<std::map<std::string, std::string>> getConnectionList( + const std::string& accountId, const std::string& conversationId); + std::vector<std::map<std::string, std::string>> getChannelList(const std::string& accountId, + const std::string& connectionId); /** * Accessor to audiodriver. @@ -441,6 +443,7 @@ public: const std::map<std::string, ::std::string>& details); void setAccountActive(const std::string& accountID, bool active, bool shutdownConnections); + void loadAccountAndConversation(const std::string& accountID, const std::string& convID); std::mt19937_64 getSeededRandomEngine(); @@ -771,6 +774,11 @@ public: */ void registerAccounts(); + /** + * Send registration for one account + */ + void registerAccount(const std::string& accountID, const std::string& convId = {}); + /** * Send unregister for all enabled accounts */ @@ -864,8 +872,8 @@ public: * @return std::optional<std::weak_ptr<ChannelSocket>> the related socket */ std::shared_ptr<dhtnet::ChannelSocket> gitSocket(const std::string_view accountId, - const std::string_view deviceId, - const std::string_view conversationId); + const std::string_view deviceId, + const std::string_view conversationId); void setDefaultModerator(const std::string& accountID, const std::string& peerURI, bool state); std::vector<std::string> getDefaultModerators(const std::string& accountID);