managerimpl.cpp 84 KB
Newer Older
jpbl's avatar
jpbl committed
1
/*
2
 *  Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 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>
jpbl's avatar
jpbl committed
9 10
 *  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
11
 *  the Free Software Foundation; either version 3 of the License, or
jpbl's avatar
jpbl committed
12
 *  (at your option) any later version.
13
 *
jpbl's avatar
jpbl committed
14 15 16 17
 *  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.
18
 *
jpbl's avatar
jpbl committed
19 20 21
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 23 24 25 26 27 28 29 30 31 32
 *
 *  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
33 34
 */

35
#ifdef HAVE_CONFIG_H
36
#include "config.h"
37
#endif
38

39
#include "logger.h"
40 41 42 43
#include "managerimpl.h"

#include "account.h"
#include "dbus/callmanager.h"
44
#include "global.h"
45
#include "fileutils.h"
46
#include "sip/sipaccount.h"
47
#include "im/instant_messaging.h"
48
#include "iax/iaxaccount.h"
49
#include "numbercleaner.h"
50 51
#include "config/yamlparser.h"
#include "config/yamlemitter.h"
52 53 54
#include "audio/alsa/alsalayer.h"
#include "audio/pulseaudio/pulselayer.h"
#include "audio/sound/tonelist.h"
55 56
#include "audio/sound/audiofile.h"
#include "audio/sound/dtmf.h"
57
#include "sip/sipvoiplink.h"
58
#include "iax/iaxvoiplink.h"
59
#include "manager.h"
60

61 62
#include "dbus/configurationmanager.h"

63 64
#include "conference.h"

65
#include <cerrno>
66
#include <algorithm>
67
#include <ctime>
jpbl's avatar
jpbl committed
68 69
#include <cstdlib>
#include <iostream>
70
#include <tr1/functional>
Tristan Matthews's avatar
cleanup  
Tristan Matthews committed
71
#include <iterator>
jpbl's avatar
jpbl committed
72
#include <fstream>
73
#include <sstream>
jpbl's avatar
jpbl committed
74
#include <sys/types.h> // mkdir(2)
75
#include <sys/stat.h>  // mkdir(2)
76

77
ManagerImpl::ManagerImpl() :
78 79
    preferences(), voipPreferences(), addressbookPreference(),
    hookPreference(),  audioPreference(), shortcutPreferences(),
80
    hasTriedToRegister_(false), audioCodecFactory(), dbus_(), config_(),
81 82
    currentCallId_(), currentCallMutex_(), audiodriver_(0), dtmfKey_(),
    toneMutex_(), telephoneTone_(), audiofile_(), audioLayerMutex_(),
83 84
    waitingCall_(), waitingCallMutex_(), nbIncomingWaitingCall_(0), path_(),
    callAccountMap_(), callAccountMapMutex_(), IPToIPMap_(), accountMap_(),
85
    mainBuffer_(), conferenceMap_(), history_()
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
86
{
87
    // initialize random generator for call id
88
    srand(time(NULL));
jpbl's avatar
jpbl committed
89 90
}

91
void ManagerImpl::init(const std::string &config_file)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
92
{
93
    path_ = config_file.empty() ? createConfigFile() : config_file;
94
    DEBUG("Manager: configuration file path: %s", path_.c_str());
95

96
    try {
97 98 99 100 101
        Conf::YamlParser parser(path_.c_str());
        parser.serializeEvents();
        parser.composeEvents();
        parser.constructNativeData();
        loadAccountMap(parser);
Tristan Matthews's avatar
Tristan Matthews committed
102
    } catch (const Conf::YamlParserException &e) {
103
        ERROR("Manager: %s", e.what());
104
        fflush(stderr);
105
        loadDefaultAccountMap();
106 107
    }

108
    initAudioDriver();
109

Tristan Matthews's avatar
Tristan Matthews committed
110 111 112
    {
        ost::MutexLock lock(audioLayerMutex_);
        if (audiodriver_) {
113
            telephoneTone_.reset(new TelephoneTone(preferences.getZoneToneChoice(), audiodriver_->getSampleRate()));
114
            dtmfKey_.reset(new DTMF(8000));
Tristan Matthews's avatar
Tristan Matthews committed
115
        }
116
    }
117

118
    history_.load(preferences.getHistoryLimit());
119
    registerAccounts();
jpbl's avatar
jpbl committed
120 121
}

