LCOV - code coverage report
Current view: top level - foo/src/plugin - chatservicesmanager.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 17 142 12.0 %
Date: 2025-12-18 10:07:43 Functions: 5 18 27.8 %

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

Generated by: LCOV version 1.14