From 70d36c88e99dbb8afaed1bf7e8475551c5535f3c Mon Sep 17 00:00:00 2001 From: agsantos <aline.gondimsantos@savoirfairelinux.com> Date: Thu, 21 Jan 2021 19:27:26 -0500 Subject: [PATCH] plugin: improve Handler toggling management - add jami preference to list non standard installation paths GitLab: #396 GitLab: #397 Change-Id: I65de9e4e9e345e866df9720918d080636541bbdc --- src/client/plugin_manager_interface.cpp | 5 +- src/manager.cpp | 14 +-- src/observer.h | 8 +- src/plugin/callservicesmanager.cpp | 109 ++++++++++++++---------- src/plugin/callservicesmanager.h | 28 +++--- src/plugin/chatservicesmanager.cpp | 90 ++++++++++--------- src/plugin/chatservicesmanager.h | 11 ++- src/plugin/jamipluginmanager.cpp | 27 ++++-- src/plugin/pluginmanager.cpp | 33 +++---- src/plugin/pluginmanager.h | 2 +- src/plugin/pluginpreferencesutils.cpp | 72 ++++++++++++++-- src/plugin/pluginpreferencesutils.h | 17 +++- src/preferences.cpp | 8 ++ src/preferences.h | 6 ++ src/sip/sipcall.cpp | 5 ++ 15 files changed, 292 insertions(+), 143 deletions(-) diff --git a/src/client/plugin_manager_interface.cpp b/src/client/plugin_manager_interface.cpp index 0840725d21..0da6fa6641 100644 --- a/src/client/plugin_manager_interface.cpp +++ b/src/client/plugin_manager_interface.cpp @@ -95,7 +95,10 @@ installPlugin(const std::string& jplPath, bool force) int uninstallPlugin(const std::string& pluginRootPath) { - return jami::Manager::instance().getJamiPluginManager().uninstallPlugin(pluginRootPath); + int status = jami::Manager::instance().getJamiPluginManager().uninstallPlugin(pluginRootPath); + jami::Manager::instance().pluginPreferences.saveStateLoadedPlugins(pluginRootPath, false); + jami::Manager::instance().saveConfig(); + return status; } std::vector<std::string> diff --git a/src/manager.cpp b/src/manager.cpp index 75482380e6..f5d1d1b1fb 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -3270,7 +3270,9 @@ Manager::setModerator(const std::string& confId, const std::string& peerId, cons } void -Manager::muteParticipant(const std::string& confId, const std::string& participant, const bool& state) +Manager::muteParticipant(const std::string& confId, + const std::string& participant, + const bool& state) { if (auto conf = getConferenceFromID(confId)) { conf->muteParticipant(participant, state); @@ -3292,7 +3294,7 @@ void Manager::setDefaultModerator(const std::string& accountID, const std::string& peerURI, bool state) { auto acc = getAccount(accountID); - if(!acc) { + if (!acc) { JAMI_ERR("Fail to change default moderator, account %s not found", accountID.c_str()); return; } @@ -3308,7 +3310,7 @@ std::vector<std::string> Manager::getDefaultModerators(const std::string& accountID) { auto acc = getAccount(accountID); - if(!acc) { + if (!acc) { JAMI_ERR("Fail to get default moderators, account %s not found", accountID.c_str()); return {}; } @@ -3321,7 +3323,7 @@ void Manager::enableLocalModerators(const std::string& accountID, bool isModEnabled) { auto acc = getAccount(accountID); - if(!acc) { + if (!acc) { JAMI_ERR("Fail to set local moderators, account %s not found", accountID.c_str()); return; } @@ -3333,9 +3335,9 @@ bool Manager::isLocalModeratorsEnabled(const std::string& accountID) { auto acc = getAccount(accountID); - if(!acc) { + if (!acc) { JAMI_ERR("Fail to get local moderators, account %s not found", accountID.c_str()); - return true; // Default value + return true; // Default value } return acc->isLocalModeratorsEnabled(); } diff --git a/src/observer.h b/src/observer.h index 8bc0663183..a0daaa3bf6 100644 --- a/src/observer.h +++ b/src/observer.h @@ -192,7 +192,13 @@ public: : map_ {f} {} - void update(Observable<T1>*, const T1& t) override { this->notify(map_(t)); } + void update(Observable<T1>*, const T1& t) override + { + std::lock_guard<std::mutex> lk(this->mutex_); + for (const auto& observer : this->observers_) { + observer->update(this, map_(t)); + } + } /** * @brief attached diff --git a/src/plugin/callservicesmanager.cpp b/src/plugin/callservicesmanager.cpp index 6ab148663b..339aaf8ee0 100644 --- a/src/plugin/callservicesmanager.cpp +++ b/src/plugin/callservicesmanager.cpp @@ -40,36 +40,32 @@ CallServicesManager::~CallServicesManager() void CallServicesManager::createAVSubject(const StreamData& data, AVSubjectSPtr subject) { - // This guarantees unicity of subjects by id - callAVsubjects_.emplace_back(data, subject); + /// callAVsubjects_ emplaces data and subject with callId key to easy of access + /// When call is ended, subjects from this call are erased. + callAVsubjects_[data.id].emplace_back(data, subject); for (auto& callMediaHandler : callMediaHandlers_) { std::size_t found = callMediaHandler->id().find_last_of(DIR_SEPARATOR_CH); - auto preferences = PluginPreferencesUtils::getPreferencesValuesMap(callMediaHandler->id().substr(0, found)); + auto preferences = PluginPreferencesUtils::getPreferencesValuesMap( + callMediaHandler->id().substr(0, found)); + bool toggle = preferences.at("always") == "1"; + for (const auto& toggledMediaHandlerPair : mediaHandlerToggled_[data.id]) { + if (toggledMediaHandlerPair.first == (uintptr_t) callMediaHandler.get()) { + toggle = toggledMediaHandlerPair.second; + break; + } + } #ifndef __ANDROID__ - if (preferences.at("always") == "1") + if (toggle) toggleCallMediaHandler((uintptr_t) callMediaHandler.get(), data.id, true); - else #endif - for (const auto& toggledMediaHandler : mediaHandlerToggled_[data.id]) { - if (toggledMediaHandler == (uintptr_t) callMediaHandler.get()) { - toggleCallMediaHandler(toggledMediaHandler, data.id, true); - break; - } - } } } void CallServicesManager::clearAVSubject(const std::string& callId) { - for (auto it = callAVsubjects_.begin(); it != callAVsubjects_.end();) { - if (it->first.id == callId) { - it = callAVsubjects_.erase(it); - } else { - ++it; - } - } + callAVsubjects_.erase(callId); } void @@ -85,13 +81,28 @@ CallServicesManager::registerComponentsLifeCycleManagers(PluginManager& pm) }; auto unregisterMediaHandler = [this](void* data) { - for (auto it = callMediaHandlers_.begin(); it != callMediaHandlers_.end(); ++it) { - if (it->get() == data) { - callMediaHandlers_.erase(it); - break; + auto handlerIt = std::find_if(callMediaHandlers_.begin(), + callMediaHandlers_.end(), + [data](CallMediaHandlerPtr& handler) { + return (handler.get() == data); + }); + + if (handlerIt != callMediaHandlers_.end()) { + for (auto& toggledList : mediaHandlerToggled_) { + auto handlerId = std::find_if(toggledList.second.begin(), + toggledList.second.end(), + [this, handlerIt]( + std::pair<uintptr_t, bool> handlerIdPair) { + return handlerIdPair.first + == (uintptr_t) handlerIt->get() + && handlerIdPair.second; + }); + if (handlerId != toggledList.second.end()) + toggleCallMediaHandler((*handlerId).first, toggledList.first, false); } + callMediaHandlers_.erase(handlerIt); } - return 0; + return true; }; pm.registerComponentManager("CallMediaHandlerManager", @@ -112,8 +123,8 @@ CallServicesManager::getCallMediaHandlers() void CallServicesManager::toggleCallMediaHandler(const std::string& mediaHandlerId, - const std::string& callId, - const bool toggle) + const std::string& callId, + const bool toggle) { toggleCallMediaHandler(std::stoull(mediaHandlerId), callId, toggle); } @@ -161,29 +172,35 @@ CallServicesManager::getCallMediaHandlerStatus(const std::string& callId) { std::vector<std::string> ret; const auto& it = mediaHandlerToggled_.find(callId); - if (it != mediaHandlerToggled_.end()) { - ret.reserve(it->second.size()); + if (it != mediaHandlerToggled_.end()) for (const auto& mediaHandlerId : it->second) - ret.emplace_back(std::to_string(mediaHandlerId)); - } + if (mediaHandlerId.second) + ret.emplace_back(std::to_string(mediaHandlerId.first)); return ret; } void -CallServicesManager::setPreference(const std::string& key, const std::string& value, const std::string& scopeStr) +CallServicesManager::setPreference(const std::string& key, + const std::string& value, + const std::string& scopeStr) { for (auto& mediaHandler : callMediaHandlers_) { - if (scopeStr.find(mediaHandler->getCallMediaHandlerDetails()["name"]) - != std::string::npos) { + if (scopeStr.find(mediaHandler->getCallMediaHandlerDetails()["name"]) != std::string::npos) { mediaHandler->setPreferenceAttribute(key, value); } } } +void +CallServicesManager::clearCallHandlerMaps(const std::string& callId) +{ + mediaHandlerToggled_.erase(callId); +} + void CallServicesManager::notifyAVSubject(CallMediaHandlerPtr& callMediaHandlerPtr, - const StreamData& data, - AVSubjectSPtr& subject) + const StreamData& data, + AVSubjectSPtr& subject) { if (auto soSubject = subject.lock()) callMediaHandlerPtr->notifyAVFrameSubject(data, soSubject); @@ -191,30 +208,28 @@ CallServicesManager::notifyAVSubject(CallMediaHandlerPtr& callMediaHandlerPtr, void CallServicesManager::toggleCallMediaHandler(const uintptr_t mediaHandlerId, - const std::string& callId, - const bool toggle) + const std::string& callId, + const bool toggle) { auto& handlers = mediaHandlerToggled_[callId]; - bool applyRestart = false; - for (auto subject : callAVsubjects_) { + for (auto subject : callAVsubjects_[callId]) { if (subject.first.id == callId) { - - auto handlerIt = std::find_if(callMediaHandlers_.begin(), callMediaHandlers_.end(), - [mediaHandlerId](CallMediaHandlerPtr& handler) { - return ((uintptr_t) handler.get() == mediaHandlerId); - }); + auto handlerIt = std::find_if(callMediaHandlers_.begin(), + callMediaHandlers_.end(), + [mediaHandlerId](CallMediaHandlerPtr& handler) { + return ((uintptr_t) handler.get() == mediaHandlerId); + }); if (handlerIt != callMediaHandlers_.end()) { if (toggle) { notifyAVSubject((*handlerIt), subject.first, subject.second); - if (isAttached((*handlerIt)) - && handlers.find(mediaHandlerId) == handlers.end()) - handlers.insert(mediaHandlerId); + if (isAttached((*handlerIt))) + handlers[mediaHandlerId] = true; } else { (*handlerIt)->detach(); - handlers.erase(mediaHandlerId); + handlers[mediaHandlerId] = false; } if (subject.first.type == StreamType::video && isVideoType((*handlerIt))) applyRestart = true; diff --git a/src/plugin/callservicesmanager.h b/src/plugin/callservicesmanager.h index 8ab1a5a62b..711db065bf 100644 --- a/src/plugin/callservicesmanager.h +++ b/src/plugin/callservicesmanager.h @@ -74,7 +74,7 @@ public: * @param mediaHandler ID handler ID * @param callId call ID * @param toggle notify with new subjects if true, detach if false. - * + * * In the case when the mediaHandler receives a hardware format * frame and converts it to main memory, we need to restart the * sender to unlink ours encoder and decoder. @@ -100,7 +100,11 @@ public: std::vector<std::string> getCallMediaHandlerStatus(const std::string& callId); - void setPreference(const std::string& key, const std::string& value, const std::string& scopeStr); + void setPreference(const std::string& key, + const std::string& value, + const std::string& scopeStr); + + void clearCallHandlerMaps(const std::string& callId); private: /** @@ -125,20 +129,14 @@ private: */ std::list<CallMediaHandlerPtr> callMediaHandlers_; - /** - * @brief callAVsubjects_ - * When there is a SIPCall, CallAVSubjects_ are created there - * Here we keep a reference to them in order to make them interact with - * CallMediaHandlers_ - * It is pushed to this list list - */ - std::list<std::pair<const StreamData, AVSubjectSPtr>> callAVsubjects_; + /// When there is a SIPCall, AVSubjects are created there. + /// Here we store their references in order to make them interact with MediaHandlers. + /// For easy access they are mapped with the callId they belong to. + std::map<std::string, std::list<std::pair<const StreamData, AVSubjectSPtr>>> callAVsubjects_; - /** - * @brief mediaHandlerToggled_ - * A map of callId and list of mediaHandlers pointers str - */ - std::map<std::string, std::set<uintptr_t>> mediaHandlerToggled_; // callId, list of mediaHandlers + /// Component that stores MediaHandlers' status for each existing call. + /// A map of callIds and MediaHandler-status pairs. + std::map<std::string, std::map<uintptr_t, bool>> mediaHandlerToggled_; }; } // namespace jami diff --git a/src/plugin/chatservicesmanager.cpp b/src/plugin/chatservicesmanager.cpp index 28f6ae1703..108eef53e5 100644 --- a/src/plugin/chatservicesmanager.cpp +++ b/src/plugin/chatservicesmanager.cpp @@ -17,7 +17,6 @@ */ #include "chatservicesmanager.h" -#include "pluginpreferencesutils.h" #include "logger.h" #include "manager.h" #include "fileutils.h" @@ -28,6 +27,7 @@ ChatServicesManager::ChatServicesManager(PluginManager& pm) { registerComponentsLifeCycleManagers(pm); registerChatService(pm); + PluginPreferencesUtils::getAllowDenyListPreferences(allowDenyList_); } void @@ -38,35 +38,37 @@ ChatServicesManager::registerComponentsLifeCycleManagers(PluginManager& pm) if (!ptr) return -1; + handlersNameMap_[ptr->getChatHandlerDetails().at("name")] = (uintptr_t) ptr.get(); chatHandlers_.emplace_back(std::move(ptr)); return 0; }; auto unregisterChatHandler = [this](void* data) { - auto handlerIt = std::find_if(chatHandlers_.begin(), chatHandlers_.end(), - [data](ChatHandlerPtr& handler) { - return (handler.get() == data); - }); + auto handlerIt = std::find_if(chatHandlers_.begin(), + chatHandlers_.end(), + [data](ChatHandlerPtr& handler) { + return (handler.get() == data); + }); if (handlerIt != chatHandlers_.end()) { - for (auto& toggledList: chatHandlerToggled_) { - auto handlerId = std::find_if(toggledList.second.begin(), toggledList.second.end(), - [this, handlerIt](uintptr_t handlerId) { - return (handlerId == (uintptr_t) handlerIt->get()); - }); + for (auto& toggledList : chatHandlerToggled_) { + auto handlerId = std::find_if(toggledList.second.begin(), + toggledList.second.end(), + [this, handlerIt](uintptr_t handlerId) { + return (handlerId == (uintptr_t) handlerIt->get()); + }); if (handlerId != toggledList.second.end()) { - handlerId = toggledList.second.erase(handlerId); (*handlerIt)->detach(chatSubjects_[toggledList.first]); + toggledList.second.erase(handlerId); } } + handlersNameMap_.erase((*handlerIt)->getChatHandlerDetails().at("name")); chatHandlers_.erase(handlerIt); } - return 0; + return true; }; - pm.registerComponentManager("ChatHandlerManager", - registerChatHandler, - unregisterChatHandler); + pm.registerComponentManager("ChatHandlerManager", registerChatHandler, unregisterChatHandler); } void @@ -99,18 +101,25 @@ ChatServicesManager::publishMessage(pluginMessagePtr& cm) return; std::pair<std::string, std::string> mPair(cm->accountId, cm->peerId); auto& handlers = chatHandlerToggled_[mPair]; + auto& chatAllowDenySet = allowDenyList_[mPair]; + for (auto& chatHandler : chatHandlers_) { + std::string chatHandlerName = chatHandler->getChatHandlerDetails().at("name"); std::size_t found = chatHandler->id().find_last_of(DIR_SEPARATOR_CH); auto preferences = PluginPreferencesUtils::getPreferencesValuesMap( chatHandler->id().substr(0, found)); - bool toggled = false; - if (handlers.find((uintptr_t) chatHandler.get()) != handlers.end()) - toggled = true; - if (preferences.at("always") == "1" || toggled) { + bool toggle = preferences.at("always") == "1"; + auto allowedIt = chatAllowDenySet.find(chatHandlerName); + if (allowedIt != chatAllowDenySet.end()) + toggle = (*allowedIt).second; + bool toggled = handlers.find((uintptr_t) chatHandler.get()) != handlers.end(); + if (toggle || toggled) { chatSubjects_.emplace(mPair, std::make_shared<PublishObservable<pluginMessagePtr>>()); if (!toggled) { handlers.insert((uintptr_t) chatHandler.get()); chatHandler->notifyChatSubject(mPair, chatSubjects_[mPair]); + chatAllowDenySet[chatHandlerName] = true; + PluginPreferencesUtils::setAllowDenyListPreferences(allowDenyList_); } chatSubjects_[mPair]->publish(cm); } @@ -133,25 +142,23 @@ ChatServicesManager::cleanChatSubjects(const std::string& accountId, const std:: void ChatServicesManager::toggleChatHandler(const std::string& chatHandlerId, - const std::string& accountId, - const std::string& peerId, - const bool toggle) + const std::string& accountId, + const std::string& peerId, + const bool toggle) { toggleChatHandler(std::stoull(chatHandlerId), accountId, peerId, toggle); } std::vector<std::string> -ChatServicesManager::getChatHandlerStatus(const std::string& accountId, - const std::string& peerId) +ChatServicesManager::getChatHandlerStatus(const std::string& accountId, const std::string& peerId) { std::pair<std::string, std::string> mPair(accountId, peerId); - const auto& it = chatHandlerToggled_.find(mPair); + const auto& it = allowDenyList_.find(mPair); std::vector<std::string> ret; - if (it != chatHandlerToggled_.end()) { - ret.reserve(it->second.size()); - for (const auto& chatHandlerId : it->second) - ret.emplace_back(std::to_string(chatHandlerId)); - return ret; + if (it != allowDenyList_.end()) { + for (const auto& chatHandlerName : it->second) + if (chatHandlerName.second) + ret.emplace_back(std::to_string(handlersNameMap_.at(chatHandlerName.first))); } return ret; @@ -170,7 +177,9 @@ ChatServicesManager::getChatHandlerDetails(const std::string& chatHandlerIdStr) } void -ChatServicesManager::setPreference(const std::string& key, const std::string& value, const std::string& scopeStr) +ChatServicesManager::setPreference(const std::string& key, + const std::string& value, + const std::string& scopeStr) { for (auto& chatHandler : chatHandlers_) { if (scopeStr.find(chatHandler->getChatHandlerDetails()["name"]) != std::string::npos) { @@ -181,28 +190,33 @@ ChatServicesManager::setPreference(const std::string& key, const std::string& va void ChatServicesManager::toggleChatHandler(const uintptr_t chatHandlerId, - const std::string& accountId, - const std::string& peerId, - const bool toggle) + const std::string& accountId, + const std::string& peerId, + const bool toggle) { std::pair<std::string, std::string> mPair(accountId, peerId); auto& handlers = chatHandlerToggled_[mPair]; + auto& chatAllowDenySet = allowDenyList_[mPair]; chatSubjects_.emplace(mPair, std::make_shared<PublishObservable<pluginMessagePtr>>()); - auto chatHandlerIt = std::find_if(chatHandlers_.begin(), chatHandlers_.end(), - [chatHandlerId](ChatHandlerPtr& handler) { - return ((uintptr_t) handler.get() == chatHandlerId); - }); + auto chatHandlerIt = std::find_if(chatHandlers_.begin(), + chatHandlers_.end(), + [chatHandlerId](ChatHandlerPtr& handler) { + return ((uintptr_t) handler.get() == chatHandlerId); + }); if (chatHandlerIt != chatHandlers_.end()) { if (toggle) { (*chatHandlerIt)->notifyChatSubject(mPair, chatSubjects_[mPair]); if (handlers.find(chatHandlerId) == handlers.end()) handlers.insert(chatHandlerId); + chatAllowDenySet[(*chatHandlerIt)->getChatHandlerDetails().at("name")] = true; } else { (*chatHandlerIt)->detach(chatSubjects_[mPair]); handlers.erase(chatHandlerId); + chatAllowDenySet[(*chatHandlerIt)->getChatHandlerDetails().at("name")] = false; } + PluginPreferencesUtils::setAllowDenyListPreferences(allowDenyList_); } } } // namespace jami diff --git a/src/plugin/chatservicesmanager.h b/src/plugin/chatservicesmanager.h index de4bceb9ce..7fdf6b9bd9 100644 --- a/src/plugin/chatservicesmanager.h +++ b/src/plugin/chatservicesmanager.h @@ -20,6 +20,7 @@ #include "noncopyable.h" #include "pluginmanager.h" #include "chathandler.h" +#include "pluginpreferencesutils.h" namespace jami { @@ -57,7 +58,9 @@ public: */ std::map<std::string, std::string> getChatHandlerDetails(const std::string& chatHandlerIdStr); - void setPreference(const std::string& key, const std::string& value, const std::string& scopeStr); + void setPreference(const std::string& key, + const std::string& value, + const std::string& scopeStr); private: void toggleChatHandler(const uintptr_t chatHandlerId, @@ -70,5 +73,11 @@ private: chatHandlerToggled_; // {account,peer}, list of chatHandlers std::map<std::pair<std::string, std::string>, chatSubjectPtr> chatSubjects_; + std::map<std::string, uintptr_t> handlersNameMap_ {}; + + /// Component that stores persistent ChatHandlers' status for each existing + /// accountId, peerId pair. + /// A map of accountId, peerId pairs and ChatHandler-status pairs. + ChatHandlerList allowDenyList_ {}; }; } // namespace jami diff --git a/src/plugin/jamipluginmanager.cpp b/src/plugin/jamipluginmanager.cpp index b6c87f4acf..980d264083 100644 --- a/src/plugin/jamipluginmanager.cpp +++ b/src/plugin/jamipluginmanager.cpp @@ -84,7 +84,10 @@ checkManifestJsonContentValidity(const Json::Value& root) std::string version = root.get("version", "").asString(); std::string iconPath = root.get("iconPath", "icon.png").asString(); if (!name.empty() || !version.empty()) { - return {{"name", name}, {"description", description}, {"version", version}, {"iconPath", iconPath}}; + return {{"name", name}, + {"description", description}, + {"version", version}, + {"iconPath", iconPath}}; } else { throw std::runtime_error("plugin manifest file: bad format"); } @@ -176,6 +179,14 @@ JamiPluginManager::getInstalledPlugins() }; auto returnIterator = std::remove_if(pluginsPaths.begin(), pluginsPaths.end(), predicate); pluginsPaths.erase(returnIterator, std::end(pluginsPaths)); + + std::vector<std::string> nonStandardInstalls = jami::Manager::instance() + .pluginPreferences.getInstalledPlugins(); + for (auto& path : nonStandardInstalls) { + if (checkPluginValidity(path)) + pluginsPaths.emplace_back(path); + } + return pluginsPaths; } @@ -221,6 +232,7 @@ JamiPluginManager::installPlugin(const std::string& jplPath, bool force) } else { archiver::uncompressArchive(jplPath, destinationDir, uncompressJplFunction); } + loadPlugin(destinationDir); } catch (const std::exception& e) { JAMI_ERR() << e.what(); } @@ -234,7 +246,7 @@ JamiPluginManager::uninstallPlugin(const std::string& rootPath) if (checkPluginValidity(rootPath)) { auto detailsIt = pluginDetailsMap_.find(rootPath); if (detailsIt != pluginDetailsMap_.end()) { - bool loaded = pm_.checkLoadedPlugin(detailsIt->second.at("soPath")); + bool loaded = pm_.checkLoadedPlugin(rootPath); if (loaded) { JAMI_INFO() << "PLUGIN: unloading before uninstall."; bool status = unloadPlugin(rootPath); @@ -319,13 +331,13 @@ JamiPluginManager::setPluginPreference(const std::string& rootPath, { std::map<std::string, std::string> pluginUserPreferencesMap = PluginPreferencesUtils::getUserPreferencesValuesMap(rootPath); - std::map<std::string, std::string> pluginPreferencesMap = PluginPreferencesUtils::getPreferencesValuesMap( - rootPath); + std::map<std::string, std::string> pluginPreferencesMap + = PluginPreferencesUtils::getPreferencesValuesMap(rootPath); auto find = pluginPreferencesMap.find(key); if (find != pluginPreferencesMap.end()) { - std::vector<std::map<std::string, std::string>> preferences = PluginPreferencesUtils::getPreferences( - rootPath); + std::vector<std::map<std::string, std::string>> preferences + = PluginPreferencesUtils::getPreferences(rootPath); for (auto& preference : preferences) { if (!preference["key"].compare(key)) { callsm_.setPreference(key, value, preference["scope"]); @@ -337,12 +349,12 @@ JamiPluginManager::setPluginPreference(const std::string& rootPath, pluginUserPreferencesMap[key] = value; const std::string preferencesValuesFilePath = PluginPreferencesUtils::valuesFilePath( rootPath); + std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath)); std::ofstream fs(preferencesValuesFilePath, std::ios::binary); if (!fs.good()) { return false; } try { - std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath)); msgpack::pack(fs, pluginUserPreferencesMap); return true; } catch (const std::exception& e) { @@ -379,6 +391,7 @@ JamiPluginManager::readPluginManifestFromArchive(const std::string& jplPath) std::map<std::string, std::string> JamiPluginManager::parseManifestFile(const std::string& manifestFilePath) { + std::lock_guard<std::mutex> guard(fileutils::getFileLock(manifestFilePath)); std::ifstream file(manifestFilePath); if (file) { try { diff --git a/src/plugin/pluginmanager.cpp b/src/plugin/pluginmanager.cpp index 56a740665a..1e53a120d5 100644 --- a/src/plugin/pluginmanager.cpp +++ b/src/plugin/pluginmanager.cpp @@ -50,10 +50,9 @@ PluginManager::~PluginManager() bool PluginManager::load(const std::string& path) { - // Don't load the same dynamic library twice - if (dynPluginMap_.find(path) != dynPluginMap_.end()) { - JAMI_WARN() << "Plugin: already loaded"; - return true; + auto it = dynPluginMap_.find(path); + if (it != dynPluginMap_.end() && !it->second.second) { + dynPluginMap_.erase(it); } std::string error; @@ -72,28 +71,30 @@ PluginManager::load(const std::string& path) if (!registerPlugin(plugin)) return false; - dynPluginMap_[path] = std::move(plugin); + dynPluginMap_[path] = {std::move(plugin), true}; return true; } bool PluginManager::unload(const std::string& path) { - bool returnValue{false}; destroyPluginComponents(path); PluginMap::iterator it = dynPluginMap_.find(path); if (it != dynPluginMap_.end()) { - dynPluginMap_.erase(it); - returnValue = true; + it->second.second = false; } - return returnValue; + return true; } bool PluginManager::checkLoadedPlugin(const std::string& rootPath) const { - return dynPluginMap_.find(rootPath) != dynPluginMap_.end(); + for (const auto& item : dynPluginMap_) { + if (item.first.find(rootPath) != std::string::npos && item.second.second) + return true; + } + return false; } std::vector<std::string> @@ -101,7 +102,8 @@ PluginManager::getLoadedPlugins() const { std::vector<std::string> res {}; for (const auto& pair : dynPluginMap_) { - res.push_back(pair.first); + if (pair.second.second) + res.push_back(pair.first); } return res; } @@ -111,10 +113,11 @@ PluginManager::destroyPluginComponents(const std::string& path) { auto itComponents = pluginComponentsMap_.find(path); if (itComponents != pluginComponentsMap_.end()) { - for (const auto& pair : itComponents->second) { - auto clcm = componentsLifeCycleManagers_.find(pair.first); + for (auto pairIt = itComponents->second.begin(); pairIt != itComponents->second.end();) { + auto clcm = componentsLifeCycleManagers_.find(pairIt->first); if (clcm != componentsLifeCycleManagers_.end()) { - clcm->second.destroyComponent(pair.second); + clcm->second.destroyComponent(pairIt->second); + pairIt = itComponents->second.erase(pairIt); } } } @@ -128,7 +131,7 @@ PluginManager::callPluginInitFunction(const std::string& path) if (it != dynPluginMap_.end()) { // Plugin found // Since the Plugin was found it is of type DLPlugin with a valid init symbol - std::shared_ptr<DLPlugin> plugin = std::static_pointer_cast<DLPlugin>(it->second); + std::shared_ptr<DLPlugin> plugin = std::static_pointer_cast<DLPlugin>(it->second.first); const auto& initFunc = plugin->getInitFunction(); JAMI_PluginExitFunc exitFunc = nullptr; diff --git a/src/plugin/pluginmanager.h b/src/plugin/pluginmanager.h index 582bc6c994..27a0c7d3e1 100644 --- a/src/plugin/pluginmanager.h +++ b/src/plugin/pluginmanager.h @@ -59,7 +59,7 @@ private: ComponentFunction destroyComponent; }; - using PluginMap = std::map<std::string, std::shared_ptr<Plugin>>; + using PluginMap = std::map<std::string, std::pair<std::shared_ptr<Plugin>, bool>>; using PluginComponentsMap = std::map<std::string, ComponentTypePtrVector>; using ExitFuncVec = std::vector<JAMI_PluginExitFunc>; using ObjectFactoryVec = std::vector<ObjectFactory>; diff --git a/src/plugin/pluginpreferencesutils.cpp b/src/plugin/pluginpreferencesutils.cpp index 9238bb0d9c..8a75a771e3 100644 --- a/src/plugin/pluginpreferencesutils.cpp +++ b/src/plugin/pluginpreferencesutils.cpp @@ -41,10 +41,17 @@ PluginPreferencesUtils::valuesFilePath(const std::string& rootPath) return rootPath + DIR_SEPARATOR_CH + "preferences.msgpack"; } +std::string +PluginPreferencesUtils::getAllowDenyListsPath() +{ + return fileutils::get_data_dir() + DIR_SEPARATOR_CH + "plugins" + DIR_SEPARATOR_CH + + "allowdeny.msgpack"; +} + std::string PluginPreferencesUtils::convertArrayToString(const Json::Value& jsonArray) { - std::string stringArray{}; + std::string stringArray {}; if (jsonArray.size()) { for (unsigned i = 0; i < jsonArray.size() - 1; i++) { @@ -65,7 +72,7 @@ PluginPreferencesUtils::convertArrayToString(const Json::Value& jsonArray) } std::map<std::string, std::string> -PluginPreferencesUtils::parsePreferenceConfig(const Json::Value& jsonPreference, const std::string& /*type*/) +PluginPreferencesUtils::parsePreferenceConfig(const Json::Value& jsonPreference) { std::map<std::string, std::string> preferenceMap; const auto& members = jsonPreference.getMemberNames(); @@ -84,7 +91,8 @@ PluginPreferencesUtils::parsePreferenceConfig(const Json::Value& jsonPreference, std::vector<std::map<std::string, std::string>> PluginPreferencesUtils::getPreferences(const std::string& rootPath) { - const std::string preferenceFilePath = getPreferencesConfigFilePath(rootPath); + std::string preferenceFilePath = getPreferencesConfigFilePath(rootPath); + std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferenceFilePath)); std::ifstream file(preferenceFilePath); Json::Value root; Json::CharReaderBuilder rbuilder; @@ -102,7 +110,7 @@ PluginPreferencesUtils::getPreferences(const std::string& rootPath) std::string key = jsonPreference.get("key", "None").asString(); if (type != "None" && key != "None") { if (keys.find(key) == keys.end()) { - auto preferenceAttributes = parsePreferenceConfig(jsonPreference, type); + auto preferenceAttributes = parsePreferenceConfig(jsonPreference); // If the parsing of the attributes was successful, commit the map and the keys auto defaultValue = preferenceAttributes.find("defaultValue"); if (type == "Path" && defaultValue != preferenceAttributes.end()) { @@ -130,12 +138,12 @@ std::map<std::string, std::string> PluginPreferencesUtils::getUserPreferencesValuesMap(const std::string& rootPath) { const std::string preferencesValuesFilePath = valuesFilePath(rootPath); + std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath)); std::ifstream file(preferencesValuesFilePath, std::ios::binary); std::map<std::string, std::string> rmap; // If file is accessible if (file.good()) { - std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath)); // Get file size std::string str; file.seekg(0, std::ios::end); @@ -166,8 +174,7 @@ PluginPreferencesUtils::getPreferencesValuesMap(const std::string& rootPath) { std::map<std::string, std::string> rmap; - std::vector<std::map<std::string, std::string>> preferences = getPreferences( - rootPath); + std::vector<std::map<std::string, std::string>> preferences = getPreferences(rootPath); for (auto& preference : preferences) { rmap[preference["key"]] = preference["defaultValue"]; } @@ -188,12 +195,12 @@ PluginPreferencesUtils::resetPreferencesValuesMap(const std::string& rootPath) std::map<std::string, std::string> pluginPreferencesMap {}; const std::string preferencesValuesFilePath = valuesFilePath(rootPath); + std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath)); std::ofstream fs(preferencesValuesFilePath, std::ios::binary); if (!fs.good()) { return false; } try { - std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath)); msgpack::pack(fs, pluginPreferencesMap); } catch (const std::exception& e) { returnValue = false; @@ -202,4 +209,53 @@ PluginPreferencesUtils::resetPreferencesValuesMap(const std::string& rootPath) return returnValue; } + +void +PluginPreferencesUtils::setAllowDenyListPreferences(const ChatHandlerList& list) +{ + std::string filePath = getAllowDenyListsPath(); + std::lock_guard<std::mutex> guard(fileutils::getFileLock(filePath)); + std::ofstream fs(filePath, std::ios::binary); + if (!fs.good()) { + return; + } + try { + msgpack::pack(fs, list); + } catch (const std::exception& e) { + JAMI_ERR() << e.what(); + } +} + +void +PluginPreferencesUtils::getAllowDenyListPreferences(ChatHandlerList& list) +{ + const std::string filePath = getAllowDenyListsPath(); + std::lock_guard<std::mutex> guard(fileutils::getFileLock(filePath)); + std::ifstream file(filePath, std::ios::binary); + + // If file is accessible + if (file.good()) { + // Get file size + std::string str; + file.seekg(0, std::ios::end); + size_t fileSize = static_cast<size_t>(file.tellg()); + // If not empty + if (fileSize > 0) { + // Read whole file content and put it in the string str + str.reserve(static_cast<size_t>(file.tellg())); + file.seekg(0, std::ios::beg); + str.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); + file.close(); + try { + // Unpack the string + msgpack::object_handle oh = msgpack::unpack(str.data(), str.size()); + // Deserialized object is valid during the msgpack::object_handle instance is alive. + msgpack::object deserialized = oh.get(); + deserialized.convert(list); + } catch (const std::exception& e) { + JAMI_ERR() << e.what(); + } + } + } +} } // namespace jami diff --git a/src/plugin/pluginpreferencesutils.h b/src/plugin/pluginpreferencesutils.h index 9a7ce1a98b..d10f923c3e 100644 --- a/src/plugin/pluginpreferencesutils.h +++ b/src/plugin/pluginpreferencesutils.h @@ -23,10 +23,14 @@ #include <json/json.h> #include <string> +#include <set> namespace jami { -class PluginPreferencesUtils { +using ChatHandlerList = std::map<std::pair<std::string, std::string>, std::map<std::string, bool>>; + +class PluginPreferencesUtils +{ public: static std::string getPreferencesConfigFilePath(const std::string& rootPath); @@ -34,8 +38,8 @@ public: static std::string convertArrayToString(const Json::Value& jsonArray); - static std::map<std::string, std::string> parsePreferenceConfig(const Json::Value& jsonPreference, - const std::string& type); + static std::map<std::string, std::string> parsePreferenceConfig( + const Json::Value& jsonPreference); static std::vector<std::map<std::string, std::string>> getPreferences( const std::string& rootPath); @@ -46,6 +50,13 @@ public: static std::map<std::string, std::string> getPreferencesValuesMap(const std::string& rootPath); static bool resetPreferencesValuesMap(const std::string& rootPath); + + static std::string getAllowDenyListsPath(); + + static void setAllowDenyListPreferences(const ChatHandlerList& list); + + static void getAllowDenyListPreferences(ChatHandlerList& list); + private: PluginPreferencesUtils() {} ~PluginPreferencesUtils() {} diff --git a/src/preferences.cpp b/src/preferences.cpp index 13348188d3..975cd418d6 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -144,6 +144,7 @@ static constexpr const char* CONFERENCE_RESOLUTION_KEY {"conferenceResolution"}; // plugin preferences constexpr const char* const PluginPreferences::CONFIG_LABEL; static constexpr const char* JAMI_PLUGIN_KEY {"pluginsEnabled"}; +static constexpr const char* JAMI_PLUGINS_INSTALLED_KEY {"installedPlugins"}; static constexpr const char* JAMI_PLUGINS_LOADED_KEY {"loadedPlugins"}; #endif @@ -621,6 +622,7 @@ PluginPreferences::serialize(YAML::Emitter& out) const { out << YAML::Key << CONFIG_LABEL << YAML::Value << YAML::BeginMap; out << YAML::Key << JAMI_PLUGIN_KEY << YAML::Value << pluginsEnabled_; + out << YAML::Key << JAMI_PLUGINS_INSTALLED_KEY << YAML::Value << installedPlugins_; out << YAML::Key << JAMI_PLUGINS_LOADED_KEY << YAML::Value << loadedPlugins_; out << YAML::EndMap; } @@ -636,6 +638,12 @@ PluginPreferences::unserialize(const YAML::Node& in) pluginsEnabled_ = false; } + const auto& installedPluginsNode = node[JAMI_PLUGINS_INSTALLED_KEY]; + try { + installedPlugins_ = yaml_utils::parseVector(installedPluginsNode); + } catch (...) { + } + const auto& loadedPluginsNode = node[JAMI_PLUGINS_LOADED_KEY]; try { loadedPlugins_ = yaml_utils::parseVector(loadedPluginsNode); diff --git a/src/preferences.h b/src/preferences.h index b10ca668ab..afec033475 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -372,6 +372,11 @@ public: return v; } + std::vector<std::string> getInstalledPlugins() const + { + return std::vector<std::string>(installedPlugins_.begin(), installedPlugins_.end()); + } + void saveStateLoadedPlugins(std::string plugin, bool loaded) { if (loaded) { @@ -387,6 +392,7 @@ public: private: bool pluginsEnabled_; + std::set<std::string> installedPlugins_; std::set<std::string> loadedPlugins_; constexpr static const char* const CONFIG_LABEL = "plugins"; }; diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp index 6dff89a5bf..3fea4cba79 100644 --- a/src/sip/sipcall.cpp +++ b/src/sip/sipcall.cpp @@ -560,6 +560,7 @@ SIPCall::hangup(int reason) // Notify the peer terminateSipSession(status); } + // Stop all RTP streams stopAllMedia(); setState(Call::ConnectionState::DISCONNECTED, reason); @@ -954,6 +955,10 @@ SIPCall::sendTextMessage(const std::map<std::string, std::string>& messages, con void SIPCall::removeCall() { +#ifdef ENABLE_PLUGIN + jami::Manager::instance().getJamiPluginManager().getCallServicesManager().clearCallHandlerMaps( + getCallId()); +#endif std::lock_guard<std::recursive_mutex> lk {callMutex_}; JAMI_WARN("[call:%s] removeCall()", getCallId().c_str()); if (sdp_) { -- GitLab