LCOV - code coverage report
Current view: top level - src/plugin - chatservicesmanager.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 12.0 % 142 17
Test Date: 2026-06-13 09:18:46 Functions: 25.0 % 20 5

            Line data    Source code
       1              : /*
       2              :  *  Copyright (C) 2004-2026 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           96 :     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_ERROR("Exception during text message sending: {}", e.what());
      97            0 :             }
      98            0 :         }
      99            0 :         return 0;
     100              :     };
     101              : 
     102              :     // Services are registered to the PluginManager.
     103           96 :     pluginManager.registerService("sendTextMessage", sendTextMessage);
     104           32 : }
     105              : 
     106              : bool
     107         1227 : ChatServicesManager::hasHandlers() const
     108              : {
     109         1227 :     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(const 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          835 : ChatServicesManager::cleanChatSubjects(const std::string& accountId, const std::string& peerId)
     166              : {
     167          835 :     std::pair<std::string, std::string> mPair(accountId, peerId);
     168          835 :     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          835 : }
     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 2.0-1