Line data Source code
1 : /*
2 : * Copyright (C) 2004-2026 Savoir-faire Linux Inc.
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "conference_protocol.h"
19 :
20 : #include "logger.h"
21 : #include "string_utils.h"
22 :
23 : namespace jami {
24 :
25 : namespace ProtocolKeys {
26 :
27 : constexpr static const char* PROTOVERSION = "version";
28 : constexpr static const char* LAYOUT = "layout";
29 : // V0
30 : constexpr static const char* HANDRAISED = "handRaised";
31 : constexpr static const char* HANDSTATE = "handState";
32 : constexpr static const char* ACTIVEPART = "activeParticipant";
33 : constexpr static const char* MUTEPART = "muteParticipant";
34 : constexpr static const char* MUTESTATE = "muteState";
35 : constexpr static const char* HANGUPPART = "hangupParticipant";
36 : // V1
37 : constexpr static const char* DEVICES = "devices";
38 : constexpr static const char* MEDIAS = "medias";
39 : constexpr static const char* RAISEHAND = "raiseHand";
40 : constexpr static const char* HANGUP = "hangup";
41 : constexpr static const char* ACTIVE = "active";
42 : constexpr static const char* MUTEAUDIO = "muteAudio";
43 : // Future
44 : constexpr static const char* MUTEVIDEO = "muteVideo";
45 : constexpr static const char* VOICEACTIVITY = "voiceActivity";
46 :
47 : } // namespace ProtocolKeys
48 :
49 : void
50 6 : ConfProtocolParser::parse()
51 : {
52 6 : if (data_.isMember(ProtocolKeys::PROTOVERSION)) {
53 6 : uint32_t version = data_[ProtocolKeys::PROTOVERSION].asUInt();
54 6 : if (version_)
55 6 : version_(version);
56 6 : if (version == 1) {
57 6 : parseV1();
58 : } else {
59 0 : JAMI_WARNING("Unsupported protocol version {}", version);
60 : }
61 : } else {
62 0 : parseV0();
63 : }
64 6 : }
65 :
66 : void
67 0 : ConfProtocolParser::parseV0()
68 : {
69 0 : if (!checkAuthorization_ || !raiseHandUri_ || !setLayout_ || !setActiveParticipant_ || !muteParticipant_
70 0 : || !kickParticipant_) {
71 0 : JAMI_ERROR("Missing methods for ConfProtocolParser");
72 0 : return;
73 : }
74 : // Check if all lambdas set
75 0 : auto isPeerModerator = checkAuthorization_(peerId_);
76 0 : if (data_.isMember(ProtocolKeys::HANDRAISED)) {
77 0 : auto state = data_[ProtocolKeys::HANDSTATE].asString() == TRUE_STR;
78 0 : auto uri = data_[ProtocolKeys::HANDRAISED].asString();
79 0 : if (peerId_ == uri) {
80 : // In this case, the user want to change their state
81 0 : raiseHandUri_(uri, state);
82 0 : } else if (!state && isPeerModerator) {
83 : // In this case a moderator can lower the hand
84 0 : raiseHandUri_(uri, state);
85 : }
86 0 : }
87 0 : if (!isPeerModerator) {
88 0 : JAMI_WARNING("Received conference order from a non-moderator ({})", peerId_);
89 0 : return;
90 : }
91 0 : if (data_.isMember(ProtocolKeys::LAYOUT)) {
92 0 : setLayout_(data_[ProtocolKeys::LAYOUT].asInt());
93 : }
94 0 : if (data_.isMember(ProtocolKeys::ACTIVEPART)) {
95 0 : setActiveParticipant_(data_[ProtocolKeys::ACTIVEPART].asString());
96 : }
97 0 : if (data_.isMember(ProtocolKeys::MUTEPART) && data_.isMember(ProtocolKeys::MUTESTATE)) {
98 0 : muteParticipant_(data_[ProtocolKeys::MUTEPART].asString(),
99 0 : data_[ProtocolKeys::MUTESTATE].asString() == TRUE_STR);
100 : }
101 0 : if (data_.isMember(ProtocolKeys::HANGUPPART)) {
102 0 : kickParticipant_(data_[ProtocolKeys::HANGUPPART].asString());
103 : }
104 : }
105 :
106 : void
107 6 : ConfProtocolParser::parseV1()
108 : {
109 12 : if (!checkAuthorization_ || !setLayout_ || !raiseHand_ || !hangupParticipant_ || !muteStreamAudio_
110 12 : || !setActiveStream_) {
111 0 : JAMI_ERROR("Missing methods for ConfProtocolParser");
112 0 : return;
113 : }
114 :
115 6 : auto isPeerModerator = checkAuthorization_(peerId_);
116 30 : for (Json::Value::const_iterator itr = data_.begin(); itr != data_.end(); itr++) {
117 12 : auto key = itr.key();
118 12 : if (key == ProtocolKeys::LAYOUT) {
119 : // Note: can be removed soon
120 0 : if (isPeerModerator)
121 0 : setLayout_(itr->asInt());
122 12 : } else if (key == ProtocolKeys::PROTOVERSION) {
123 6 : continue;
124 : } else {
125 6 : auto accValue = *itr;
126 6 : if (accValue.isMember(ProtocolKeys::DEVICES)) {
127 6 : auto accountUri = key.asString();
128 6 : for (Json::Value::const_iterator itrd = accValue[ProtocolKeys::DEVICES].begin();
129 12 : itrd != accValue[ProtocolKeys::DEVICES].end();
130 6 : itrd++) {
131 6 : auto deviceId = itrd.key().asString();
132 6 : const auto& deviceValue = *itrd;
133 6 : if (deviceValue.isMember(ProtocolKeys::RAISEHAND)) {
134 6 : auto newState = deviceValue[ProtocolKeys::RAISEHAND].asBool();
135 6 : if (peerId_ == accountUri || (!newState && isPeerModerator))
136 5 : raiseHand_(deviceId, newState);
137 : }
138 6 : if (isPeerModerator && deviceValue.isMember(ProtocolKeys::HANGUP)) {
139 0 : hangupParticipant_(accountUri, deviceId);
140 : }
141 6 : if (deviceValue.isMember(ProtocolKeys::MEDIAS)) {
142 0 : for (Json::Value::const_iterator itrm = accValue[ProtocolKeys::MEDIAS].begin();
143 0 : itrm != accValue[ProtocolKeys::MEDIAS].end();
144 0 : itrm++) {
145 0 : auto streamId = itrm.key().asString();
146 0 : const auto& mediaVal = *itrm;
147 0 : if (mediaVal.isMember(ProtocolKeys::VOICEACTIVITY)) {
148 0 : voiceActivity_(streamId, mediaVal[ProtocolKeys::VOICEACTIVITY].asBool());
149 : }
150 0 : if (isPeerModerator) {
151 0 : if (mediaVal.isMember(ProtocolKeys::MUTEVIDEO) && !muteStreamVideo_) {
152 : // Note: For now, it's not implemented so not set
153 0 : muteStreamVideo_(accountUri,
154 : deviceId,
155 : streamId,
156 0 : mediaVal[ProtocolKeys::MUTEVIDEO].asBool());
157 : }
158 0 : if (mediaVal.isMember(ProtocolKeys::MUTEAUDIO)) {
159 0 : muteStreamAudio_(accountUri,
160 : deviceId,
161 : streamId,
162 0 : mediaVal[ProtocolKeys::MUTEAUDIO].asBool());
163 : }
164 0 : if (mediaVal.isMember(ProtocolKeys::ACTIVE)) {
165 0 : setActiveStream_(streamId, mediaVal[ProtocolKeys::ACTIVE].asBool());
166 : }
167 : }
168 0 : }
169 : }
170 6 : }
171 6 : }
172 6 : }
173 12 : }
174 : }
175 :
176 : } // namespace jami
|