122
void ManagerImpl::terminate()
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
123
{
124
    std::vector<std::string> callList(getCallList());
125
    DEBUG("Manager: Hangup %zu remaining call", callList.size());
126

127 128
    for (std::vector<std::string>::iterator iter = callList.begin();
         iter != callList.end(); ++iter)
129
        hangupCall(*iter);
130

131 132
    saveConfig();

133
    SIPVoIPLink::destroy();
134 135 136 137
    // Unload account map AFTER destroying
    // the SIPVoIPLink, the link still needs the accounts for pjsip cleanup
    unloadAccountMap();

Tristan Matthews's avatar
Tristan Matthews committed
138
    ost::MutexLock lock(audioLayerMutex_);
139

140 141
    delete audiodriver_;
    audiodriver_ = NULL;
jpbl's avatar
jpbl committed
142 143
}

144
bool ManagerImpl::isCurrentCall(const std::string& callId) const
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
145
{
146
    return currentCallId_ == callId;
jpbl's avatar
jpbl committed
147 148
}

149
bool ManagerImpl::hasCurrentCall() const
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
150
{
151
    return not currentCallId_.empty();
jpbl's avatar
jpbl committed
152 153
}

154
std::string
155
ManagerImpl::getCurrentCallId() const
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
156
{
157
    return currentCallId_;
jpbl's avatar
jpbl committed
158 159
}

160
void ManagerImpl::switchCall(const std::string& id)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
161
{
162
    ost::MutexLock m(currentCallMutex_);
163
    DEBUG("----- Switch current call id to %s -----", id.c_str());
164
    currentCallId_ = id;
jpbl's avatar
jpbl committed
165 166 167 168 169
}

///////////////////////////////////////////////////////////////////////////////
// Management of events' IP-phone user
///////////////////////////////////////////////////////////////////////////////
170
/* Main Thread */
Emmanuel Milou's avatar
Emmanuel Milou committed
171

172
bool ManagerImpl::outgoingCall(const std::string& account_id,
173 174 175
                               const std::string& call_id,
                               const std::string& to,
                               const std::string& conf_id)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
176
{
177
    if (call_id.empty()) {
178
        DEBUG("Manager: New outgoing call abort, missing callid");
179 180 181
        return false;
    }

182
    // Call ID must be unique
183
    if (not getAccountFromCall(call_id).empty()) {
184
        ERROR("Manager: Error: Call id already exists in outgoing call");
185 186 187
        return false;
    }

188
    DEBUG("Manager: New outgoing call %s to %s", call_id.c_str(), to.c_str());
189

190 191
    stopTone();

192
    std::string current_call_id(getCurrentCallId());
193

194
    std::string prefix(hookPreference.getNumberAddPrefix());
195

196
    std::string to_cleaned(NumberCleaner::clean(to, prefix));
Emmanuel Milou's avatar
Emmanuel Milou committed
197

198 199
    // in any cases we have to detach from current communication
    if (hasCurrentCall()) {
200
        DEBUG("Manager: Has current call (%s) put it onhold", current_call_id.c_str());
201

202
        // if this is not a conferenceand this and is not a conference participant
203
        if (not isConference(current_call_id) and not isConferenceParticipant(current_call_id))
204
            onHoldCall(current_call_id);
205
        else if (isConference(current_call_id) and not isConferenceParticipant(call_id))
206
            detachParticipant(MainBuffer::DEFAULT_ID, current_call_id);
207
    }
208

209
    DEBUG("Manager: Selecting account %s", account_id.c_str());
210

211 212
    // fallback using the default sip account if the specied doesn't exist
    std::string use_account_id = "";
213
    if (!accountExists(account_id)) {
214 215 216 217 218
        WARN("Manager: Account does not exist, trying with default SIP account");
        use_account_id = SIPAccount::IP2IP_PROFILE;
    }
    else {
        use_account_id = account_id;
219
    }
220

221 222 223
    // Is this account exist
    if (!associateCallToAccount(call_id, use_account_id))
        WARN("Manager: Warning: Could not associate call id %s to account id %s", call_id.c_str(), use_account_id.c_str());
224

225
    try {
226 227 228
        Call *call = getAccountLink(account_id)->newOutgoingCall(call_id, to_cleaned);

        switchCall(call_id);
229
        call->setConfId(conf_id);
230
    } catch (const VoipLinkException &e) {
231
        callFailure(call_id);
232
        ERROR("Manager: %s", e.what());
233
        return false;
234 235
    }

Alexandre Savard's avatar
Alexandre Savard committed
236 237
    getMainBuffer()->stateInfo();

238
    return true;
jpbl's avatar
jpbl committed
239 240
}

