LCOV - code coverage report
Current view: top level - src/plugin - chatservicesmanager.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 111 144 77.1 %
Date: 2024-04-25 08:05:53 Functions: 16 18 88.9 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2021-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  This program is free software; you can redistribute it and/or modify
       5             :  *  it under the terms of the GNU General Public License as published by
       6             :  *  the Free Software Foundation; either version 3 of the License, or
       7             :  *  (at your option) any later version.
       8             :  *
       9             :  *  This program is distributed in the hope that it will be useful,
      10             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  *  GNU General Public License for more details.
      13             :  *
      14             :  *  You should have received a copy of the GNU General Public License
      15             :  *  along with this program; if not, write to the Free Software
      16             :  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
      17             :  */
      18             : 
      19             : #include "chatservicesmanager.h"
      20             : #include "pluginmanager.h"
      21             : #include "logger.h"
      22             : #include "manager.h"
      23             : #include "jamidht/jamiaccount.h"
      24             : #include "fileutils.h"
      25             : 
      26             : namespace jami {
      27             : 
      28          33 : ChatServicesManager::ChatServicesManager(PluginManager& pluginManager)
      29             : {
      30          33 :     registerComponentsLifeCycleManagers(pluginManager);
      31          33 :     registerChatService(pluginManager);
      32          33 :     PluginPreferencesUtils::getAllowDenyListPreferences(allowDenyList_);
      33          33 : }
      34             : 
      35             : void
      36          33 : ChatServicesManager::registerComponentsLifeCycleManagers(PluginManager& pluginManager)
      37             : {
      38             :     // registerChatHandler may be called by the PluginManager upon loading a plugin.
      39          24 :     auto registerChatHandler = [this](void* data, std::mutex& pmMtx_) {
      40           8 :         std::lock_guard lk(pmMtx_);
      41           8 :         ChatHandlerPtr ptr {(static_cast<ChatHandler*>(data))};
      42             : 
      43           8 :         if (!ptr)
      44           0 :             return -1;
      45           8 :         handlersNameMap_[ptr->getChatHandlerDetails().at("name")] = (uintptr_t) ptr.get();
      46           8 :         std::size_t found = ptr->id().find_last_of(DIR_SEPARATOR_CH);
      47             :         // Adding preference that tells us to automatically activate a ChatHandler.
      48           8 :         PluginPreferencesUtils::addAlwaysHandlerPreference(ptr->getChatHandlerDetails().at("name"),
      49          16 :                                                            ptr->id().substr(0, found));
      50           8 :         chatHandlers_.emplace_back(std::move(ptr));
      51           8 :         return 0;
      52           8 :     };
      53             : 
      54             :     // unregisterChatHandler may be called by the PluginManager while unloading.
      55          56 :     auto unregisterChatHandler = [this](void* data, std::mutex& pmMtx_) {
      56           8 :         std::lock_guard lk(pmMtx_);
      57           8 :         auto handlerIt = std::find_if(chatHandlers_.begin(),
      58             :                                       chatHandlers_.end(),
      59          16 :                                       [data](ChatHandlerPtr& handler) {
      60           8 :                                           return (handler.get() == data);
      61             :                                       });
      62             : 
      63           8 :         if (handlerIt != chatHandlers_.end()) {
      64          10 :             for (auto& toggledList : chatHandlerToggled_) {
      65           2 :                 auto handlerId = std::find_if(toggledList.second.begin(),
      66             :                                               toggledList.second.end(),
      67           2 :                                               [id = (uintptr_t) handlerIt->get()](
      68           0 :                                                   uintptr_t handlerId) {
      69           0 :                                                   return (handlerId == id);
      70             :                                               });
      71             :                 // If ChatHandler we're trying to destroy is currently in use, we deactivate it.
      72           2 :                 if (handlerId != toggledList.second.end()) {
      73           0 :                     (*handlerIt)->detach(chatSubjects_[toggledList.first]);
      74           0 :                     toggledList.second.erase(handlerId);
      75             :                 }
      76             :             }
      77           8 :             handlersNameMap_.erase((*handlerIt)->getChatHandlerDetails().at("name"));
      78           8 :             chatHandlers_.erase(handlerIt);
      79             :         }
      80           8 :         return true;
      81           8 :     };
      82             : 
      83             :     // Services are registered to the PluginManager.
      84          33 :     pluginManager.registerComponentManager("ChatHandlerManager",
      85             :                                            registerChatHandler,
      86             :                                            unregisterChatHandler);
      87          33 : }
      88             : 
      89             : void
      90          33 : ChatServicesManager::registerChatService(PluginManager& pluginManager)
      91             : {
      92             :     // sendTextMessage is a service that allows plugins to send a message in a conversation.
      93           0 :     auto sendTextMessage = [](const DLPlugin*, void* data) {
      94           0 :         auto cm = static_cast<JamiMessage*>(data);
      95           0 :         if (const auto acc = jami::Manager::instance().getAccount<jami::JamiAccount>(
      96           0 :                 cm->accountId)) {
      97             :             try {
      98           0 :                 if (cm->isSwarm)
      99           0 :                     acc->convModule()->sendMessage(cm->peerId, cm->data.at("body"));
     100             :                 else
     101           0 :                     jami::Manager::instance().sendTextMessage(cm->accountId,
     102           0 :                                                               cm->peerId,
     103           0 :                                                               cm->data,
     104             :                                                               true);
     105           0 :             } catch (const std::exception& e) {
     106           0 :                 JAMI_ERR("Exception during text message sending: %s", e.what());
     107           0 :             }
     108           0 :         }
     109           0 :         return 0;
     110             :     };
     111             : 
     112             :     // Services are registered to the PluginManager.
     113          33 :     pluginManager.registerService("sendTextMessage", sendTextMessage);
     114          33 : }
     115             : 
     116             : bool
     117        1224 : ChatServicesManager::hasHandlers() const
     118             : {
     119        1224 :     return not chatHandlers_.empty();
     120             : }
     121             : 
     122             : 
     123             : std::vector<std::string>
     124           2 : ChatServicesManager::getChatHandlers() const
     125             : {
     126           2 :     std::vector<std::string> res;
     127           2 :     res.reserve(chatHandlers_.size());
     128           4 :     for (const auto& chatHandler : chatHandlers_) {
     129           2 :         res.emplace_back(std::to_string((uintptr_t) chatHandler.get()));
     130             :     }
     131           2 :     return res;
     132           0 : }
     133             : 
     134             : void
     135           4 : ChatServicesManager::publishMessage(pluginMessagePtr message)
     136             : {
     137           4 :     if (message->fromPlugin or chatHandlers_.empty())
     138           0 :         return;
     139             : 
     140           4 :     std::pair<std::string, std::string> mPair(message->accountId, message->peerId);
     141           4 :     auto& handlers = chatHandlerToggled_[mPair];
     142           4 :     auto& chatAllowDenySet = allowDenyList_[mPair];
     143             : 
     144             :     // Search for activation flag.
     145           8 :     for (auto& chatHandler : chatHandlers_) {
     146           8 :         std::string chatHandlerName = chatHandler->getChatHandlerDetails().at("name");
     147           4 :         std::size_t found = chatHandler->id().find_last_of(DIR_SEPARATOR_CH);
     148             :         // toggle is true if we should automatically activate the ChatHandler.
     149           4 :         bool toggle = PluginPreferencesUtils::getAlwaysPreference(chatHandler->id().substr(0, found),
     150             :                                                                   chatHandlerName,
     151           4 :                                                                   message->accountId);
     152             :         // toggle is overwritten if we have previously activated/deactivated the ChatHandler
     153             :         // for the given conversation.
     154           4 :         auto allowedIt = chatAllowDenySet.find(chatHandlerName);
     155           4 :         if (allowedIt != chatAllowDenySet.end())
     156           1 :             toggle = (*allowedIt).second;
     157           4 :         bool toggled = handlers.find((uintptr_t) chatHandler.get()) != handlers.end();
     158           4 :         if (toggle || toggled) {
     159             :             // Creates chat subjects if it doesn't exist yet.
     160           1 :             auto& subject = chatSubjects_.emplace(mPair, std::make_shared<PublishObservable<pluginMessagePtr>>()).first->second;
     161           1 :             if (!toggled) {
     162             :                 // If activation is expected, and not yet performed, we perform activation
     163           0 :                 handlers.insert((uintptr_t) chatHandler.get());
     164           0 :                 chatHandler->notifyChatSubject(mPair, subject);
     165           0 :                 chatAllowDenySet[chatHandlerName] = true;
     166           0 :                 PluginPreferencesUtils::setAllowDenyListPreferences(allowDenyList_);
     167             :             }
     168             :             // Finally we feed Chat subject with the message.
     169           1 :             subject->publish(message);
     170             :         }
     171           4 :     }
     172           4 : }
     173             : 
     174             : void
     175         834 : ChatServicesManager::cleanChatSubjects(const std::string& accountId, const std::string& peerId)
     176             : {
     177         834 :     std::pair<std::string, std::string> mPair(accountId, peerId);
     178         835 :     for (auto it = chatSubjects_.begin(); it != chatSubjects_.end();) {
     179           1 :         if (peerId.empty() && it->first.first == accountId)
     180           1 :             it = chatSubjects_.erase(it);
     181           0 :         else if (!peerId.empty() && it->first == mPair)
     182           0 :             it = chatSubjects_.erase(it);
     183             :         else
     184           0 :             ++it;
     185             :     }
     186         834 : }
     187             : 
     188             : void
     189           2 : ChatServicesManager::toggleChatHandler(const std::string& chatHandlerId,
     190             :                                        const std::string& accountId,
     191             :                                        const std::string& peerId,
     192             :                                        const bool toggle)
     193             : {
     194           2 :     toggleChatHandler(std::stoull(chatHandlerId), accountId, peerId, toggle);
     195           2 : }
     196             : 
     197             : std::vector<std::string>
     198           2 : ChatServicesManager::getChatHandlerStatus(const std::string& accountId, const std::string& peerId)
     199             : {
     200           2 :     std::pair<std::string, std::string> mPair(accountId, peerId);
     201           2 :     const auto& it = allowDenyList_.find(mPair);
     202           2 :     std::vector<std::string> ret;
     203           2 :     if (it != allowDenyList_.end()) {
     204           4 :         for (const auto& chatHandlerName : it->second)
     205           2 :             if (chatHandlerName.second && handlersNameMap_.find(chatHandlerName.first) != handlersNameMap_.end()) { // We only return active ChatHandler ids
     206           1 :                 ret.emplace_back(std::to_string(handlersNameMap_.at(chatHandlerName.first)));
     207             :             }
     208             :     }
     209             : 
     210           4 :     return ret;
     211           2 : }
     212             : 
     213             : std::map<std::string, std::string>
     214           2 : ChatServicesManager::getChatHandlerDetails(const std::string& chatHandlerIdStr)
     215             : {
     216           2 :     auto chatHandlerId = std::stoull(chatHandlerIdStr);
     217           2 :     for (auto& chatHandler : chatHandlers_) {
     218           2 :         if ((uintptr_t) chatHandler.get() == chatHandlerId) {
     219           2 :             return chatHandler->getChatHandlerDetails();
     220             :         }
     221             :     }
     222           0 :     return {};
     223             : }
     224             : 
     225             : bool
     226           2 : ChatServicesManager::setPreference(const std::string& key,
     227             :                                    const std::string& value,
     228             :                                    const std::string& rootPath)
     229             : {
     230           2 :     bool status {true};
     231           2 :     for (auto& chatHandler : chatHandlers_) {
     232           0 :         if (chatHandler->id().find(rootPath) != std::string::npos) {
     233           0 :             if (chatHandler->preferenceMapHasKey(key)) {
     234           0 :                 chatHandler->setPreferenceAttribute(key, value);
     235           0 :                 status &= false;
     236             :             }
     237             :         }
     238             :     }
     239           2 :     return status;
     240             : }
     241             : 
     242             : void
     243           2 : ChatServicesManager::toggleChatHandler(const uintptr_t chatHandlerId,
     244             :                                        const std::string& accountId,
     245             :                                        const std::string& peerId,
     246             :                                        const bool toggle)
     247             : {
     248           2 :     std::pair<std::string, std::string> mPair(accountId, peerId);
     249           2 :     auto& handlers = chatHandlerToggled_[mPair];
     250           2 :     auto& chatAllowDenySet = allowDenyList_[mPair];
     251           2 :     chatSubjects_.emplace(mPair, std::make_shared<PublishObservable<pluginMessagePtr>>());
     252             : 
     253           2 :     auto chatHandlerIt = std::find_if(chatHandlers_.begin(),
     254             :                                       chatHandlers_.end(),
     255           4 :                                       [chatHandlerId](ChatHandlerPtr& handler) {
     256           2 :                                           return ((uintptr_t) handler.get() == chatHandlerId);
     257             :                                       });
     258             : 
     259           2 :     if (chatHandlerIt != chatHandlers_.end()) {
     260           2 :         if (toggle) {
     261           1 :             (*chatHandlerIt)->notifyChatSubject(mPair, chatSubjects_[mPair]);
     262           1 :             if (handlers.find(chatHandlerId) == handlers.end())
     263           1 :                 handlers.insert(chatHandlerId);
     264           1 :             chatAllowDenySet[(*chatHandlerIt)->getChatHandlerDetails().at("name")] = true;
     265             :         } else {
     266           1 :             (*chatHandlerIt)->detach(chatSubjects_[mPair]);
     267           1 :             handlers.erase(chatHandlerId);
     268           1 :             chatAllowDenySet[(*chatHandlerIt)->getChatHandlerDetails().at("name")] = false;
     269             :         }
     270           2 :         PluginPreferencesUtils::setAllowDenyListPreferences(allowDenyList_);
     271             :     }
     272           2 : }
     273             : } // namespace jami

Generated by: LCOV version 1.14