From 75ec461623bee3a808f5a88a4a66ade729f74bc3 Mon Sep 17 00:00:00 2001
From: Pierre Lespagnol <pierre.lespagnol@savoirfairelinux.com>
Date: Mon, 9 Nov 2020 11:02:27 -0500
Subject: [PATCH] conference: host can add or remove moderator

Change-Id: Idc4c6db249de7247d02c7cabb897d638c69674f3
---
 bin/dbus/cx.ring.Ring.CallManager.xml |  6 ++++
 bin/dbus/dbuscallmanager.cpp          |  6 ++++
 bin/dbus/dbuscallmanager.h            |  1 +
 bin/jni/callmanager.i                 |  1 +
 bin/nodejs/callmanager.i              |  1 +
 configure.ac                          |  2 +-
 src/client/callmanager.cpp            |  8 +++++
 src/conference.cpp                    | 47 +++++++++++++++++++++++++--
 src/conference.h                      |  5 ++-
 src/dring/callmanager_interface.h     |  1 +
 src/manager.cpp                       |  9 +++++
 src/manager.h                         |  2 ++
 12 files changed, 84 insertions(+), 5 deletions(-)

diff --git a/bin/dbus/cx.ring.Ring.CallManager.xml b/bin/dbus/cx.ring.Ring.CallManager.xml
index 7ae32d13dc..febb490e8a 100644
--- a/bin/dbus/cx.ring.Ring.CallManager.xml
+++ b/bin/dbus/cx.ring.Ring.CallManager.xml
@@ -251,6 +251,12 @@
             <arg type="s" name="callId" direction="in"/>
         </method>
 
+        <method name="setModerator" tp:name-for-bindings="setModerator">
+            <tp:added version="9.6.0"/>
+            <arg type="s" name="confId" direction="in"/>
+            <arg type="s" name="peerId" direction="in"/>
+            <arg type="b" name="state" direction="in"/>
+        </method>
 
         <method name="isConferenceParticipant" tp:name-for-bindings="isConferenceParticipant">
             <arg type="s" name="callID" direction="in"/>
diff --git a/bin/dbus/dbuscallmanager.cpp b/bin/dbus/dbuscallmanager.cpp
index ff4da48846..9ccbc71462 100644
--- a/bin/dbus/dbuscallmanager.cpp
+++ b/bin/dbus/dbuscallmanager.cpp
@@ -297,3 +297,9 @@ DBusCallManager::stopSmartInfo()
 {
     DRing::stopSmartInfo();
 }
+
+void
+DBusCallManager::setModerator(const std::string& confId, const std::string& peerId, const bool& state)
+{
+    DRing::setModerator(confId, peerId, state);
+}
diff --git a/bin/dbus/dbuscallmanager.h b/bin/dbus/dbuscallmanager.h
index bb3f547c13..ecbe7f64e2 100644
--- a/bin/dbus/dbuscallmanager.h
+++ b/bin/dbus/dbuscallmanager.h
@@ -101,6 +101,7 @@ class DRING_PUBLIC DBusCallManager :
         void sendTextMessage(const std::string& callID, const std::map<std::string, std::string>& messages, const bool& isMixed);
         void startSmartInfo(const uint32_t& refreshTimeMs);
         void stopSmartInfo();
+        void setModerator(const std::string& confId, const std::string& peerId, const bool& state);
 };
 
 #endif // __RING_CALLMANAGER_H__
diff --git a/bin/jni/callmanager.i b/bin/jni/callmanager.i
index 077e6b4b8d..d0167c43c5 100644
--- a/bin/jni/callmanager.i
+++ b/bin/jni/callmanager.i
@@ -92,6 +92,7 @@ std::vector<std::string> getDisplayNames(const std::string& confID);
 std::string getConferenceId(const std::string& callID);
 std::map<std::string, std::string> getConferenceDetails(const std::string& callID);
 std::vector<std::map<std::string, std::string>> getConferenceInfos(const std::string& confId);
+void setModerator(const std::string& confId, const std::string& peerId, const bool& state);
 
 /* File Playback methods */
 bool startRecordedFilePlayback(const std::string& filepath);
diff --git a/bin/nodejs/callmanager.i b/bin/nodejs/callmanager.i
index b213005a0b..4b5fdc40fc 100644
--- a/bin/nodejs/callmanager.i
+++ b/bin/nodejs/callmanager.i
@@ -90,6 +90,7 @@ std::vector<std::string> getDisplayNames(const std::string& confID);
 std::string getConferenceId(const std::string& callID);
 std::map<std::string, std::string> getConferenceDetails(const std::string& callID);
 std::vector<std::map<std::string, std::string>> getConferenceInfos(const std::string& confId);
+void setModerator(const std::string& confId, const std::string& peerId, const bool& state);
 
 /* File Playback methods */
 bool startRecordedFilePlayback(const std::string& filepath);
diff --git a/configure.ac b/configure.ac
index 604c07c7c0..c59ca27264 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@ dnl Jami - configure.ac for automake 1.9 and autoconf 2.59
 
 dnl Process this file with autoconf to produce a configure script.
 AC_PREREQ([2.65])
-AC_INIT([Jami Daemon],[9.5.0],[ring@gnu.org],[jami])
+AC_INIT([Jami Daemon],[9.6.0],[ring@gnu.org],[jami])
 
 AC_COPYRIGHT([[Copyright (c) Savoir-faire Linux 2004-2020]])
 AC_REVISION([$Revision$])
