managerimpl.cpp 121 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>
9
 *
jpbl's avatar
jpbl committed
10
11
 *  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
12
 *  the Free Software Foundation; either version 3 of the License, or
jpbl's avatar
jpbl committed
13
 *  (at your option) any later version.
14
 *
jpbl's avatar
jpbl committed
15
16
17
18
 *  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.
19
 *
jpbl's avatar
jpbl committed
20
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
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24
25
26
27
28
29
30
31
32
33
 *
 *  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
34
35
 */

36
37
#include "config.h"

38
39
40
41
#include "managerimpl.h"

#include "account.h"
#include "dbus/callmanager.h"
42
#include "global.h"
43
#include "sip/sipaccount.h"
44

45
46
47
48
49
50
#include "audio/alsa/alsalayer.h"
#include "audio/pulseaudio/pulselayer.h"
#include "audio/sound/tonelist.h"
#include "history/historymanager.h"
#include "accountcreator.h" // create new account
#include "sip/sipvoiplink.h"
51
#include "iax/iaxvoiplink.h"
52
53
54
#include "manager.h"
#include "dbus/configurationmanager.h"

55
56
#include "conference.h"

57
58
#include <cerrno>
#include <ctime>
jpbl's avatar
jpbl committed
59
60
61
#include <cstdlib>
#include <iostream>
#include <fstream>
62
#include <sstream>
jpbl's avatar
jpbl committed
63
#include <sys/types.h> // mkdir(2)
64
#include <sys/stat.h>  // mkdir(2)
65

66
#include <pwd.h>       // getpwuid
67

68
#define DIRECT_IP_CALL	"IP CALL"
jpbl's avatar
jpbl committed
69
70
71
72
73
74

#define fill_config_str(name, value) \
  (_config.addConfigTreeItem(section, Conf::ConfigTreeItem(std::string(name), std::string(value), type_str)))
#define fill_config_int(name, value) \
  (_config.addConfigTreeItem(section, Conf::ConfigTreeItem(std::string(name), std::string(value), type_int)))

75
76
#define MD5_APPEND(pms,buf,len) pj_md5_update(pms, (const pj_uint8_t*)buf, len)

77
ManagerImpl::ManagerImpl (void) :
Alexandre Savard's avatar
Alexandre Savard committed
78
    _hasTriedToRegister (false), _config(), _currentCallId2(),
79
80
    _currentCallMutex(), _audiodriver (NULL),
    _dtmfKey (NULL), _audioCodecFactory(), _toneMutex(),
Alexandre Savard's avatar
Alexandre Savard committed
81
    _telephoneTone (NULL), _audiofile (NULL), _spkr_volume (0),
82
    _mic_volume (0), _mutex(), _waitingCall(),
Alexandre Savard's avatar
Alexandre Savard committed
83
    _waitingCallMutex(), _nbIncomingWaitingCall (0), _path (""),
84
    _setupLoaded (false), _callAccountMap(),
Alexandre Savard's avatar
Alexandre Savard committed
85
86
    _callAccountMapMutex(), _callConfigMap(), _accountMap(),
    _directIpAccount (NULL), _cleaner (NULL), _history (NULL)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
87
{
88
89
90
91
92
93

    // initialize random generator for call id
    srand (time (NULL));

    _cleaner = new NumberCleaner();
    _history = new HistoryManager();
94
    _imModule = new sfl::InstantMessaging();
95

yanmorin's avatar
   
yanmorin committed
96
#ifdef TEST
97
98
99
100
    testAccountMap();
    loadAccountMap();
    testCallAccountMap();
    unloadAccountMap();
yanmorin's avatar
   
yanmorin committed
101
102
#endif

103
104
    // should be call before initConfigFile
    // loadAccountMap();, called in init() now.
jpbl's avatar
jpbl committed
105
106
107
}

