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