managerimpl.cpp 65.4 KB
Newer Older
jpbl's avatar
jpbl committed
1
/*
2 3
 *  Copyright (C) 2004-2007 Savoir-Faire Linux inc.
 *  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: Guillaume Carmel-Archambault <guillaume.carmel-archambault@savoirfairelinux.com>
8
 *
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 22 23 24 25 26 27 28
 *  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.
 */

#include <errno.h>
#include <time.h>
#include <cstdlib>
#include <iostream>
#include <fstream>
29
#include <sstream>  
jpbl's avatar
jpbl committed
30 31 32 33 34 35 36 37 38
#include <sys/types.h> // mkdir(2)
#include <sys/stat.h>	// mkdir(2)

#include <cc++/socket.h>   // why do I need this here?
#include <ccrtp/channel.h> // why do I need this here?
#include <ccrtp/rtp.h>     // why do I need this here?
#include <cc++/file.h>

#include "manager.h"
39
#include "account.h"
40
#include "sipaccount.h"
jpbl's avatar
jpbl committed
41
#include "audio/audiolayer.h"
42 43
#include "audio/alsalayer.h"
#include "audio/pulselayer.h"
jpbl's avatar
jpbl committed
44 45
#include "audio/tonelist.h"

yanmorin's avatar
 
yanmorin committed
46
#include "accountcreator.h" // create new account
47 48
#include "sipvoiplink.h"

jpbl's avatar
jpbl committed
49 50 51 52 53 54 55
#include "user_cfg.h"

#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)))

Yun Liu's avatar
Yun Liu committed
56 57 58 59
ManagerImpl::ManagerImpl (void) 
	: _hasTriedToRegister(false)
        , _config()
	, _currentCallId2()
60
        , _currentCallMutex()
Yun Liu's avatar
Yun Liu committed
61 62 63 64
        , _codecBuilder(NULL)
        , _audiodriver(NULL)
        , _dtmfKey(NULL)
        , _codecDescriptorMap()
65
        , _toneMutex()
Yun Liu's avatar
Yun Liu committed
66 67 68 69
        , _telephoneTone(NULL)
        , _audiofile()
        , _spkr_volume(0)
        , _mic_volume(0)
70
        , _mutex()
Yun Liu's avatar
Yun Liu committed
71 72
	, _dbus(NULL)
        , _waitingCall()
73
        , _waitingCallMutex()
Yun Liu's avatar
Yun Liu committed
74 75 76 77 78 79 80 81
        , _nbIncomingWaitingCall(0)
        , _path("")
        , _exist(0)
        , _setupLoaded(false)
        , _firewallPort()
        , _firewallAddr("")
        , _hasZeroconf(false)
        , _callAccountMap()
82
        , _callAccountMapMutex()
Yun Liu's avatar
Yun Liu committed
83
        , _accountMap()
jpbl's avatar
jpbl committed
84
{
85 86 87
  
    // initialize random generator for call id
    srand (time(NULL));
yanmorin's avatar
 
yanmorin committed
88 89 90 91 92 93 94 95 96

#ifdef TEST
  testAccountMap();
  loadAccountMap();
  testCallAccountMap();
  unloadAccountMap();
#endif

  // should be call before initConfigFile
97
  // loadAccountMap();, called in init() now.
jpbl's avatar
jpbl committed
98 99 100 101 102
}

// never call if we use only the singleton...
ManagerImpl::~ManagerImpl (void) 
{
103 104
    terminate();
    _debug("%s stop correctly.\n", PROGNAME);
jpbl's avatar
jpbl committed
105 106
}

107
  void 
jpbl's avatar
jpbl committed
108 109
ManagerImpl::init() 
{
110 111
    // Load accounts, init map
    loadAccountMap();
112
 
113
    initVolume();
114

115 116 117
    if (_exist == 0) {
        _debug("Cannot create config file in your home directory\n");
    }
118

119 120
    initAudioDriver();
    selectAudioDriver();
jpbl's avatar
jpbl committed
121

122 123
    // Initialize the list of supported audio codecs
    initAudioCodec();
124

125
    AudioLayer *audiolayer = getAudioDriver();
126
    
127 128
    if (audiolayer!=0) {
        unsigned int sampleRate = audiolayer->getSampleRate();
jpbl's avatar
jpbl committed
129

130 131 132
        _debugInit("Load Telephone Tone");
        std::string country = getConfigString(PREFERENCES, ZONE_TONE);
        _telephoneTone = new TelephoneTone(country, sampleRate);
jpbl's avatar
jpbl committed
133

134 135 136
        _debugInit("Loading DTMF key");
        _dtmfKey = new DTMF(sampleRate);
    }
jpbl's avatar
jpbl committed
137 138 139 140
}