// never call if we use only the singleton...
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
108
109
ManagerImpl::~ManagerImpl (void)
{
110
	delete _audiofile;
111
112
    delete _cleaner;
    delete _history;
113
    delete _imModule;
jpbl's avatar
jpbl committed
114
115
}

Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
116
117
void ManagerImpl::init ()
{
118
    // Load accounts, init map
Julien Bonjean's avatar
Julien Bonjean committed
119
    buildConfiguration();
120
121
122
    initVolume();
    initAudioDriver();
    selectAudioDriver();
123

124
125
    // Initialize the list of supported audio codecs
    initAudioCodec();
126

127
128
    audioLayerMutexLock();

129
130
    if (_audiodriver != NULL) {
        unsigned int sampleRate = _audiodriver->getSampleRate();
jpbl's avatar
jpbl committed
131

132
        _debugInit ("Manager: Load telephone tone");
Julien Bonjean's avatar
Julien Bonjean committed
133
        std::string country = preferences.getZoneToneChoice();
134
        _telephoneTone = new TelephoneTone (country, sampleRate);
135

136
137
138
139
140
        _debugInit ("Manager: Loading DTMF key (%d)", sampleRate);
        // if(sampleRate > 44100)

        sampleRate = 8000;

141
142
        _dtmfKey = new DTMF (sampleRate);
    }
143

144
145
    audioLayerMutexUnlock();

146
    // Load the history
Julien Bonjean's avatar
Julien Bonjean committed
147
    _history->load_history (preferences.getHistoryLimit());
148
149
150

    // Init the instant messaging module
    _imModule->init();
151

152
153
    // Register accounts
    initRegisterAccounts(); //getEvents();
jpbl's avatar
jpbl committed
154
155
}

Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
156
157
void ManagerImpl::terminate ()
{
158

159
    _debug ("Manager: Terminate ");
jpbl's avatar
jpbl committed
160

161
162
163
164
165
166
167
168
169
    std::vector<std::string> callList = getCallList();
    _debug ("Manager: Hangup %d remaining call", callList.size());
    std::vector<std::string>::iterator iter = callList.begin();

    while (iter != callList.end()) {
        hangupCall (*iter);
        iter++;
    }

170
    unloadAccountMap();
171

172
173
    _debug ("Manager: Unload DTMF key");
    delete _dtmfKey;
jpbl's avatar
jpbl committed
174

175
176
177
    _debug ("Manager: Unload telephone tone");
    delete _telephoneTone;
    _telephoneTone = NULL;
178

179
180
    audioLayerMutexLock();

181
182
183
    _debug ("Manager: Unload audio driver");
    delete _audiodriver;
    _audiodriver = NULL;
jpbl's avatar
jpbl committed
184

185
    _debug ("Manager: Unload audio codecs ");
186
    _audioCodecFactory.deleteHandlePointer();
187
    audioLayerMutexUnlock();
jpbl's avatar
jpbl committed
188
189
}

190
bool ManagerImpl::isCurrentCall (const std::string& callId)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
191
{
Rafaël Carré's avatar
Rafaël Carré committed
192
    return _currentCallId2 == callId;
jpbl's avatar
jpbl committed
193
194
}

Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
195
196
bool ManagerImpl::hasCurrentCall ()
{
Rafaël Carré's avatar
Rafaël Carré committed
197
	return _currentCallId2 != "";
jpbl's avatar
jpbl committed
198
199
}

200
const std::string&
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
201
202
ManagerImpl::getCurrentCallId ()
{
203
    return _currentCallId2;
jpbl's avatar
jpbl committed
204
205
}

