From a04ed042640a8002490c9268a5485ba744e5c6e4 Mon Sep 17 00:00:00 2001 From: agsantos Date: Thu, 21 Jan 2021 13:23:06 -0500 Subject: [PATCH] plugins: add ChatHandler API - add internal preference for always turn on plugin GitLab: jami-plugins#4 GitLab: #394 Change-Id: Ic37fb486531cf436df4346246ec74f6480e4c0fd --- .../cx.ring.Ring.PluginManagerInterface.xml | 82 ++++-- bin/dbus/dbuspluginmanagerinterface.cpp | 58 ++-- bin/dbus/dbuspluginmanagerinterface.h | 18 +- bin/jni/plugin_manager_interface.i | 13 +- contrib/src/ffnvcodec/rules.mak | 2 +- meson.build | 2 +- src/call.cpp | 12 + src/client/plugin_manager_interface.cpp | 75 ++++-- src/dring/plugin_manager_interface.h | 19 +- src/jamidht/contact_list.cpp | 34 ++- src/jamidht/jamiaccount.cpp | 9 + src/manager.cpp | 33 ++- src/manager.h | 5 +- src/meson.build | 3 +- src/plugin/CMakeLists.txt | 8 +- src/plugin/Makefile.am | 10 +- src/plugin/callservicesmanager.cpp | 229 ++++++++++++++++ src/plugin/callservicesmanager.h | 248 +++--------------- src/plugin/chathandler.h | 55 ++++ src/plugin/chatservicesmanager.cpp | 208 +++++++++++++++ src/plugin/chatservicesmanager.h | 74 ++++++ src/plugin/conversationhandler.h | 48 ---- src/plugin/conversationservicesmanager.h | 108 -------- src/plugin/jamipluginmanager.cpp | 189 ++----------- src/plugin/jamipluginmanager.h | 39 +-- src/plugin/mediahandler.h | 6 +- src/plugin/pluginmanager.cpp | 5 +- src/plugin/pluginmanager.h | 5 +- src/plugin/pluginpreferencesutils.cpp | 205 +++++++++++++++ src/plugin/pluginpreferencesutils.h | 53 ++++ src/plugin/streamdata.h | 28 +- src/sip/sipaccountbase.cpp | 17 +- src/sip/sipcall.cpp | 8 +- 33 files changed, 1215 insertions(+), 693 deletions(-) create mode 100644 src/plugin/callservicesmanager.cpp create mode 100644 src/plugin/chathandler.h create mode 100644 src/plugin/chatservicesmanager.cpp create mode 100644 src/plugin/chatservicesmanager.h delete mode 100644 src/plugin/conversationhandler.h delete mode 100644 src/plugin/conversationservicesmanager.h create mode 100644 src/plugin/pluginpreferencesutils.cpp create mode 100644 src/plugin/pluginpreferencesutils.h diff --git a/bin/dbus/cx.ring.Ring.PluginManagerInterface.xml b/bin/dbus/cx.ring.Ring.PluginManagerInterface.xml index bdfaedd98..2e6e32be1 100644 --- a/bin/dbus/cx.ring.Ring.PluginManagerInterface.xml +++ b/bin/dbus/cx.ring.Ring.PluginManagerInterface.xml @@ -19,14 +19,6 @@ - - - - - - - - @@ -77,14 +69,14 @@ - - + + - - + + @@ -107,9 +99,15 @@ - - - + + + + + + + + + @@ -123,6 +121,18 @@ + + + + + + + + + + + + @@ -133,6 +143,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -148,15 +190,5 @@ - - - - - - - - - - diff --git a/bin/dbus/dbuspluginmanagerinterface.cpp b/bin/dbus/dbuspluginmanagerinterface.cpp index af648739b..94d12a40d 100644 --- a/bin/dbus/dbuspluginmanagerinterface.cpp +++ b/bin/dbus/dbuspluginmanagerinterface.cpp @@ -37,12 +37,6 @@ DBusPluginManagerInterface::unloadPlugin(const std::string& path) return DRing::unloadPlugin(path); } -void -DBusPluginManagerInterface::togglePlugin(const std::string& path, const bool& toggle) -{ - DRing::togglePlugin(path, toggle); -} - std::map DBusPluginManagerInterface::getPluginDetails(const std::string& path) { @@ -76,15 +70,15 @@ DBusPluginManagerInterface::resetPluginPreferencesValues(const std::string& path } auto -DBusPluginManagerInterface::listAvailablePlugins() -> decltype(DRing::listAvailablePlugins()) +DBusPluginManagerInterface::getInstalledPlugins() -> decltype(DRing::getInstalledPlugins()) { - return DRing::listAvailablePlugins(); + return DRing::getInstalledPlugins(); } auto -DBusPluginManagerInterface::listLoadedPlugins() -> decltype(DRing::listLoadedPlugins()) +DBusPluginManagerInterface::getLoadedPlugins() -> decltype(DRing::getLoadedPlugins()) { - return DRing::listLoadedPlugins(); + return DRing::getLoadedPlugins(); } int @@ -100,9 +94,15 @@ DBusPluginManagerInterface::uninstallPlugin(const std::string& pluginRootPath) } auto -DBusPluginManagerInterface::listCallMediaHandlers() -> decltype(DRing::listCallMediaHandlers()) +DBusPluginManagerInterface::getCallMediaHandlers() -> decltype(DRing::getCallMediaHandlers()) { - return DRing::listCallMediaHandlers(); + return DRing::getCallMediaHandlers(); +} + +auto +DBusPluginManagerInterface::getChatHandlers() -> decltype(DRing::getChatHandlers()) +{ + return DRing::getChatHandlers(); } void @@ -113,12 +113,40 @@ DBusPluginManagerInterface::toggleCallMediaHandler(const std::string& mediaHandl DRing::toggleCallMediaHandler(mediaHandlerId, callId, toggle); } +void +DBusPluginManagerInterface::toggleChatHandler(const std::string& chatHandlerId, + const std::string& accountId, + const std::string& peerId, + const bool& toggle) +{ + DRing::toggleChatHandler(chatHandlerId, accountId, peerId, toggle); +} + std::map DBusPluginManagerInterface::getCallMediaHandlerDetails(const std::string& mediaHanlderId) { return DRing::getCallMediaHandlerDetails(mediaHanlderId); } +std::vector +DBusPluginManagerInterface::getCallMediaHandlerStatus(const std::string& callId) +{ + return DRing::getCallMediaHandlerStatus(callId); +} + +std::map +DBusPluginManagerInterface::getChatHandlerDetails(const std::string& chatHanlderId) +{ + return DRing::getChatHandlerDetails(chatHanlderId); +} + +std::vector +DBusPluginManagerInterface::getChatHandlerStatus(const std::string& accountId, + const std::string& peerId) +{ + return DRing::getChatHandlerStatus(accountId, peerId); +} + bool DBusPluginManagerInterface::getPluginsEnabled() { @@ -130,9 +158,3 @@ DBusPluginManagerInterface::setPluginsEnabled(const bool& state) { DRing::setPluginsEnabled(state); } - -std::map> -DBusPluginManagerInterface::getCallMediaHandlerStatus(const std::string& callId) -{ - return DRing::getCallMediaHandlerStatus(callId); -} diff --git a/bin/dbus/dbuspluginmanagerinterface.h b/bin/dbus/dbuspluginmanagerinterface.h index 5474754ad..681af5954 100644 --- a/bin/dbus/dbuspluginmanagerinterface.h +++ b/bin/dbus/dbuspluginmanagerinterface.h @@ -54,7 +54,6 @@ public: // Methods bool loadPlugin(const std::string& path); bool unloadPlugin(const std::string& path); - void togglePlugin(const std::string& path, const bool& toggle); std::map getPluginDetails(const std::string& path); std::vector> getPluginPreferences(const std::string& path); bool setPluginPreference(const std::string& path, @@ -62,18 +61,25 @@ public: const std::string& value); std::map getPluginPreferencesValues(const std::string& path); bool resetPluginPreferencesValues(const std::string& path); - std::vector listAvailablePlugins(); - std::vector listLoadedPlugins(); + std::vector getInstalledPlugins(); + std::vector getLoadedPlugins(); int installPlugin(const std::string& jplPath, const bool& force); int uninstallPlugin(const std::string& pluginRootPath); - std::vector listCallMediaHandlers(); + std::vector getCallMediaHandlers(); + std::vector getChatHandlers(); void toggleCallMediaHandler(const std::string& mediaHandlerId, const std::string& callId, const bool& toggle); + void toggleChatHandler(const std::string& chatHandlerId, + const std::string& accountId, + const std::string& peerId, + const bool& toggle); std::map getCallMediaHandlerDetails(const std::string& mediaHandlerId); + std::vector getCallMediaHandlerStatus(const std::string& callId); + std::map getChatHandlerDetails(const std::string& chatHandlerId); + std::vector getChatHandlerStatus(const std::string& accontId, + const std::string& peerId); bool getPluginsEnabled(); void setPluginsEnabled(const bool& state); - std::map> getCallMediaHandlerStatus( - const std::string& callId); }; diff --git a/bin/jni/plugin_manager_interface.i b/bin/jni/plugin_manager_interface.i index 4552457ea..8f7086df6 100644 --- a/bin/jni/plugin_manager_interface.i +++ b/bin/jni/plugin_manager_interface.i @@ -26,20 +26,23 @@ namespace DRing { bool loadPlugin(const std::string& path); bool unloadPlugin(const std::string& path); -void togglePlugin(const std::string& path, bool toggle); std::map getPluginDetails(const std::string& path); std::vector> getPluginPreferences(const std::string& path); bool setPluginPreference(const std::string& path, const std::string& key, const std::string& value); std::map getPluginPreferencesValues(const std::string& path); bool resetPluginPreferencesValues(const std::string& path); -std::vector listAvailablePlugins(); -std::vector listLoadedPlugins(); +std::vector getInstalledPlugins(); +std::vector getLoadedPlugins(); int installPlugin(const std::string& jplPath, bool force); int uninstallPlugin(const std::string& pluginRootPath); -std::vector listCallMediaHandlers(); +std::vector getCallMediaHandlers(); +std::vector getChatHandlers(); void toggleCallMediaHandler(const std::string& mediaHandlerId, const std::string& callId, bool toggle); +void toggleChatHandler(const std::string& chatHandlerId, const std::string& accountId, const std::string& peerId, bool toggle); std::map getCallMediaHandlerDetails(const std::string& mediaHandlerId); +std::vector getCallMediaHandlerStatus(const std::string& callId); +std::map getChatHandlerDetails(const std::string& chatHandlerId); +std::vector getChatHandlerStatus(const std::string& accountId, const std::string& peerId); bool getPluginsEnabled(); void setPluginsEnabled(bool state); -std::map> getCallMediaHandlerStatus(const std::string& callId); } diff --git a/contrib/src/ffnvcodec/rules.mak b/contrib/src/ffnvcodec/rules.mak index 6c7ea15e3..26dd855db 100644 --- a/contrib/src/ffnvcodec/rules.mak +++ b/contrib/src/ffnvcodec/rules.mak @@ -1,5 +1,5 @@ # ffnvcodec -FFNVCODEC_VERSION := n10.0.26.0 +FFNVCODEC_VERSION := n11.0.10.0 FFNVCODEC_GITURL := https://git.videolan.org/git/ffmpeg/nv-codec-headers.git ifeq ($(call need_pkg,"ffnvcodec >= 8"),) diff --git a/meson.build b/meson.build index 86b0643e0..f8e660811 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('jami-daemon', ['c', 'cpp'], - version: '9.6.0', + version: '9.9.0', license: 'GPL3+', default_options: ['cpp_std=gnu++17', 'buildtype=debugoptimized'], meson_version:'>= 0.54' diff --git a/src/call.cpp b/src/call.cpp index 757992842..556c20b03 100644 --- a/src/call.cpp +++ b/src/call.cpp @@ -23,6 +23,10 @@ #include "call.h" #include "account.h" #include "manager.h" +#ifdef ENABLE_PLUGIN +#include "plugin/jamipluginmanager.h" +#include "plugin/streamdata.h" +#endif #include "audio/ringbufferpool.h" #include "dring/call_const.h" #include "client/ring_signal.h" @@ -410,6 +414,14 @@ Call::onTextMessage(std::map&& messages) return; } } +#ifdef ENABLE_PLUGIN + auto& pluginChatManager + = jami::Manager::instance().getJamiPluginManager().getChatServicesManager(); + std::shared_ptr cm = std::make_shared( + getAccountId(), getPeerNumber(), true, const_cast&>(messages), false); + pluginChatManager.publishMessage(cm); + +#endif Manager::instance().incomingMessage(getCallId(), getPeerNumber(), messages); } diff --git a/src/client/plugin_manager_interface.cpp b/src/client/plugin_manager_interface.cpp index 5b7911fc8..0840725d2 100644 --- a/src/client/plugin_manager_interface.cpp +++ b/src/client/plugin_manager_interface.cpp @@ -45,12 +45,6 @@ unloadPlugin(const std::string& path) return status; } -void -togglePlugin(const std::string& path, bool toggle) -{ - jami::Manager::instance().getJamiPluginManager().togglePlugin(path, toggle); -} - std::map getPluginDetails(const std::string& path) { @@ -81,15 +75,15 @@ resetPluginPreferencesValues(const std::string& path) } std::vector -listAvailablePlugins() +getInstalledPlugins() { - return jami::Manager::instance().getJamiPluginManager().listAvailablePlugins(); + return jami::Manager::instance().getJamiPluginManager().getInstalledPlugins(); } std::vector -listLoadedPlugins() +getLoadedPlugins() { - return jami::Manager::instance().getJamiPluginManager().listLoadedPlugins(); + return jami::Manager::instance().getJamiPluginManager().getLoadedPlugins(); } int @@ -105,12 +99,21 @@ uninstallPlugin(const std::string& pluginRootPath) } std::vector -listCallMediaHandlers() +getCallMediaHandlers() { return jami::Manager::instance() .getJamiPluginManager() .getCallServicesManager() - .listCallMediaHandlers(); + .getCallMediaHandlers(); +} + +std::vector +getChatHandlers() +{ + return jami::Manager::instance() + .getJamiPluginManager() + .getChatServicesManager() + .getChatHandlers(); } void @@ -122,6 +125,18 @@ toggleCallMediaHandler(const std::string& mediaHandlerId, const std::string& cal .toggleCallMediaHandler(mediaHandlerId, callId, toggle); } +void +toggleChatHandler(const std::string& chatHandlerId, + const std::string& accountId, + const std::string& peerId, + bool toggle) +{ + return jami::Manager::instance() + .getJamiPluginManager() + .getChatServicesManager() + .toggleChatHandler(chatHandlerId, accountId, peerId, toggle); +} + std::map getCallMediaHandlerDetails(const std::string& mediaHandlerId) { @@ -131,6 +146,33 @@ getCallMediaHandlerDetails(const std::string& mediaHandlerId) .getCallMediaHandlerDetails(mediaHandlerId); } +std::vector +getCallMediaHandlerStatus(const std::string& callId) +{ + return jami::Manager::instance() + .getJamiPluginManager() + .getCallServicesManager() + .getCallMediaHandlerStatus(callId); +} + +std::map +getChatHandlerDetails(const std::string& chatHandlerId) +{ + return jami::Manager::instance() + .getJamiPluginManager() + .getChatServicesManager() + .getChatHandlerDetails(chatHandlerId); +} + +std::vector +getChatHandlerStatus(const std::string& accountId, const std::string& peerId) +{ + return jami::Manager::instance() + .getJamiPluginManager() + .getChatServicesManager() + .getChatHandlerStatus(accountId, peerId); +} + bool getPluginsEnabled() { @@ -143,13 +185,4 @@ setPluginsEnabled(bool state) jami::Manager::instance().pluginPreferences.setPluginsEnabled(state); jami::Manager::instance().saveConfig(); } - -std::map> -getCallMediaHandlerStatus(const std::string& callId) -{ - return jami::Manager::instance() - .getJamiPluginManager() - .getCallServicesManager() - .getCallMediaHandlerStatus(callId); -} } // namespace DRing diff --git a/src/dring/plugin_manager_interface.h b/src/dring/plugin_manager_interface.h index 19bd8ade9..6c91e57d5 100644 --- a/src/dring/plugin_manager_interface.h +++ b/src/dring/plugin_manager_interface.h @@ -36,7 +36,6 @@ namespace DRing { DRING_PUBLIC bool loadPlugin(const std::string& path); DRING_PUBLIC bool unloadPlugin(const std::string& path); -DRING_PUBLIC void togglePlugin(const std::string& path, bool toggle); DRING_PUBLIC std::map getPluginDetails(const std::string& path); DRING_PUBLIC std::vector> getPluginPreferences( const std::string& path); @@ -45,18 +44,26 @@ DRING_PUBLIC bool setPluginPreference(const std::string& path, const std::string& value); DRING_PUBLIC std::map getPluginPreferencesValues(const std::string& path); DRING_PUBLIC bool resetPluginPreferencesValues(const std::string& path); -DRING_PUBLIC std::vector listAvailablePlugins(); -DRING_PUBLIC std::vector listLoadedPlugins(); +DRING_PUBLIC std::vector getInstalledPlugins(); +DRING_PUBLIC std::vector getLoadedPlugins(); DRING_PUBLIC int installPlugin(const std::string& jplPath, bool force); DRING_PUBLIC int uninstallPlugin(const std::string& pluginRootPath); -DRING_PUBLIC std::vector listCallMediaHandlers(); +DRING_PUBLIC std::vector getCallMediaHandlers(); +DRING_PUBLIC std::vector getChatHandlers(); DRING_PUBLIC void toggleCallMediaHandler(const std::string& mediaHandlerId, const std::string& callId, bool toggle); +DRING_PUBLIC void toggleChatHandler(const std::string& chatHandlerId, + const std::string& accountId, + const std::string& peerId, + bool toggle); DRING_PUBLIC std::map getCallMediaHandlerDetails( const std::string& mediaHandlerId); +DRING_PUBLIC std::vector getCallMediaHandlerStatus(const std::string& callId); +DRING_PUBLIC std::map getChatHandlerDetails( + const std::string& chatHandlerId); +DRING_PUBLIC std::vector getChatHandlerStatus(const std::string& accountId, + const std::string& peerId); DRING_PUBLIC bool getPluginsEnabled(); DRING_PUBLIC void setPluginsEnabled(bool state); -DRING_PUBLIC std::map> getCallMediaHandlerStatus( - const std::string& callId); } // namespace DRing diff --git a/src/jamidht/contact_list.cpp b/src/jamidht/contact_list.cpp index ad8ff4446..bb4b4ec8f 100644 --- a/src/jamidht/contact_list.cpp +++ b/src/jamidht/contact_list.cpp @@ -20,6 +20,11 @@ #include "jamiaccount.h" #include "fileutils.h" +#ifdef ENABLE_PLUGIN +#include "manager.h" +#include "plugin/jamipluginmanager.h" +#endif + #include "account_const.h" #include @@ -102,6 +107,16 @@ ContactList::removeContact(const dht::InfoHash& h, bool ban) if (ban and trustRequests_.erase(h) > 0) saveTrustRequests(); saveContacts(); +#ifdef ENABLE_PLUGIN + std::size_t found = path_.find_last_of(DIR_SEPARATOR_CH); + if (found != std::string::npos) { + auto filename = path_.substr(found + 1); + jami::Manager::instance() + .getJamiPluginManager() + .getChatServicesManager() + .cleanChatSubjects(filename, uri); + } +#endif callbacks_.contactRemoved(uri, ban); return true; } @@ -414,19 +429,20 @@ ContactList::foundAccountDevice(const std::shared_ptr& name.c_str(), crt->getId().toString().c_str()); tls::CertificateStore::instance().pinCertificate(crt); - if (crt->ocspResponse){ + if (crt->ocspResponse) { unsigned int status = crt->ocspResponse->getCertificateStatus(); - if (status == GNUTLS_OCSP_CERT_GOOD){ + if (status == GNUTLS_OCSP_CERT_GOOD) { JAMI_DBG("Certificate %s has good OCSP status", crt->getId().to_c_str()); - trust_.setCertificateStatus(crt->getId().toString(), tls::TrustStore::PermissionStatus::ALLOWED); - } - else if (status == GNUTLS_OCSP_CERT_REVOKED){ + trust_.setCertificateStatus(crt->getId().toString(), + tls::TrustStore::PermissionStatus::ALLOWED); + } else if (status == GNUTLS_OCSP_CERT_REVOKED) { JAMI_ERR("Certificate %s has revoked OCSP status", crt->getId().to_c_str()); - trust_.setCertificateStatus(crt->getId().toString(), tls::TrustStore::PermissionStatus::BANNED); - } - else { + trust_.setCertificateStatus(crt->getId().toString(), + tls::TrustStore::PermissionStatus::BANNED); + } else { JAMI_ERR("Certificate %s has unknown OCSP status", crt->getId().to_c_str()); - trust_.setCertificateStatus(crt->getId().toString(), tls::TrustStore::PermissionStatus::UNDEFINED); + trust_.setCertificateStatus(crt->getId().toString(), + tls::TrustStore::PermissionStatus::UNDEFINED); } } saveKnownDevices(); diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index 1a03c9772..a1357123d 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -61,6 +61,11 @@ #include "manager.h" #include "utf8_utils.h" +#ifdef ENABLE_PLUGIN +#include "plugin/jamipluginmanager.h" +#include "plugin/chatservicesmanager.h" +#endif + #ifdef ENABLE_VIDEO #include "libav_utils.h" #endif @@ -2570,6 +2575,10 @@ JamiAccount::doUnregister(std::function released_cb) if (released_cb) released_cb(false); +#ifdef ENABLE_PLUGIN + jami::Manager::instance().getJamiPluginManager().getChatServicesManager().cleanChatSubjects( + getAccountID()); +#endif } void diff --git a/src/manager.cpp b/src/manager.cpp index c309bd6bf..3dbf5a87d 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -68,6 +68,7 @@ using random_device = dht::crypto::random_device; #ifdef ENABLE_PLUGIN #include "plugin/jamipluginmanager.h" +#include "plugin/streamdata.h" #endif #ifdef ENABLE_VIDEO @@ -1708,8 +1709,8 @@ Manager::scheduleTask(std::function&& task, std::chrono::steady_clock::t return pimpl_->scheduler_.schedule(std::move(task), when); } -std::shared_ptr Manager::scheduleTaskIn(std::function&& task, - std::chrono::steady_clock::duration timeout) +std::shared_ptr +Manager::scheduleTaskIn(std::function&& task, std::chrono::steady_clock::duration timeout) { return pimpl_->scheduler_.scheduleIn(std::move(task), timeout); } @@ -3071,18 +3072,24 @@ Manager::isPasswordValid(const std::string& accountID, const std::string& passwo uint64_t Manager::sendTextMessage(const std::string& accountID, const std::string& to, - const std::map& payloads) + const std::map& payloads, + const bool fromPlugin) { if (const auto acc = getAccount(accountID)) { try { -#ifdef ENABLE_PLUGIN - auto& convManager - = jami::Manager::instance().getJamiPluginManager().getConversationServicesManager(); - std::shared_ptr cm - = std::make_shared( - accountID, to, const_cast&>(payloads)); - convManager.sendTextMessage(cm); - return acc->sendTextMessage(cm->to_, cm->data_); +#ifdef ENABLE_PLUGIN // modifies send message + auto& pluginChatManager + = jami::Manager::instance().getJamiPluginManager().getChatServicesManager(); + std::shared_ptr cm + = std::make_shared(accountID, + to, + false, + const_cast&>( + payloads), + fromPlugin); + if (!fromPlugin) + pluginChatManager.publishMessage(cm); + return acc->sendTextMessage(cm->peerId, cm->data); #else return acc->sendTextMessage(to, payloads); #endif // ENABLE_PLUGIN @@ -3257,7 +3264,9 @@ Manager::setModerator(const std::string& confId, const std::string& peerId, cons if (auto conf = getConferenceFromID(confId)) { conf->setModerator(peerId, state); } else - JAMI_WARN("Fail to change moderator %s, conference %s not found", peerId.c_str(), confId.c_str()); + JAMI_WARN("Fail to change moderator %s, conference %s not found", + peerId.c_str(), + confId.c_str()); } void diff --git a/src/manager.h b/src/manager.h index b4a62f3b4..60012200f 100644 --- a/src/manager.h +++ b/src/manager.h @@ -415,7 +415,8 @@ public: uint64_t sendTextMessage(const std::string& accountID, const std::string& to, - const std::map& payloads); + const std::map& payloads, + const bool fromPlugin = false); int getMessageStatus(uint64_t id) const; int getMessageStatus(const std::string& accountID, uint64_t id) const; @@ -894,7 +895,7 @@ public: std::shared_ptr scheduleTask(std::function&& task, std::chrono::steady_clock::time_point when); std::shared_ptr scheduleTaskIn(std::function&& task, - std::chrono::steady_clock::duration timeout); + std::chrono::steady_clock::duration timeout); std::map getNearbyPeers(const std::string& accountID); diff --git a/src/meson.build b/src/meson.build index 82a31297f..5a8731905 100644 --- a/src/meson.build +++ b/src/meson.build @@ -273,7 +273,8 @@ if conf.get('ENABLE_PLUGIN') 'client/plugin_manager_interface.cpp', 'plugin/jamipluginmanager.cpp', 'plugin/pluginloaderdl.cpp', - 'plugin/pluginmanager.cpp' + 'plugin/pluginmanager.cpp', + 'plugin/pluginpreferencesutils.cpp' ) libjami_dependencies += [deplibarchive, depdl] endif diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt index 25224bd81..65a18b391 100644 --- a/src/plugin/CMakeLists.txt +++ b/src/plugin/CMakeLists.txt @@ -5,14 +5,18 @@ list (APPEND Source_Files__plugin "${CMAKE_CURRENT_SOURCE_DIR}/jamipluginmanager.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/pluginloaderdl.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/pluginmanager.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/pluginpreferencesutils.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/callservicesmanager.h" - "${CMAKE_CURRENT_SOURCE_DIR}/conversationservicesmanager.h" - "${CMAKE_CURRENT_SOURCE_DIR}/conversationhandler.h" + "${CMAKE_CURRENT_SOURCE_DIR}/callservicesmanager.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/chatservicesmanager.h" + "${CMAKE_CURRENT_SOURCE_DIR}/chatservicesmanager.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/chathandler.h" "${CMAKE_CURRENT_SOURCE_DIR}/jamiplugin.h" "${CMAKE_CURRENT_SOURCE_DIR}/jamipluginmanager.h" "${CMAKE_CURRENT_SOURCE_DIR}/mediahandler.h" "${CMAKE_CURRENT_SOURCE_DIR}/pluginloader.h" "${CMAKE_CURRENT_SOURCE_DIR}/pluginmanager.h" + "${CMAKE_CURRENT_SOURCE_DIR}/pluginpreferencesutils.h" "${CMAKE_CURRENT_SOURCE_DIR}/streamdata.h" ) diff --git a/src/plugin/Makefile.am b/src/plugin/Makefile.am index b528b2902..b02cadf6f 100644 --- a/src/plugin/Makefile.am +++ b/src/plugin/Makefile.am @@ -4,19 +4,23 @@ noinst_LTLIBRARIES = libplugin.la noinst_HEADERS = \ callservicesmanager.h \ - conversationhandler.h \ - conversationservicesmanager.h \ + chathandler.h \ + chatservicesmanager.h \ jamiplugin.h \ jamipluginmanager.h \ mediahandler.h \ pluginloader.h \ pluginmanager.h \ + pluginpreferencesutils.h \ streamdata.h libplugin_la_SOURCES = \ jamipluginmanager.cpp \ pluginloaderdl.cpp \ - pluginmanager.cpp + pluginmanager.cpp \ + pluginpreferencesutils.cpp \ + chatservicesmanager.cpp \ + callservicesmanager.cpp libplugin_la_LIBADD = @ARCHIVE_LIBS@ diff --git a/src/plugin/callservicesmanager.cpp b/src/plugin/callservicesmanager.cpp new file mode 100644 index 000000000..6ab148663 --- /dev/null +++ b/src/plugin/callservicesmanager.cpp @@ -0,0 +1,229 @@ +/** + * Copyright (C)2020-2021 Savoir-faire Linux Inc. + * + * Author: Aline Gondim Santos + * + * 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 "callservicesmanager.h" +#include "logger.h" +#include "sip/sipcall.h" +#include "fileutils.h" +#include "pluginpreferencesutils.h" +#include "manager.h" + +namespace jami { + +CallServicesManager::CallServicesManager(PluginManager& pm) +{ + registerComponentsLifeCycleManagers(pm); +} + +CallServicesManager::~CallServicesManager() +{ + callMediaHandlers_.clear(); +} + +void +CallServicesManager::createAVSubject(const StreamData& data, AVSubjectSPtr subject) +{ + // This guarantees unicity of subjects by id + callAVsubjects_.emplace_back(data, subject); + + for (auto& callMediaHandler : callMediaHandlers_) { + std::size_t found = callMediaHandler->id().find_last_of(DIR_SEPARATOR_CH); + auto preferences = PluginPreferencesUtils::getPreferencesValuesMap(callMediaHandler->id().substr(0, found)); +#ifndef __ANDROID__ + if (preferences.at("always") == "1") + toggleCallMediaHandler((uintptr_t) callMediaHandler.get(), data.id, true); + else +#endif + for (const auto& toggledMediaHandler : mediaHandlerToggled_[data.id]) { + if (toggledMediaHandler == (uintptr_t) callMediaHandler.get()) { + toggleCallMediaHandler(toggledMediaHandler, data.id, true); + break; + } + } + } +} + +void +CallServicesManager::clearAVSubject(const std::string& callId) +{ + for (auto it = callAVsubjects_.begin(); it != callAVsubjects_.end();) { + if (it->first.id == callId) { + it = callAVsubjects_.erase(it); + } else { + ++it; + } + } +} + +void +CallServicesManager::registerComponentsLifeCycleManagers(PluginManager& pm) +{ + auto registerCallMediaHandler = [this](void* data) { + CallMediaHandlerPtr ptr {(static_cast(data))}; + + if (!ptr) + return -1; + callMediaHandlers_.emplace_back(std::move(ptr)); + return 0; + }; + + auto unregisterMediaHandler = [this](void* data) { + for (auto it = callMediaHandlers_.begin(); it != callMediaHandlers_.end(); ++it) { + if (it->get() == data) { + callMediaHandlers_.erase(it); + break; + } + } + return 0; + }; + + pm.registerComponentManager("CallMediaHandlerManager", + registerCallMediaHandler, + unregisterMediaHandler); +} + +std::vector +CallServicesManager::getCallMediaHandlers() +{ + std::vector res; + res.reserve(callMediaHandlers_.size()); + for (const auto& mediaHandler : callMediaHandlers_) { + res.emplace_back(std::to_string((uintptr_t) mediaHandler.get())); + } + return res; +} + +void +CallServicesManager::toggleCallMediaHandler(const std::string& mediaHandlerId, + const std::string& callId, + const bool toggle) +{ + toggleCallMediaHandler(std::stoull(mediaHandlerId), callId, toggle); +} + +std::map +CallServicesManager::getCallMediaHandlerDetails(const std::string& mediaHandlerIdStr) +{ + auto mediaHandlerId = std::stoull(mediaHandlerIdStr); + for (auto& mediaHandler : callMediaHandlers_) { + if ((uintptr_t) mediaHandler.get() == mediaHandlerId) { + return mediaHandler->getCallMediaHandlerDetails(); + } + } + return {}; +} + +bool +CallServicesManager::isVideoType(const CallMediaHandlerPtr& mediaHandler) +{ + const auto& details = mediaHandler->getCallMediaHandlerDetails(); + const auto& it = details.find("dataType"); + if (it != details.end()) { + bool status; + std::istringstream(it->second) >> status; + return status; + } + return true; +} + +bool +CallServicesManager::isAttached(const CallMediaHandlerPtr& mediaHandler) +{ + const auto& details = mediaHandler->getCallMediaHandlerDetails(); + const auto& it = details.find("attached"); + if (it != details.end()) { + bool status; + std::istringstream(it->second) >> status; + return status; + } + return true; +} + +std::vector +CallServicesManager::getCallMediaHandlerStatus(const std::string& callId) +{ + std::vector ret; + const auto& it = mediaHandlerToggled_.find(callId); + if (it != mediaHandlerToggled_.end()) { + ret.reserve(it->second.size()); + for (const auto& mediaHandlerId : it->second) + ret.emplace_back(std::to_string(mediaHandlerId)); + } + return ret; +} + +void +CallServicesManager::setPreference(const std::string& key, const std::string& value, const std::string& scopeStr) +{ + for (auto& mediaHandler : callMediaHandlers_) { + if (scopeStr.find(mediaHandler->getCallMediaHandlerDetails()["name"]) + != std::string::npos) { + mediaHandler->setPreferenceAttribute(key, value); + } + } +} + +void +CallServicesManager::notifyAVSubject(CallMediaHandlerPtr& callMediaHandlerPtr, + const StreamData& data, + AVSubjectSPtr& subject) +{ + if (auto soSubject = subject.lock()) + callMediaHandlerPtr->notifyAVFrameSubject(data, soSubject); +} + +void +CallServicesManager::toggleCallMediaHandler(const uintptr_t mediaHandlerId, + const std::string& callId, + const bool toggle) +{ + auto& handlers = mediaHandlerToggled_[callId]; + + bool applyRestart = false; + + for (auto subject : callAVsubjects_) { + if (subject.first.id == callId) { + + auto handlerIt = std::find_if(callMediaHandlers_.begin(), callMediaHandlers_.end(), + [mediaHandlerId](CallMediaHandlerPtr& handler) { + return ((uintptr_t) handler.get() == mediaHandlerId); + }); + + if (handlerIt != callMediaHandlers_.end()) { + if (toggle) { + notifyAVSubject((*handlerIt), subject.first, subject.second); + if (isAttached((*handlerIt)) + && handlers.find(mediaHandlerId) == handlers.end()) + handlers.insert(mediaHandlerId); + } else { + (*handlerIt)->detach(); + handlers.erase(mediaHandlerId); + } + if (subject.first.type == StreamType::video && isVideoType((*handlerIt))) + applyRestart = true; + } + } + } +#ifndef __ANDROID__ + if (applyRestart) + Manager::instance().callFactory.getCall(callId)->getVideoRtp().restartSender(); +#endif +} +} // namespace jami diff --git a/src/plugin/callservicesmanager.h b/src/plugin/callservicesmanager.h index 38c3a7de1..8ab1a5a62 100644 --- a/src/plugin/callservicesmanager.h +++ b/src/plugin/callservicesmanager.h @@ -19,16 +19,11 @@ */ #pragma once -// Utils + #include "noncopyable.h" -#include "logger.h" -#include "manager.h" -#include "sip/sipcall.h" -// Plugin Manager #include "pluginmanager.h" #include "streamdata.h" #include "mediahandler.h" -// STL #include #include #include @@ -41,205 +36,71 @@ using AVSubjectSPtr = std::weak_ptr>; class CallServicesManager { public: - CallServicesManager(PluginManager& pm) { registerComponentsLifeCycleManagers(pm); } + CallServicesManager(PluginManager& pm); /** * unload all media handlers **/ - ~CallServicesManager() { callMediaHandlers.clear(); } + ~CallServicesManager(); NON_COPYABLE(CallServicesManager); -public: /** * @brief createAVSubject * @param data * Creates an av frame subject with properties StreamData */ - void createAVSubject(const StreamData& data, AVSubjectSPtr subject) - { - // This guarantees unicity of subjects by id - callAVsubjects.push_back(std::make_pair(data, subject)); - for (const auto& toggledMediaHandler : mediaHandlerToggled_[data.id]) { - toggleCallMediaHandler(toggledMediaHandler, data.id, true); - } - } + void createAVSubject(const StreamData& data, AVSubjectSPtr subject); - void clearAVSubject(const std::string& callId) - { - for (auto it = callAVsubjects.begin(); it != callAVsubjects.end();) { - if (it->first.id == callId) { - it = callAVsubjects.erase(it); - } else { - ++it; - } - } - } + void clearAVSubject(const std::string& callId); /** * @brief registerComponentsLifeCycleManagers * Exposes components life cycle managers to the main API */ - void registerComponentsLifeCycleManagers(PluginManager& pm) - { - auto registerCallMediaHandler = [this](void* data) { - CallMediaHandlerPtr ptr {(static_cast(data))}; - - if (ptr) { - callMediaHandlers.emplace_back(std::move(ptr)); - } - return 0; - }; - - auto unregisterMediaHandler = [this](void* data) { - for (auto it = callMediaHandlers.begin(); it != callMediaHandlers.end(); ++it) { - if (it->get() == data) { - callMediaHandlers.erase(it); - break; - } - } - return 0; - }; - - pm.registerComponentManager("CallMediaHandlerManager", - registerCallMediaHandler, - unregisterMediaHandler); - } + void registerComponentsLifeCycleManagers(PluginManager& pm); /** - * @brief listCallMediaHandlers + * @brief getCallMediaHandlers * List all call media handlers * @return */ - std::vector listCallMediaHandlers() - { - std::vector res; - for (const auto& mediaHandler : callMediaHandlers) { - res.emplace_back(getCallHandlerId(mediaHandler)); - } - return res; - } + std::vector getCallMediaHandlers(); /** * @brief toggleCallMediaHandler * Toggle CallMediaHandler, if on, notify with new subjects * if off, detach it - * @param id + * @param mediaHandler ID handler ID + * @param callId call ID + * @param toggle notify with new subjects if true, detach if false. + * + * In the case when the mediaHandler receives a hardware format + * frame and converts it to main memory, we need to restart the + * sender to unlink ours encoder and decoder. + * + * When we deactivate a mediaHandler, we try to relink the encoder + * and decoder by restarting the sender. */ void toggleCallMediaHandler(const std::string& mediaHandlerId, const std::string& callId, - const bool toggle) - { - if (mediaHandlerId.empty() || callId.empty()) - return; - - auto find = mediaHandlerToggled_.find(callId); - if (find == mediaHandlerToggled_.end()) - mediaHandlerToggled_[callId] = {}; - bool applyRestart = false; - for (auto it = callAVsubjects.begin(); it != callAVsubjects.end(); ++it) { - if (it->first.id == callId) { - for (auto& mediaHandler : callMediaHandlers) { - if (getCallHandlerId(mediaHandler) == mediaHandlerId) { - if (toggle) { - notifyAVSubject(mediaHandler, it->first, it->second); - if (isAttached(mediaHandler) - && mediaHandlerToggled_[callId].find(mediaHandlerId) - == mediaHandlerToggled_[callId].end()) - mediaHandlerToggled_[callId].insert(mediaHandlerId); - } else { - mediaHandler->detach(); - if (mediaHandlerToggled_[callId].find(mediaHandlerId) - != mediaHandlerToggled_[callId].end()) - mediaHandlerToggled_[callId].erase(mediaHandlerId); - } - if (it->first.type == StreamType::video && isVideoType(mediaHandler)) - applyRestart = true; - break; - } - } - } - } - - /* In the case when the mediaHandler receives a hardware format - * frame and converts it to main memory, we need to restart the - * sender to unlink ours encoder and decoder. - * - * When we deactivate a mediaHandler, we try to relink the encoder - * and decoder by restarting the sender. - */ - if (applyRestart) - Manager::instance().callFactory.getCall(callId)->getVideoRtp().restartSender(); - } + const bool toggle); /** * @brief getCallMediaHandlerDetails * @param id of the call media handler * @return map of Call Media Handler Details */ - std::map getCallMediaHandlerDetails(const std::string& mediaHandlerId) - { - for (auto& mediaHandler : callMediaHandlers) { - if (getCallHandlerId(mediaHandler) == mediaHandlerId) { - return mediaHandler->getCallMediaHandlerDetails(); - } - } - return {}; - } + std::map getCallMediaHandlerDetails( + const std::string& mediaHandlerIdStr); - bool isVideoType(const CallMediaHandlerPtr& mediaHandler) - { - const auto& details = mediaHandler->getCallMediaHandlerDetails(); - const auto& it = details.find("dataType"); - if (it != details.end()) { - JAMI_INFO() << "type: "; - bool status; - std::istringstream(it->second) >> status; - JAMI_INFO() << status; - return status; - } - JAMI_INFO() << "dataType not found"; - return true; - } + bool isVideoType(const CallMediaHandlerPtr& mediaHandler); - bool isAttached(const CallMediaHandlerPtr& mediaHandler) - { - const auto& details = mediaHandler->getCallMediaHandlerDetails(); - const auto& it = details.find("attached"); - if (it != details.end()) { - JAMI_INFO() << "status: "; - bool status; - std::istringstream(it->second) >> status; - JAMI_INFO() << status; - return status; - } - JAMI_INFO() << "attached not found"; - return true; - } + bool isAttached(const CallMediaHandlerPtr& mediaHandler); - std::map> getCallMediaHandlerStatus( - const std::string& callId) - { - const auto& it = mediaHandlerToggled_.find(callId); - if (it != mediaHandlerToggled_.end()) { - std::vector ret; - for (const auto& mediaHandlerId : it->second) - ret.push_back(mediaHandlerId); - return {{callId, ret}}; - } + std::vector getCallMediaHandlerStatus(const std::string& callId); - return {{callId, {}}}; - } - - void setPreference(const std::string& key, const std::string& value, const std::string& scopeStr) - { - for (auto& mediaHandler : callMediaHandlers) { - if (scopeStr.find(mediaHandler->getCallMediaHandlerDetails()["name"]) - != std::string::npos) { - mediaHandler->setPreferenceAttribute(key, value); - } - } - } + void setPreference(const std::string& key, const std::string& value, const std::string& scopeStr); private: /** @@ -250,61 +111,34 @@ private: */ void notifyAVSubject(CallMediaHandlerPtr& callMediaHandlerPtr, const StreamData& data, - AVSubjectSPtr& subject) - { - if (auto soSubject = subject.lock()) - callMediaHandlerPtr->notifyAVFrameSubject(data, soSubject); - } + AVSubjectSPtr& subject); + + void toggleCallMediaHandler(const uintptr_t mediaHandlerId, + const std::string& callId, + const bool toggle); /** - * @brief listAvailableSubjects - * @param callMediaHandlerPtr - * This functions lets the call media handler component know which subjects are available - */ - void listAvailableSubjects(const std::string& callID, CallMediaHandlerPtr& callMediaHandlerPtr) - { - for (auto it = callAVsubjects.begin(); it != callAVsubjects.end(); ++it) { - if (it->first.id == callID) - notifyAVSubject(callMediaHandlerPtr, it->first, it->second); - } - } - - /** - * @brief getCallHandlerId - * Returns the callMediaHandler id from a callMediaHandler pointer - * @param callMediaHandler - * @return string id - */ - std::string getCallHandlerId(const CallMediaHandlerPtr& callMediaHandler) - { - if (callMediaHandler) { - std::ostringstream callHandlerIdStream; - callHandlerIdStream << callMediaHandler.get(); - return callHandlerIdStream.str(); - } - return ""; - } - -private: - /** - * @brief callMediaHandlers + * @brief callMediaHandlers_ * Components that a plugin can register through registerCallMediaHandler service * These objects can then be notified with notifySubject * whenever there is a new CallAVSubject like a video receive */ - std::list callMediaHandlers; + std::list callMediaHandlers_; /** - * @brief callAVsubjects - * When there is a SIPCall, CallAVSubjects are created there + * @brief callAVsubjects_ + * When there is a SIPCall, CallAVSubjects_ are created there * Here we keep a reference to them in order to make them interact with - * CallMediaHandlers + * CallMediaHandlers_ * It is pushed to this list list */ - std::list> callAVsubjects; - // std::map> callAVsubjects; + std::list> callAVsubjects_; - std::map> mediaHandlerToggled_; + /** + * @brief mediaHandlerToggled_ + * A map of callId and list of mediaHandlers pointers str + */ + std::map> mediaHandlerToggled_; // callId, list of mediaHandlers }; } // namespace jami diff --git a/src/plugin/chathandler.h b/src/plugin/chathandler.h new file mode 100644 index 000000000..411f10019 --- /dev/null +++ b/src/plugin/chathandler.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 Savoir-faire Linux Inc. + * + * Author: Aline Gondim Santos + * + * 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. + */ + +#pragma once + +#include "observer.h" +#include "streamdata.h" +#include +#include + +namespace jami { + +using pluginMessagePtr = std::shared_ptr; +using chatSubjectPtr = std::shared_ptr>; + +class ChatHandler +{ +public: + virtual void notifyChatSubject(std::pair& subjectConnection, + chatSubjectPtr subject) + = 0; + virtual std::map getChatHandlerDetails() = 0; + virtual void detach(chatSubjectPtr subject) = 0; + virtual void setPreferenceAttribute(const std::string& key, const std::string& value) = 0; + virtual bool preferenceMapHasKey(const std::string& key) = 0; + + /** + * @brief id + * The id is the path of the plugin that created this MediaHandler + * @return + */ + std::string id() const { return id_; } + virtual void setId(const std::string& id) final { id_ = id; } + +private: + std::string id_; +}; +} // namespace jami diff --git a/src/plugin/chatservicesmanager.cpp b/src/plugin/chatservicesmanager.cpp new file mode 100644 index 000000000..28f6ae170 --- /dev/null +++ b/src/plugin/chatservicesmanager.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2021 Savoir-faire Linux Inc. + * + * 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" +#include "pluginpreferencesutils.h" +#include "logger.h" +#include "manager.h" +#include "fileutils.h" + +namespace jami { + +ChatServicesManager::ChatServicesManager(PluginManager& pm) +{ + registerComponentsLifeCycleManagers(pm); + registerChatService(pm); +} + +void +ChatServicesManager::registerComponentsLifeCycleManagers(PluginManager& pm) +{ + auto registerChatHandler = [this](void* data) { + ChatHandlerPtr ptr {(static_cast(data))}; + + if (!ptr) + return -1; + chatHandlers_.emplace_back(std::move(ptr)); + return 0; + }; + + auto unregisterChatHandler = [this](void* data) { + auto handlerIt = std::find_if(chatHandlers_.begin(), chatHandlers_.end(), + [data](ChatHandlerPtr& handler) { + return (handler.get() == data); + }); + + if (handlerIt != chatHandlers_.end()) { + for (auto& toggledList: chatHandlerToggled_) { + auto handlerId = std::find_if(toggledList.second.begin(), toggledList.second.end(), + [this, handlerIt](uintptr_t handlerId) { + return (handlerId == (uintptr_t) handlerIt->get()); + }); + if (handlerId != toggledList.second.end()) { + handlerId = toggledList.second.erase(handlerId); + (*handlerIt)->detach(chatSubjects_[toggledList.first]); + } + } + chatHandlers_.erase(handlerIt); + } + return 0; + }; + + pm.registerComponentManager("ChatHandlerManager", + registerChatHandler, + unregisterChatHandler); +} + +void +ChatServicesManager::registerChatService(PluginManager& pm) +{ + auto sendTextMessage = [this](const DLPlugin*, void* data) { + auto cm = static_cast(data); + jami::Manager::instance().sendTextMessage(cm->accountId, cm->peerId, cm->data, true); + return 0; + }; + + pm.registerService("sendTextMessage", sendTextMessage); +} + +std::vector +ChatServicesManager::getChatHandlers() +{ + std::vector res; + res.reserve(chatHandlers_.size()); + for (const auto& chatHandler : chatHandlers_) { + res.emplace_back(std::to_string((uintptr_t) chatHandler.get())); + } + return res; +} + +void +ChatServicesManager::publishMessage(pluginMessagePtr& cm) +{ + if (cm->fromPlugin) + return; + std::pair mPair(cm->accountId, cm->peerId); + auto& handlers = chatHandlerToggled_[mPair]; + for (auto& chatHandler : chatHandlers_) { + std::size_t found = chatHandler->id().find_last_of(DIR_SEPARATOR_CH); + auto preferences = PluginPreferencesUtils::getPreferencesValuesMap( + chatHandler->id().substr(0, found)); + bool toggled = false; + if (handlers.find((uintptr_t) chatHandler.get()) != handlers.end()) + toggled = true; + if (preferences.at("always") == "1" || toggled) { + chatSubjects_.emplace(mPair, std::make_shared>()); + if (!toggled) { + handlers.insert((uintptr_t) chatHandler.get()); + chatHandler->notifyChatSubject(mPair, chatSubjects_[mPair]); + } + chatSubjects_[mPair]->publish(cm); + } + } +} + +void +ChatServicesManager::cleanChatSubjects(const std::string& accountId, const std::string& peerId) +{ + std::pair 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, + const std::string& accountId, + const std::string& peerId, + const bool toggle) +{ + toggleChatHandler(std::stoull(chatHandlerId), accountId, peerId, toggle); +} + +std::vector +ChatServicesManager::getChatHandlerStatus(const std::string& accountId, + const std::string& peerId) +{ + std::pair mPair(accountId, peerId); + const auto& it = chatHandlerToggled_.find(mPair); + std::vector ret; + if (it != chatHandlerToggled_.end()) { + ret.reserve(it->second.size()); + for (const auto& chatHandlerId : it->second) + ret.emplace_back(std::to_string(chatHandlerId)); + return ret; + } + + return ret; +} + +std::map +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 {}; +} + +void +ChatServicesManager::setPreference(const std::string& key, const std::string& value, const std::string& scopeStr) +{ + for (auto& chatHandler : chatHandlers_) { + if (scopeStr.find(chatHandler->getChatHandlerDetails()["name"]) != std::string::npos) { + chatHandler->setPreferenceAttribute(key, value); + } + } +} + +void +ChatServicesManager::toggleChatHandler(const uintptr_t chatHandlerId, + const std::string& accountId, + const std::string& peerId, + const bool toggle) +{ + std::pair mPair(accountId, peerId); + auto& handlers = chatHandlerToggled_[mPair]; + chatSubjects_.emplace(mPair, std::make_shared>()); + + auto chatHandlerIt = std::find_if(chatHandlers_.begin(), chatHandlers_.end(), + [chatHandlerId](ChatHandlerPtr& handler) { + return ((uintptr_t) handler.get() == chatHandlerId); + }); + + if (chatHandlerIt != chatHandlers_.end()) { + if (toggle) { + (*chatHandlerIt)->notifyChatSubject(mPair, chatSubjects_[mPair]); + if (handlers.find(chatHandlerId) == handlers.end()) + handlers.insert(chatHandlerId); + } else { + (*chatHandlerIt)->detach(chatSubjects_[mPair]); + handlers.erase(chatHandlerId); + } + } +} +} // namespace jami diff --git a/src/plugin/chatservicesmanager.h b/src/plugin/chatservicesmanager.h new file mode 100644 index 000000000..de4bceb9c --- /dev/null +++ b/src/plugin/chatservicesmanager.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 Savoir-faire Linux Inc. + * + * 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. + */ +#pragma once + +#include "noncopyable.h" +#include "pluginmanager.h" +#include "chathandler.h" + +namespace jami { + +using ChatHandlerPtr = std::unique_ptr; + +class ChatServicesManager +{ +public: + ChatServicesManager(PluginManager& pm); + + NON_COPYABLE(ChatServicesManager); + + void registerComponentsLifeCycleManagers(PluginManager& pm); + + void registerChatService(PluginManager& pm); + + std::vector getChatHandlers(); + + void publishMessage(pluginMessagePtr& cm); + + void cleanChatSubjects(const std::string& accountId, const std::string& peerId = ""); + + void toggleChatHandler(const std::string& chatHandlerId, + const std::string& accountId, + const std::string& peerId, + const bool toggle); + + std::vector getChatHandlerStatus(const std::string& accountId, + const std::string& peerId); + + /** + * @brief getChatHandlerDetails + * @param chatHandlerIdStr of the chat handler + * @return map of Chat Handler Details + */ + std::map getChatHandlerDetails(const std::string& chatHandlerIdStr); + + void setPreference(const std::string& key, const std::string& value, const std::string& scopeStr); + +private: + void toggleChatHandler(const uintptr_t chatHandlerId, + const std::string& accountId, + const std::string& peerId, + const bool toggle); + + std::list chatHandlers_; + std::map, std::set> + chatHandlerToggled_; // {account,peer}, list of chatHandlers + + std::map, chatSubjectPtr> chatSubjects_; +}; +} // namespace jami diff --git a/src/plugin/conversationhandler.h b/src/plugin/conversationhandler.h deleted file mode 100644 index 86c36fb79..000000000 --- a/src/plugin/conversationhandler.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2004-2019 Savoir-faire Linux Inc. - * - * 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. - */ -#pragma once -#include "observer.h" -#include -#include - -namespace jami { -struct ConversationMessage -{ - ConversationMessage(const std::string& author, - const std::string& to, - std::map& dataMap) - : author_ {author} - , to_ {to} - , data_ {dataMap} - {} - std::string author_; - std::string to_; - std::map data_; -}; - -using ConvMsgPtr = std::shared_ptr; - -using strMapSubjectPtr = std::shared_ptr>; - -class ConversationHandler -{ -public: - virtual ~ConversationHandler() = default; - virtual void notifyStrMapSubject(const bool direction, strMapSubjectPtr subject) = 0; -}; -} // namespace jami diff --git a/src/plugin/conversationservicesmanager.h b/src/plugin/conversationservicesmanager.h deleted file mode 100644 index a4df61096..000000000 --- a/src/plugin/conversationservicesmanager.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2004-2019 Savoir-faire Linux Inc. - * - * 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. - */ -#pragma once -// Utils -#include "noncopyable.h" -// Plugin Manager -#include "pluginmanager.h" -#include "conversationhandler.h" -#include "logger.h" -// Manager -#include "manager.h" - -namespace jami { - -using ConversationHandlerPtr = std::unique_ptr; - -class ConversationServicesManager -{ -public: - ConversationServicesManager(PluginManager& pm) - { - registerComponentsLifeCycleManagers(pm); - - auto sendTextMessage = [this](const DLPlugin* plugin, void* data) { - auto cm = static_cast(data); - jami::Manager::instance().sendTextMessage(cm->author_, cm->to_, cm->data_); - return 0; - }; - - pm.registerService("sendTextMessage", sendTextMessage); - } - NON_COPYABLE(ConversationServicesManager); - -public: - void registerComponentsLifeCycleManagers(PluginManager& pm) - { - auto registerConversationHandler = [this](void* data) { - ConversationHandlerPtr ptr {(static_cast(data))}; - - if (ptr) { - conversationHandlers.push_back(std::make_pair(false, std::move(ptr))); - if (!conversationHandlers.empty()) { - listAvailableSubjects(conversationHandlers.back().second); - } - } - - return 0; - }; - - auto unregisterConversationHandler = [this](void* data) { - for (auto it = conversationHandlers.begin(); it != conversationHandlers.end(); ++it) { - if (it->second.get() == data) { - conversationHandlers.erase(it); - break; - } - } - return 0; - }; - - pm.registerComponentManager("ConversationHandlerManager", - registerConversationHandler, - unregisterConversationHandler); - } - - void onTextMessage(std::shared_ptr& cm) - { - if (!conversationHandlers.empty()) { - receiveSubject->publish(cm); - } - } - - void sendTextMessage(std::shared_ptr& cm) - { - if (!conversationHandlers.empty()) { - sendSubject->publish(cm); - } - } - -private: - void listAvailableSubjects(ConversationHandlerPtr& plugin) - { - plugin->notifyStrMapSubject(false, sendSubject); - plugin->notifyStrMapSubject(true, receiveSubject); - } - -private: - std::list> conversationHandlers; - strMapSubjectPtr sendSubject - = std::make_shared>>(); - strMapSubjectPtr receiveSubject - = std::make_shared>>(); -}; -} // namespace jami diff --git a/src/plugin/jamipluginmanager.cpp b/src/plugin/jamipluginmanager.cpp index 2f080318f..267a2d4bb 100644 --- a/src/plugin/jamipluginmanager.cpp +++ b/src/plugin/jamipluginmanager.cpp @@ -25,8 +25,7 @@ #include #include #include - -// Manager +#include #include "manager.h" #include "preferences.h" @@ -34,9 +33,6 @@ extern "C" { #include } -#include -#include - #if defined(__arm__) #if defined(__ARM_ARCH_7A__) #define ABI "armeabi-v7a" @@ -147,46 +143,6 @@ uncompressJplFunction(const std::string& relativeFileName) return std::make_pair(false, std::string {""}); } -std::string -convertArrayToString(const Json::Value& jsonArray) -{ - std::string stringArray = ""; - - if (jsonArray.size()) { - for (unsigned i = 0; i < jsonArray.size() - 1; i++) { - if (jsonArray[i].isString()) { - stringArray += jsonArray[i].asString() + ","; - } else if (jsonArray[i].isArray()) { - stringArray += convertArrayToString(jsonArray[i]) + ","; - } - } - - unsigned lastIndex = jsonArray.size() - 1; - if (jsonArray[lastIndex].isString()) { - stringArray += jsonArray[lastIndex].asString(); - } - } - - return stringArray; -} - -std::map -parsePreferenceConfig(const Json::Value& jsonPreference, const std::string& type) -{ - std::map preferenceMap; - const auto& members = jsonPreference.getMemberNames(); - // Insert other fields - for (const auto& member : members) { - const Json::Value& value = jsonPreference[member]; - if (value.isString()) { - preferenceMap.emplace(member, jsonPreference[member].asString()); - } else if (value.isArray()) { - preferenceMap.emplace(member, convertArrayToString(jsonPreference[member])); - } - } - return preferenceMap; -} - std::map JamiPluginManager::getPluginDetails(const std::string& rootPath) { @@ -206,7 +162,7 @@ JamiPluginManager::getPluginDetails(const std::string& rootPath) } std::vector -JamiPluginManager::listAvailablePlugins() +JamiPluginManager::getInstalledPlugins() { std::string pluginsPath = fileutils::get_data_dir() + DIR_SEPARATOR_CH + "plugins"; std::vector pluginsPaths = fileutils::readDirectory(pluginsPath); @@ -329,30 +285,10 @@ JamiPluginManager::unloadPlugin(const std::string& rootPath) return false; } -void -JamiPluginManager::togglePlugin(const std::string& rootPath, bool toggle) -{ - // This function should not be used as is - // One should modify it to perform plugin install followed by load - // rootPath should be the jplpath! - try { - std::string soPath = getPluginDetails(rootPath).at("soPath"); - // remove the previous plugin object if it was registered - pm_.destroyPluginComponents(soPath); - // If toggle, register a new instance of the plugin - // function - if (toggle) { - pm_.callPluginInitFunction(soPath); - } - } catch (const std::exception& e) { - JAMI_ERR() << e.what(); - } -} - std::vector -JamiPluginManager::listLoadedPlugins() const +JamiPluginManager::getLoadedPlugins() const { - std::vector loadedSoPlugins = pm_.listLoadedPlugins(); + std::vector loadedSoPlugins = pm_.getLoadedPlugins(); std::vector loadedPlugins {}; loadedPlugins.reserve(loadedSoPlugins.size()); std::transform(loadedSoPlugins.begin(), @@ -365,81 +301,13 @@ JamiPluginManager::listLoadedPlugins() const std::vector> JamiPluginManager::getPluginPreferences(const std::string& rootPath) { - const std::string preferenceFilePath = getPreferencesConfigFilePath(rootPath); - std::ifstream file(preferenceFilePath); - Json::Value root; - Json::CharReaderBuilder rbuilder; - rbuilder["collectComments"] = false; - std::string errs; - std::set keys; - std::vector> preferences; - if (file) { - bool ok = Json::parseFromStream(rbuilder, file, &root, &errs); - if (ok && root.isArray()) { - for (unsigned i = 0; i < root.size(); i++) { - const Json::Value& jsonPreference = root[i]; - std::string category = jsonPreference.get("category", "NoCategory").asString(); - std::string type = jsonPreference.get("type", "None").asString(); - std::string key = jsonPreference.get("key", "None").asString(); - if (type != "None" && key != "None") { - if (keys.find(key) == keys.end()) { - auto preferenceAttributes = parsePreferenceConfig(jsonPreference, type); - // If the parsing of the attributes was successful, commit the map and the keys - auto defaultValue = preferenceAttributes.find("defaultValue"); - if (type == "Path" && defaultValue != preferenceAttributes.end()) { - defaultValue->second = rootPath + DIR_SEPARATOR_STR - + defaultValue->second; - } - - if (!preferenceAttributes.empty()) { - preferences.push_back(std::move(preferenceAttributes)); - keys.insert(key); - } - } - } - } - } else { - JAMI_ERR() << "PluginPreferencesParser:: Failed to parse preferences.json for plugin: " - << preferenceFilePath; - } - } - - return preferences; + return PluginPreferencesUtils::getPreferences(rootPath); } std::map JamiPluginManager::getPluginUserPreferencesValuesMap(const std::string& rootPath) { - const std::string preferencesValuesFilePath = pluginPreferencesValuesFilePath(rootPath); - std::ifstream file(preferencesValuesFilePath, std::ios::binary); - std::map rmap; - - // If file is accessible - if (file.good()) { - std::lock_guard guard(fileutils::getFileLock(preferencesValuesFilePath)); - // Get file size - std::string str; - file.seekg(0, std::ios::end); - size_t fileSize = static_cast(file.tellg()); - // If not empty - if (fileSize > 0) { - // Read whole file content and put it in the string str - str.reserve(static_cast(file.tellg())); - file.seekg(0, std::ios::beg); - str.assign((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - file.close(); - try { - // Unpack the string - msgpack::object_handle oh = msgpack::unpack(str.data(), str.size()); - // Deserialized object is valid during the msgpack::object_handle instance is alive. - msgpack::object deserialized = oh.get(); - deserialized.convert(rmap); - } catch (const std::exception& e) { - JAMI_ERR() << e.what(); - } - } - } - return rmap; + return PluginPreferencesUtils::getUserPreferencesValuesMap(rootPath); } bool @@ -447,23 +315,26 @@ JamiPluginManager::setPluginPreference(const std::string& rootPath, const std::string& key, const std::string& value) { - std::map pluginUserPreferencesMap = getPluginUserPreferencesValuesMap( - rootPath); - std::map pluginPreferencesMap = getPluginPreferencesValuesMap( + std::map pluginUserPreferencesMap + = PluginPreferencesUtils::getUserPreferencesValuesMap(rootPath); + std::map pluginPreferencesMap = PluginPreferencesUtils::getPreferencesValuesMap( rootPath); auto find = pluginPreferencesMap.find(key); if (find != pluginPreferencesMap.end()) { - std::vector> preferences = getPluginPreferences(rootPath); + std::vector> preferences = PluginPreferencesUtils::getPreferences( + rootPath); for (auto& preference : preferences) { if (!preference["key"].compare(key)) { - csm_.setPreference(key, value, preference["scope"]); + callsm_.setPreference(key, value, preference["scope"]); + chatsm_.setPreference(key, value, preference["scope"]); break; } } pluginUserPreferencesMap[key] = value; - const std::string preferencesValuesFilePath = pluginPreferencesValuesFilePath(rootPath); + const std::string preferencesValuesFilePath = PluginPreferencesUtils::valuesFilePath( + rootPath); std::ofstream fs(preferencesValuesFilePath, std::ios::binary); if (!fs.good()) { return false; @@ -483,39 +354,13 @@ JamiPluginManager::setPluginPreference(const std::string& rootPath, std::map JamiPluginManager::getPluginPreferencesValuesMap(const std::string& rootPath) { - std::map rmap; - - std::vector> preferences = getPluginPreferences(rootPath); - for (auto& preference : preferences) { - rmap[preference["key"]] = preference["defaultValue"]; - } - - for (const auto& pair : getPluginUserPreferencesValuesMap(rootPath)) { - rmap[pair.first] = pair.second; - } - return rmap; + return PluginPreferencesUtils::getPreferencesValuesMap(rootPath); } bool JamiPluginManager::resetPluginPreferencesValuesMap(const std::string& rootPath) { - bool returnValue = true; - std::map pluginPreferencesMap {}; - - const std::string preferencesValuesFilePath = pluginPreferencesValuesFilePath(rootPath); - std::ofstream fs(preferencesValuesFilePath, std::ios::binary); - if (!fs.good()) { - return false; - } - try { - std::lock_guard guard(fileutils::getFileLock(preferencesValuesFilePath)); - msgpack::pack(fs, pluginPreferencesMap); - } catch (const std::exception& e) { - returnValue = false; - JAMI_ERR() << e.what(); - } - - return returnValue; + return PluginPreferencesUtils::resetPreferencesValuesMap(rootPath); } std::map diff --git a/src/plugin/jamipluginmanager.h b/src/plugin/jamipluginmanager.h index 3083050ac..8f44beeaf 100644 --- a/src/plugin/jamipluginmanager.h +++ b/src/plugin/jamipluginmanager.h @@ -23,10 +23,11 @@ #include "fileutils.h" #include "archiver.h" #include "pluginmanager.h" +#include "pluginpreferencesutils.h" // Services #include "callservicesmanager.h" -#include "conversationservicesmanager.h" +#include "chatservicesmanager.h" #include #include @@ -39,8 +40,8 @@ class JamiPluginManager { public: JamiPluginManager() - : csm_ {pm_} - , convsm_ {pm_} + : callsm_ {pm_} + , chatsm_ {pm_} { registerServices(); } @@ -59,11 +60,11 @@ public: std::map getPluginDetails(const std::string& rootPath); /** - * @brief listAvailablePlugins + * @brief getInstalledPlugins * Lists available plugins with valid manifest files * @return list of plugin directory names */ - std::vector listAvailablePlugins(); + std::vector getInstalledPlugins(); /** * @brief installPlugin @@ -102,19 +103,10 @@ public: bool unloadPlugin(const std::string& rootPath); /** - * @brief togglePlugin - * @param rootPath of the plugin folder - * @param toggle: if true, register a new instance of the plugin - * else, remove the existing instance - * N.B: before adding a new instance, remove any existing one - */ - void togglePlugin(const std::string& rootPath, bool toggle); - - /** - * @brief listLoadedPlugins + * @brief getLoadedPlugins * @return vector of rootpaths of the loaded plugins */ - std::vector listLoadedPlugins() const; + std::vector getLoadedPlugins() const; std::vector> getPluginPreferences(const std::string& rootPath); @@ -126,10 +118,9 @@ public: bool resetPluginPreferencesValuesMap(const std::string& rootPath); -public: - CallServicesManager& getCallServicesManager() { return csm_; } + CallServicesManager& getCallServicesManager() { return callsm_; } - ConversationServicesManager& getConversationServicesManager() { return convsm_; } + ChatServicesManager& getChatServicesManager() { return chatsm_; } private: NON_COPYABLE(JamiPluginManager); @@ -191,7 +182,7 @@ private: */ std::string getPreferencesConfigFilePath(const std::string& rootPath) const { - return rootPath + DIR_SEPARATOR_CH + "data" + DIR_SEPARATOR_CH + "preferences.json"; + return PluginPreferencesUtils::getPreferencesConfigFilePath(rootPath); } /** @@ -203,18 +194,16 @@ private: */ std::string pluginPreferencesValuesFilePath(const std::string& rootPath) const { - return rootPath + DIR_SEPARATOR_CH + "preferences.msgpack"; + return PluginPreferencesUtils::valuesFilePath(rootPath); } void registerServices(); -private: PluginManager pm_; std::map> pluginDetailsMap_; // Services -private: - CallServicesManager csm_; - ConversationServicesManager convsm_; + CallServicesManager callsm_; + ChatServicesManager chatsm_; }; } // namespace jami diff --git a/src/plugin/mediahandler.h b/src/plugin/mediahandler.h index 7a25457da..81cb2c368 100644 --- a/src/plugin/mediahandler.h +++ b/src/plugin/mediahandler.h @@ -1,5 +1,7 @@ /* - * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * Copyright (C) 2020 Savoir-faire Linux Inc. + * + * Author: Aline Gondim Santos * * 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 @@ -15,7 +17,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ + #pragma once + #include "streamdata.h" #include "observer.h" #include diff --git a/src/plugin/pluginmanager.cpp b/src/plugin/pluginmanager.cpp index e63ad5c13..56a740665 100644 --- a/src/plugin/pluginmanager.cpp +++ b/src/plugin/pluginmanager.cpp @@ -79,7 +79,7 @@ PluginManager::load(const std::string& path) bool PluginManager::unload(const std::string& path) { - bool returnValue {false}; + bool returnValue{false}; destroyPluginComponents(path); PluginMap::iterator it = dynPluginMap_.find(path); if (it != dynPluginMap_.end()) { @@ -97,7 +97,7 @@ PluginManager::checkLoadedPlugin(const std::string& rootPath) const } std::vector -PluginManager::listLoadedPlugins() const +PluginManager::getLoadedPlugins() const { std::vector res {}; for (const auto& pair : dynPluginMap_) { @@ -375,5 +375,4 @@ PluginManager::registerObjectFactory_(const JAMI_PluginAPI* api, const char* typ const auto factory = reinterpret_cast(data); return manager->registerObjectFactory(type, *factory) ? 0 : -1; } - } // namespace jami diff --git a/src/plugin/pluginmanager.h b/src/plugin/pluginmanager.h index f2bcb408f..582bc6c99 100644 --- a/src/plugin/pluginmanager.h +++ b/src/plugin/pluginmanager.h @@ -85,10 +85,10 @@ public: bool unload(const std::string& path); /** - * @brief listLoadedPlugins + * @brief getLoadedPlugins * @return vector of strings of so files of the loaded plugins */ - std::vector listLoadedPlugins() const; + std::vector getLoadedPlugins() const; /** * @brief checkLoadedPlugin @@ -195,5 +195,4 @@ private: // references to plugins components, used for cleanup PluginComponentsMap pluginComponentsMap_ {}; }; - } // namespace jami diff --git a/src/plugin/pluginpreferencesutils.cpp b/src/plugin/pluginpreferencesutils.cpp new file mode 100644 index 000000000..2b4813191 --- /dev/null +++ b/src/plugin/pluginpreferencesutils.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2020 Savoir-faire Linux Inc. + * + * Author: Aline Gondim Santos + * + * 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 "pluginpreferencesutils.h" + +#include +#include +#include +#include "logger.h" +#include "fileutils.h" + +namespace jami { + +std::string +PluginPreferencesUtils::getPreferencesConfigFilePath(const std::string& rootPath) +{ + return rootPath + DIR_SEPARATOR_CH + "data" + DIR_SEPARATOR_CH + "preferences.json"; +} + +std::string +PluginPreferencesUtils::valuesFilePath(const std::string& rootPath) +{ + return rootPath + DIR_SEPARATOR_CH + "preferences.msgpack"; +} + +std::string +PluginPreferencesUtils::convertArrayToString(const Json::Value& jsonArray) +{ + std::string stringArray{}; + + if (jsonArray.size()) { + for (unsigned i = 0; i < jsonArray.size() - 1; i++) { + if (jsonArray[i].isString()) { + stringArray += jsonArray[i].asString() + ","; + } else if (jsonArray[i].isArray()) { + stringArray += convertArrayToString(jsonArray[i]) + ","; + } + } + + unsigned lastIndex = jsonArray.size() - 1; + if (jsonArray[lastIndex].isString()) { + stringArray += jsonArray[lastIndex].asString(); + } + } + + return stringArray; +} + +std::map +PluginPreferencesUtils::parsePreferenceConfig(const Json::Value& jsonPreference, const std::string& type) +{ + std::map preferenceMap; + const auto& members = jsonPreference.getMemberNames(); + // Insert other fields + for (const auto& member : members) { + const Json::Value& value = jsonPreference[member]; + if (value.isString()) { + preferenceMap.emplace(member, jsonPreference[member].asString()); + } else if (value.isArray()) { + preferenceMap.emplace(member, convertArrayToString(jsonPreference[member])); + } + } + return preferenceMap; +} + +std::vector> +PluginPreferencesUtils::getPreferences(const std::string& rootPath) +{ + const std::string preferenceFilePath = getPreferencesConfigFilePath(rootPath); + std::ifstream file(preferenceFilePath); + Json::Value root; + Json::CharReaderBuilder rbuilder; + rbuilder["collectComments"] = false; + std::string errs; + std::set keys; + std::vector> preferences; + if (file) { + bool ok = Json::parseFromStream(rbuilder, file, &root, &errs); + if (ok && root.isArray()) { + for (unsigned i = 0; i < root.size(); i++) { + const Json::Value& jsonPreference = root[i]; + std::string category = jsonPreference.get("category", "NoCategory").asString(); + std::string type = jsonPreference.get("type", "None").asString(); + std::string key = jsonPreference.get("key", "None").asString(); + if (type != "None" && key != "None") { + if (keys.find(key) == keys.end()) { + auto preferenceAttributes = parsePreferenceConfig(jsonPreference, type); + // If the parsing of the attributes was successful, commit the map and the keys + auto defaultValue = preferenceAttributes.find("defaultValue"); + if (type == "Path" && defaultValue != preferenceAttributes.end()) { + defaultValue->second = rootPath + DIR_SEPARATOR_STR + + defaultValue->second; + } + + if (!preferenceAttributes.empty()) { + preferences.push_back(std::move(preferenceAttributes)); + keys.insert(key); + } + } + } + } + } else { + JAMI_ERR() << "PluginPreferencesParser:: Failed to parse preferences.json for plugin: " + << preferenceFilePath; + } + } + + return preferences; +} + +std::map +PluginPreferencesUtils::getUserPreferencesValuesMap(const std::string& rootPath) +{ + const std::string preferencesValuesFilePath = valuesFilePath(rootPath); + std::ifstream file(preferencesValuesFilePath, std::ios::binary); + std::map rmap; + + // If file is accessible + if (file.good()) { + std::lock_guard guard(fileutils::getFileLock(preferencesValuesFilePath)); + // Get file size + std::string str; + file.seekg(0, std::ios::end); + size_t fileSize = static_cast(file.tellg()); + // If not empty + if (fileSize > 0) { + // Read whole file content and put it in the string str + str.reserve(static_cast(file.tellg())); + file.seekg(0, std::ios::beg); + str.assign((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + try { + // Unpack the string + msgpack::object_handle oh = msgpack::unpack(str.data(), str.size()); + // Deserialized object is valid during the msgpack::object_handle instance is alive. + msgpack::object deserialized = oh.get(); + deserialized.convert(rmap); + } catch (const std::exception& e) { + JAMI_ERR() << e.what(); + } + } + } + return rmap; +} + +std::map +PluginPreferencesUtils::getPreferencesValuesMap(const std::string& rootPath) +{ + std::map rmap; + + std::vector> preferences = getPreferences( + rootPath); + for (auto& preference : preferences) { + rmap[preference["key"]] = preference["defaultValue"]; + } + + for (const auto& pair : getUserPreferencesValuesMap(rootPath)) { + rmap[pair.first] = pair.second; + } + + rmap.emplace("always", "0"); + + return rmap; +} + +bool +PluginPreferencesUtils::resetPreferencesValuesMap(const std::string& rootPath) +{ + bool returnValue = true; + std::map pluginPreferencesMap {}; + + const std::string preferencesValuesFilePath = valuesFilePath(rootPath); + std::ofstream fs(preferencesValuesFilePath, std::ios::binary); + if (!fs.good()) { + return false; + } + try { + std::lock_guard guard(fileutils::getFileLock(preferencesValuesFilePath)); + msgpack::pack(fs, pluginPreferencesMap); + } catch (const std::exception& e) { + returnValue = false; + JAMI_ERR() << e.what(); + } + + return returnValue; +} +} // namespace jami diff --git a/src/plugin/pluginpreferencesutils.h b/src/plugin/pluginpreferencesutils.h new file mode 100644 index 000000000..9a7ce1a98 --- /dev/null +++ b/src/plugin/pluginpreferencesutils.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 Savoir-faire Linux Inc. + * + * Author: Aline Gondim Santos + * + * 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. + */ + +#pragma once + +#include +#include + +namespace jami { + +class PluginPreferencesUtils { +public: + static std::string getPreferencesConfigFilePath(const std::string& rootPath); + + static std::string valuesFilePath(const std::string& rootPath); + + static std::string convertArrayToString(const Json::Value& jsonArray); + + static std::map parsePreferenceConfig(const Json::Value& jsonPreference, + const std::string& type); + + static std::vector> getPreferences( + const std::string& rootPath); + + static std::map getUserPreferencesValuesMap( + const std::string& rootPath); + + static std::map getPreferencesValuesMap(const std::string& rootPath); + + static bool resetPreferencesValuesMap(const std::string& rootPath); +private: + PluginPreferencesUtils() {} + ~PluginPreferencesUtils() {} +}; +} // namespace jami diff --git a/src/plugin/streamdata.h b/src/plugin/streamdata.h index 522a67b3e..9746bf428 100644 --- a/src/plugin/streamdata.h +++ b/src/plugin/streamdata.h @@ -1,5 +1,7 @@ /* - * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * Copyright (C) 2020 Savoir-faire Linux Inc. + * + * Author: Aline Gondim Santos * * 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 @@ -17,10 +19,11 @@ */ #pragma once #include +#include enum class StreamType { audio, video }; -struct StreamData +struct StreamData // for calls { StreamData(const std::string& i, bool d, StreamType&& t, const std::string& s) : id {std::move(i)} @@ -33,3 +36,24 @@ struct StreamData const StreamType type; const std::string source; }; + +struct JamiMessage // for chat +{ + JamiMessage(const std::string& accId, + const std::string& pId, + bool direction, + std::map& dataMap, + bool pPlugin) + : accountId {accId} + , peerId {pId} + , direction {direction} + , data {dataMap} + , fromPlugin {pPlugin} + {} + + std::string accountId; // accountID + std::string peerId; // peer + const bool direction; // 0 -> send; 1 -> received + std::map data; + bool fromPlugin; +}; \ No newline at end of file diff --git a/src/sip/sipaccountbase.cpp b/src/sip/sipaccountbase.cpp index 9f6983d2e..3749b37fd 100644 --- a/src/sip/sipaccountbase.cpp +++ b/src/sip/sipaccountbase.cpp @@ -51,6 +51,7 @@ #include "manager.h" #ifdef ENABLE_PLUGIN #include "plugin/jamipluginmanager.h" +#include "plugin/streamdata.h" #endif namespace jami { @@ -562,16 +563,16 @@ SIPAccountBase::onTextMessage(const std::string& id, } #ifdef ENABLE_PLUGIN - auto& convManager - = jami::Manager::instance().getJamiPluginManager().getConversationServicesManager(); - std::shared_ptr cm = std::make_shared( - from, accountID_, const_cast&>(payloads)); - convManager.onTextMessage(cm); - emitSignal(accountID_, id, from, cm->data_); + auto& pluginChatManager + = jami::Manager::instance().getJamiPluginManager().getChatServicesManager(); + std::shared_ptr cm = std::make_shared( + accountID_, from, true, const_cast&>(payloads), false); + pluginChatManager.publishMessage(cm); + emitSignal(accountID_, id, from, cm->data); DRing::Message message; - message.from = cm->author_; - message.payloads = cm->data_; + message.from = from; + message.payloads = cm->data; #else emitSignal(accountID_, id, from, payloads); diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp index 3cb910f42..7ff93c7bc 100644 --- a/src/sip/sipcall.cpp +++ b/src/sip/sipcall.cpp @@ -140,14 +140,14 @@ SIPCall::createCallAVStreams() // Preview if (auto& localAudio = avformatrtp_->getAudioLocal()) { auto previewSubject = std::make_shared(audioMap); - StreamData microStreamData {getCallId(), 0, StreamType::audio, getPeerNumber()}; + StreamData microStreamData {getCallId(), false, StreamType::audio, getPeerNumber()}; createCallAVStream(microStreamData, *localAudio, previewSubject); } // Receive if (auto& audioReceive = avformatrtp_->getAudioReceive()) { auto receiveSubject = std::make_shared(audioMap); - StreamData phoneStreamData {getCallId(), 1, StreamType::audio, getPeerNumber()}; + StreamData phoneStreamData {getCallId(), true, StreamType::audio, getPeerNumber()}; createCallAVStream(phoneStreamData, (AVMediaStream&) *audioReceive, receiveSubject); } #ifdef ENABLE_VIDEO @@ -162,14 +162,14 @@ SIPCall::createCallAVStreams() // Preview if (auto& videoPreview = videortp_->getVideoLocal()) { auto previewSubject = std::make_shared(videoMap); - StreamData previewStreamData {getCallId(), 0, StreamType::video, getPeerNumber()}; + StreamData previewStreamData {getCallId(), false, StreamType::video, getPeerNumber()}; createCallAVStream(previewStreamData, *videoPreview, previewSubject); } // Receive if (auto& videoReceive = videortp_->getVideoReceive()) { auto receiveSubject = std::make_shared(videoMap); - StreamData receiveStreamData {getCallId(), 1, StreamType::video, getPeerNumber()}; + StreamData receiveStreamData {getCallId(), true, StreamType::video, getPeerNumber()}; createCallAVStream(receiveStreamData, *videoReceive, receiveSubject); } }