void ManagerImpl::terminate()
{
141
    saveConfig();
jpbl's avatar
jpbl committed
142

143
    unloadAccountMap();
144
  
145 146
    _debug("Unload DTMF Key\n");
    delete _dtmfKey;
jpbl's avatar
jpbl committed
147

148 149
    _debug("Unload Audio Driver\n");
    delete _audiodriver; _audiodriver = NULL;
jpbl's avatar
jpbl committed
150

151 152
    _debug("Unload Telephone Tone\n");
    delete _telephoneTone; _telephoneTone = NULL;
153

154 155
    _debug("Unload Audio Codecs\n");
    _codecDescriptorMap.deleteHandlePointer();
jpbl's avatar
jpbl committed
156 157
}

yanmorin's avatar
 
yanmorin committed
158 159 160
bool
ManagerImpl::isCurrentCall(const CallID& callId) {
  return (_currentCallId2 == callId ? true : false);
jpbl's avatar
jpbl committed
161 162
}

yanmorin's avatar
 
yanmorin committed
163 164
bool
ManagerImpl::hasCurrentCall() {
Emmanuel Milou's avatar
Bug fix  
Emmanuel Milou committed
165
  _debug("Current call ID = %s\n", _currentCallId2.c_str());
yanmorin's avatar
 
yanmorin committed
166 167
  if ( _currentCallId2 != "") {
    return true;
jpbl's avatar
jpbl committed
168
  }
yanmorin's avatar
 
yanmorin committed
169
  return false;
jpbl's avatar
jpbl committed
170 171
}

yanmorin's avatar
 
yanmorin committed
172 173 174
const CallID& 
ManagerImpl::getCurrentCallId() {
  return _currentCallId2;
jpbl's avatar
jpbl committed
175 176 177
}

void
yanmorin's avatar
 
yanmorin committed
178
ManagerImpl::switchCall(const CallID& id ) {
179
  ost::MutexLock m(_currentCallMutex);
yanmorin's avatar
 
yanmorin committed
180
  _currentCallId2 = id;
jpbl's avatar
jpbl committed
181 182 183 184 185 186
}


///////////////////////////////////////////////////////////////////////////////
// Management of events' IP-phone user
///////////////////////////////////////////////////////////////////////////////
yanmorin's avatar
 
yanmorin committed
187
/* Main Thread */ 
188
  bool
yanmorin's avatar
 
yanmorin committed
189 190
ManagerImpl::outgoingCall(const std::string& accountid, const CallID& id, const std::string& to)
{
191 192
    _debug("ManagerImpl::outgoingCall() method \n");

Emmanuel Milou's avatar
Emmanuel Milou committed
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
    if (!accountExists(accountid)) {
        _debug("! Manager Error: Outgoing Call: account doesn't exist\n");
        return false;
    }
  
    if (getAccountFromCall(id) != AccountNULL) {
        _debug("! Manager Error: Outgoing Call: call id already exists\n");
        return false;
    }
  
    if (hasCurrentCall()) {
        _debug("* Manager Info: there is currently a call, try to hold it\n");
        onHoldCall(getCurrentCallId());
    }
  
    _debug("- Manager Action: Adding Outgoing Call %s on account %s\n", id.data(), accountid.data());
yanmorin's avatar
 
yanmorin committed
209
    associateCallToAccount( id, accountid );
Emmanuel Milou's avatar
Emmanuel Milou committed
210 211 212 213 214 215 216 217
    if ( getAccountLink(accountid)->newOutgoingCall(id, to) ) {
        switchCall(id);
        return true;
    } else {
        callFailure(id);
        _debug("! Manager Error: An error occur, the call was not created\n");
    }
    return false;
jpbl's avatar
jpbl committed
218 219
}

yanmorin's avatar
 
yanmorin committed
220
//THREAD=Main : for outgoing Call
221
  bool
yanmorin's avatar
 
yanmorin committed
222
ManagerImpl::answerCall(const CallID& id)
jpbl's avatar
jpbl committed
223
{
224

225
  stopTone(false); 
226
  _debug("Try to answer call: %s\n", id.data());
yanmorin's avatar
 
yanmorin committed
227 228 229 230
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("Answering Call: Call doesn't exists\n");
    return false;
jpbl's avatar
jpbl committed
231
  }
yanmorin's avatar
 
yanmorin committed
232

233
  if (id != getCurrentCallId()) {
Yun Liu's avatar
Yun Liu committed
234 235 236 237
    _debug("* Manager Info: there is currently a call, try to hold it\n");
    onHoldCall(getCurrentCallId());
  }

yanmorin's avatar
 
yanmorin committed
238 239 240 241
  if (!getAccountLink(accountid)->answer(id)) {
    // error when receiving...
    removeCallAccount(id);
    return false;
jpbl's avatar
jpbl committed
242
  }
