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-12-21 08:56:24 Functions: 16 18 88.9 %

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

Generated by: LCOV version 1.14