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
{
192
    return (_currentCallId2 == callId ? true : false);
jpbl's avatar
jpbl committed
193
194
}

Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
195
196
bool ManagerImpl::hasCurrentCall ()
{
197
    // _debug ("ManagerImpl::hasCurrentCall current call ID = %s", _currentCallId2.c_str());
198

199
200
201
    if (_currentCallId2 != "") {
        return true;
    }
202

203
    return false;
jpbl's avatar
jpbl committed
204
205
}

206
const std::string&
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
207
208
ManagerImpl::getCurrentCallId ()
{
209
    return _currentCallId2;
jpbl's avatar
jpbl committed
210
211
}

212
void ManagerImpl::switchCall (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
213
{
214
215
216
    ost::MutexLock m (_currentCallMutex);
    _debug ("----- Switch current call id to %s -----", id.c_str());
    _currentCallId2 = id;
jpbl's avatar
jpbl committed
217
218
219
220
221
}

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

224
bool ManagerImpl::outgoingCall (const std::string& account_id,
225
                                const std::string& call_id, const std::string& to, const std::string& conf_id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
226
{
227
228
229
230

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

232
233
234
235
236
    if (call_id.empty()) {
        _debug ("Manager: New outgoing call abbort, missing callid");
        return false;
    }

237
    // Call ID must be unique
238
    if (getAccountFromCall (call_id) != "") {
239
240
241
242
243
        _error ("Manager: Error: Call id already exists in outgoing call");
        return false;
    }


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

246
247
    stopTone();

248
    std::string current_call_id = getCurrentCallId();
249

250
    if (hookPreference.getNumberEnabled()) {
Julien Bonjean's avatar
Julien Bonjean committed
251
        _cleaner->set_phone_number_prefix (hookPreference.getNumberAddPrefix());
252
253
    }
    else {
254
        _cleaner->set_phone_number_prefix ("");
255
    }
256
    to_cleaned = _cleaner->clean (to);
Emmanuel Milou's avatar
Emmanuel Milou committed
257

258
    /* Check what kind of call we are dealing with */
259
    checkCallConfiguration (call_id, to_cleaned, &callConfig);
260

261
262
263
    // 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());
264

265
266
        // if this is not a conferenceand this and is not a conference participant
        if (!isConference (current_call_id) && !participToConference (current_call_id)) {
267
       	    onHoldCall (current_call_id);
268
269
270
271
        } else if (isConference (current_call_id) && !participToConference (call_id)) {
            detachParticipant (default_id, current_call_id);
        }
    }
272

273
274
275
    if (callConfig == Call::IPtoIP) {
        _debug ("Manager: Start IP2IP call");
        /* We need to retrieve the sip voiplink instance */
276
        siplink = SIPVoIPLink::instance ();
277

278
        if (siplink->SIPNewIpToIpCall(call_id, to_cleaned)) {
279
280
281
282
283
            switchCall (call_id);
            return true;
        } else {
            callFailure (call_id);
        }
284

285
286
        return false;
    }
287

288
    _debug ("Manager: Selecting account %s", account_id.c_str());
289

290
    // Is this account exist
291
292
293
294
    if (!accountExists (account_id)) {
        _error ("Manager: Error: Account doesn't exist in new outgoing call");
        return false;
    }
295

296
297
298
    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());
    }
299

300
    Call *call = NULL;
301
302
303
    try {
        call = getAccountLink(account_id)->newOutgoingCall (call_id, to_cleaned);
        
304
        switchCall (call_id);
305
306
307
        
        call->setConfId(conf_id);
    } catch (VoipLinkException &e) {
308
        callFailure (call_id);
309
310
        _error ("Manager: %s", e.what());
	return false;
311
312
    }

Alexandre Savard's avatar
Alexandre Savard committed
313
314
    getMainBuffer()->stateInfo();

315
    return true;
jpbl's avatar
jpbl committed
316
317
}

yanmorin's avatar
   