Alexandre Savard's avatar
Alexandre Savard committed
243

yanmorin's avatar
 
yanmorin committed
244
  // if it was waiting, it's waiting no more
245
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "CURRENT");
yanmorin's avatar
 
yanmorin committed
246 247
  removeWaitingCall(id);
  switchCall(id);
248 249 250 251 252
 
  std::string codecName = getCurrentCodecName(id);
  _debug("ManagerImpl::hangupCall(): broadcast codec name %s \n",codecName.c_str());
  if (_dbus) _dbus->getCallManager()->currentSelectedCodec(id,codecName.c_str());

yanmorin's avatar
 
yanmorin committed
253
  return true;
jpbl's avatar
jpbl committed
254 255
}

yanmorin's avatar
 
yanmorin committed
256
//THREAD=Main
257
  bool
yanmorin's avatar
 
yanmorin committed
258
ManagerImpl::hangupCall(const CallID& id)
jpbl's avatar
jpbl committed
259
{
Alexandre Savard's avatar
Alexandre Savard committed
260
    _debug("ManagerImpl::hangupCall(): This function is called when user hangup \n");
261 262 263
    PulseLayer *pulselayer;
    AccountID accountid;
    bool returnValue;
Emmanuel Milou's avatar
Emmanuel Milou committed
264

265
    stopTone(true);
yanmorin's avatar
 
yanmorin committed
266

267 268 269 270 271 272 273 274 275 276
    /* Broadcast a signal over DBus */
    if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
  
    accountid = getAccountFromCall( id );
    if (accountid == AccountNULL) {
        /** @todo We should tell the GUI that the call doesn't exist, so
        * it clears up. This can happen. */
        _debug("! Manager Hangup Call: Call doesn't exists\n");
        return false;
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
277

278
    returnValue = getAccountLink(accountid)->hangup(id);
279

280 281 282
    _debug("After voip link hungup!\n");
    removeCallAccount(id);
    switchCall("");
283

284 285 286 287 288 289
    if( _audiodriver->getLayerType() == PULSEAUDIO && getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) ) {
        pulselayer = dynamic_cast<PulseLayer *> (getAudioDriver());
        if(pulselayer)  pulselayer->restorePulseAppsVolume();
    }
  
    return returnValue;
jpbl's avatar
jpbl committed
290 291
}

yanmorin's avatar
 
yanmorin committed
292
//THREAD=Main
293
  bool
yanmorin's avatar
 
yanmorin committed
294
ManagerImpl::cancelCall (const CallID& id)
jpbl's avatar
jpbl committed
295
{
296
  stopTone(true);
yanmorin's avatar
 
yanmorin committed
297 298
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
299
    _debug("! Manager Cancel Call: Call doesn't exists\n");
yanmorin's avatar
 
yanmorin committed
300
    return false;
jpbl's avatar
jpbl committed
301
  }
yanmorin's avatar
 
yanmorin committed
302 303 304 305 306 307

  bool returnValue = getAccountLink(accountid)->cancel(id);
  // it could be a waiting call?
  removeWaitingCall(id);
  removeCallAccount(id);
  switchCall("");
308

yanmorin's avatar
 
yanmorin committed
309
  return returnValue;
jpbl's avatar
jpbl committed
310 311
}

yanmorin's avatar
 
yanmorin committed
312
//THREAD=Main
313
  bool
yanmorin's avatar
 
yanmorin committed
314
ManagerImpl::onHoldCall(const CallID& id)
jpbl's avatar
jpbl committed
315
{
Alexandre Savard's avatar
Alexandre Savard committed
316
  _debug("*************** ON HOLD ***********************************\n");
317
  stopTone(true);
yanmorin's avatar
 
yanmorin committed
318 319
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
320
    _debug("5 Manager On Hold Call: Account ID %s or callid %s desn't exists\n", accountid.c_str(), id.c_str());
yanmorin's avatar
 
yanmorin committed
321
    return false;
jpbl's avatar
jpbl committed
322
  }
yanmorin's avatar
 
yanmorin committed
323

324 325
  _debug("Setting ONHOLD, Account %s, callid %s\n", accountid.c_str(), id.c_str());

yanmorin's avatar
 
yanmorin committed
326
  bool returnValue = getAccountLink(accountid)->onhold(id);
327

yanmorin's avatar
 
yanmorin committed
328
  removeWaitingCall(id);
329
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HOLD");
yanmorin's avatar
 
yanmorin committed
330
  switchCall("");
331

yanmorin's avatar
 
yanmorin committed
332 333 334 335
  return returnValue;
}

//THREAD=Main
336
  bool
yanmorin's avatar
 
