managerimpl.cpp 76.5 KB
Newer Older
jpbl's avatar
jpbl committed
1
/*
Adrien Béraud's avatar
Adrien Béraud committed
2
 *  Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
3
 *  Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>
jpbl's avatar
jpbl committed
4
 *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
5
 *  Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com>
6
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
7
 *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
8
 *  Author: Guillaume Carmel-Archambault <guillaume.carmel-archambault@savoirfairelinux.com>
9
 *  Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
10
 *
jpbl's avatar
jpbl committed
11 12
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 3 of the License, or
jpbl's avatar
jpbl committed
14
 *  (at your option) any later version.
15
 *
jpbl's avatar
jpbl committed
16 17 18 19
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
20
 *
jpbl's avatar
jpbl committed
21 22
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
23
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
24 25 26 27 28 29 30 31 32 33 34
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
jpbl's avatar
jpbl committed
35 36
 */

37
#ifdef HAVE_CONFIG_H
38
#include "config.h"
39
#endif
40

41
#include "logger.h"
42
#include "managerimpl.h"
43
#include "account_schema.h"
44
#include "plugin_manager.h"
45

46
#include "fileutils.h"
47
#include "map_utils.h"
48
#include "account.h"
49
#include "string_utils.h"
Adrien Béraud's avatar
Adrien Béraud committed
50
#if HAVE_DHT
51
#include "ringdht/ringaccount.h"
Adrien Béraud's avatar
Adrien Béraud committed
52
#endif
53

54 55 56 57
#include "call_factory.h"

#include "sip/sip_utils.h"

58 59
#include "im/instant_messaging.h"

60
#include "numbercleaner.h"
61
#include "config/yamlparser.h"
62

63
#if HAVE_ALSA
64
#include "audio/alsa/alsalayer.h"
65
#endif
66

67
#include "audio/sound/tonelist.h"
68 69
#include "audio/sound/audiofile.h"
#include "audio/sound/dtmf.h"
70
#include "audio/ringbufferpool.h"
71
#include "manager.h"
72

Adrien Béraud's avatar
Adrien Béraud committed
73
#ifdef RING_VIDEO
74
#include "client/videomanager.h"
75
#endif
76

77
#include "conference.h"
78
#include "ice_transport.h"
79

80
#include "client/ring_signal.h"
81

82
#if HAVE_TLS
83
#include "gnutls_support.h"
84 85
#endif

86 87 88
#include "libav_utils.h"
#include "video/sinkclient.h"

89
#include <cerrno>
90
#include <algorithm>
91
#include <ctime>
jpbl's avatar
jpbl committed
92 93 94
#include <cstdlib>
#include <iostream>
#include <fstream>
95
#include <sstream>
jpbl's avatar
jpbl committed
96
#include <sys/types.h> // mkdir(2)
97
#include <sys/stat.h>  // mkdir(2)
98
#include <memory>
99
#include <mutex>
100

