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