yanmorin committed
337 338
ManagerImpl::offHoldCall(const CallID& id)
{
Alexandre Savard's avatar
Alexandre Savard committed
339
  _debug("*************** OFF HOLD ***********************************\n");
340
  stopTone(false);
yanmorin's avatar
 
yanmorin committed
341 342
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
343
    _debug("5 Manager OffHold Call: Call doesn't exists\n");
yanmorin's avatar
 
yanmorin committed
344
    return false;
jpbl's avatar
jpbl committed
345
  }
346

347 348 349 350 351
  //Place current call on hold if it isn't
  if (hasCurrentCall()) 
  { 
    onHoldCall(getCurrentCallId());
  }
352

353 354
  _debug("Setting OFFHOLD, Account %s, callid %s\n", accountid.c_str(), id.c_str());

alexandresavard's avatar
alexandresavard committed
355 356 357 358 359 360 361 362 363
  bool rec = getAccountLink(accountid)->isRecording(id);
  
  /*
  if(rec)
    _debug("ManagerImpl::offHoldCall(): Record state is true \n");
  else
    _debug("ManagerImpl::offHoldCall(): Record state is false \n");
  */

yanmorin's avatar
 
yanmorin committed
364
  bool returnValue = getAccountLink(accountid)->offhold(id);
alexandresavard's avatar
alexandresavard committed
365 366 367 368 369 370 371

  if (_dbus){ 
    if (rec)
      _dbus->getCallManager()->callStateChanged(id, "UNHOLD_RECORD");
    else 
      _dbus->getCallManager()->callStateChanged(id, "UNHOLD_CURRENT");
  }
yanmorin's avatar
 
yanmorin committed
372
  switchCall(id);
373

374 375 376 377
  std::string codecName = getCurrentCodecName(id);
  _debug("ManagerImpl::hangupCall(): broadcast codec name %s \n",codecName.c_str());
  if (_dbus) _dbus->getCallManager()->currentSelectedCodec(id,codecName.c_str());

jpbl's avatar
jpbl committed
378 379 380
  return returnValue;
}

yanmorin's avatar
 
yanmorin committed
381
//THREAD=Main
382
  bool
yanmorin's avatar
 
yanmorin committed
383
ManagerImpl::transferCall(const CallID& id, const std::string& to)
jpbl's avatar
jpbl committed
384
{
385
  stopTone(true);
yanmorin's avatar
 
yanmorin committed
386 387
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
388
    _debug("! Manager Transfer Call: Call doesn't exists\n");
yanmorin's avatar
 
yanmorin committed
389
    return false;
jpbl's avatar
jpbl committed
390
  }
yanmorin's avatar
 
yanmorin committed
391 392 393
  bool returnValue = getAccountLink(accountid)->transfer(id, to);
  removeWaitingCall(id);
  removeCallAccount(id);
394
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
yanmorin's avatar
 
yanmorin committed
395 396
  switchCall("");
  return returnValue;
jpbl's avatar
jpbl committed
397 398
}

yanmorin's avatar
 
yanmorin committed
399
//THREAD=Main : Call:Incoming
400
  bool
yanmorin's avatar
 
yanmorin committed
401
ManagerImpl::refuseCall (const CallID& id)
jpbl's avatar
jpbl committed
402
{
403
  _debug("ManagerImpl::refuseCall(): method called");
404
  stopTone(true);
yanmorin's avatar
 
yanmorin committed
405 406
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
407
    _debug("! Manager OffHold Call: Call doesn't exists\n");
yanmorin's avatar
 
yanmorin committed
408 409 410
    return false;
  }
  bool returnValue = getAccountLink(accountid)->refuse(id);
yanmorin's avatar
 
yanmorin committed
411 412 413 414 415
  // if the call was outgoing or established, we didn't refuse it
  // so the method did nothing
  if (returnValue) {
    removeWaitingCall(id);
    removeCallAccount(id);
416
    if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
yanmorin's avatar
 
yanmorin committed
417 418
    switchCall("");
  }
yanmorin's avatar
 
yanmorin committed
419
  return returnValue;
jpbl's avatar
jpbl committed
420 421
}

yanmorin's avatar
 
yanmorin committed
422
//THREAD=Main
423
  bool
jpbl's avatar
jpbl committed
424 425 426 427 428 429 430 431 432 433
ManagerImpl::saveConfig (void)
{
  _debug("Saving Configuration...\n");
  setConfig(AUDIO, VOLUME_SPKR, getSpkrVolume());
  setConfig(AUDIO, VOLUME_MICRO, getMicVolume());

  _setupLoaded = _config.saveConfigTree(_path.data());
  return _setupLoaded;
}

yanmorin's avatar
 
yanmorin committed
434
//THREAD=Main
435
 int 
