Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 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 : #include <vector>
18 : #include <cstring>
19 :
20 : #include "callmanager_interface.h"
21 : #include "call_factory.h"
22 : #include "client/ring_signal.h"
23 :
24 : #include "sip/siptransport.h"
25 : #include "sip/sipvoiplink.h"
26 : #include "sip/sipcall.h"
27 : #include "audio/audiolayer.h"
28 : #include "media/media_attribute.h"
29 : #include "string_utils.h"
30 :
31 : #include "logger.h"
32 : #include "manager.h"
33 : #include "jamidht/jamiaccount.h"
34 :
35 : namespace libjami {
36 :
37 : void
38 0 : registerCallHandlers(const std::map<std::string, std::shared_ptr<CallbackWrapperBase>>& handlers)
39 : {
40 0 : registerSignalHandlers(handlers);
41 0 : }
42 :
43 : std::string
44 0 : placeCall(const std::string& accountId, const std::string& to)
45 : {
46 : // TODO. Remove ASAP.
47 0 : JAMI_WARN("This API is deprecated, use placeCallWithMedia() instead");
48 0 : return placeCallWithMedia(accountId, to, {});
49 : }
50 :
51 : std::string
52 117 : placeCallWithMedia(const std::string& accountId,
53 : const std::string& to,
54 : const std::vector<libjami::MediaMap>& mediaList)
55 : {
56 : // Check if a destination number is available
57 117 : if (to.empty()) {
58 0 : JAMI_DBG("No number entered - Call aborted");
59 0 : return {};
60 : } else {
61 117 : return jami::Manager::instance().outgoingCall(accountId, to, mediaList);
62 : }
63 : }
64 :
65 : bool
66 18 : requestMediaChange(const std::string& accountId,
67 : const std::string& callId,
68 : const std::vector<libjami::MediaMap>& mediaList)
69 : {
70 18 : if (auto account = jami::Manager::instance().getAccount(accountId)) {
71 18 : if (auto call = account->getCall(callId)) {
72 15 : return call->requestMediaChange(mediaList);
73 3 : } else if (auto conf = account->getConference(callId)) {
74 3 : return conf->requestMediaChange(mediaList);
75 21 : }
76 18 : }
77 0 : return false;
78 : }
79 :
80 : bool
81 1 : refuse(const std::string& accountId, const std::string& callId)
82 : {
83 1 : return jami::Manager::instance().refuseCall(accountId, callId);
84 : }
85 :
86 : bool
87 0 : accept(const std::string& accountId, const std::string& callId)
88 : {
89 0 : return jami::Manager::instance().answerCall(accountId, callId);
90 : }
91 :
92 : bool
93 20 : acceptWithMedia(const std::string& accountId,
94 : const std::string& callId,
95 : const std::vector<libjami::MediaMap>& mediaList)
96 : {
97 20 : return jami::Manager::instance().answerCall(accountId, callId, mediaList);
98 : }
99 :
100 : bool
101 3 : answerMediaChangeRequest(const std::string& accountId,
102 : const std::string& callId,
103 : const std::vector<libjami::MediaMap>& mediaList)
104 : {
105 3 : if (auto account = jami::Manager::instance().getAccount(accountId))
106 3 : if (auto call = account->getCall(callId)) {
107 : try {
108 3 : call->answerMediaChangeRequest(mediaList);
109 3 : return true;
110 0 : } catch (const std::runtime_error& e) {
111 0 : JAMI_ERR("%s", e.what());
112 0 : }
113 6 : }
114 0 : return false;
115 : }
116 :
117 : bool
118 16 : hangUp(const std::string& accountId, const std::string& callId)
119 : {
120 16 : return jami::Manager::instance().hangupCall(accountId, callId);
121 : }
122 :
123 : bool
124 0 : hangUpConference(const std::string& accountId, const std::string& confId)
125 : {
126 0 : return jami::Manager::instance().hangupConference(accountId, confId);
127 : }
128 :
129 : bool
130 3 : hold(const std::string& accountId, const std::string& callId)
131 : {
132 3 : return jami::Manager::instance().onHoldCall(accountId, callId);
133 : }
134 :
135 : bool
136 3 : unhold(const std::string& accountId, const std::string& callId)
137 : {
138 3 : return jami::Manager::instance().offHoldCall(accountId, callId);
139 : }
140 :
141 : bool
142 0 : muteLocalMedia(const std::string& accountId,
143 : const std::string& callId,
144 : const std::string& mediaType,
145 : bool mute)
146 : {
147 0 : if (auto account = jami::Manager::instance().getAccount(accountId)) {
148 0 : if (auto call = account->getCall(callId)) {
149 0 : JAMI_DBG("Muting [%s] for call %s", mediaType.c_str(), callId.c_str());
150 0 : call->muteMedia(mediaType, mute);
151 0 : return true;
152 0 : } else if (auto conf = account->getConference(callId)) {
153 0 : JAMI_DBG("Muting local host [%s] for conference %s", mediaType.c_str(), callId.c_str());
154 0 : conf->muteLocalHost(mute, mediaType);
155 0 : return true;
156 : } else {
157 0 : JAMI_WARN("ID %s doesn't match any call or conference", callId.c_str());
158 0 : }
159 0 : }
160 0 : return false;
161 : }
162 :
163 : bool
164 2 : transfer(const std::string& accountId, const std::string& callId, const std::string& to)
165 : {
166 2 : return jami::Manager::instance().transferCall(accountId, callId, to);
167 : }
168 :
169 : bool
170 0 : attendedTransfer(const std::string& accountId,
171 : const std::string& transferID,
172 : const std::string& targetID)
173 : {
174 0 : if (auto account = jami::Manager::instance().getAccount(accountId))
175 0 : if (auto call = account->getCall(transferID))
176 0 : return call->attendedTransfer(targetID);
177 0 : return false;
178 : }
179 :
180 : bool
181 0 : joinParticipant(const std::string& accountId,
182 : const std::string& sel_callId,
183 : const std::string& account2Id,
184 : const std::string& drag_callId)
185 : {
186 0 : return jami::Manager::instance().joinParticipant(accountId, sel_callId, account2Id, drag_callId);
187 : }
188 :
189 : void
190 0 : createConfFromParticipantList(const std::string& accountId,
191 : const std::vector<std::string>& participants)
192 : {
193 0 : jami::Manager::instance().createConfFromParticipantList(accountId, participants);
194 0 : }
195 :
196 : void
197 0 : setConferenceLayout(const std::string& accountId, const std::string& confId, uint32_t layout)
198 : {
199 0 : if (const auto account = jami::Manager::instance().getAccount(accountId)) {
200 0 : if (auto conf = account->getConference(confId)) {
201 0 : conf->setLayout(layout);
202 0 : } else if (auto call = account->getCall(confId)) {
203 0 : Json::Value root;
204 0 : root["layout"] = layout;
205 0 : call->sendConfOrder(root);
206 0 : }
207 0 : }
208 0 : }
209 :
210 : bool
211 0 : isConferenceParticipant(const std::string& accountId, const std::string& callId)
212 : {
213 0 : if (auto account = jami::Manager::instance().getAccount(accountId))
214 0 : if (auto call = account->getCall(callId))
215 0 : return call->isConferenceParticipant();
216 0 : return false;
217 : }
218 :
219 : void
220 0 : startSmartInfo(uint32_t refreshTimeMs)
221 : {
222 0 : JAMI_WARNING("startSmartInfo is deprecated and does nothing.");
223 0 : }
224 :
225 : void
226 0 : stopSmartInfo()
227 : {
228 0 : JAMI_WARNING("stopSmartInfo is deprecated and does nothing.");
229 0 : }
230 :
231 : bool
232 0 : addParticipant(const std::string& accountId,
233 : const std::string& callId,
234 : const std::string& account2Id,
235 : const std::string& confId)
236 : {
237 0 : return jami::Manager::instance().addSubCall(accountId, callId, account2Id, confId);
238 : }
239 :
240 : bool
241 0 : addMainParticipant(const std::string& accountId, const std::string& confId)
242 : {
243 0 : return jami::Manager::instance().addMainParticipant(accountId, confId);
244 : }
245 :
246 : bool
247 0 : detachLocalParticipant()
248 : {
249 0 : return jami::Manager::instance().detachHost();
250 : }
251 :
252 : bool
253 0 : detachParticipant(const std::string&, const std::string& callId)
254 : {
255 0 : return jami::Manager::instance().detachParticipant(callId);
256 : }
257 :
258 : bool
259 0 : joinConference(const std::string& accountId,
260 : const std::string& sel_confId,
261 : const std::string& account2Id,
262 : const std::string& drag_confId)
263 : {
264 0 : return jami::Manager::instance().joinConference(accountId, sel_confId, account2Id, drag_confId);
265 : }
266 :
267 : bool
268 0 : holdConference(const std::string& accountId, const std::string& confId)
269 : {
270 0 : return jami::Manager::instance().holdConference(accountId, confId);
271 : }
272 :
273 : bool
274 0 : unholdConference(const std::string& accountId, const std::string& confId)
275 : {
276 0 : return jami::Manager::instance().unHoldConference(accountId, confId);
277 : }
278 :
279 : std::map<std::string, std::string>
280 0 : getConferenceDetails(const std::string& accountId, const std::string& confId)
281 : {
282 0 : if (const auto account = jami::Manager::instance().getAccount(accountId))
283 0 : if (auto conf = account->getConference(confId))
284 : return {{"ID", confId},
285 0 : {"STATE", conf->getStateStr()},
286 : #ifdef ENABLE_VIDEO
287 0 : {"VIDEO_SOURCE", conf->getVideoInput()},
288 : #endif
289 0 : {"RECORDING", conf->isRecording() ? jami::TRUE_STR : jami::FALSE_STR}};
290 0 : return {};
291 : }
292 :
293 : std::vector<std::map<std::string, std::string>>
294 0 : currentMediaList(const std::string& accountId, const std::string& callId)
295 : {
296 0 : if (const auto account = jami::Manager::instance().getAccount(accountId)) {
297 0 : if (auto call = account->getCall(callId)) {
298 0 : return call->currentMediaList();
299 0 : } else if (auto conf = account->getConference(callId)) {
300 0 : return conf->currentMediaList();
301 0 : }
302 0 : }
303 0 : JAMI_WARN("Call not found %s", callId.c_str());
304 0 : return {};
305 : }
306 :
307 : std::vector<std::string>
308 4 : getConferenceList(const std::string& accountId)
309 : {
310 4 : if (const auto account = jami::Manager::instance().getAccount(accountId))
311 4 : return account->getConferenceList();
312 0 : return {};
313 : }
314 :
315 : std::vector<std::string>
316 4 : getParticipantList(const std::string& accountId, const std::string& confId)
317 : {
318 4 : if (const auto account = jami::Manager::instance().getAccount(accountId))
319 4 : if (auto conf = account->getConference(confId)) {
320 4 : const auto& subcalls(conf->getSubCalls());
321 4 : return {subcalls.begin(), subcalls.end()};
322 12 : }
323 0 : return {};
324 : }
325 :
326 : std::string
327 0 : getConferenceId(const std::string& accountId, const std::string& callId)
328 : {
329 0 : if (const auto account = jami::Manager::instance().getAccount(accountId))
330 0 : if (auto call = account->getCall(callId))
331 0 : if (auto conf = call->getConference())
332 0 : return conf->getConfId();
333 0 : return {};
334 : }
335 :
336 : bool
337 0 : startRecordedFilePlayback(const std::string& filepath)
338 : {
339 0 : return jami::Manager::instance().startRecordedFilePlayback(filepath);
340 : }
341 :
342 : void
343 0 : stopRecordedFilePlayback()
344 : {
345 0 : jami::Manager::instance().stopRecordedFilePlayback();
346 0 : }
347 :
348 : bool
349 11 : toggleRecording(const std::string& accountId, const std::string& callId)
350 : {
351 11 : return jami::Manager::instance().toggleRecordingCall(accountId, callId);
352 : }
353 :
354 : void
355 0 : setRecording(const std::string& accountId, const std::string& callId)
356 : {
357 0 : toggleRecording(accountId, callId);
358 0 : }
359 :
360 : void
361 0 : recordPlaybackSeek(double value)
362 : {
363 0 : jami::Manager::instance().recordingPlaybackSeek(value);
364 0 : }
365 :
366 : bool
367 10 : getIsRecording(const std::string& accountId, const std::string& callId)
368 : {
369 10 : if (const auto account = jami::Manager::instance().getAccount(accountId)) {
370 10 : if (auto call = account->getCall(callId)) {
371 10 : return call->isRecording();
372 0 : } else if (auto conf = account->getConference(callId)) {
373 0 : return conf->isRecording();
374 10 : }
375 10 : }
376 0 : return false;
377 : }
378 :
379 : std::map<std::string, std::string>
380 369 : getCallDetails(const std::string& accountId, const std::string& callId)
381 : {
382 369 : if (const auto account = jami::Manager::instance().getAccount(accountId))
383 369 : if (auto call = account->getCall(callId))
384 738 : return call->getDetails();
385 0 : return {};
386 : }
387 :
388 : std::vector<std::string>
389 0 : getCallList()
390 : {
391 0 : return jami::Manager::instance().getCallList();
392 : }
393 :
394 : std::vector<std::string>
395 0 : getCallList(const std::string& accountId)
396 : {
397 0 : if (accountId.empty())
398 0 : return jami::Manager::instance().getCallList();
399 0 : else if (const auto account = jami::Manager::instance().getAccount(accountId))
400 0 : return account->getCallList();
401 0 : JAMI_WARN("Unknown account: %s", accountId.c_str());
402 0 : return {};
403 : }
404 :
405 : std::vector<std::map<std::string, std::string>>
406 0 : getConferenceInfos(const std::string& accountId, const std::string& confId)
407 : {
408 0 : if (const auto account = jami::Manager::instance().getAccount(accountId)) {
409 0 : if (auto conf = account->getConference(confId))
410 0 : return conf->getConferenceInfos();
411 0 : else if (auto call = account->getCall(confId))
412 0 : return call->getConferenceInfos();
413 0 : }
414 0 : return {};
415 : }
416 :
417 : void
418 0 : playDTMF(const std::string& key)
419 : {
420 0 : auto code = key.data()[0];
421 0 : jami::Manager::instance().playDtmf(code);
422 :
423 0 : if (auto current_call = jami::Manager::instance().getCurrentCall())
424 0 : current_call->carryingDTMFdigits(code);
425 0 : }
426 :
427 : void
428 0 : startTone(int32_t start, int32_t type)
429 : {
430 0 : if (start) {
431 0 : if (type == 0)
432 0 : jami::Manager::instance().playTone();
433 : else
434 0 : jami::Manager::instance().playToneWithMessage();
435 : } else
436 0 : jami::Manager::instance().stopTone();
437 0 : }
438 :
439 : bool
440 1 : switchInput(const std::string& accountId, const std::string& callId, const std::string& resource)
441 : {
442 1 : if (const auto account = jami::Manager::instance().getAccount(accountId)) {
443 0 : if (auto conf = account->getConference(callId)) {
444 0 : conf->switchInput(resource);
445 0 : return true;
446 0 : } else if (auto call = account->getCall(callId)) {
447 0 : call->switchInput(resource);
448 0 : return true;
449 0 : }
450 1 : }
451 1 : return false;
452 : }
453 :
454 : bool
455 0 : switchSecondaryInput(const std::string& accountId,
456 : const std::string& confId,
457 : const std::string& resource)
458 : {
459 0 : JAMI_ERR("Use requestMediaChange");
460 0 : return false;
461 : }
462 :
463 : void
464 0 : sendTextMessage(const std::string& accountId,
465 : const std::string& callId,
466 : const std::map<std::string, std::string>& messages,
467 : const std::string& from,
468 : bool isMixed)
469 : {
470 0 : jami::runOnMainThread([accountId, callId, messages, from, isMixed] {
471 0 : jami::Manager::instance().sendCallTextMessage(accountId, callId, messages, from, isMixed);
472 0 : });
473 0 : }
474 :
475 : void
476 1 : setModerator(const std::string& accountId,
477 : const std::string& confId,
478 : const std::string& peerId,
479 : const bool& state)
480 : {
481 1 : if (const auto account = jami::Manager::instance().getAccount(accountId)) {
482 1 : if (auto conf = account->getConference(confId)) {
483 1 : conf->setModerator(peerId, state);
484 : } else {
485 0 : JAMI_WARN("Fail to change moderator %s, conference %s not found",
486 : peerId.c_str(),
487 : confId.c_str());
488 1 : }
489 1 : }
490 1 : }
491 :
492 : void
493 0 : muteParticipant(const std::string& accountId,
494 : const std::string& confId,
495 : const std::string& peerId,
496 : const bool& state)
497 : {
498 0 : JAMI_ERR() << "muteParticipant is deprecated, please use muteStream";
499 0 : if (const auto account = jami::Manager::instance().getAccount(accountId)) {
500 0 : if (auto conf = account->getConference(confId)) {
501 0 : conf->muteParticipant(peerId, state);
502 0 : } else if (auto call = account->getCall(confId)) {
503 0 : Json::Value root;
504 0 : root["muteParticipant"] = peerId;
505 0 : root["muteState"] = state ? jami::TRUE_STR : jami::FALSE_STR;
506 0 : call->sendConfOrder(root);
507 0 : }
508 0 : }
509 0 : }
510 :
511 : void
512 4 : muteStream(const std::string& accountId,
513 : const std::string& confId,
514 : const std::string& accountUri,
515 : const std::string& deviceId,
516 : const std::string& streamId,
517 : const bool& state)
518 : {
519 4 : if (const auto account = jami::Manager::instance().getAccount(accountId)) {
520 4 : if (auto conf = account->getConference(confId)) {
521 3 : conf->muteStream(accountUri, deviceId, streamId, state);
522 1 : } else if (auto call = account->getCall(confId)) {
523 0 : if (call->conferenceProtocolVersion() == 1) {
524 0 : Json::Value sinkVal;
525 0 : sinkVal["muteAudio"] = state;
526 0 : Json::Value mediasObj;
527 0 : mediasObj[streamId] = sinkVal;
528 0 : Json::Value deviceVal;
529 0 : deviceVal["medias"] = mediasObj;
530 0 : Json::Value deviceObj;
531 0 : deviceObj[deviceId] = deviceVal;
532 0 : Json::Value accountVal;
533 0 : deviceVal["devices"] = deviceObj;
534 0 : Json::Value root;
535 0 : root[accountUri] = deviceVal;
536 0 : root["version"] = 1;
537 0 : call->sendConfOrder(root);
538 0 : } else if (call->conferenceProtocolVersion() == 0) {
539 0 : Json::Value root;
540 0 : root["muteParticipant"] = accountUri;
541 0 : root["muteState"] = state ? jami::TRUE_STR : jami::FALSE_STR;
542 0 : call->sendConfOrder(root);
543 0 : }
544 5 : }
545 4 : }
546 4 : }
547 :
548 : void
549 0 : setActiveParticipant(const std::string& accountId,
550 : const std::string& confId,
551 : const std::string& participant)
552 : {
553 0 : JAMI_ERR() << "setActiveParticipant is deprecated, please use setActiveStream";
554 0 : if (const auto account = jami::Manager::instance().getAccount(accountId)) {
555 0 : if (auto conf = account->getConference(confId)) {
556 0 : conf->setActiveParticipant(participant);
557 0 : } else if (auto call = account->getCall(confId)) {
558 0 : Json::Value root;
559 0 : root["activeParticipant"] = participant;
560 0 : call->sendConfOrder(root);
561 0 : }
562 0 : }
563 0 : }
564 :
565 : void
566 2 : setActiveStream(const std::string& accountId,
567 : const std::string& confId,
568 : const std::string& accountUri,
569 : const std::string& deviceId,
570 : const std::string& streamId,
571 : const bool& state)
572 : {
573 2 : if (const auto account = jami::Manager::instance().getAccount<jami::JamiAccount>(accountId)) {
574 2 : if (auto conf = account->getConference(confId)) {
575 1 : conf->setActiveStream(streamId, state);
576 2 : } else if (auto call = std::static_pointer_cast<jami::SIPCall>(account->getCall(confId))) {
577 0 : call->setActiveMediaStream(accountUri, deviceId, streamId, state);
578 3 : }
579 2 : }
580 2 : }
581 :
582 : void
583 2 : hangupParticipant(const std::string& accountId,
584 : const std::string& confId,
585 : const std::string& accountUri,
586 : const std::string& deviceId)
587 : {
588 2 : if (const auto account = jami::Manager::instance().getAccount(accountId)) {
589 2 : if (auto conf = account->getConference(confId)) {
590 1 : conf->hangupParticipant(accountUri, deviceId);
591 2 : } else if (auto call = std::static_pointer_cast<jami::SIPCall>(account->getCall(confId))) {
592 0 : if (call->conferenceProtocolVersion() == 1) {
593 0 : Json::Value deviceVal;
594 0 : deviceVal["hangup"] = jami::TRUE_STR;
595 0 : Json::Value deviceObj;
596 0 : deviceObj[deviceId] = deviceVal;
597 0 : Json::Value accountVal;
598 0 : deviceVal["devices"] = deviceObj;
599 0 : Json::Value root;
600 0 : root[accountUri] = deviceVal;
601 0 : root["version"] = 1;
602 0 : call->sendConfOrder(root);
603 0 : } else if (call->conferenceProtocolVersion() == 0) {
604 0 : Json::Value root;
605 0 : root["hangupParticipant"] = accountUri;
606 0 : call->sendConfOrder(root);
607 0 : }
608 3 : }
609 2 : }
610 2 : }
611 :
612 : void
613 0 : raiseParticipantHand(const std::string& accountId,
614 : const std::string& confId,
615 : const std::string& peerId,
616 : const bool& state)
617 : {
618 0 : JAMI_ERR() << "raiseParticipantHand is deprecated, please use raiseHand";
619 0 : if (const auto account = jami::Manager::instance().getAccount(accountId)) {
620 0 : if (auto conf = account->getConference(confId)) {
621 0 : if (auto call = std::static_pointer_cast<jami::SIPCall>(
622 0 : conf->getCallFromPeerID(peerId))) {
623 0 : if (auto* transport = call->getTransport())
624 0 : conf->setHandRaised(std::string(transport->deviceId()), state);
625 0 : }
626 0 : } else if (auto call = account->getCall(confId)) {
627 0 : Json::Value root;
628 0 : root["handRaised"] = peerId;
629 0 : root["handState"] = state ? jami::TRUE_STR : jami::FALSE_STR;
630 0 : call->sendConfOrder(root);
631 0 : }
632 0 : }
633 0 : }
634 :
635 : void
636 9 : raiseHand(const std::string& accountId,
637 : const std::string& confId,
638 : const std::string& accountUri,
639 : const std::string& deviceId,
640 : const bool& state)
641 : {
642 9 : if (const auto account = jami::Manager::instance().getAccount<jami::JamiAccount>(accountId)) {
643 9 : if (auto conf = account->getConference(confId)) {
644 2 : auto device = deviceId;
645 2 : if (device.empty())
646 0 : device = std::string(account->currentDeviceId());
647 2 : conf->setHandRaised(device, state);
648 16 : } else if (auto call = std::static_pointer_cast<jami::SIPCall>(account->getCall(confId))) {
649 6 : if (call->conferenceProtocolVersion() == 1) {
650 6 : Json::Value deviceVal;
651 6 : deviceVal["raiseHand"] = state;
652 6 : Json::Value deviceObj;
653 6 : std::string device = deviceId.empty() ? std::string(account->currentDeviceId())
654 6 : : deviceId;
655 6 : deviceObj[device] = deviceVal;
656 6 : Json::Value accountVal;
657 6 : deviceVal["devices"] = deviceObj;
658 6 : Json::Value root;
659 6 : std::string uri = accountUri.empty() ? account->getUsername() : accountUri;
660 6 : root[uri] = deviceVal;
661 6 : root["version"] = 1;
662 6 : call->sendConfOrder(root);
663 6 : } else if (call->conferenceProtocolVersion() == 0) {
664 0 : Json::Value root;
665 0 : root["handRaised"] = account->getUsername();
666 0 : root["handState"] = state ? jami::TRUE_STR : jami::FALSE_STR;
667 0 : call->sendConfOrder(root);
668 0 : }
669 16 : }
670 9 : }
671 9 : }
672 :
673 : } // namespace libjami
|