yanmorin committed
318
//THREAD=Main : for outgoing Call
319
bool ManagerImpl::answerCall (const std::string& call_id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
320
{
321

322
    _debug ("Manager: Answer call %s", call_id.c_str());
323

324
    // If sflphone is ringing
325
    stopTone();
326

327
    // store the current call id
328
    std::string current_call_id = getCurrentCallId();
329

330
    // Retreive call coresponding to this id
331
    std::string account_id = getAccountFromCall (call_id);
332
    Call *call = getAccountLink (account_id)->getCall (call_id);
333
    if (call == NULL) {
334
        _error("Manager: Error: Call is null");
335
    }
336

337
338
    // in any cases we have to detach from current communication
    if (hasCurrentCall()) {
339

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

342
343
344
        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());
345
            onHoldCall (current_call_id);
346
347
348
        } 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");
349
350
351
            detachParticipant (default_id, current_call_id);
        }
    }
352

353
354
355
356
357
358
359
360
    try {
        if (!getAccountLink (account_id)->answer (call_id)) {
            removeCallAccount (call_id);
            return false;
        }
    }
    catch(VoipLinkException &e) {
    	_error("Manager: Error: %s", e.what());
361
    }
Alexandre Savard's avatar
Alexandre Savard committed
362

Emmanuel Milou's avatar
Emmanuel Milou committed
363

364
    // if it was waiting, it's waiting no more
365
    removeWaitingCall (call_id);
366

367
368
369
370
371
372
    // if we dragged this call into a conference already
    if (participToConference (call_id)) {
        switchCall (call->getConfId());
    } else {
        switchCall (call_id);
    }
373

374
375
376
    // Connect streams
    addStream (call_id);

Alexandre Savard's avatar
Alexandre Savard committed
377
378
    getMainBuffer()->stateInfo();

379
380
381
382
383
384
385
    // Start recording if set in preference
    if(audioPreference.getIsAlwaysRecording()) {
    	setRecordingCall(call_id);
    }

    // update call state on client side
    if(audioPreference.getIsAlwaysRecording()) {
386
        _dbus.getCallManager()->callStateChanged (call_id, "RECORD");
387
388
    }
    else {
389
    	_dbus.getCallManager()->callStateChanged(call_id, "CURRENT");
390
391
    }

392
    return true;
jpbl's avatar
jpbl committed
393
394
}

yanmorin's avatar
   
yanmorin committed
395
//THREAD=Main
396
bool ManagerImpl::hangupCall (const std::string& callId)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
397
{
398
    bool returnValue = true;
399

400
401
    _info ("Manager: Hangup call %s", callId.c_str());

402

403
404
405
    // First stop audio layer if there is no call anymore
    int nbCalls = getCallList().size();
    if(nbCalls <= 0) {
406
407
408

    	audioLayerMutexLock();

409
        if(_audiodriver == NULL) {
410
        	audioLayerMutexUnlock();
411
412
413
414
415
        	_error("Manager: Error: Audio layer was not instantiated");
        	return returnValue;
        }

        _debug ("Manager: stop audio stream, there is no call remaining", nbCalls);
416
        _audiodriver->stopStream();
417
        audioLayerMutexUnlock();
418
419
420
    }


421
    // store the current call id
422
    std::string currentCallId = getCurrentCallId();
yanmorin's avatar
   
yanmorin committed
423

424
    stopTone();
425

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

430
    if (not isValidCall(callId) and not getConfigFromCall(callId) == Call::IPtoIP) {
431
    	_error("Manager: Error: Could not hang up call, call not valid");
432
        return false;
433
434
    }

435
436
437
    // Disconnect streams
    removeStream(callId);

438
    if (participToConference (callId)) {
439
    	Conference *conf = getConferenceFromCallID (callId);
440
441
        if (conf != NULL) {
            // remove this participant
442
443
            removeParticipant (callId);
            processRemainingParticipant (currentCallId, conf);
444
445
446
        }
    } else {
        // we are not participating to a conference, current call switched to ""
447
        if (!isConference (currentCallId)) {
448
            switchCall ("");
449
        }
450
    }
451

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

Alexandre Savard's avatar
Alexandre Savard committed
469
470
    getMainBuffer()->stateInfo();

471
    return returnValue;
jpbl's avatar
jpbl committed
472
473
}

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

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