436
ManagerImpl::initRegisterAccounts() 
jpbl's avatar
jpbl committed
437
{
438
    int status; 
439
    bool flag = true;
440 441 442 443 444
    AccountMap::iterator iter;

    _debugInit("Initiate VoIP Links Registration");
    iter = _accountMap.begin();

445
    /* Loop on the account map previously loaded */
446
    while( iter != _accountMap.end() ) {
447 448 449 450 451 452 453 454 455
        if ( iter->second ) {
            iter->second->loadConfig();
            /* If the account is set as enabled, try to register */
            if ( iter->second->isEnabled() ) {
	            status = iter->second->registerVoIPLink();
	            if (status != SUCCESS){
		            flag = false;
                }
            }
456
        }
457
        iter++;
Emmanuel Milou's avatar
Emmanuel Milou committed
458
    }
459

460 461 462 463
    // calls the client notification here in case of errors at startup...
    if( _audiodriver -> getErrorMessage() != -1 )
      notifyErrClient( _audiodriver -> getErrorMessage() );
    
464
    ASSERT( flag, true );
465
    return SUCCESS;
jpbl's avatar
jpbl committed
466 467
}

yanmorin's avatar
 
yanmorin committed
468
//THREAD=Main
469
  bool 
yanmorin's avatar
 
yanmorin committed
470
ManagerImpl::sendDtmf(const CallID& id, char code)
jpbl's avatar
jpbl committed
471
{
yanmorin's avatar
 
yanmorin committed
472 473
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
Emmanuel Milou's avatar
Emmanuel Milou committed
474 475
    //_debug("Send DTMF: call doesn't exists\n");
    playDtmf(code, false);
yanmorin's avatar
 
yanmorin committed
476 477 478
    return false;
  }

jpbl's avatar
jpbl committed
479
  int sendType = getConfigInt(SIGNALISATION, SEND_DTMF_AS);
yanmorin's avatar
 
yanmorin committed
480
  bool returnValue = false;
jpbl's avatar
jpbl committed
481
  switch (sendType) {
482
    case 0: // SIP INFO
Emmanuel Milou's avatar
Emmanuel Milou committed
483
      playDtmf(code , true);
484 485 486 487 488 489 490 491 492
      returnValue = getAccountLink(accountid)->carryingDTMFdigits(id, code);
      break;

    case 1: // Audio way
      break;
    case 2: // rfc 2833
      break;
    default: // unknown - error config?
      break;
jpbl's avatar
jpbl committed
493 494 495 496
  }
  return returnValue;
}

yanmorin's avatar
 
yanmorin committed
497
//THREAD=Main | VoIPLink
498
  bool
Emmanuel Milou's avatar
Emmanuel Milou committed
499
ManagerImpl::playDtmf(char code, bool isTalking)
jpbl's avatar
jpbl committed
500
{
501 502 503 504 505 506 507 508 509 510
    int hasToPlayTone, pulselen, layer, size;
    bool ret = false;
    AudioLayer *audiolayer;
    SFLDataFormat *buf;
  
    stopTone(false);
    
    hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_DTMF);
    if (!hasToPlayTone) 
        return false;
jpbl's avatar
jpbl committed
511

512 513 514 515
    // length in milliseconds
    pulselen = getConfigInt(SIGNALISATION, PULSE_LENGTH);
    if (!pulselen)
        return false;
jpbl's avatar
jpbl committed
516

517 518 519 520
    // numbers of int = length in milliseconds / 1000 (number of seconds)
    //                = number of seconds * SAMPLING_RATE by SECONDS
    audiolayer = getAudioDriver();
    layer = audiolayer->getLayerType();
jpbl's avatar
jpbl committed
521

522 523 524
    // fast return, no sound, so no dtmf
    if (audiolayer==0 || _dtmfKey == 0)
        return false;
525

526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
    // number of data sampling in one pulselen depends on samplerate
    // size (n sampling) = time_ms * sampling/s 
    //                     ---------------------
    //                            ms/s
    size = (int)(pulselen * ((float)audiolayer->getSampleRate()/1000));

    // this buffer is for mono
    // TODO <-- this should be global and hide if same size
    buf = new SFLDataFormat[size];

    // Handle dtmf
    _dtmfKey->startTone(code);

    // copy the sound
    if ( _dtmfKey->generateDTMF(buf, size) ) {
        // Put buffer to urgentRingBuffer 
        // put the size in bytes...
        // so size * 1 channel (mono) * sizeof (bytes for the data)
544
        audiolayer->startStream();
545 546 547
        audiolayer->putUrgent (buf, size * sizeof(SFLDataFormat));
    }
    ret = true;
548

549
    // TODO Cache the DTMF
Emmanuel Milou's avatar
Emmanuel Milou committed
550

551 552 553
    delete[] buf; buf = 0;
    
    return ret;
