chatservicesmanager.cpp 11.1 KB
Newer Older
1
/*
2
 *  Copyright (C) 2021-2022 Savoir-faire Linux Inc.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
 */

#include "chatservicesmanager.h"
20
#include "pluginmanager.h"
21
22
#include "logger.h"
#include "manager.h"
23
#include "jamidht/jamiaccount.h"
24
25
26
27
#include "fileutils.h"

namespace jami {

28
ChatServicesManager::ChatServicesManager(PluginManager& pluginManager)
29
{
30
31
    registerComponentsLifeCycleManagers(pluginManager);
    registerChatService(pluginManager);
32
    PluginPreferencesUtils::getAllowDenyListPreferences(allowDenyList_);
33
34
35
}

void
36
ChatServicesManager::registerComponentsLifeCycleManagers(PluginManager& pluginManager)
37
{
38
    // registerChatHandler may be called by the PluginManager upon loading a plugin.
39
40
    auto registerChatHandler = [this](void* data, std::mutex& pmMtx_) {
        std::lock_guard<std::mutex> lk(pmMtx_);
41
42
43
44
        ChatHandlerPtr ptr {(static_cast<ChatHandler*>(data))};

        if (!ptr)
            return -1;
45
        handlersNameMap_[ptr->getChatHandlerDetails().at("name")] = (uintptr_t) ptr.get();
46
        std::size_t found = ptr->id().find_last_of(DIR_SEPARATOR_CH);
47
        // Adding preference that tells us to automatically activate a ChatHandler.
48
49
        PluginPreferencesUtils::addAlwaysHandlerPreference(ptr->getChatHandlerDetails().at("name"),
                                                           ptr->id().substr(0, found));
50
51
52
53
        chatHandlers_.emplace_back(std::move(ptr));
        return 0;
    };

54
    // unregisterChatHandler may be called by the PluginManager while unloading.
55
56
    auto unregisterChatHandler = [this](void* data, std::mutex& pmMtx_) {
        std::lock_guard<std::mutex> lk(pmMtx_);
57
58
59
60
61
        auto handlerIt = std::find_if(chatHandlers_.begin(),
                                      chatHandlers_.end(),
                                      [data](ChatHandlerPtr& handler) {
                                          return (handler.get() == data);
                                      });
62
63

        if (handlerIt != chatHandlers_.end()) {
64
65
66
            for (auto& toggledList : chatHandlerToggled_) {
                auto handlerId = std::find_if(toggledList.second.begin(),
                                              toggledList.second.end(),
67
68
                                              [id = (uintptr_t) handlerIt->get()](
                                                  uintptr_t handlerId) {
Adrien Béraud's avatar
Adrien Béraud committed
69
                                                  return (handlerId == id);
70
                                              });
71
                // If ChatHandler we're trying to destroy is currently in use, we deactivate it.
72
73
                if (handlerId != toggledList.second.end()) {
                    (*handlerIt)->detach(chatSubjects_[toggledList.first]);
74
                    toggledList.second.erase(handlerId);
75
76
                }
            }
77
            handlersNameMap_.erase((*handlerIt)->getChatHandlerDetails().at("name"));
78
79
            chatHandlers_.erase(handlerIt);
        }
80
        return true;
81
82
    };

83
84
85
86
    // Services are registered to the PluginManager.
    pluginManager.registerComponentManager("ChatHandlerManager",
                                           registerChatHandler,
                                           unregisterChatHandler);
87
88
89
}

void
90
ChatServicesManager::registerChatService(PluginManager& pluginManager)
91
{
92
    // sendTextMessage is a service that allows plugins to send a message in a conversation.
Adrien Béraud's avatar
Adrien Béraud committed
93
    auto sendTextMessage = [](const DLPlugin*, void* data) {
94
        auto cm = static_cast<JamiMessage*>(data);
95
96
97
98
        if (const auto acc = jami::Manager::instance().getAccount<jami::JamiAccount>(
                cm->accountId)) {
            try {
                if (cm->isSwarm)
99
                    acc->convModule()->sendMessage(cm->peerId, cm->data.at("body"));
100
101
102
103
104
105
106
107
108
                else
                    jami::Manager::instance().sendTextMessage(cm->accountId,
                                                              cm->peerId,
                                                              cm->data,
                                                              true);
            } catch (const std::exception& e) {
                JAMI_ERR("Exception during text message sending: %s", e.what());
            }
        }
109
110
111
        return 0;
    };

112
113
    // Services are registered to the PluginManager.
    pluginManager.registerService("sendTextMessage", sendTextMessage);
114
115
}

116
117
118
119
120
121
122
bool
ChatServicesManager::hasHandlers() const
{
    return not chatHandlers_.empty();
}


123
std::vector<std::string>
124
ChatServicesManager::getChatHandlers() const
125
126
127
128
129
130
131
132
133
134
{
    std::vector<std::string> res;
    res.reserve(chatHandlers_.size());
    for (const auto& chatHandler : chatHandlers_) {
        res.emplace_back(std::to_string((uintptr_t) chatHandler.get()));
    }
    return res;
}

void
135
ChatServicesManager::publishMessage(pluginMessagePtr message)
136
{
137
    if (message->fromPlugin or chatHandlers_.empty())
138
        return;
139

140
    std::pair<std::string, std::string> mPair(message->accountId, message->peerId);
141
    auto& handlers = chatHandlerToggled_[mPair];
142
143
    auto& chatAllowDenySet = allowDenyList_[mPair];

144
    // Search for activation flag.
145
    for (auto& chatHandler : chatHandlers_) {
146
        std::string chatHandlerName = chatHandler->getChatHandlerDetails().at("name");
147
        std::size_t found = chatHandler->id().find_last_of(DIR_SEPARATOR_CH);
148
        // toggle is true if we should automatically activate the ChatHandler.
149
        bool toggle = PluginPreferencesUtils::getAlwaysPreference(chatHandler->id().substr(0, found),
150
151
                                                                  chatHandlerName,
                                                                  message->accountId);
152
153
        // toggle is overwritten if we have previously activated/deactivated the ChatHandler
        // for the given conversation.
154
155
156
157
158
        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) {
159
            // Creates chat subjects if it doesn't exist yet.
160
            auto& subject = chatSubjects_.emplace(mPair, std::make_shared<PublishObservable<pluginMessagePtr>>()).first->second;
161
            if (!toggled) {
162
                // If activation is expected, and not yet performed, we perform activation
163
                handlers.insert((uintptr_t) chatHandler.get());
164
                chatHandler->notifyChatSubject(mPair, subject);
165
166
                chatAllowDenySet[chatHandlerName] = true;
                PluginPreferencesUtils::setAllowDenyListPreferences(allowDenyList_);
167
            }
168
            // Finally we feed Chat subject with the message.
169
            subject->publish(message);
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
        }
    }
}