diff --git a/src/client/callmanager.cpp b/src/client/callmanager.cpp
index 12616ef286..bd7b63bc7d 100644
--- a/src/client/callmanager.cpp
+++ b/src/client/callmanager.cpp
@@ -343,4 +343,12 @@ sendTextMessage(const std::string& callID,
     });
 }
 
+void
+setModerator(const std::string& confId,
+             const std::string& peerId,
+             const bool& state)
+{
+    jami::Manager::instance().setModerator(confId, peerId, state);
+}
+
 } // namespace DRing
diff --git a/src/conference.cpp b/src/conference.cpp
index 4aa222e352..88550b8eeb 100644
--- a/src/conference.cpp
+++ b/src/conference.cpp
@@ -53,11 +53,10 @@ Conference::Conference()
     // conference master. In the future, this should be
     // retrieven with another way
     auto accounts = jami::Manager::instance().getAllAccounts<JamiAccount>();
-    moderators_.reserve(accounts.size());
     for (const auto& account : accounts) {
         if (!account)
             continue;
-        moderators_.emplace_back(account->getUsername());
+        moderators_.emplace(account->getUsername());
     }
 
 #ifdef ENABLE_VIDEO
@@ -493,7 +492,7 @@ Conference::onConfOrder(const std::string& callId, const std::string& confOrder)
         auto uri = call->getPeerNumber();
         auto separator = uri.find('@');
         if (separator != std::string::npos)
-            uri = uri.substr(0, separator - 1);
+            uri = uri.substr(0, separator);
         if (!isModerator(uri)) {
             JAMI_WARN("Received conference order from a non master (%s)", uri.c_str());
             return;
@@ -527,4 +526,46 @@ Conference::isModerator(const std::string& uri) const
            != moderators_.end();
 }
 
+void
+Conference::setModerator(const std::string& uri, const bool& state)
+{
+    for (const auto& p : participants_) {
+        if (auto call = Manager::instance().callFactory.getCall<SIPCall>(p)) {
+            auto partURI = call->getPeerNumber();
+            auto separator = partURI.find('@');
+            if (separator != std::string::npos)
+                partURI = partURI.substr(0, separator);
+            if (partURI == uri) {
+                if (state and not isModerator(uri)) {
+                    JAMI_DBG("Add %s as moderator", partURI.c_str());
+                    moderators_.emplace(uri);
+                    updateModerators();
+                } else if (not state and isModerator(uri)) {
+                    JAMI_DBG("Remove %s as moderator", partURI.c_str());
+                    moderators_.erase(uri);
+                    updateModerators();
+                }
+                return;
+            }
+        }
+    }
+    JAMI_WARN("Fail to set %s as moderator (participant not found)", uri.c_str());
+}
+
+void
+Conference::updateModerators()
+{
+    {
+        std::lock_guard<std::mutex> lk2(confInfoMutex_);
+        for (auto& info : confInfo_) {
+            auto uri = info.uri;
+            auto separator = uri.find('@');
+            if (separator != std::string::npos)
+                uri = uri.substr(0, separator);
+            info.isModerator = isModerator(uri);
+        }
+    }
+    sendConferenceInfos();
+}
+
 } // namespace jami
diff --git a/src/conference.h b/src/conference.h
index 709274b236..51c338aee5 100644
--- a/src/conference.h
+++ b/src/conference.h
@@ -202,6 +202,7 @@ public:
     void detachVideo(Observable<std::shared_ptr<MediaFrame>>* frame);
 
     void onConfOrder(const std::string& callId, const std::string& order);
+    void setModerator(const std::string& uri, const bool& state);
 
 #ifdef ENABLE_VIDEO
     std::shared_ptr<video::VideoMixer> getVideoMixer();
@@ -240,10 +241,12 @@ private:
 #endif
 
     std::shared_ptr<jami::AudioInput> audioMixer_;
-    std::vector<std::string> moderators_ {};
+    std::set<std::string> moderators_ {};
 
     void initRecorder(std::shared_ptr<MediaRecorder>& rec);
     void deinitRecorder(std::shared_ptr<MediaRecorder>& rec);
+
+    void updateModerators();
 };
 
 } // namespace jami
diff --git a/src/dring/callmanager_interface.h b/src/dring/callmanager_interface.h
index 41baed45c6..0fb9873aa1 100644
--- a/src/dring/callmanager_interface.h
+++ b/src/dring/callmanager_interface.h
@@ -77,6 +77,7 @@ DRING_PUBLIC std::string getConferenceId(const std::string& callID);
 DRING_PUBLIC std::map<std::string, std::string> getConferenceDetails(const std::string& callID);
 DRING_PUBLIC std::vector<std::map<std::string, std::string>> getConferenceInfos(
     const std::string& confId);
+DRING_PUBLIC void setModerator(const std::string& confId, const std::string& peerId, const bool& state);
 
 /* Statistic related methods */
 DRING_PUBLIC void startSmartInfo(uint32_t refreshTimeMs);
diff --git a/src/manager.cpp b/src/manager.cpp
index 413cd37037..b92a8b592b 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -3222,4 +3222,13 @@ Manager::getNearbyPeers(const std::string& accountID)
     return {};
 }
 
+void
+Manager::setModerator(const std::string& confId, const std::string& peerId, const bool& state)
+{
+    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());
+}
+
 } // namespace jami
diff --git a/src/manager.h b/src/manager.h
index db7420e28b..759f78fc2a 100644
--- a/src/manager.h
+++ b/src/manager.h
@@ -934,6 +934,8 @@ public:
     JamiPluginManager& getJamiPluginManager() const;
 #endif
 
+    void setModerator(const std::string& confId, const std::string& peerId, const bool& state);
+
 private:
     Manager();
     ~Manager();
-- 
GitLab