jpbl's avatar
jpbl committed
554 555
}

yanmorin's avatar
 
yanmorin committed
556
// Multi-thread 
jpbl's avatar
jpbl committed
557 558 559 560 561 562
bool
ManagerImpl::incomingCallWaiting() {
  return (_nbIncomingWaitingCall > 0) ? true : false;
}

void
yanmorin's avatar
 
yanmorin committed
563
ManagerImpl::addWaitingCall(const CallID& id) {
564
  ost::MutexLock m(_waitingCallMutex);
yanmorin's avatar
 
yanmorin committed
565
  _waitingCall.insert(id);
jpbl's avatar
jpbl committed
566 567 568 569
  _nbIncomingWaitingCall++;
}

void
yanmorin's avatar
 
yanmorin committed
570
ManagerImpl::removeWaitingCall(const CallID& id) {
571
  ost::MutexLock m(_waitingCallMutex);
yanmorin's avatar
 
yanmorin committed
572 573 574
  // should return more than 1 if it erase a call
  if (_waitingCall.erase(id)) {
    _nbIncomingWaitingCall--;
jpbl's avatar
jpbl committed
575 576 577 578
  }
}

bool
yanmorin's avatar
 
yanmorin committed
579 580 581 582
ManagerImpl::isWaitingCall(const CallID& id) {
  CallIDSet::iterator iter = _waitingCall.find(id);
  if (iter != _waitingCall.end()) {
    return false;
jpbl's avatar
jpbl committed
583
  }
yanmorin's avatar
 
yanmorin committed
584
  return true;
jpbl's avatar
jpbl committed
585 586
}

yanmorin's avatar
 
yanmorin committed
587 588 589 590
///////////////////////////////////////////////////////////////////////////////
// Management of event peer IP-phone 
////////////////////////////////////////////////////////////////////////////////
// SipEvent Thread 
Alexandre Savard's avatar
Alexandre Savard committed
591
bool 
yanmorin's avatar
 
yanmorin committed
592
ManagerImpl::incomingCall(Call* call, const AccountID& accountId) 
jpbl's avatar
jpbl committed
593
{
594 595
    PulseLayer *pulselayer;
    std::string from, number;
596

597
    _debug("Incoming call %s\n", call->getCallId().data());
598

599
    associateCallToAccount(call->getCallId(), accountId);
jpbl's avatar
jpbl committed
600

601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
    if ( !hasCurrentCall() ) {
        call->setConnectionState(Call::Ringing);
        ringtone();
        switchCall(call->getCallId());
    } else {
        addWaitingCall(call->getCallId());
    }

    from = call->getPeerName();
    number = call->getPeerNumber();

    if (from != "" && number != "") {
        from.append(" <");
        from.append(number);
        from.append(">");
    } else if ( from.empty() ) {
        from.append("<");
        from.append(number);
        from.append(">");
    }
621
  
622 623
    /* Broadcast a signal over DBus */
    _dbus->getCallManager()->incomingCall(accountId, call->getCallId(), from);
624
  
625 626 627 628 629
    // Reduce volume of the other pulseaudio-connected audio applications
    if( _audiodriver->getLayerType() == PULSEAUDIO && getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) ) {
        pulselayer = dynamic_cast<PulseLayer *> (getAudioDriver());
        if(pulselayer)  pulselayer->reducePulseAppsVolume();
    }
630
  
631
    return true;
jpbl's avatar
jpbl committed
632 633
}

yanmorin's avatar
 
yanmorin committed
634 635 636
//THREAD=VoIP
void
ManagerImpl::incomingMessage(const AccountID& accountId, const std::string& message) {
637 638
  if (_dbus) {
    _dbus->getCallManager()->incomingMessage(accountId, message);
jpbl's avatar
jpbl committed
639 640 641
  }
}

yanmorin's avatar
 
yanmorin committed
642
//THREAD=VoIP CALL=Outgoing
643
  void
yanmorin's avatar
 
yanmorin committed
644
ManagerImpl::peerAnsweredCall(const CallID& id)
jpbl's avatar
jpbl committed
645
{
yanmorin's avatar
 
yanmorin committed
646
  if (isCurrentCall(id)) {
647
    stopTone(false);
jpbl's avatar
jpbl committed
648
  }
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
649
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "CURRENT");
Alexandre Savard's avatar
Alexandre Savard committed
650 651 652 653
  
  std::string codecName = getCurrentCodecName(id);
  _debug("ManagerImpl::hangupCall(): broadcast codec name %s \n",codecName.c_str());
  if (_dbus) _dbus->getCallManager()->currentSelectedCodec(id,codecName.c_str());
jpbl's avatar
jpbl committed
654 655
}

yanmorin's avatar
 
yanmorin committed
656
//THREAD=VoIP Call=Outgoing
657
  void