yanmorin's avatar
 
yanmorin committed
241
//THREAD=Main : for outgoing Call
242
bool ManagerImpl::answerCall(const std::string& call_id)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
243
{
244
    DEBUG("Manager: Answer call %s", call_id.c_str());
245

246
    // If sflphone is ringing
247
    stopTone();
248

249
    // store the current call id
250
    std::string current_call_id(getCurrentCallId());
251

252
    // Retreive call coresponding to this id
253 254 255
    std::string account_id = getAccountFromCall(call_id);
    Call *call = getAccountLink(account_id)->getCall(call_id);

256
    if (call == NULL) {
257
        ERROR("Manager: Error: Call is null");
258
    }
259

260 261
    // in any cases we have to detach from current communication
    if (hasCurrentCall()) {
262

263
        DEBUG("Manager: Currently conversing with %s", current_call_id.c_str());
264

265
        if (not isConference(current_call_id) and not isConferenceParticipant(current_call_id)) {
266
            DEBUG("Manager: Answer call: Put the current call (%s) on hold", current_call_id.c_str());
267
            onHoldCall(current_call_id);
268
        } else if (isConference(current_call_id) and not isConferenceParticipant(call_id)) {
269
            // if we are talking to a conference and we are answering an incoming call
270
            DEBUG("Manager: Detach main participant from conference");
271
            detachParticipant(MainBuffer::DEFAULT_ID, current_call_id);
272 273
        }
    }
274

275
    try {
276
        getAccountLink(account_id)->answer(call);
277
    } catch (const std::runtime_error &e) {
278
        ERROR("Manager: Error: %s", e.what());
279
    }
Alexandre Savard's avatar
Alexandre Savard committed
280

281
    // if it was waiting, it's waiting no more
282
    removeWaitingCall(call_id);
283

284
    // if we dragged this call into a conference already
285
    if (isConferenceParticipant(call_id))
286
        switchCall(call->getConfId());
287
    else
288
        switchCall(call_id);
289

290
    // Connect streams
291
    addStream(call_id);
292

Alexandre Savard's avatar
Alexandre Savard committed
293 294
    getMainBuffer()->stateInfo();

295
    // Start recording if set in preference
296
    if (audioPreference.getIsAlwaysRecording())
297
        setRecordingCall(call_id);
298 299

    // update call state on client side
300
    if (audioPreference.getIsAlwaysRecording())
301
        dbus_.getCallManager()->callStateChanged(call_id, "RECORD");
302
    else
303
        dbus_.getCallManager()->callStateChanged(call_id, "CURRENT");
304

305
    return true;
jpbl's avatar
jpbl committed
306 307
}

yanmorin's avatar
 