480
    std::string currentAccountId;
Alexandre Savard's avatar
Alexandre Savard committed
481

482
    if (iter_conf != _conferencemap.end()) {
483
        Conference *conf = iter_conf->second;
Alexandre Savard's avatar
Alexandre Savard committed
484

485
486
        ParticipantSet participants = conf->getParticipantList();
        ParticipantSet::iterator iter_participant = participants.begin();
Alexandre Savard's avatar
Alexandre Savard committed
487

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

491
            hangupCall (*iter_participant);
Alexandre Savard's avatar
Alexandre Savard committed
492

493
494
495
            iter_participant++;
        }
    }
Alexandre Savard's avatar
Alexandre Savard committed
496

497
    switchCall ("");
498

Alexandre Savard's avatar
Alexandre Savard committed
499
    getMainBuffer()->stateInfo();
500

501
    return true;
Alexandre Savard's avatar
Alexandre Savard committed
502
503
}

yanmorin's avatar
   
yanmorin committed
504
//THREAD=Main
505
bool ManagerImpl::cancelCall (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
506
{
507
    std::string accountid;
508
    bool returnValue;
yanmorin's avatar
   
yanmorin committed
509

510
    _debug ("Manager: Cancel call");
511

512
    stopTone();
513

514
    /* Direct IP to IP call */
515

516
    if (getConfigFromCall (id) == Call::IPtoIP) {
517
        returnValue = SIPVoIPLink::instance ()->cancel (id);
518
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
519

520
521
522
    /* Classic call, attached to an account */
    else {
        accountid = getAccountFromCall (id);
523

524
        if (accountid == "") {
525
526
527
            _debug ("! Manager Cancel Call: Call doesn't exists");
            return false;
        }
528

529
        returnValue = getAccountLink (accountid)->cancel (id);
530

531
532
        removeCallAccount (id);
    }
533

534
535
    // it could be a waiting call?
    removeWaitingCall (id);
536

537
538
    removeStream(id);

539
    switchCall ("");
Emmanuel Milou's avatar
Emmanuel Milou committed
540

Alexandre Savard's avatar
Alexandre Savard committed
541
542
    getMainBuffer()->stateInfo();

543
    return returnValue;
jpbl's avatar
jpbl committed
544
545
}

yanmorin's avatar
   
yanmorin committed
546
//THREAD=Main
547
bool ManagerImpl::onHoldCall (const std::string& callId)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
548
{
549
    std::string account_id;
550
    bool returnValue = false;
yanmorin's avatar
   
yanmorin committed
551

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

554
    stopTone();
555

556
    std::string current_call_id = getCurrentCallId();
557

558
    try {
559

560
561
    	if (getConfigFromCall (callId) == Call::IPtoIP) {
    		/* Direct IP to IP call */
562
    		returnValue = SIPVoIPLink::instance ()-> onhold (callId);
563
564
565
566
567
    	}
    	else {
    		/* Classic call, attached to an account */
    		account_id = getAccountFromCall (callId);

568
    		if (account_id == "") {
569
570
571
572
573
574
575
576
    			_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());
577
    }
578

579
580
581
    // Unbind calls in main buffer
    removeStream(callId);

582
    // Remove call from teh queue if it was still there
583
    removeWaitingCall (callId);
584

585
    // keeps current call id if the action is not holding this call or a new outgoing call
586
587
    // this could happen in case of a conference
    if (current_call_id == callId) {
588
589
        switchCall ("");
    }
590

591
    _dbus.getCallManager()->callStateChanged (callId, "HOLD");
592

Alexandre Savard's avatar
Alexandre Savard committed
593
    getMainBuffer()->stateInfo();
594

595
    return returnValue;
yanmorin's avatar
   
yanmorin committed
596
597
598
}

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

602
    std::string accountId;
603
    bool returnValue, isRec;
604
    std::string codecName;
605

606
    isRec = false;
607

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

610
    stopTone();
611

612
    std::string currentCallId = getCurrentCallId();
613

614
    //Place current call on hold if it isn't
615

616
    if (hasCurrentCall()) {
617

618
        // if this is not a conference and this and is not a conference participant
619
620
621
622
623
        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);
624
625
        }
    }
alexandresavard's avatar
alexandresavard committed
626

627
    /* Direct IP to IP call */
628
    if (getConfigFromCall (callId) == Call::IPtoIP) {
629
630
        // is_rec = SIPVoIPLink::instance ()-> isRecording (call_id);
        returnValue = SIPVoIPLink::instance ()-> offhold (callId);
631
632
633
    }
    /* Classic call, attached to an account */
    else {
634
        accountId = getAccountFromCall (callId);
635

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

638
639
        isRec = getAccountLink (accountId)->getCall (callId)->isRecording();
        returnValue = getAccountLink (accountId)->offhold (callId);
640
    }
641

642
    _dbus.getCallManager()->callStateChanged (callId, isRec ? "UNHOLD_RECORD" : "UNHOLD_CURRENT");
643
644

    if (participToConference (callId)) {
645
        std::string currentAccountId;
646
        Call* call = NULL;
647

648
649
        currentAccountId = getAccountFromCall (callId);
        call = getAccountLink (currentAccountId)->getCall (callId);
650

651
        switchCall (call->getConfId());
652

653
    } else {
654
        switchCall (callId);
655
    }
656

657
658
    addStream(callId);

Alexandre Savard's avatar
Alexandre Savard committed
659
660
    getMainBuffer()->stateInfo();

661
    return returnValue;
jpbl's avatar
jpbl committed
662
663
}