101
namespace ring {
102

103 104
std::atomic_bool ManagerImpl::initialized = {false};

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
static void
copy_over(const std::string &srcPath, const std::string &destPath)
{
    std::ifstream src(srcPath.c_str());
    std::ofstream dest(destPath.c_str());
    dest << src.rdbuf();
    src.close();
    dest.close();
}

// Creates a backup of the file at "path" with a .bak suffix appended
static void
make_backup(const std::string &path)
{
    const std::string backup_path(path + ".bak");
    copy_over(path, backup_path);
}
Guillaume Roguez's avatar
Guillaume Roguez committed
122

123 124 125 126 127 128 129 130
// Restore last backup of the configuration file
static void
restore_backup(const std::string &path)
{
    const std::string backup_path(path + ".bak");
    copy_over(backup_path, path);
}

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
/**
 * Set pjsip's log level based on the SIPLOGLEVEL environment variable.
 * SIPLOGLEVEL = 0 minimum logging
 * SIPLOGLEVEL = 6 maximum logging
 */

/** Environment variable used to set pjsip's logging level */
static constexpr const char* SIPLOGLEVEL = "SIPLOGLEVEL";

static void
setSipLogLevel()
{
    char* envvar = getenv(SIPLOGLEVEL);
    int level = 0;

    if (envvar != nullptr) {
        if (not (std::istringstream(envvar) >> level))
            level = 0;

        // From 0 (min) to 6 (max)
        level = std::max(0, std::min(level, 6));
    }

    pj_log_set_level(level);
}

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
#if HAVE_TLS
/**
 * Set gnutls's log level based on the RING_TLS_LOGLEVEL environment variable.
 * RING_TLS_LOGLEVEL = 0 minimum logging (default)
 * RING_TLS_LOGLEVEL = 9 maximum logging
 */

static constexpr int RING_TLS_LOGLEVEL = 0;

static void
tls_print_logs(int level, const char* msg)
{
    RING_DBG("GnuTLS [%d]: %s", level, msg);
}

static void
setGnuTlsLogLevel()
{
    char* envvar = getenv("RING_TLS_LOGLEVEL");
    int level = RING_TLS_LOGLEVEL;

    if (envvar != nullptr) {
        int var_level;
        if (std::istringstream(envvar) >> var_level)
            level = var_level;

        // From 0 (min) to 9 (max)
        level = std::max(0, std::min(level, 9));
    }

    gnutls_global_set_log_level(level);
    gnutls_global_set_log_function(tls_print_logs);
}
#endif // HAVE_TLS

192 193
void
ManagerImpl::loadDefaultAccountMap()
194
{
195
    accountFactory_.initIP2IPAccount();
196 197
}

198
ManagerImpl::ManagerImpl() :
199 200
    pluginManager_(new PluginManager)
    , preferences(), voipPreferences(),
201
    hookPreference(),  audioPreference(), shortcutPreferences(),
202
    hasTriedToRegister_(false),
203
    currentCallMutex_(), dtmfKey_(), dtmfBuf_(0, AudioFormat::MONO()),
204
    toneMutex_(), telephoneTone_(), audiofile_(), audioLayerMutex_(),
205
    waitingCalls_(), waitingCallsMutex_(), path_()
206
    , ringbufferpool_(new RingBufferPool)
207
    , callFactory(), conferenceMap_()
208
    , accountFactory_(), ice_tf_()
209
    , gnutlGIG_ {tls::GnuTlsGlobalInit::make_guard()}
Emmanuel Milou's avatar
Emmanuel Milou committed
210
{
211 212 213 214 215
    // initialize random generator
    // mt19937_64 should be seeded with 2 x 32 bits
    std::random_device rdev;
    std::seed_seq seed {rdev(), rdev()};
    rand_.seed(seed);
216

217
    ring::libav_utils::ring_avcodec_init();
jpbl's avatar
jpbl committed
218 219
}

220
ManagerImpl::~ManagerImpl()
Guillaume Roguez's avatar
Guillaume Roguez committed
221
{}
222

Guillaume Roguez's avatar
Guillaume Roguez committed
223 224
bool
ManagerImpl::parseConfiguration()
Emmanuel Milou's avatar
Emmanuel Milou committed
225
{
226
    bool result = true;
227

228
    try {
229 230 231 232
        YAML::Node parsedFile = YAML::LoadFile(path_);
        const int error_count = loadAccountMap(parsedFile);

        if (error_count > 0) {
Adrien Béraud's avatar
Adrien Béraud committed
233
            RING_WARN("Errors while parsing %s", path_.c_str());
234
            result = false;
235
        }
236
    } catch (const YAML::BadFile &e) {
Adrien Béraud's avatar
Adrien Béraud committed
237
        RING_WARN("Could not open config file: creating default account map");
238
        loadDefaultAccountMap();
239 240 241 242 243
    }

    return result;
}

Guillaume Roguez's avatar
Guillaume Roguez committed
244 245
void
ManagerImpl::init(const std::string &config_file)
246
{
247 248 249
    // FIXME: this is no good
    initialized = true;

250
#define PJSIP_TRY(ret) do {                                 \
251 252 253 254
        if (ret != PJ_SUCCESS)                               \
            throw std::runtime_error(#ret " failed");        \
    } while (0)

255 256 257
    srand(time(NULL)); // to get random number for RANDOM_PORT

    // Initialize PJSIP (SIP and ICE implementation)
258
    PJSIP_TRY(pj_init());
259
    setSipLogLevel();
260 261
    PJSIP_TRY(pjlib_util_init());
    PJSIP_TRY(pjnath_init());
262 263
#undef TRY

264 265 266
    RING_DBG("pjsip version %s for %s initialized",
             pj_get_version(), PJ_OS_NAME);

267 268 269
#if HAVE_TLS
    setGnuTlsLogLevel();
    RING_DBG("GNU TLS version %s initialized", gnutls_check_version(nullptr));
270
#endif
271

272 273
    ice_tf_.reset(new IceTransportFactory());

274
    path_ = config_file.empty() ? retrieveConfigPath() : config_file;
Adrien Béraud's avatar
Adrien Béraud committed
275
    RING_DBG("Configuration file path: %s", path_.c_str());
276 277 278

    bool no_errors = true;

279 280 281
    // manager can restart without being recreated (android)
    finished_ = false;

282 283
    try {
        no_errors = parseConfiguration();
284
    } catch (const YAML::Exception &e) {
Adrien Béraud's avatar
Adrien Béraud committed
285
        RING_ERR("%s", e.what());
286
        no_errors = false;
287 288
    }

289
    // always back up last error-free configuration
290
    if (no_errors) {
291
        make_backup(path_);
292 293
    } else {
        // restore previous configuration
Adrien Béraud's avatar
Adrien Béraud committed
294
        RING_WARN("Restoring last working configuration");
Guillaume Roguez's avatar
Guillaume Roguez committed
295

296
        try {
297 298
            // remove accounts from broken configuration
            removeAccounts();
299 300
            restore_backup(path_);
            parseConfiguration();
301
        } catch (const YAML::Exception &e) {
Adrien Béraud's avatar
Adrien Béraud committed
302 303
            RING_ERR("%s", e.what());
            RING_WARN("Restoring backup failed, creating default account map");
304 305 306
            loadDefaultAccountMap();
        }
    }
307

308
    initAudioDriver();
309

Tristan Matthews's avatar
Tristan Matthews committed
310
    {
311
        std::lock_guard<std::mutex> lock(audioLayerMutex_);
Guillaume Roguez's avatar
Guillaume Roguez committed
312

Tristan Matthews's avatar
Tristan Matthews committed
313
        if (audiodriver_) {
314
            {
315
                std::lock_guard<std::mutex> toneLock(toneMutex_);
316
                telephoneTone_.reset(new TelephoneTone(preferences.getZoneToneChoice(), audiodriver_->getSampleRate()));
317
            }
318
            dtmfKey_.reset(new DTMF(getRingBufferPool().getInternalSamplingRate()));
Tristan Matthews's avatar
Tristan Matthews committed
319
        }
320
    }
321

322
    registerAccounts();
jpbl's avatar
jpbl committed
323 324
}

Guillaume Roguez's avatar
Guillaume Roguez committed
325
void
326
ManagerImpl::setPath(const std::string&)
Guillaume Roguez's avatar
Guillaume Roguez committed
327
{
328
    // FIME: needed?
Emeric Vigier's avatar
Emeric Vigier committed
329 330
}

Guillaume Roguez's avatar
Guillaume Roguez committed
331
void
332
ManagerImpl::finish() noexcept
333
{
334 335
    bool expected = false;
    if (not finished_.compare_exchange_strong(expected, true))
336 337
        return;

338
    try {
339 340
        // Forbid call creation
        callFactory.forbid();
341

342
        // Hangup all remaining active calls
Adrien Béraud's avatar
Adrien Béraud committed
343
        RING_DBG("Hangup %zu remaining call(s)", callFactory.callCount());
344 345 346
        for (const auto call : callFactory.getAllCalls())
            hangupCall(call->getCallId());
        callFactory.clear();
347

348
        saveConfig();
349

350
        // Disconnect accounts, close link stacks and free allocated ressources
351
        unregisterAccounts();
352
        accountFactory_.clear();
353

354 355
        {
            std::lock_guard<std::mutex> lock(audioLayerMutex_);
356

357
            audiodriver_.reset();
358
        }
359 360 361

        ice_tf_.reset();
        pj_shutdown();
362
    } catch (const VoipLinkException &err) {
Adrien Béraud's avatar
Adrien Béraud committed
363
        RING_ERR("%s", err.what());
364
    }
jpbl's avatar
jpbl committed
365 366
}

Guillaume Roguez's avatar
Guillaume Roguez committed
367
bool
368
ManagerImpl::isCurrentCall(const Call& call) const
Emmanuel Milou's avatar
Emmanuel Milou committed
369
{
370
    return currentCall_.get() == &call;
jpbl's avatar
jpbl committed
371 372
}

Guillaume Roguez's avatar
Guillaume Roguez committed
373 374
bool
ManagerImpl::hasCurrentCall() const
Emmanuel Milou's avatar
Emmanuel Milou committed
375
{
376
    return static_cast<bool>(currentCall_);
jpbl's avatar
jpbl committed
377 378
}

379 380 381 382 383 384 385
std::shared_ptr<Call>
ManagerImpl::getCurrentCall() const
{
    return currentCall_;
}

const std::string
386
ManagerImpl::getCurrentCallId() const
Emmanuel Milou's avatar
Emmanuel Milou committed
387
{
388
    return currentCall_ ? currentCall_->getCallId() : "";
jpbl's avatar
jpbl committed
389 390
}

391 392 393 394 395
/**
 * Set current call ID to empty string
 */
void
ManagerImpl::unsetCurrentCall()
396
{
397
    currentCall_.reset();
398 399
}

400 401 402 403 404 405
/**
 * Switch of current call id
 * @param id The new callid
 */
void
ManagerImpl::switchCall(std::shared_ptr<Call> call)
Emmanuel Milou's avatar
Emmanuel Milou committed
406
{
407
    std::lock_guard<std::mutex> m(currentCallMutex_);
Adrien Béraud's avatar
Adrien Béraud committed
408
    RING_DBG("----- Switch current call id to '%s' -----",
409
          call ? call->getCallId().c_str() : "<nullptr>");
410
    currentCall_ = call;
jpbl's avatar
jpbl committed
411 412 413 414 415
}

///////////////////////////////////////////////////////////////////////////////
// Management of events' IP-phone user
///////////////////////////////////////////////////////////////////////////////
416
/* Main Thread */
417

418
std::string
419
ManagerImpl::outgoingCall(const std::string& preferred_account_id,
Guillaume Roguez's avatar
Guillaume Roguez committed
420 421
                          const std::string& to,
                          const std::string& conf_id)
Emmanuel Milou's avatar
Emmanuel Milou committed
422
{
423 424 425 426
    std::string current_call_id(getCurrentCallId());
    std::string prefix(hookPreference.getNumberAddPrefix());
    std::string to_cleaned(NumberCleaner::clean(to, prefix));
    std::shared_ptr<Call> call;
427

428 429 430 431 432 433 434 435 436
    try {
        /* RING_WARN: after this call the account_id is obsolete
         * as the factory may decide to use another account (like IP2IP).
         */
        RING_DBG("New outgoing call to %s", to_cleaned.c_str());
        call = newOutgoingCall(to_cleaned, preferred_account_id);
    } catch (const std::exception &e) {
        RING_ERR("%s", e.what());
        return {};
437 438
    }

439 440
    if (not call)
        return {};
441

442
    auto call_id = call->getCallId();
443

444
    stopTone();
445

446 447
    // in any cases we have to detach from current communication
    if (hasCurrentCall()) {
Adrien Béraud's avatar
Adrien Béraud committed
448
        RING_DBG("Has current call (%s) put it onhold", current_call_id.c_str());
449

450
        // if this is not a conference and this and is not a conference participant
451
        if (not isConference(current_call_id) and not isConferenceParticipant(current_call_id))
452
            onHoldCall(current_call_id);
453
        else if (isConference(current_call_id) and not isConferenceParticipant(call_id))
454
            detachParticipant(RingBufferPool::DEFAULT_ID);
455
    }
456

457 458
    switchCall(call);
    call->setConfId(conf_id);
459 460

    return call_id;
jpbl's avatar
jpbl committed
461 462
}

yanmorin's avatar
yanmorin committed
463
//THREAD=Main : for outgoing Call
Guillaume Roguez's avatar
Guillaume Roguez committed
464 465
bool
ManagerImpl::answerCall(const std::string& call_id)
Emmanuel Milou's avatar
Emmanuel Milou committed
466
{
467
    bool result = true;
468

469 470
    auto call = getCallFromCallID(call_id);
    if (!call) {
Adrien Béraud's avatar
Adrien Béraud committed
471
        RING_ERR("Call %s is NULL", call_id.c_str());
472 473
        return false;
    }
474

Adrien Béraud's avatar
Adrien Béraud committed
475
    // If ring is ringing
476
    stopTone();
477

478
    // store the current call id
479
    std::string current_call_id(getCurrentCallId());
480

481 482
    // in any cases we have to detach from current communication
    if (hasCurrentCall()) {
483

Adrien Béraud's avatar
Adrien Béraud committed
484
        RING_DBG("Currently conversing with %s", current_call_id.c_str());
485

486
        if (not isConference(current_call_id) and not isConferenceParticipant(current_call_id)) {
Adrien Béraud's avatar
Adrien Béraud committed
487
            RING_DBG("Answer call: Put the current call (%s) on hold", current_call_id.c_str());
488
            onHoldCall(current_call_id);
489
        } else if (isConference(current_call_id) and not isConferenceParticipant(call_id)) {
490
            // if we are talking to a conference and we are answering an incoming call
Adrien Béraud's avatar
Adrien Béraud committed
491
            RING_DBG("Detach main participant from conference");
492
            detachParticipant(RingBufferPool::DEFAULT_ID);
493 494
        }
    }
495

496
    try {
497
        call->answer();
498
    } catch (const std::runtime_error &e) {
Adrien Béraud's avatar
Adrien Béraud committed
499
        RING_ERR("%s", e.what());
500
        result = false;
501
    }
Alexandre Savard's avatar
Alexandre Savard committed
502

503
    // if it was waiting, it's waiting no more
504
    removeWaitingCall(call_id);
505

506
    // if we dragged this call into a conference already
507
    if (isConferenceParticipant(call_id))
508
        switchCall(callFactory.getCall(call->getConfId()));
509
    else
510
        switchCall(call);
511

512
    // Connect streams
513
    addStream(*call);
514

515
    // Start recording if set in preference
516
    if (audioPreference.getIsAlwaysRecording())
517
        toggleRecordingCall(call_id);
518

519
    //callStateChanged(call_id, "CURRENT");
520
    emitSignal<DRing::CallSignal::StateChange>(call_id, "CURRENT", 0);
521

522
    return result;
jpbl's avatar
jpbl committed
523 524
}

Guillaume Roguez's avatar
Guillaume Roguez committed
525 526
void
ManagerImpl::checkAudio()
527 528
{
    if (getCallList().empty()) {
529
        std::lock_guard<std::mutex> lock(audioLayerMutex_);
530 531 532
        if (audiodriver_)
            audiodriver_->stopStream();
    }
jpbl's avatar
jpbl committed
533 534
}

yanmorin's avatar
yanmorin committed
535
//THREAD=Main
Guillaume Roguez's avatar
Guillaume Roguez committed
536 537
bool
ManagerImpl::hangupCall(const std::string& callId)
Emmanuel Milou's avatar
Emmanuel Milou committed
538
{
539
    // store the current call id
540
    std::string currentCallId(getCurrentCallId());
yanmorin's avatar
yanmorin committed
541

542
    stopTone();
543

Adrien Béraud's avatar
Adrien Béraud committed
544
    RING_DBG("Send call state change (HUNGUP) for id %s", callId.c_str());
545
    emitSignal<DRing::CallSignal::StateChange>(callId, "HUNGUP", 0);
546

547
    /* We often get here when the call was hungup before being created */
548 549
    auto call = getCallFromCallID(callId);
    if (not call) {
Adrien Béraud's avatar
Adrien Béraud committed
550
        RING_WARN("Could not hang up non-existant call %s", callId.c_str());
551
        checkAudio();
552
        return false;
553 554
    }

555
    // Disconnect streams
556
    removeStream(*call);
557

558
    if (isConferenceParticipant(callId)) {
559
        removeParticipant(callId);
560
    } else {
561 562
        // we are not participating in a conference, current call switched to ""
        if (not isConference(currentCallId))
563
            unsetCurrentCall();
564
    }
565

566
    try {
567 568
        call->hangup(0);
        checkAudio();
569
    } catch (const VoipLinkException &e) {
Adrien Béraud's avatar
Adrien Béraud committed
570
        RING_ERR("%s", e.what());
571
        return false;
572
    }
573

574
    return true;
jpbl's avatar
jpbl committed
575 576
}

Guillaume Roguez's avatar
Guillaume Roguez committed
577 578
bool
ManagerImpl::hangupConference(const std::string& id)
Emmanuel Milou's avatar
Emmanuel Milou committed
579
{
Adrien Béraud's avatar
Adrien Béraud committed
580
    RING_DBG("Hangup conference %s", id.c_str());
581

582
    ConferenceMap::iterator iter_conf = conferenceMap_.find(id);
583

584
    if (iter_conf != conferenceMap_.end()) {
585
        auto conf = iter_conf->second;
586

587 588
        if (conf) {
            ParticipantSet participants(conf->getParticipantList());
589

590 591
            for (const auto &item : participants)
                hangupCall(item);
592
        } else {
Adrien Béraud's avatar
Adrien Béraud committed
593
            RING_ERR("No such conference %s", id.c_str());
594 595
            return false;
        }
596
    }
597

598
    unsetCurrentCall();
599

600
    return true;
601 602
}

yanmorin's avatar
yanmorin committed
603
//THREAD=Main
Guillaume Roguez's avatar
Guillaume Roguez committed
604 605
bool
ManagerImpl::onHoldCall(const std::string& callId)
Emmanuel Milou's avatar
Emmanuel Milou committed
606
{
607
    bool result = true;
608

609
    stopTone();
610

611
    std::string current_call_id(getCurrentCallId());
612

613 614
    if (auto call = getCallFromCallID(callId)) {
        try {
615
            call->onhold();
616 617
            removeStream(*call); // Unbind calls in main buffer
        } catch (const VoipLinkException &e) {
Adrien Béraud's avatar
Adrien Béraud committed
618
            RING_ERR("%s", e.what());
619
            result = false;
620
        }
621
    } else {
Adrien Béraud's avatar
Adrien Béraud committed
622
        RING_DBG("CallID %s doesn't exist in call onHold", callId.c_str());
623
        return false;
624
    }
625

626
    // Remove call from teh queue if it was still there
627
    removeWaitingCall(callId);
628

629
    // keeps current call id if the action is not holding this call or a new outgoing call
630
    // this could happen in case of a conference
631
    if (current_call_id == callId)
632
        unsetCurrentCall();
633

634
    emitSignal<DRing::CallSignal::StateChange>(callId, "HOLD", 0);
635

636
    return result;
yanmorin's avatar
yanmorin committed
637 638 639
}

//THREAD=Main
Guillaume Roguez's avatar
Guillaume Roguez committed
640 641
bool
ManagerImpl::offHoldCall(const std::string& callId)
Emmanuel Milou's avatar
Emmanuel Milou committed
642
{
643
    bool result = true;
644

645
    stopTone();
646

Tristan Matthews's avatar
Tristan Matthews committed
647
    const std::string currentCallId(getCurrentCallId());
648

Tristan Matthews's avatar
Tristan Matthews committed
649
    // Place current call on hold if it isn't
650
    if (hasCurrentCall()) {
Tristan Matthews's avatar
Tristan Matthews committed
651
        if (not isConference(currentCallId) and not isConferenceParticipant(currentCallId)) {
Adrien Béraud's avatar
Adrien Béraud committed
652
            RING_DBG("Has current call (%s), put on hold", currentCallId.c_str());
653 654 655
            //FIXME: ebail
            // if 2 consecutive offHoldCall done, the second one should be ignored (already offhold)
            // this call put the call onHold
656
            onHoldCall(currentCallId);
657 658
        } else if (isConference(currentCallId) && callId != currentCallId) {
            holdConference(currentCallId);
659
        } else if (isConference(currentCallId) and not isConferenceParticipant(callId))
660
            detachParticipant(RingBufferPool::DEFAULT_ID);
661
    }
662

663
    std::shared_ptr<Call> call;
664
    try {
665 666
        call = getCallFromCallID(callId);
        if (call)
667
            call->offhold();
668 669
        else
            result = false;
670
    } catch (const VoipLinkException &e) {
Adrien Béraud's avatar
Adrien Béraud committed
671
        RING_ERR("%s", e.what());
672
        return false;
673
    }
674

675
    emitSignal<DRing::CallSignal::StateChange>(callId, "UNHOLD", 0);
676

677 678 679 680
    if (isConferenceParticipant(callId))
        switchCall(getCallFromCallID(call->getConfId()));
    else
        switchCall(call);
681

682
    addStream(*call);
683

684
    return result;
jpbl's avatar
jpbl committed
685 686
}

yanmorin's avatar
yanmorin committed
687
//THREAD=Main
Guillaume Roguez's avatar
Guillaume Roguez committed
688 689
bool
ManagerImpl::transferCall(const std::string& callId, const std::string& to)
Emmanuel Milou's avatar
Emmanuel Milou committed
690
{
691
    if (isConferenceParticipant(callId)) {
692
        removeParticipant(callId);
Tristan Matthews's avatar
Tristan Matthews committed
693
    } else if (not isConference(getCurrentCallId()))
694
        unsetCurrentCall();
695

696 697 698
    if (auto call = getCallFromCallID(callId))
        call->transfer(to);
    else
699
        return false;
700

701
    // remove waiting call in case we make transfer without even answer
702
    removeWaitingCall(callId);
703

704
    return true;
jpbl's avatar
jpbl committed
705 706
}

Guillaume Roguez's avatar
Guillaume Roguez committed
707 708
void
ManagerImpl::transferFailed()
Emmanuel Milou's avatar
Emmanuel Milou committed
709
{
710
    emitSignal<DRing::CallSignal::TransferFailed>();
711 712
}

Guillaume Roguez's avatar
Guillaume Roguez committed
713 714
void
ManagerImpl::transferSucceeded()
Emmanuel Milou's avatar
Emmanuel Milou committed
715
{
716
    transferSucceeded();
717 718
}

Guillaume Roguez's avatar
Guillaume Roguez committed
719 720 721
bool
ManagerImpl::attendedTransfer(const std::string& transferID,
                              const std::string& targetID)
722
{
723 724
    if (auto call = getCallFromCallID(transferID))
        return call->attendedTransfer(targetID);
Guillaume Roguez's avatar
Guillaume Roguez committed
725

726
    return false;
727 728
}

yanmorin's avatar
yanmorin committed
729
//THREAD=Main : Call:Incoming
Guillaume Roguez's avatar
Guillaume Roguez committed
730 731
bool
ManagerImpl::refuseCall(const std::string& id)
Emmanuel Milou's avatar
Emmanuel Milou committed
732
{
733 734
    auto call = getCallFromCallID(id);
    if (!call)
735 736
        return false;

737
    stopTone();
738

Rafaël Carré's avatar
Rafaël Carré committed
739
    if (getCallList().size() <= 1) {
740
        std::lock_guard<std::mutex> lock(audioLayerMutex_);
741
        audiodriver_->stopStream();
742
    }
743

744
    call->refuse();
745

746
    checkAudio();
747

748
    removeWaitingCall(id);
749

750
    emitSignal<DRing::CallSignal::StateChange>(id, "HUNGUP", 0);
751

752
    // Disconnect streams
753
    removeStream(*call);
754

755
    return true;
jpbl's avatar
jpbl committed
756 757
}

758
std::shared_ptr<Conference>
759
ManagerImpl::createConference(const std::string& id1, const std::string& id2)
Emmanuel Milou's avatar
Emmanuel Milou committed
760
{
Adrien Béraud's avatar
Adrien Béraud committed
761
    RING_DBG("Create conference with call %s and %s", id1.c_str(), id2.c_str());
762

763
    auto conf = std::make_shared<Conference>();
764

765 766
    conf->add(id1);
    conf->add(id2);
767

768
    // Add conference to map
769
    conferenceMap_.insert(std::make_pair(conf->getConfID(), conf));
Alexandre Savard's avatar
Alexandre Savard committed
770

771
    emitSignal<DRing::CallSignal::ConferenceCreated>(conf->getConfID());
772

773
    return conf;
774 775
}

Guillaume Roguez's avatar
Guillaume Roguez committed
776 777
void
ManagerImpl::removeConference(const std::string& conference_id)
Emmanuel Milou's avatar
Emmanuel Milou committed
778
{
Adrien Béraud's avatar
Adrien Béraud committed
779 780
    RING_DBG("Remove conference %s", conference_id.c_str());
    RING_DBG("number of participants: %u", conferenceMap_.size());
781
    ConferenceMap::iterator iter = conferenceMap_.find(conference_id);
782

783
    std::shared_ptr<Conference> conf;
784

785
    if (iter != conferenceMap_.end())
786
        conf = iter->second;
787

788
    if (not conf) {
Adrien Béraud's avatar
Adrien Béraud committed
789
        RING_ERR("Conference not found");
790 791
        return;
    }
792

793
    emitSignal<DRing::CallSignal::ConferenceRemoved>(conference_id);
794

795
    // We now need to bind the audio to the remain participant
796

797
    // Unbind main participant audio from conference
798
    getRingBufferPool().unBindAll(RingBufferPool::DEFAULT_ID);
799

800
    ParticipantSet participants(conf->getParticipantList());
801

802 803
    // bind main participant audio to remaining conference call
    ParticipantSet::iterator iter_p = participants.begin();
804

805
    if (iter_p != participants.end())
806
        getRingBufferPool().bindCallID(*iter_p, RingBufferPool::DEFAULT_ID);
807

808
    // Then remove the conference from the conference map
809
    if (conferenceMap_.erase(conference_id))
Adrien Béraud's avatar
Adrien Béraud committed
810
        RING_DBG("Conference %s removed successfully", conference_id.c_str());
811
    else
Adrien Béraud's avatar
Adrien Béraud committed
812
        RING_ERR("Cannot remove conference: %s", conference_id.c_str());
813 814
}

815
std::shared_ptr<Conference>
816
ManagerImpl::getConferenceFromCallID(const std::string& call_id)
Emmanuel Milou's avatar
Emmanuel Milou committed
817
{
818
    auto call = getCallFromCallID(call_id);
819
    if (!call)
820
        return nullptr;
821

822
    ConferenceMap::const_iterator iter(conferenceMap_.find(call->getConfId()));
823

824
    if (iter != conferenceMap_.end())
825
        return iter->second;
826
    else
827
        return nullptr;
828 829
}

830 831
bool
ManagerImpl::holdConference(const std::string& id)
Emmanuel Milou's avatar
Emmanuel Milou committed
832
{
833
    ConferenceMap::iterator iter_conf = conferenceMap_.find(id);
834

835
    if (iter_conf == conferenceMap_.end())
836
        return false;
837

838
    auto conf = iter_conf->second;
839

840 841
    bool isRec = conf->getState() == Conference::ACTIVE_ATTACHED_REC or
                 conf->getState() == Conference::ACTIVE_DETACHED_REC or
842
                 conf->getState() == Conference::HOLD_REC;
843

844
    ParticipantSet participants(conf->getParticipantList());
845

846
    for (const auto &item : participants) {
847
        switchCall(getCallFromCallID(item));
848
        onHoldCall(item);
849
    }
850

851
    conf->setState(isRec ? Conference::HOLD_REC : Conference::HOLD);
852

853
    emitSignal<DRing::CallSignal::ConferenceChanged>(conf->getConfID(), conf->getStateStr());
854

855
    return true;
856 857
}

858 859
bool
ManagerImpl::unHoldConference(const std::string& id)
Emmanuel Milou's avatar
Emmanuel Milou committed
860
{
861
    ConferenceMap::iterator iter_conf = conferenceMap_.find(id);
862

863 864
    if (iter_conf == conferenceMap_.end() or iter_conf->second == 0)
        return false;
865

866
    auto conf = iter_conf->second;
867

868 869 870
    bool isRec = conf->getState() == Conference::ACTIVE_ATTACHED_REC or
        conf->getState() == Conference::ACTIVE_DETACHED_REC or
        conf->getState() == Conference::HOLD_REC;
871

872
    ParticipantSet participants(conf->getParticipantList());
873

874
    for (const auto &item : participants) {
875
        if (auto call = getCallFromCallID(item)) {
876 877
            // if one call is currently recording, the conference is in state recording
            isRec |= call->isRecording();
878

879
            switchCall(call);
880
            offHoldCall(item);
881 882
        }
    }
883

884
    conf->setState(isRec ? Conference::ACTIVE_ATTACHED_REC : Conference::ACTIVE_ATTACHED);
885

886
    emitSignal<DRing::CallSignal::ConferenceChanged>(conf->getConfID(), conf->getStateStr());
887

888
    return true;
889 890
}

Guillaume Roguez's avatar
Guillaume Roguez committed
891 892
bool
ManagerImpl::isConference(const std::string& id) const
Emmanuel Milou's avatar
Emmanuel Milou committed
893
{
894
    return conferenceMap_.find(id) != conferenceMap_.end();
895 896
}

Guillaume Roguez's avatar
Guillaume Roguez committed
897 898
bool
ManagerImpl::isConferenceParticipant(const std::string& call_id)
Emmanuel Milou's avatar
Emmanuel Milou committed
899
{
900
    auto call = getCallFromCallID(call_id);
901
    return call and not call->getConfId().empty();
902
}
</