yanmorin committed
308
//THREAD=Main
309
void ManagerImpl::hangupCall(const std::string& callId)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
310
{
Tristan Matthews's avatar
Tristan Matthews committed
311
    DEBUG("Manager: Hangup call %s", callId.c_str());
312

313
    // store the current call id
314
    std::string currentCallId(getCurrentCallId());
yanmorin's avatar
 
yanmorin committed
315

316
    stopTone();
317

318
    /* Broadcast a signal over DBus */
319
    DEBUG("Manager: Send DBUS call state change (HUNGUP) for id %s", callId.c_str());
320
    dbus_.getCallManager()->callStateChanged(callId, "HUNGUP");
321

322
    if (not isValidCall(callId) and not isIPToIP(callId)) {
323
        ERROR("Manager: Error: Could not hang up call, call not valid");
324
        return;
325 326
    }

327 328 329
    // Disconnect streams
    removeStream(callId);

330
    if (isConferenceParticipant(callId)) {
331 332
        Conference *conf = getConferenceFromCallID(callId);

333 334
        if (conf != NULL) {
            // remove this participant
335
            removeParticipant(callId);
336
            processRemainingParticipants(currentCallId, conf);
337 338
        }
    } else {
339 340
        // we are not participating in a conference, current call switched to ""
        if (not isConference(currentCallId))
341
            switchCall("");
342
    }
343

344
    if (isIPToIP(callId)) {
345
        /* Direct IP to IP call */
346
        try {
347
            Call * call = SIPVoIPLink::instance()->getCall(callId);
348
            history_.addCall(call, preferences.getHistoryLimit());
349 350
            SIPVoIPLink::instance()->hangup(callId);
        } catch (const VoipLinkException &e) {
351
            ERROR("%s", e.what());
352
        }
353 354
    } else {
        std::string accountId(getAccountFromCall(callId));
355 356
        VoIPLink *link = getAccountLink(accountId);
        Call * call = link->getCall(callId);
357
        history_.addCall(call, preferences.getHistoryLimit());
358
        link->hangup(callId);
359
        removeCallAccount(callId);
360
    }
361

Alexandre Savard's avatar
Alexandre Savard committed
362
    getMainBuffer()->stateInfo();
jpbl's avatar
jpbl committed
363 364
}

365
bool ManagerImpl::hangupConference(const std::string& id)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
366
{
367
    DEBUG("Manager: Hangup conference %s", id.c_str());
Alexandre Savard's avatar
Alexandre Savard committed
368

369
    ConferenceMap::iterator iter_conf = conferenceMap_.find(id);
Alexandre Savard's avatar
Alexandre Savard committed
370

371
    if (iter_conf != conferenceMap_.end()) {
372
        Conference *conf = iter_conf->second;
373

374 375
        if (conf) {
            ParticipantSet participants(conf->getParticipantList());
376

377 378 379
            for (ParticipantSet::const_iterator iter = participants.begin();
                    iter != participants.end(); ++iter)
                hangupCall(*iter);
380
        } else {
381
            ERROR("Manager: No such conference %s", id.c_str());
382 383
            return false;
        }
384
    }
Alexandre Savard's avatar
Alexandre Savard committed
385

386
    switchCall("");
387

Alexandre Savard's avatar
Alexandre Savard committed
388
    getMainBuffer()->stateInfo();
389

390
    return true;
Alexandre Savard's avatar
Alexandre Savard committed
391 392
}

jpbl's avatar
jpbl committed
393

yanmorin's avatar
 