yanmorin's avatar
 
yanmorin committed
658
ManagerImpl::peerRingingCall(const CallID& id)
jpbl's avatar
jpbl committed
659
{
yanmorin's avatar
 
yanmorin committed
660
  if (isCurrentCall(id)) {
jpbl's avatar
jpbl committed
661 662
    ringback();
  }
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
663
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "RINGING");
jpbl's avatar
jpbl committed
664 665
}

yanmorin's avatar
 
yanmorin committed
666
//THREAD=VoIP Call=Outgoing/Ingoing
667
  void
yanmorin's avatar
 
yanmorin committed
668
ManagerImpl::peerHungupCall(const CallID& id)
jpbl's avatar
jpbl committed
669
{
670
    _debug("ManagerImpl::peerHungupCall():this function is called when peer hangup \n");
671 672
    PulseLayer *pulselayer;
    AccountID accountid;
673
    bool returnValue;
674 675 676 677 678 679

    accountid = getAccountFromCall( id );
    if (accountid == AccountNULL) {
        _debug("peerHungupCall: Call doesn't exists\n");
        return;
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
680
  
681 682 683 684 685 686 687 688
    /* Broadcast a signal over DBus */
    if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
    
    if (isCurrentCall(id)) {
        stopTone(true);
        switchCall("");
    }

689
    returnValue = getAccountLink(accountid)->peerHungup(id);
690

691 692
    removeWaitingCall(id);
    removeCallAccount(id);
Emmanuel Milou's avatar
Emmanuel Milou committed
693
  
694 695 696 697
    if( _audiodriver->getLayerType() == PULSEAUDIO && getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) ) {
        pulselayer = dynamic_cast<PulseLayer *> (getAudioDriver());
        if(pulselayer)  pulselayer->restorePulseAppsVolume();
    }
jpbl's avatar
jpbl committed
698 699
}

yanmorin's avatar
 
yanmorin committed
700
//THREAD=VoIP
jpbl's avatar
jpbl committed
701
void
yanmorin's avatar
 
yanmorin committed
702 703
ManagerImpl::callBusy(const CallID& id) {
  _debug("Call busy\n");
704

705
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "BUSY");
yanmorin's avatar
 
yanmorin committed
706 707 708
  if (isCurrentCall(id) ) {
    playATone(Tone::TONE_BUSY);
    switchCall("");
jpbl's avatar
jpbl committed
709
  }
yanmorin's avatar
 
yanmorin committed
710 711
  removeCallAccount(id);
  removeWaitingCall(id);
jpbl's avatar
jpbl committed
712 713
}

yanmorin's avatar
 
yanmorin committed
714
//THREAD=VoIP
715
  void
yanmorin's avatar
 
yanmorin committed
716 717
ManagerImpl::callFailure(const CallID& id) 
{
718
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "FAILURE");
Emmanuel Milou's avatar
Emmanuel Milou committed
719
  _debug("CALL ID = %s\n" , id.c_str());
yanmorin's avatar
 
yanmorin committed
720 721 722 723 724 725
  if (isCurrentCall(id) ) {
    playATone(Tone::TONE_BUSY);
    switchCall("");
  }
  removeCallAccount(id);
  removeWaitingCall(id);
726

jpbl's avatar
jpbl committed
727 728
}

yanmorin's avatar
 
yanmorin committed
729
//THREAD=VoIP
730
  void
Emmanuel Milou's avatar
Emmanuel Milou committed
731
ManagerImpl::startVoiceMessageNotification(const AccountID& accountId, int nb_msg)
jpbl's avatar
jpbl committed
732
{
Emmanuel Milou's avatar
Emmanuel Milou committed
733
  if (_dbus) _dbus->getCallManager()->voiceMailNotify(accountId, nb_msg) ;
jpbl's avatar
jpbl committed
734 735
}

736
void ManagerImpl::connectionStatusNotification(  )
737
{
738 739 740 741
    if (_dbus)
        _dbus->getConfigurationManager()->accountsChanged();
    else
        _debug("Error: DBus connection not found\n");
Emmanuel Milou's avatar
nothing  
Emmanuel Milou committed
742 743
}

jpbl's avatar
jpbl committed
744 745 746
/**
 * Multi Thread
 */