206
void ManagerImpl::switchCall (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
207
{
208
209
210
    ost::MutexLock m (_currentCallMutex);
    _debug ("----- Switch current call id to %s -----", id.c_str());
    _currentCallId2 = id;
jpbl's avatar
jpbl committed
211
212
213
214
215
}

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

218
bool ManagerImpl::outgoingCall (const std::string& account_id,
219
                                const std::string& call_id, const std::string& to, const std::string& conf_id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
220
{
221
222
223
224

    std::string pattern, to_cleaned;
    Call::CallConfiguration callConfig;
    SIPVoIPLink *siplink;
225

226
227
228
229
230
    if (call_id.empty()) {
        _debug ("Manager: New outgoing call abbort, missing callid");
        return false;
    }

231
    // Call ID must be unique
232
    if (getAccountFromCall (call_id) != "") {
233
234
235
236
237
        _error ("Manager: Error: Call id already exists in outgoing call");
        return false;
    }


238
    _debug ("Manager: New outgoing call %s to %s", call_id.c_str(), to.c_str());
239

240
241
    stopTone();

242
    std::string current_call_id = getCurrentCallId();
243

244
    if (hookPreference.getNumberEnabled()) {
Julien Bonjean's avatar
Julien Bonjean committed
245
        _cleaner->set_phone_number_prefix (hookPreference.getNumberAddPrefix());
246
247
    }
    else {
248
        _cleaner->set_phone_number_prefix ("");
249
    }
250
    to_cleaned = _cleaner->clean (to);
Emmanuel Milou's avatar
Emmanuel Milou committed
251

252
    /* Check what kind of call we are dealing with */
253
    checkCallConfiguration (call_id, to_cleaned, &callConfig);
254

255
256
257
    // in any cases we have to detach from current communication
    if (hasCurrentCall()) {
        _debug ("Manager: Has current call (%s) put it onhold", current_call_id.c_str());
258

259
260
        // if this is not a conferenceand this and is not a conference participant
        if (!isConference (current_call_id) && !participToConference (current_call_id)) {
261
       	    onHoldCall (current_call_id);
262
263
264
265
        } else if (isConference (current_call_id) && !participToConference (call_id)) {
            detachParticipant (default_id, current_call_id);
        }
    }
266

267
268
269
    if (callConfig == Call::IPtoIP) {
        _debug ("Manager: Start IP2IP call");
        /* We need to retrieve the sip voiplink instance */
270
        siplink = SIPVoIPLink::instance ();
271

272
        if (siplink->SIPNewIpToIpCall(call_id, to_cleaned)) {
273
274
275
276
277
            switchCall (call_id);
            return true;
        } else {
            callFailure (call_id);
        }
278

279
280
        return false;
    }
281

282
    _debug ("Manager: Selecting account %s", account_id.c_str());
283

284
    // Is this account exist
285
286
287
288
    if (!accountExists (account_id)) {
        _error ("Manager: Error: Account doesn't exist in new outgoing call");
        return false;
    }
289

290
291
292
    if(!associateCallToAccount (call_id, account_id)) {
    	_warn("Manager: Warning: Could not associate call id %s to account id %s", call_id.c_str(), account_id.c_str());
    }
293

294
    Call *call = NULL;
295
296
297
    try {
        call = getAccountLink(account_id)->newOutgoingCall (call_id, to_cleaned);
        
298
        switchCall (call_id);
299
300
301
        
        call->setConfId(conf_id);
    } catch (VoipLinkException &e) {
302
        callFailure (call_id);
303
304
        _error ("Manager: %s", e.what());
	return false;
305
306
    }

Alexandre Savard's avatar
Alexandre Savard committed
307
308
    getMainBuffer()->stateInfo();

309
    return true;
jpbl's avatar
jpbl committed
310
311
}

yanmorin's avatar
   
yanmorin committed
312
//THREAD=Main : for outgoing Call
313
bool ManagerImpl::answerCall (const std::string& call_id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
314
{
315

316
    _debug ("Manager: Answer call %s", call_id.c_str());
317

318
    // If sflphone is ringing
319
    stopTone();
320

321
    // store the current call id
322
    std::string current_call_id = getCurrentCallId();
323

324
    // Retreive call coresponding to this id
325
    std::string account_id = getAccountFromCall (call_id);
326
    Call *call = getAccountLink (account_id)->getCall (call_id);
327
    if (call == NULL) {
328
        _error("Manager: Error: Call is null");
329
    }
330

331
332
    // in any cases we have to detach from current communication
    if (hasCurrentCall()) {
333

334
        _debug ("Manager: Currently conversing with %s", current_call_id.c_str());
335

336
337
338
        if (!isConference(current_call_id) && !participToConference (current_call_id)) {
            // if it is not a conference and is not a conference participant
            _debug ("Manager: Answer call: Put the current call (%s) on hold", current_call_id.c_str());
339
            onHoldCall (current_call_id);
340
341
342
        } else if (isConference (current_call_id) && !participToConference (call_id)) {
            // if we are talking to a conference and we are answering an incoming call
            _debug ("Manager: Detach main participant from conference");
343
344
345
            detachParticipant (default_id, current_call_id);
        }
    }
346

347
348
349
350
351
352
353
354
    try {
        if (!getAccountLink (account_id)->answer (call_id)) {
            removeCallAccount (call_id);
            return false;
        }
    }
    catch(VoipLinkException &e) {
    	_error("Manager: Error: %s", e.what());
355
    }
Alexandre Savard's avatar
Alexandre Savard committed
356

Emmanuel Milou's avatar
Emmanuel Milou committed
357

358
    // if it was waiting, it's waiting no more
359
    removeWaitingCall (call_id);
360

361
362
363
364
365
366
    // if we dragged this call into a conference already
    if (participToConference (call_id)) {
        switchCall (call->getConfId());
    } else {
        switchCall (call_id);
    }
367

368
369
370
    // Connect streams
    addStream (call_id);

Alexandre Savard's avatar
Alexandre Savard committed
371
372
    getMainBuffer()->stateInfo();

373
374
375
376
377
378
379
    // Start recording if set in preference
    if(audioPreference.getIsAlwaysRecording()) {
    	setRecordingCall(call_id);
    }

    // update call state on client side
    if(audioPreference.getIsAlwaysRecording()) {
380
        _dbus.getCallManager()->callStateChanged (call_id, "RECORD");
381
382
    }
    else {
383
    	_dbus.getCallManager()->callStateChanged(call_id, "CURRENT");
384
385
    }

386
    return true;
jpbl's avatar
jpbl committed
387
388
}

yanmorin's avatar
   
yanmorin committed
389
//THREAD=Main
390
bool ManagerImpl::hangupCall (const std::string& callId)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
391
{
392
    bool returnValue = true;
393

394
395
    _info ("Manager: Hangup call %s", callId.c_str());

396

397
398
399
    // First stop audio layer if there is no call anymore
    int nbCalls = getCallList().size();
    if(nbCalls <= 0) {
400
401
402

    	audioLayerMutexLock();

403
        if(_audiodriver == NULL) {
404
        	audioLayerMutexUnlock();
405
406
407
408
409
        	_error("Manager: Error: Audio layer was not instantiated");
        	return returnValue;
        }

        _debug ("Manager: stop audio stream, there is no call remaining", nbCalls);
410
        _audiodriver->stopStream();
411
        audioLayerMutexUnlock();
412
413
414
    }


415
    // store the current call id
416
    std::string currentCallId = getCurrentCallId();
yanmorin's avatar
   
yanmorin committed
417

418
    stopTone();
419

420
    /* Broadcast a signal over DBus */
421
    _debug ("Manager: Send DBUS call state change (HUNGUP) for id %s", callId.c_str());
422
    _dbus.getCallManager()->callStateChanged (callId, "HUNGUP");
423

424
    if (not isValidCall(callId) and not getConfigFromCall(callId) == Call::IPtoIP) {
425
    	_error("Manager: Error: Could not hang up call, call not valid");
426
        return false;
427
428
    }

429
430
431
    // Disconnect streams
    removeStream(callId);

432
    if (participToConference (callId)) {
433
    	Conference *conf = getConferenceFromCallID (callId);
434
435
        if (conf != NULL) {
            // remove this participant
436
437
            removeParticipant (callId);
            processRemainingParticipant (currentCallId, conf);
438
439
440
        }
    } else {
        // we are not participating to a conference, current call switched to ""
441
        if (!isConference (currentCallId)) {
442
            switchCall ("");
443
        }
444
    }
445

446
447
    if (getConfigFromCall (callId) == Call::IPtoIP) {
        /* Direct IP to IP call */
448
449
450
451
452
453
454
455
        try {
            returnValue = SIPVoIPLink::instance ()->hangup (callId);
        }
        catch (const VoipLinkException &e)
        {
            _error("%s", e.what());
            returnValue = 1;
        }
456
457
    }
    else {
458
    	std::string accountId = getAccountFromCall (callId);
459
460
        returnValue = getAccountLink (accountId)->hangup (callId);
        removeCallAccount (callId);
461
    }
462

Alexandre Savard's avatar
Alexandre Savard committed
463
464
    getMainBuffer()->stateInfo();

465
    return returnValue;
jpbl's avatar
jpbl committed
466
467
}

468
bool ManagerImpl::hangupConference (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
469
{
470
    _debug ("Manager: Hangup conference %s", id.c_str());
Alexandre Savard's avatar
Alexandre Savard committed
471

472
    ConferenceMap::iterator iter_conf = _conferencemap.find (id);
Alexandre Savard's avatar
Alexandre Savard committed
473

474
    std::string currentAccountId;
Alexandre Savard's avatar
Alexandre Savard committed
475

476
    if (iter_conf != _conferencemap.end()) {
477
        Conference *conf = iter_conf->second;
Alexandre Savard's avatar
Alexandre Savard committed
478

479
480
        ParticipantSet participants = conf->getParticipantList();
        ParticipantSet::iterator iter_participant = participants.begin();
Alexandre Savard's avatar
Alexandre Savard committed
481

482
        while (iter_participant != participants.end()) {
483
            _debug ("Manager: Hangup conference participant %s", (*iter_participant).c_str());
Alexandre Savard's avatar
Alexandre Savard committed
484

485
            hangupCall (*iter_participant);
Alexandre Savard's avatar
Alexandre Savard committed
486

487
488
489
            iter_participant++;
        }
    }
Alexandre Savard's avatar
Alexandre Savard committed
490

491
    switchCall ("");
492

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

495
    return true;
Alexandre Savard's avatar
Alexandre Savard committed
496
497
}

yanmorin's avatar
   
yanmorin committed
498
//THREAD=Main
499
bool ManagerImpl::cancelCall (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
500
{
501
    std::string accountid;
502
    bool returnValue;
yanmorin's avatar
   
yanmorin committed
503

504
    _debug ("Manager: Cancel call");
505

506
    stopTone();
507

508
    /* Direct IP to IP call */
509

510
    if (getConfigFromCall (id) == Call::IPtoIP) {
511
        returnValue = SIPVoIPLink::instance ()->cancel (id);
512
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
513

514
515
516
    /* Classic call, attached to an account */
    else {
        accountid = getAccountFromCall (id);
517

518
        if (accountid == "") {
519
520
521
            _debug ("! Manager Cancel Call: Call doesn't exists");
            return false;
        }
522

523
        returnValue = getAccountLink (accountid)->cancel (id);
524

525
526
        removeCallAccount (id);
    }
527

528
529
    // it could be a waiting call?
    removeWaitingCall (id);
530

531
532
    removeStream(id);

533
    switchCall ("");
Emmanuel Milou's avatar
Emmanuel Milou committed
534

Alexandre Savard's avatar
Alexandre Savard committed
535
536
    getMainBuffer()->stateInfo();

537
    return returnValue;
jpbl's avatar
jpbl committed
538
539
}

yanmorin's avatar
   
yanmorin committed
540
//THREAD=Main
541
bool ManagerImpl::onHoldCall (const std::string& callId)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
542
{
543
    std::string account_id;
544
    bool returnValue = false;
yanmorin's avatar
   
yanmorin committed
545

546
    _debug ("Manager: Put call %s on hold", callId.c_str());
547

548
    stopTone();
549

550
    std::string current_call_id = getCurrentCallId();
551

552
    try {
553

554
555
    	if (getConfigFromCall (callId) == Call::IPtoIP) {
    		/* Direct IP to IP call */
556
    		returnValue = SIPVoIPLink::instance ()-> onhold (callId);
557
558
559
560
561
    	}
    	else {
    		/* Classic call, attached to an account */
    		account_id = getAccountFromCall (callId);

562
    		if (account_id == "") {
563
564
565
566
567
568
569
570
    			_debug ("Manager: Account ID %s or callid %s doesn't exists in call onHold", account_id.c_str(), callId.c_str());
    			return false;
    		}
    		returnValue = getAccountLink (account_id)->onhold (callId);
    	}
    }
    catch (VoipLinkException &e){
    	_error("Manager: Error: %s", e.what());
571
    }
572

573
574
575
    // Unbind calls in main buffer
    removeStream(callId);

576
    // Remove call from teh queue if it was still there
577
    removeWaitingCall (callId);
578

579
    // keeps current call id if the action is not holding this call or a new outgoing call
580
581
    // this could happen in case of a conference
    if (current_call_id == callId) {
582
583
        switchCall ("");
    }
584

585
    _dbus.getCallManager()->callStateChanged (callId, "HOLD");
586

Alexandre Savard's avatar
Alexandre Savard committed
587
    getMainBuffer()->stateInfo();
588

589
    return returnValue;
yanmorin's avatar
   
yanmorin committed
590
591
592
}

//THREAD=Main
593
bool ManagerImpl::offHoldCall (const std::string& callId)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
594
{
595

596
    std::string accountId;
597
    bool returnValue, isRec;
598
    std::string codecName;
599

600
    isRec = false;
601

602
    _debug ("Manager: Put call %s off hold", callId.c_str());
603

604
    stopTone();
605

606
    std::string currentCallId = getCurrentCallId();
607

608
    //Place current call on hold if it isn't
609

610
    if (hasCurrentCall()) {
611

612
        // if this is not a conference and this and is not a conference participant
613
614
615
616
617
        if (!isConference (currentCallId) && !participToConference (currentCallId)) {
        	_debug ("Manager: Has current call (%s), put on hold", currentCallId.c_str());
            onHoldCall (currentCallId);
        } else if (isConference (currentCallId) && !participToConference (callId)) {
            detachParticipant (default_id, currentCallId);
618
619
        }
    }
alexandresavard's avatar
alexandresavard committed
620

621
    /* Direct IP to IP call */
622
    if (getConfigFromCall (callId) == Call::IPtoIP) {
623
624
        // is_rec = SIPVoIPLink::instance ()-> isRecording (call_id);
        returnValue = SIPVoIPLink::instance ()-> offhold (callId);
625
626
627
    }
    /* Classic call, attached to an account */
    else {
628
        accountId = getAccountFromCall (callId);
629

630
        _debug ("Manager: Setting offhold, Account %s, callid %s", accountId.c_str(), callId.c_str());
631

632
633
        isRec = getAccountLink (accountId)->getCall (callId)->isRecording();
        returnValue = getAccountLink (accountId)->offhold (callId);
634
    }
635

636
    _dbus.getCallManager()->callStateChanged (callId, isRec ? "UNHOLD_RECORD" : "UNHOLD_CURRENT");
637
638

    if (participToConference (callId)) {
639
        std::string currentAccountId;
640
        Call* call = NULL;
641

642
643
        currentAccountId = getAccountFromCall (callId);
        call = getAccountLink (currentAccountId)->getCall (callId);
644

645
        switchCall (call->getConfId());
646

647
    } else {
648
        switchCall (callId);
649
    }
650

651
652
    addStream(callId);

Alexandre Savard's avatar
Alexandre Savard committed
653
654
    getMainBuffer()->stateInfo();

655
    return returnValue;
jpbl's avatar
jpbl committed
656
657
}

yanmorin's avatar
   
yanmorin committed
658
//THREAD=Main
659
bool ManagerImpl::transferCall (const std::string& callId, const std::string& to)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
660
{
661
    bool returnValue = false;;
662

663
664
    _info ("Manager: Transfer call %s", callId.c_str());

665
    std::string currentCallId = getCurrentCallId();
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680

    if(participToConference(callId)) {
	Conference *conf = getConferenceFromCallID(callId);
	if(conf == NULL) {
	    _error("Manager: Error: Could not find conference from call id");
        }

	removeParticipant (callId);
	processRemainingParticipant (callId, conf);
    }
    else {
	if(!isConference(currentCallId)) {
	    switchCall("");
	}
    }
681

682
    // Direct IP to IP call
683
    if (getConfigFromCall (callId) == Call::IPtoIP) {
684
        returnValue = SIPVoIPLink::instance ()-> transfer (callId, to);
685
686
687
    }
    // Classic call, attached to an account
    else {
688

689
        std::string accountid = getAccountFromCall (callId);
690

691
        if (accountid == "") {
692
693
694
            _warn ("Manager: Call doesn't exists");
            return false;
        }
695

696
        returnValue = getAccountLink (accountid)->transfer (callId, to);
697

698
    }
699

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

Alexandre Savard's avatar
Alexandre Savard committed
703
704
    getMainBuffer()->stateInfo();

705
    return returnValue;
jpbl's avatar
jpbl committed
706
707
}

Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
708
709
void ManagerImpl::transferFailed ()
{
710
	_dbus.getCallManager()->transferFailed();
711
712
}

Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
713
714
void ManagerImpl::transferSucceded ()
{
715
	_dbus.getCallManager()->transferSucceded();
716
717
}

718
bool ManagerImpl::attendedTransfer(const std::string& transferID, const std::string& targetID)
719
{
720
    bool returnValue = false;
721

722
    _debug("Manager: Attended transfer");
723

724
725
    // Direct IP to IP call
    if (getConfigFromCall (transferID) == Call::IPtoIP) {
726
        returnValue = SIPVoIPLink::instance ()-> attendedTransfer(transferID, targetID);
727
728
    }
    else {	// Classic call, attached to an account
729

730
        std::string accountid = getAccountFromCall (transferID);
731

732
        if (accountid == "") {
733
734
735
            _warn ("Manager: Call doesn't exists");
	    return false;
        }
736

737
        returnValue = getAccountLink (accountid)->attendedTransfer (transferID, targetID);
738

739
    }
740

741
    getMainBuffer()->stateInfo();
Alexandre Savard's avatar
Alexandre Savard committed
742

743
    return returnValue;
744
745
}

yanmorin's avatar
   
yanmorin committed
746
//THREAD=Main : Call:Incoming
747
bool ManagerImpl::refuseCall (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
748
{
749
    std::string accountid;
750
    bool returnValue;
751

752
    _debug ("Manager: Refuse call %s", id.c_str());
753

754