yanmorin committed
394
//THREAD=Main
395
void ManagerImpl::onHoldCall(const std::string& callId)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
396
{
397
    DEBUG("Manager: Put call %s on hold", callId.c_str());
398

399
    stopTone();
400

401
    std::string current_call_id(getCurrentCallId());
402

403
    try {
404
        if (isIPToIP(callId)) {
405 406 407 408
            SIPVoIPLink::instance()-> onhold(callId);
        } else {
            /* Classic call, attached to an account */
            std::string account_id(getAccountFromCall(callId));
409

410
            if (account_id.empty()) {
411
                DEBUG("Manager: Account ID %s or callid %s doesn't exists in call onHold", account_id.c_str(), callId.c_str());
412 413
                return;
            }
414 415 416 417

            getAccountLink(account_id)->onhold(callId);
        }
    } catch (const VoipLinkException &e) {
418
        ERROR("Manager: Error: %s", e.what());
419
    }
420

421
    // Unbind calls in main buffer
422
    removeStream(callId);
423

424
    // Remove call from teh queue if it was still there
425
    removeWaitingCall(callId);
426

427
    // keeps current call id if the action is not holding this call or a new outgoing call
428
    // this could happen in case of a conference
429
    if (current_call_id == callId)
430
        switchCall("");
431

432
    dbus_.getCallManager()->callStateChanged(callId, "HOLD");
433

Alexandre Savard's avatar
Alexandre Savard committed
434
    getMainBuffer()->stateInfo();
yanmorin's avatar
 
yanmorin committed
435 436 437
}

//THREAD=Main
438
void ManagerImpl::offHoldCall(const std::string& callId)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
439
{
440
    std::string accountId;
441
    std::string codecName;
442

443
    DEBUG("Manager: Put call %s off hold", callId.c_str());
444

445
    stopTone();
446

447
    std::string currentCallId(getCurrentCallId());
448

449
    //Place current call on hold if it isn't
450

451
    if (hasCurrentCall()) {
452

453
        if (not isConference(currentCallId) and not isConferenceParticipant(currentCallId)) {
454
            DEBUG("Manager: Has current call (%s), put on hold", currentCallId.c_str());
455
            onHoldCall(currentCallId);
456
        } else if (isConference(currentCallId) and not isConferenceParticipant(callId))
457
            detachParticipant(MainBuffer::DEFAULT_ID, currentCallId);
458
    }
alexandresavard's avatar
alexandresavard committed
459

460 461
    bool isRec = false;

462
    if (isIPToIP(callId))
Tristan Matthews's avatar
Tristan Matthews committed
463
        SIPVoIPLink::instance()->offhold(callId);
464
    else {
465
        /* Classic call, attached to an account */
466
        accountId = getAccountFromCall(callId);
467

468
        DEBUG("Manager: Setting offhold, Account %s, callid %s", accountId.c_str(), callId.c_str());
469

470 471 472
        Call * call = getAccountLink(accountId)->getCall(callId);

        if (call) {
473
            isRec = call->isRecording();
474
            getAccountLink(accountId)->offhold(callId);
475
        }
476
    }
477

478
    dbus_.getCallManager()->callStateChanged(callId, isRec ? "UNHOLD_RECORD" : "UNHOLD_CURRENT");
479

480
    if (isConferenceParticipant(callId)) {
481
        std::string currentAccountId(getAccountFromCall(callId));
482
        Call *call = getAccountLink(currentAccountId)->getCall(callId);
483

484
        if (call)
485
            switchCall(call->getConfId());
486

487
    } else
488
        switchCall(callId);
489

490 491
    addStream(callId);

Alexandre Savard's avatar
Alexandre Savard committed
492
    getMainBuffer()->stateInfo();
jpbl's avatar
jpbl committed
493 494
}

yanmorin's avatar
 
yanmorin committed
495
//THREAD=Main
496
bool ManagerImpl::transferCall(const std::string& callId, const std::string& to)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
497
{
498
    if (isConferenceParticipant(callId)) {
499
        removeParticipant(callId);
500 501
        Conference *conf = getConferenceFromCallID(callId);
        processRemainingParticipants(callId, conf);
Tristan Matthews's avatar
cleanup  
Tristan Matthews committed
502
    } else if (not isConference(getCurrentCallId()))
503
        switchCall("");
504

505
    // Direct IP to IP call
506
    if (isIPToIP(callId)) {
507
        SIPVoIPLink::instance()->transfer(callId, to);
508 509
    } else {
        std::string accountID(getAccountFromCall(callId));
510

511
        if (accountID.empty())
512
            return false;
513

514 515
        VoIPLink *link = getAccountLink(accountID);
        link->transfer(callId, to);
516
    }
517

518
    // remove waiting call in case we make transfer without even answer
519
    removeWaitingCall(callId);
520

Alexandre Savard's avatar
Alexandre Savard committed
521 522
    getMainBuffer()->stateInfo();

523
    return true;
jpbl's avatar
jpbl committed
524 525
}