747 748 749 750 751 752
bool ManagerImpl::playATone(Tone::TONEID toneId) 
{
    int hasToPlayTone;
    AudioLoop *audioloop;
    AudioLayer *audiolayer;
    unsigned int nbSamples;
yanmorin's avatar
 
yanmorin committed
753

754 755 756 757 758
    hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
    if (!hasToPlayTone) 
        return false;
    
    audiolayer = getAudioDriver();
759
   
760 761 762 763 764 765 766 767 768 769 770 771 772
    if (_telephoneTone != 0) {
        _toneMutex.enterMutex();
        _telephoneTone->setCurrentTone(toneId);
        _toneMutex.leaveMutex();

        audioloop = getTelephoneTone();
        nbSamples = audioloop->getSize();
        SFLDataFormat buf[nbSamples];
    
        if ( audiolayer ){ 
            audiolayer->putUrgent( buf, nbSamples );
        } else 
            return false;
jpbl's avatar
jpbl committed
773
  }
774
  return true;
jpbl's avatar
jpbl committed
775 776 777 778 779
}

/**
 * Multi Thread
 */
780 781 782 783
void ManagerImpl::stopTone (bool stopAudio=true)
{
    int hasToPlayTone;
    AudioLayer *audiolayer;
yanmorin's avatar
 
yanmorin committed
784

785 786 787
    hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
    if (!hasToPlayTone) 
        return;
788

789 790 791 792
    if (stopAudio) {
        audiolayer = getAudioDriver();
        if (audiolayer) audiolayer->stopStream();
    }
jpbl's avatar
jpbl committed
793

794 795 796 797 798
    _toneMutex.enterMutex();
    if (_telephoneTone != 0) {
        _telephoneTone->setCurrentTone(Tone::TONE_NULL);
    }
    _toneMutex.leaveMutex();
jpbl's avatar
jpbl committed
799

800 801 802 803
    // for ringing tone..
    _toneMutex.enterMutex();
    _audiofile.stop();
    _toneMutex.leaveMutex();
jpbl's avatar
jpbl committed
804 805 806 807 808
}

/**
 * Multi Thread
 */
809
  bool
jpbl's avatar
jpbl committed
810 811
ManagerImpl::playTone()
{
812
  playATone(Tone::TONE_DIALTONE);
Yun Liu's avatar
Yun Liu committed
813
  return true;
jpbl's avatar
jpbl committed
814 815
}

816 817 818 819 820 821 822
/**
 * Multi Thread
 */
  bool
ManagerImpl::playToneWithMessage()
{
  playATone(Tone::TONE_CONGESTION);
Yun Liu's avatar
Yun Liu committed
823
  return true;
824 825
}

jpbl's avatar
jpbl committed
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
/**
 * Multi Thread
 */
void
ManagerImpl::congestion () {
  playATone(Tone::TONE_CONGESTION);
}

/**
 * Multi Thread
 */
void
ManagerImpl::ringback () {
  playATone(Tone::TONE_RINGTONE);
}

/**
 * Multi Thread
 */
845
  void
jpbl's avatar
jpbl committed
846 847
ManagerImpl::ringtone() 
{
848 849 850 851 852 853
    std::string ringchoice;
    AudioLayer *audiolayer;
    AudioCodec *codecForTone;
    int layer, samplerate;
    bool loadFile;

854 855 856 857 858
    if( isRingtoneEnabled() )
    {
        //TODO Comment this because it makes the daemon crashes since the main thread
        //synchronizes the ringtone thread.
        
859
        ringchoice = getConfigString(AUDIO, RING_CHOICE);
860 861 862 863 864 865
        //if there is no / inside the path
        if ( ringchoice.find(DIR_SEPARATOR_CH) == std::string::npos ) {
            // check inside global share directory
            ringchoice = std::string(PROGSHAREDIR) + DIR_SEPARATOR_STR + RINGDIR + DIR_SEPARATOR_STR + ringchoice; 
        }

866 867 868 869 870 871 872
        audiolayer = getAudioDriver();
        layer = audiolayer->getLayerType();
        if (audiolayer == 0)
            return;

        samplerate  = audiolayer->getSampleRate();
        codecForTone = _codecDescriptorMap.getFirstCodecAvailable();
Emmanuel Milou's avatar
Emmanuel Milou committed
873

874
        _toneMutex.enterMutex(); 
875
        loadFile = _audiofile.loadFile(ringchoice, codecForTone , samplerate);
876
        _toneMutex.leaveMutex(); 
877

878
        if (loadFile) {
879
            _toneMutex.enterMutex(); 
880
            _audiofile.start();
881
            _toneMutex.leaveMutex(); 
882
            if(CHECK_INTERFACE( layer, ALSA )){
883
                //ringback();
884 885 886 887 888 889
            }
            else{
                audiolayer->startStream();
            }
        } else {
            ringback();
890
        }
891 892 893 894 895
    
    }
    else
    {
        ringback();
Emmanuel Milou's avatar
Emmanuel Milou committed
896
    }
jpbl's avatar
jpbl committed
897 898
}

899
  AudioLoop*
jpbl's avatar
jpbl committed
900 901 902
ManagerImpl::getTelephoneTone()
{
  if(_telephoneTone != 0) {