void
ChatServicesManager::cleanChatSubjects(const std::string& accountId, const std::string& peerId)
{
    std::pair<std::string, std::string> mPair(accountId, peerId);
    for (auto it = chatSubjects_.begin(); it != chatSubjects_.end();) {
        if (peerId.empty() && it->first.first == accountId)
            it = chatSubjects_.erase(it);
        else if (!peerId.empty() && it->first == mPair)
            it = chatSubjects_.erase(it);
        else
            ++it;
    }
}

void
ChatServicesManager::toggleChatHandler(const std::string& chatHandlerId,
190
191
192
                                       const std::string& accountId,
                                       const std::string& peerId,
                                       const bool toggle)
193
194
195
196
197
{
    toggleChatHandler(std::stoull(chatHandlerId), accountId, peerId, toggle);
}

std::vector<std::string>
198
ChatServicesManager::getChatHandlerStatus(const std::string& accountId, const std::string& peerId)
199
200
{
    std::pair<std::string, std::string> mPair(accountId, peerId);
201
    const auto& it = allowDenyList_.find(mPair);
202
    std::vector<std::string> ret;
203
204
    if (it != allowDenyList_.end()) {
        for (const auto& chatHandlerName : it->second)
205
            if (chatHandlerName.second) // We only return active ChatHandler ids
206
                ret.emplace_back(std::to_string(handlersNameMap_.at(chatHandlerName.first)));
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
    }

    return ret;
}

std::map<std::string, std::string>
ChatServicesManager::getChatHandlerDetails(const std::string& chatHandlerIdStr)
{
    auto chatHandlerId = std::stoull(chatHandlerIdStr);
    for (auto& chatHandler : chatHandlers_) {
        if ((uintptr_t) chatHandler.get() == chatHandlerId) {
            return chatHandler->getChatHandlerDetails();
        }
    }
    return {};
}

224
bool
225
226
ChatServicesManager::setPreference(const std::string& key,
                                   const std::string& value,
227
                                   const std::string& rootPath)
228
{
229
    bool status {true};
230
    for (auto& chatHandler : chatHandlers_) {
231
232
233
234
235
        if (chatHandler->id().find(rootPath) != std::string::npos) {
            if (chatHandler->preferenceMapHasKey(key)) {
                chatHandler->setPreferenceAttribute(key, value);
                status &= false;
            }
236
237
        }
    }
238
    return status;
239
240
241
242
}

void
ChatServicesManager::toggleChatHandler(const uintptr_t chatHandlerId,
243
244
245
                                       const std::string& accountId,
                                       const std::string& peerId,
                                       const bool toggle)
246
247
248
{
    std::pair<std::string, std::string> mPair(accountId, peerId);
    auto& handlers = chatHandlerToggled_[mPair];
249
    auto& chatAllowDenySet = allowDenyList_[mPair];
250
251
    chatSubjects_.emplace(mPair, std::make_shared<PublishObservable<pluginMessagePtr>>());

252
253
254
255
256
    auto chatHandlerIt = std::find_if(chatHandlers_.begin(),
                                      chatHandlers_.end(),
                                      [chatHandlerId](ChatHandlerPtr& handler) {
                                          return ((uintptr_t) handler.get() == chatHandlerId);
                                      });
257
258
259
260
261
262

    if (chatHandlerIt != chatHandlers_.end()) {
        if (toggle) {
            (*chatHandlerIt)->notifyChatSubject(mPair, chatSubjects_[mPair]);
            if (handlers.find(chatHandlerId) == handlers.end())
                handlers.insert(chatHandlerId);
263
            chatAllowDenySet[(*chatHandlerIt)->getChatHandlerDetails().at("name")] = true;
264
265
266
        } else {
            (*chatHandlerIt)->detach(chatSubjects_[mPair]);
            handlers.erase(chatHandlerId);
267
            chatAllowDenySet[(*chatHandlerIt)->getChatHandlerDetails().at("name")] = false;
268
        }
269
        PluginPreferencesUtils::setAllowDenyListPreferences(allowDenyList_);
270
271
272
    }
}
} // namespace jami