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