yanmorin's avatar
   
yanmorin committed
664
//THREAD=Main
665
bool ManagerImpl::transferCall (const std::string& callId, const std::string& to)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
666
{
667
    bool returnValue = false;;
668

669
670
    _info ("Manager: Transfer call %s", callId.c_str());

671
    std::string currentCallId = getCurrentCallId();
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686

    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("");
	}
    }
687

688
    // Direct IP to IP call
689
    if (getConfigFromCall (callId) == Call::IPtoIP) {
690
        returnValue = SIPVoIPLink::instance ()-> transfer (callId, to);
691
692
693
    }
    // Classic call, attached to an account
    else {
694

695
        std::string accountid = getAccountFromCall (callId);
696

697
        if (accountid == "") {
698
699
700
            _warn ("Manager: Call doesn't exists");
            return false;
        }
701

702
        returnValue = getAccountLink (accountid)->transfer (callId, to);
703

704
    }
705

706
    // remove waiting call in case we make transfer without even answer
707
    removeWaitingCall (callId);
708

Alexandre Savard's avatar
Alexandre Savard committed
709
710
    getMainBuffer()->stateInfo();

711
    return returnValue;
jpbl's avatar
jpbl committed
712
713
}

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

Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
719
720
void ManagerImpl::transferSucceded ()
{
721
	_dbus.getCallManager()->transferSucceded();
722
723
}

724
bool ManagerImpl::attendedTransfer(const std::string& transferID, const std::string& targetID)
725
{
726
    bool returnValue = false;
727

728
    _debug("Manager: Attended transfer");
729

730
731
    // Direct IP to IP call
    if (getConfigFromCall (transferID) == Call::IPtoIP) {
732
        returnValue = SIPVoIPLink::instance ()-> attendedTransfer(transferID, targetID);
733
734
    }
    else {	// Classic call, attached to an account
735

736
        std::string accountid = getAccountFromCall (transferID);
737

738
        if (accountid == "") {
739
740
741
            _warn ("Manager: Call doesn't exists");
	    return false;
        }
742

743
        returnValue = getAccountLink (accountid)->attendedTransfer (transferID, targetID);
744

745
    }
746

747
    getMainBuffer()->stateInfo();
Alexandre Savard's avatar
Alexandre Savard committed
748

749
    return returnValue;
750
751
}

yanmorin's avatar
   
yanmorin committed
752
//THREAD=Main : Call:Incoming
753
bool ManagerImpl::refuseCall (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
754
{
755
    std::string accountid;