526
void ManagerImpl::transferFailed()
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
527
{
528
    dbus_.getCallManager()->transferFailed();
529 530
}

Tristan Matthews's avatar
Tristan Matthews committed
531
void ManagerImpl::transferSucceeded()
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
532
{
Tristan Matthews's avatar
Tristan Matthews committed
533
    dbus_.getCallManager()->transferSucceeded();
534 535
}

536
bool ManagerImpl::attendedTransfer(const std::string& transferID, const std::string& targetID)
537
{
538
    if (isIPToIP(transferID))
Rafaël Carré's avatar
Rafaël Carré committed
539
        return SIPVoIPLink::instance()->attendedTransfer(transferID, targetID);
540

Rafaël Carré's avatar
Rafaël Carré committed
541
    // Classic call, attached to an account
542
    std::string accountid(getAccountFromCall(transferID));
Alexandre Savard's avatar
Alexandre Savard committed
543

544 545 546 547
    if (accountid.empty())
        return false;

    return getAccountLink(accountid)->attendedTransfer(transferID, targetID);
548 549
}

yanmorin's avatar
 
yanmorin committed
550
//THREAD=Main : Call:Incoming
551
void ManagerImpl::refuseCall(const std::string& id)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
552
{
553
    stopTone();
554

Rafaël Carré's avatar
Rafaël Carré committed
555
    if (getCallList().size() <= 1) {
Tristan Matthews's avatar
Tristan Matthews committed
556
        ost::MutexLock lock(audioLayerMutex_);
557
        audiodriver_->stopStream();
558
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
559

560
    /* Direct IP to IP call */
561

562
    if (isIPToIP(id))
563
        SIPVoIPLink::instance()->refuse(id);
564
    else {
565
        /* Classic call, attached to an account */
566 567
        std::string accountid = getAccountFromCall(id);

Rafaël Carré's avatar
Rafaël Carré committed
568
        if (accountid.empty())
Rafaël Carré's avatar
Rafaël Carré committed
569
            return;
570

Rafaël Carré's avatar
Rafaël Carré committed
571
        getAccountLink(accountid)->refuse(id);
572

573
        removeCallAccount(id);
574
    }
575

576
    removeWaitingCall(id);
577
    dbus_.getCallManager()->callStateChanged(id, "HUNGUP");
578

579
    // Disconnect streams
580
    removeStream(id);
581

Alexandre Savard's avatar
Alexandre Savard committed
582
    getMainBuffer()->stateInfo();
jpbl's avatar
jpbl committed
583 584
}

585
Conference*
586
ManagerImpl::createConference(const std::string& id1, const std::string& id2)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
587
{
588
    DEBUG("Manager: Create conference with call %s and %s", id1.c_str(), id2.c_str());
589

590
    Conference* conf = new Conference;
591

592 593
    conf->add(id1);
    conf->add(id2);
594

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

598
    // broadcast a signal over dbus
599
    dbus_.getCallManager()->conferenceCreated(conf->getConfID());
600

601
    return conf;
602 603
}

604
void ManagerImpl::removeConference(const std::string& conference_id)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
605
{
606 607
    DEBUG("Manager: Remove conference %s", conference_id.c_str());
    DEBUG("Manager: number of participants: %u", conferenceMap_.size());
608
    ConferenceMap::iterator iter = conferenceMap_.find(conference_id);
609

610
    Conference* conf = 0;
611

612
    if (iter != conferenceMap_.end())
613
        conf = iter->second;
614

615
    if (conf == NULL) {