Skip to content
Snippets Groups Projects
Select Git revision
  • acc4c413925d255591f1c7dbd5e86a815789957c
  • master default protected
  • release/201811
  • release/201812
  • release/201901
  • release/201902
  • release/201903
  • release/201904
  • release/201905
  • release/201906
  • release/201908
  • release/201912
  • release/202001
  • release/202005
  • release/windows-test/201910
  • release/201808
  • wip/smartlist_refacto
  • wip/patches_poly_2017/JimmyHamel/MathieuGirouxHuppe
18 results

RingDebug.cpp

Blame
    • Andreas Traczyk's avatar
      acc4c413
      settings: add the ability to modify video device settings · acc4c413
      Andreas Traczyk authored and Andreas Traczyk's avatar Andreas Traczyk committed
      - adds the ability to select device, resolution, and frame rate
      
      - modifies the initialization of the daemon by seperating the
        registration of the callbacks from the init function and places
        the start and run loop in an IAsyncAction worker thread with
        forced high priority
      
      - uses std::ofstream for debug log file instead of Platform functions
      
      Change-Id: I32439088fe58513c46d11297db4898ca237174e7
      Tuleap: #790
      acc4c413
      History
      settings: add the ability to modify video device settings
      Andreas Traczyk authored and Andreas Traczyk's avatar Andreas Traczyk committed
      - adds the ability to select device, resolution, and frame rate
      
      - modifies the initialization of the daemon by seperating the
        registration of the callbacks from the init function and places
        the start and run loop in an IAsyncAction worker thread with
        forced high priority
      
      - uses std::ofstream for debug log file instead of Platform functions
      
      Change-Id: I32439088fe58513c46d11297db4898ca237174e7
      Tuleap: #790
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    managerimpl.cpp 44.12 KiB
    /*
     *  Copyright (C) 2004-2006 Savoir-Faire Linux inc.
     *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
     *  Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com>
     *                                                                              
     *  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
     *  the Free Software Foundation; either version 2 of the License, or
     *  (at your option) any later version.
     *                                                                              
     *  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.
     *                                                                              
     *  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>
    
    #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"
    #include "audio/audiolayer.h"
    #include "audio/audiocodec.h"
    #include "audio/tonelist.h"
    
    #include "accountcreator.h" // create new account
    #include "voIPLink.h"
    
    #include "user_cfg.h"
    #include "gui/guiframework.h"
    
    #ifdef USE_ZEROCONF
    #include "zeroconf/DNSService.h"
    #include "zeroconf/DNSServiceTXTRecord.h"
    #endif
    
    #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)))
    
    ManagerImpl::ManagerImpl (void)
    {
      // Init private variables 
      _hasZeroconf = false;
    #ifdef USE_ZEROCONF
      _hasZeroconf = true;
      _DNSService = new DNSService();
    #endif
    
      // setup
      _path = ""; 
      _exist = 0;
      _setupLoaded = false;
      _gui = 0;
    
      // sound
      _audiodriver = 0;
      _dtmfKey = 0;
      _spkr_volume = 0;  // Initialize after by init() -> initVolume()
      _mic_volume  = 0;  // Initialize after by init() -> initVolume()
      _mic_volume_before_mute = 0; 
    
      // Call
      _nbIncomingWaitingCall=0;
      _hasTriedToRegister = false;
    
      // initialize random generator for call id
      srand (time(NULL));
    
    #ifdef TEST
      testAccountMap();
      loadAccountMap();
      testCallAccountMap();
      unloadAccountMap();
    #endif
    
      // should be call before initConfigFile
      loadAccountMap();
    }
    
    // never call if we use only the singleton...
    ManagerImpl::~ManagerImpl (void) 
    {
      terminate();
    
    #ifdef USE_ZEROCONF
      delete _DNSService; _DNSService = 0;
    #endif
    
      _debug("%s stop correctly.\n", PROGNAME);
    }
    
    void 
    ManagerImpl::init() 
    {
      initVolume();
    
      if (_exist == 0) {
        _debug("Cannot create config file in your home directory\n");
      }
    
      initAudioDriver();
      selectAudioDriver();
    
      initAudioCodec();
    
      AudioLayer *audiolayer = getAudioDriver();
      if (audiolayer!=0) {
        unsigned int sampleRate = audiolayer->getSampleRate();
    
        _debugInit("Load Telephone Tone");
        std::string country = getConfigString(PREFERENCES, ZONE_TONE);
        _telephoneTone = new TelephoneTone(country, sampleRate);
    
        _debugInit("Loading DTMF key");
        _dtmfKey = new DTMF(sampleRate);
      }
    
      // initRegisterVoIP was here, but we doing it after the gui loaded... 
      // the stun detection is long, so it's a better idea to do it after getEvents
      initZeroconf();
    }
    
    void ManagerImpl::terminate()
    {
      saveConfig();
    
      unloadAccountMap();
    
      _debug("Unload DTMF Key\n");
      delete _dtmfKey;
    
      _debug("Unload Audio Driver\n");
      delete _audiodriver; _audiodriver = 0;
    
      _debug("Unload Telephone Tone\n");
      delete _telephoneTone; _telephoneTone = 0;
    }
    
    bool
    ManagerImpl::isCurrentCall(const CallID& callId) {
      ost::MutexLock m(_currentCallMutex);
      return (_currentCallId2 == callId ? true : false);
    }
    
    bool
    ManagerImpl::hasCurrentCall() {
      ost::MutexLock m(_currentCallMutex);
      if ( _currentCallId2 != "") {
        return true;
      }
      return false;
    }
    
    const CallID& 
    ManagerImpl::getCurrentCallId() {
      ost::MutexLock m(_currentCallMutex);
      return _currentCallId2;
    }
    
    void
    ManagerImpl::switchCall(const CallID& id ) {
      ost::MutexLock m(_currentCallMutex);
      _currentCallId2 = id;
    }
    
    
    ///////////////////////////////////////////////////////////////////////////////
    // Management of events' IP-phone user
    ///////////////////////////////////////////////////////////////////////////////
    /* Main Thread */ 
    bool
    ManagerImpl::outgoingCall(const std::string& accountid, const CallID& id, const std::string& to)
    {
      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());
      if ( getAccountLink(accountid)->newOutgoingCall(id, to) ) {
        associateCallToAccount( id, accountid );
        switchCall(id);
        return true;
      } else {
        _debug("! Manager Error: An error occur, the call was not created\n");
      }
      return false;
    }
    
    //THREAD=Main : for outgoing Call
    bool
    ManagerImpl::answerCall(const CallID& id)
    {
      stopTone(false); 
    
      AccountID accountid = getAccountFromCall( id );
      if (accountid == AccountNULL) {
        _debug("Answering Call: Call doesn't exists\n");
        return false;
      }
    
      if (!getAccountLink(accountid)->answer(id)) {
        // error when receiving...
        removeCallAccount(id);
        return false;
      }
    
      // if it was waiting, it's waiting no more
      removeWaitingCall(id);
      switchCall(id);
      return true;
    }
    
    //THREAD=Main
    bool 
    ManagerImpl::sendTextMessage(const AccountID& accountId, const std::string& to, const std::string& message) 
    {
      if (accountExists(accountId)) {
        return getAccountLink(accountId)->sendMessage(to, message);
      }
      return false;
    }
    
    //THREAD=Main
    bool
    ManagerImpl::hangupCall(const CallID& id)
    {
      stopTone(true);
    
      AccountID accountid = getAccountFromCall( id );
      if (accountid == AccountNULL) {
        _debug("! Manager Hangup Call: Call doesn't exists\n");
        return false;
      }
    
      bool returnValue = getAccountLink(accountid)->hangup(id);
      removeCallAccount(id);
      switchCall("");
    
      return returnValue;
    }
    
    //THREAD=Main
    bool
    ManagerImpl::cancelCall (const CallID& id)
    {
      stopTone(true);
      AccountID accountid = getAccountFromCall( id );
      if (accountid == AccountNULL) {
        _debug("! Manager Cancel Call: Call doesn't exists\n");
        return false;
      }
    
      bool returnValue = getAccountLink(accountid)->cancel(id);
      // it could be a waiting call?
      removeWaitingCall(id);
      removeCallAccount(id);
      switchCall("");
      
      return returnValue;
    }
    
    //THREAD=Main
    bool
    ManagerImpl::onHoldCall(const CallID& id)
    {
      stopTone(true);
      AccountID accountid = getAccountFromCall( id );
      if (accountid == AccountNULL) {
        _debug("5 Manager On Hold Call: Account ID %s or callid %s desn't exists\n", accountid.c_str(), id.c_str());
        return false;
      }
    
      bool returnValue = getAccountLink(accountid)->onhold(id);
      removeWaitingCall(id);
      switchCall("");
      
      return returnValue;
    }
    
    //THREAD=Main
    bool
    ManagerImpl::offHoldCall(const CallID& id)
    {
      stopTone(false);
      AccountID accountid = getAccountFromCall( id );
      if (accountid == AccountNULL) {
        _debug("5 Manager OffHold Call: Call doesn't exists\n");
        return false;
      }
      bool returnValue = getAccountLink(accountid)->offhold(id);
      switchCall(id);
    
      if (returnValue) {
        try {
          getAudioDriver()->startStream();
        } catch(...) {
          _debugException("! Manager Off hold could not start audio stream");
        }
      }
      return returnValue;
    }
    
    //THREAD=Main
    bool
    ManagerImpl::transferCall(const CallID& id, const std::string& to)
    {
      stopTone(true);
      AccountID accountid = getAccountFromCall( id );
      if (accountid == AccountNULL) {
        _debug("! Manager Transfer Call: Call doesn't exists\n");
        return false;
      }
      bool returnValue = getAccountLink(accountid)->transfer(id, to);
      removeWaitingCall(id);
      removeCallAccount(id);
      switchCall("");
    
      return returnValue;
    }
    
    //THREAD=Main
    void
    ManagerImpl::mute() {
      _mic_volume_before_mute = _mic_volume;
      setMicVolume(0);
    }
    
    //THREAD=Main
    void
    ManagerImpl::unmute() {
      if ( _mic_volume == 0 ) {
        setMicVolume(_mic_volume_before_mute);
      }
    }
    
    //THREAD=Main : Call:Incoming
    bool
    ManagerImpl::refuseCall (const CallID& id)
    {
      stopTone(true);
      AccountID accountid = getAccountFromCall( id );
      if (accountid == AccountNULL) {
        _debug("! Manager OffHold Call: Call doesn't exists\n");
        return false;
      }
      bool returnValue = getAccountLink(accountid)->refuse(id);
      // if the call was outgoing or established, we didn't refuse it
      // so the method did nothing
      if (returnValue) {
        removeWaitingCall(id);
        removeCallAccount(id);
        switchCall("");
      }
      return returnValue;
    }
    
    //THREAD=Main
    bool
    ManagerImpl::saveConfig (void)
    {
      _debug("Saving Configuration...\n");
      setConfig(AUDIO, VOLUME_SPKR, getSpkrVolume());
      setConfig(AUDIO, VOLUME_MICRO, getMicVolume());
    
      _setupLoaded = _config.saveConfigTree(_path.data());
      return _setupLoaded;
    }
    
    //THREAD=Main
    bool
    ManagerImpl::initRegisterVoIPLink() 
    {
      _debugInit("Initiate VoIP Links Registration");
      AccountMap::iterator iter = _accountMap.begin();
      while( iter != _accountMap.end() ) {
        if ( iter->second) {
          iter->second->loadConfig();
          if ( iter->second->shouldInitOnStart() ) {
            if ( iter->second->init() && iter->second->shouldRegisterOnStart()) {
                iter->second->registerAccount();
            }
            // init only the first account
            break;
          }
        }
        iter++;
      }
      return true;
    }
    
    //THREAD=Main
    bool
    ManagerImpl::registerVoIPLink(const AccountID& accountId)
    {
      _debug("Register VoIP Link\n");
      int returnValue = false;
      // right now, we don't support two SIP account
      // so we close everything before registring a new account
      Account* account = getAccount(accountId);
      if (account != 0) {
        AccountMap::iterator iter = _accountMap.begin();
        while ( iter != _accountMap.end() ) {
          if ( iter->second ) {
            iter->second->unregisterAccount();
            iter->second->terminate();
          }
          iter++;
        }
        returnValue = account->registerAccount();
      }
      return returnValue;
    }
    
    //THREAD=Main
    bool 
    ManagerImpl::unregisterVoIPLink(const AccountID& accountId)
    {
      _debug("Unregister VoIP Link\n");
      int returnValue = false;
      if (accountExists( accountId ) ) {
        returnValue = getAccount(accountId)->unregisterAccount();
      }
      return returnValue;
    }
    
    //THREAD=Main
    bool 
    ManagerImpl::sendDtmf(const CallID& id, char code)
    {
      AccountID accountid = getAccountFromCall( id );
      if (accountid == AccountNULL) {
        _debug("Send DTMF: call doesn't exists\n");
        return false;
      }
    
      int sendType = getConfigInt(SIGNALISATION, SEND_DTMF_AS);
      bool returnValue = false;
      switch (sendType) {
      case 0: // SIP INFO
        playDtmf(code);
        returnValue = getAccountLink(accountid)->carryingDTMFdigits(id, code);
        break;
    
      case 1: // Audio way
        break;
      case 2: // rfc 2833
        break;
      default: // unknown - error config?
        break;
      }
      return returnValue;
    }
    
    //THREAD=Main | VoIPLink
    bool
    ManagerImpl::playDtmf(char code)
    {
      // HERE are the variable:
      // - boolean variable to play or not (config)
      // - length in milliseconds to play
      // - sample of audiolayer
      stopTone(false);
      int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_DTMF);
      if (!hasToPlayTone) return false;
    
      // length in milliseconds
      int pulselen = getConfigInt(SIGNALISATION, PULSE_LENGTH);
      if (!pulselen) { return false; }
    
      // numbers of int = length in milliseconds / 1000 (number of seconds)
      //                = number of seconds * SAMPLING_RATE by SECONDS
      AudioLayer* audiolayer = getAudioDriver();
    
      // fast return, no sound, so no dtmf
      if (audiolayer==0 || _dtmfKey == 0) { return false; }
      // number of data sampling in one pulselen depends on samplerate
      // size (n sampling) = time_ms * sampling/s 
      //                     ---------------------
      //                            ms/s
      int size = (int)(pulselen * ((float)audiolayer->getSampleRate()/1000));
    
      // this buffer is for mono
      // TODO <-- this should be global and hide if same size
      SFLDataFormat* _buf = new SFLDataFormat[size];
      bool returnValue = false;
    
      // 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)
        audiolayer->putUrgent(_buf, size * sizeof(SFLDataFormat));
    
        try {
          // We activate the stream if it's not active yet.
          if (!audiolayer->isStreamActive()) {
            audiolayer->startStream();
          } else {
            audiolayer->sleep(pulselen); // in milliseconds
          }
        } catch(...) {
          _debugException("Portaudio exception when playing a dtmf");
        }
        returnValue = true;
      }
    
      // TODO: add caching
      delete[] _buf; _buf = 0;
      return returnValue;
    }
    
    
    
    // Multi-thread 
    bool
    ManagerImpl::incomingCallWaiting() {
      ost::MutexLock m(_waitingCallMutex);
      return (_nbIncomingWaitingCall > 0) ? true : false;
    }
    
    void
    ManagerImpl::addWaitingCall(const CallID& id) {
      ost::MutexLock m(_waitingCallMutex);
      _waitingCall.insert(id);
      _nbIncomingWaitingCall++;
    }
    
    void
    ManagerImpl::removeWaitingCall(const CallID& id) {
      ost::MutexLock m(_waitingCallMutex);
      // should return more than 1 if it erase a call
      if (_waitingCall.erase(id)) {
        _nbIncomingWaitingCall--;
      }
    }
    
    bool
    ManagerImpl::isWaitingCall(const CallID& id) {
      ost::MutexLock m(_waitingCallMutex);
      CallIDSet::iterator iter = _waitingCall.find(id);
      if (iter != _waitingCall.end()) {
        return false;
      }
      return true;
    }
    
    
    
    ///////////////////////////////////////////////////////////////////////////////
    // Management of event peer IP-phone 
    ////////////////////////////////////////////////////////////////////////////////
    // SipEvent Thread 
    bool 
    ManagerImpl::incomingCall(Call* call, const AccountID& accountId) 
    {
      _debug("Incoming call\n");
      associateCallToAccount(call->getCallId(), accountId);
      if ( !hasCurrentCall() ) {
        call->setConnectionState(Call::Ringing);
        ringtone();
        switchCall(call->getCallId());
      } else {
         addWaitingCall(call->getCallId());
      }
    
      std::string from = call->getPeerName();
      std::string number = call->getPeerNumber();
    
      if ( !number.empty() ) {
        from.append(" <");
        from.append(number);
        from.append(">");
      }
      _gui->incomingCall(accountId, call->getCallId(), from);
    
      return true;
    }
    
    //THREAD=VoIP
    void
    ManagerImpl::incomingMessage(const AccountID& accountId, const std::string& message) {
      if (_gui) {
        _gui->incomingMessage(accountId, message);
      }
    }
    
    //THREAD=VoIP CALL=Outgoing
    void
    ManagerImpl::peerAnsweredCall(const CallID& id)
    {
      if (isCurrentCall(id)) {
        stopTone(false);
      }
      if (_gui) _gui->peerAnsweredCall(id);
    }
    
    //THREAD=VoIP Call=Outgoing
    void
    ManagerImpl::peerRingingCall(const CallID& id)
    {
      if (isCurrentCall(id)) {
        ringback();
      }
      if (_gui) _gui->peerRingingCall(id);
    }
    
    //THREAD=VoIP Call=Outgoing/Ingoing
    void
    ManagerImpl::peerHungupCall(const CallID& id)
    {
      AccountID accountid = getAccountFromCall( id );
      if (accountid == AccountNULL) {
        _debug("peerHungupCall: Call doesn't exists\n");
        return;
      }
      if (isCurrentCall(id)) {
        stopTone(true);
        switchCall("");
      }
      removeWaitingCall(id);
      removeCallAccount(id);
      if (_gui) _gui->peerHungupCall(id);
    }
    
    //THREAD=VoIP
    void
    ManagerImpl::callBusy(const CallID& id) {
      _debug("Call busy\n");
      if (isCurrentCall(id) ) {
        playATone(Tone::TONE_BUSY);
        switchCall("");
      }
      removeCallAccount(id);
      removeWaitingCall(id);
      if(_gui) _gui->displayErrorText( id, "Call is busy");
    }
    
    //THREAD=VoIP
    void
    ManagerImpl::callFailure(const CallID& id) 
    {
      _debug("Call failed\n");
      if (isCurrentCall(id) ) {
        playATone(Tone::TONE_BUSY);
        switchCall("");
      }
      if (_gui) {
        _gui->callFailure(id);
      }
      removeCallAccount(id);
      removeWaitingCall(id);
    }
    
    //THREAD=VoIP
    void 
    ManagerImpl::displayTextMessage(const CallID& id, const std::string& message)
    {
      if(_gui) {
       _gui->displayTextMessage(id, message);
      }
    }
    
    //THREAD=VoIP
    void 
    ManagerImpl::displayErrorText(const CallID& id, const std::string& message)
    {
      if(_gui) {
        _gui->displayErrorText(id, message);
      } else {
        std::cerr << message << std::endl;
      }
    }
    
    //THREAD=VoIP
    void 
    ManagerImpl::displayError (const std::string& error)
    {
      if(_gui) {
        _gui->displayError(error);
      }
    }
    
    //THREAD=VoIP
    void 
    ManagerImpl::displayStatus(const std::string& status)
    {
      if(_gui) {
        _gui->displayStatus(status);
      }
    }
    
    //THREAD=VoIP
    void 
    ManagerImpl::displayConfigError (const std::string& message)
    {
      if(_gui) {
        _gui->displayConfigError(message);
      }
    }
    
    //THREAD=VoIP
    void
    ManagerImpl::startVoiceMessageNotification(const AccountID& accountId, const std::string& nb_msg)
    {
      if (_gui) _gui->sendVoiceNbMessage(accountId, nb_msg);
    }
    
    //THREAD=VoIP
    void
    ManagerImpl::stopVoiceMessageNotification(const AccountID& accountId)
    {
      if (_gui) _gui->sendVoiceNbMessage(accountId, std::string("0"));
    } 
    
    //THREAD=VoIP
    void 
    ManagerImpl::registrationSucceed(const AccountID& accountid)
    {
      Account* acc = getAccount(accountid);
      if ( acc ) { 
        acc->setState(true); 
        if (_gui) _gui->sendRegistrationState(accountid, true);
      }
    }
    
    //THREAD=VoIP
    void 
    ManagerImpl::registrationFailed(const AccountID& accountid)
    {
      Account* acc = getAccount(accountid);
      if ( acc ) { 
        acc->setState(false);
        if (_gui) _gui->sendRegistrationState(accountid, false);
      }
    }
    
    /**
     * Multi Thread
     */
    bool 
    ManagerImpl::playATone(Tone::TONEID toneId) {
      int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
      if (!hasToPlayTone) return false;
    
      if (_telephoneTone != 0) {
        _toneMutex.enterMutex();
        _telephoneTone->setCurrentTone(toneId);
        _toneMutex.leaveMutex();
    
        try {
          AudioLayer* audiolayer = getAudioDriver();
          if (audiolayer) { audiolayer->startStream(); }
        } catch(...) {
          _debugException("Off hold could not start audio stream");
          return false;
        }
      }
      return true;
    }
    
    /**
     * Multi Thread
     */
    void 
    ManagerImpl::stopTone(bool stopAudio=true) {
      int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
      if (!hasToPlayTone) return;
    
      if (stopAudio) {
        try {
          AudioLayer* audiolayer = getAudioDriver();
          if (audiolayer) { audiolayer->stopStream(); }
        } catch(...) {
          _debugException("Stop tone and stop stream");
        }
      }
    
      _toneMutex.enterMutex();
      if (_telephoneTone != 0) {
        _telephoneTone->setCurrentTone(Tone::TONE_NULL);
      }
      _toneMutex.leaveMutex();
    
      // for ringing tone..
      _toneMutex.enterMutex();
      _audiofile.stop();
      _toneMutex.leaveMutex();
    }
    
    /**
     * Multi Thread
     */
    bool
    ManagerImpl::playTone()
    {
      //return playATone(Tone::TONE_DIALTONE);
      playATone(Tone::TONE_DIALTONE);
    }
    
    /**
     * Multi Thread
     */
    void
    ManagerImpl::congestion () {
      playATone(Tone::TONE_CONGESTION);
    }
    
    /**
     * Multi Thread
     */
    void
    ManagerImpl::ringback () {
      playATone(Tone::TONE_RINGTONE);
    }
    
    /**
     * Multi Thread
     */
    void
    ManagerImpl::ringtone() 
    {
      int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
      if (!hasToPlayTone) { return; }
    
      std::string ringchoice = getConfigString(AUDIO, RING_CHOICE);
      //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; 
      }
    
      AudioLayer* audiolayer = getAudioDriver();
      if (audiolayer==0) { return; }
      int sampleRate  = audiolayer->getSampleRate();
    
      _toneMutex.enterMutex(); 
      bool loadFile = _audiofile.loadFile(ringchoice, sampleRate);
      _toneMutex.leaveMutex(); 
      if (loadFile) {
        _toneMutex.enterMutex(); 
        _audiofile.start();
        _toneMutex.leaveMutex(); 
        try {
          audiolayer->startStream();
        } catch(...) {
          _debugException("Audio file couldn't start audio stream");
        }
      } else {
        ringback();
      }
    }
    
    AudioLoop*
    ManagerImpl::getTelephoneTone()
    {
      if(_telephoneTone != 0) {
        ost::MutexLock m(_toneMutex);
        return _telephoneTone->getCurrentTone();
      }
      else {
        return 0;
      }
    }
    
    AudioLoop*
    ManagerImpl::getTelephoneFile()
    {
      ost::MutexLock m(_toneMutex);
      if(_audiofile.isStarted()) {
        return &_audiofile;
      } else {
        return 0;
      }
    }
    
    
    /**
     * Use Urgent Buffer
     * By AudioRTP thread
     */
    void
    ManagerImpl::notificationIncomingCall(void) {
    
      AudioLayer* audiolayer = getAudioDriver();
      if (audiolayer != 0) {
        unsigned int samplerate = audiolayer->getSampleRate();
        std::ostringstream frequency;
        frequency << "440/" << FRAME_PER_BUFFER;
    
        Tone tone(frequency.str(), samplerate);
        unsigned int nbSampling = tone.getSize();
        SFLDataFormat buf[nbSampling];
        tone.getNext(buf, tone.getSize());
        audiolayer->putUrgent(buf, sizeof(SFLDataFormat)*nbSampling);
      }
    }
    
    /**
     * Multi Thread
     */
    bool
    ManagerImpl::getStunInfo (StunAddress4& stunSvrAddr, int port) 
    {
      StunAddress4 mappedAddr;
      struct in_addr in;
      char* addr;
    
      //int fd3, fd4;
      // bool ok = stunOpenSocketPair(stunSvrAddr, &mappedAddr, &fd3, &fd4, port);
      int fd1 = stunOpenSocket(stunSvrAddr, &mappedAddr, port);
      bool ok = (fd1 == -1 || fd1 == INVALID_SOCKET) ? false : true;
      if (ok) {
        closesocket(fd1);
        //closesocket(fd3);
        //closesocket(fd4);
        _firewallPort = mappedAddr.port;
        // Convert ipv4 address to host byte ordering
        in.s_addr = ntohl (mappedAddr.addr);
        addr = inet_ntoa(in);
        _firewallAddr = std::string(addr);
        _debug("STUN Firewall: [%s:%d]\n", _firewallAddr.data(), _firewallPort);
        return true;
      } else {
        _debug("Opening a stun socket pair failed\n");
      }
      return false;
    }
    
    bool
    ManagerImpl::behindNat(const std::string& svr, int port)
    {
      StunAddress4 stunSvrAddr;
      stunSvrAddr.addr = 0;
      
      // Convert char* to StunAddress4 structure
      bool ret = stunParseServerName ((char*)svr.data(), stunSvrAddr);
      if (!ret) {
        _debug("SIP: Stun server address (%s) is not valid\n", svr.data());
        return 0;
      }
      
      // Firewall address
      //_debug("STUN server: %s\n", svr.data());
      return getStunInfo(stunSvrAddr, port);
    }
    
    
    ///////////////////////////////////////////////////////////////////////////////
    // Private functions
    ///////////////////////////////////////////////////////////////////////////////
    /**
     * Initialization: Main Thread
     * @return 1: ok
              -1: error directory
               0: unable to load the setting
               2: file doesn't exist yet
     */
    int
    ManagerImpl::createSettingsPath (void) {
      _path = std::string(HOMEDIR) + DIR_SEPARATOR_STR + "." + PROGDIR;
    
      if (mkdir (_path.data(), 0755) != 0) {
        // If directory	creation failed
        if (errno != EEXIST) {
          _debug("Cannot create directory: %s\n", strerror(errno));
          return -1;
        }
      }
    
      // Load user's configuration
      _path = _path + DIR_SEPARATOR_STR + PROGNAME + "rc";
      return _config.populateFromFile(_path);
    }
    
    /**
     * Initialization: Main Thread
     */
    void
    ManagerImpl::initConfigFile (void) 
    {
      std::string type_str("string");
      std::string type_int("int");
    
      std::string section;
      section = SIGNALISATION;
      fill_config_int(SYMMETRIC, YES_STR);
      fill_config_int(PLAY_DTMF, YES_STR);
      fill_config_int(PLAY_TONES, YES_STR);
      fill_config_int(PULSE_LENGTH, DFT_PULSE_LENGTH_STR);
      fill_config_int(SEND_DTMF_AS, SIP_INFO_STR);
    
      section = AUDIO;
      fill_config_int(DRIVER_NAME, DFT_DRIVER_STR);
      fill_config_int(DRIVER_NAME_IN, DFT_DRIVER_STR);
      fill_config_int(DRIVER_NAME_OUT, DFT_DRIVER_STR);
      fill_config_int(DRIVER_SAMPLE_RATE, DRIVER_SAMPLE_RATE_DEFAULT);
      fill_config_str(CODEC1, DFT_CODEC);
      fill_config_str(CODEC2, DFT_CODEC);
      fill_config_str(CODEC3, DFT_CODEC);
      fill_config_str(RING_CHOICE, DFT_RINGTONE);
      fill_config_int(VOLUME_SPKR, DFT_VOL_SPKR_STR);
      fill_config_int(VOLUME_MICRO, DFT_VOL_MICRO_STR);
    
      section = PREFERENCES;
      fill_config_str(SKIN_CHOICE, DFT_SKIN);
      fill_config_int(CONFIRM_QUIT, YES_STR);
      fill_config_str(ZONE_TONE, DFT_ZONE);
      fill_config_int(CHECKED_TRAY, NO_STR);
      fill_config_str(VOICEMAIL_NUM, DFT_VOICEMAIL);
      fill_config_int(CONFIG_ZEROCONF, CONFIG_ZEROCONF_DEFAULT_STR);
    
      initConfigAccount();
      
      _exist = createSettingsPath();
      _setupLoaded = (_exist == 2 ) ? false : true;
    }
    
    /**
     * Initialization: Main Thread
     */
    void
    ManagerImpl::initAudioCodec (void)
    {
      _debugInit("Active Codecs");
      // TODO: need to be more dynamic...
      _codecDescriptorMap.setActive(getConfigString(AUDIO, CODEC1));
      _codecDescriptorMap.setActive(getConfigString(AUDIO, CODEC2));
      _codecDescriptorMap.setActive(getConfigString(AUDIO, CODEC3));
    }
    
    
    /**
     * Initialization: Main Thread
     */
    void
    ManagerImpl::initAudioDriver(void) 
    {
      _debugInit("AudioLayer Creation");
      _audiodriver = new AudioLayer(this);
      if (_audiodriver == 0) {
        _debug("Init audio driver error\n");
      } else {
        std::string error = getAudioDriver()->getErrorMessage();
        if (!error.empty()) {
          _debug("Init audio driver: %s\n", error.c_str());
        }
      } 
    }
    
    /**
     * Initialization: Main Thread and gui
     */
    void
    ManagerImpl::selectAudioDriver (void)
    {
      int noDevice  = getConfigInt(AUDIO, DRIVER_NAME);
      int noDeviceIn  = getConfigInt(AUDIO, DRIVER_NAME_IN);
      int noDeviceOut = getConfigInt(AUDIO, DRIVER_NAME_OUT);
      int sampleRate  = getConfigInt(AUDIO, DRIVER_SAMPLE_RATE);
      if (sampleRate <=0 || sampleRate > 48000) {
          sampleRate = 8000;
      }
    
      // this is when no audio device in/out are set
      // or the audio device in/out are set to 0
      // we take the nodevice instead
      if (noDeviceIn == 0 && noDeviceOut == 0) {
        noDeviceIn = noDeviceOut = noDevice;
      }
      _debugInit(" AudioLayer Opening Device");
      _audiodriver->setErrorMessage("");
      _audiodriver->openDevice(noDeviceIn, noDeviceOut, sampleRate);
    }
    
    /**
     * Initialize the Zeroconf scanning services loop
     * Informations will be store inside a map DNSService->_services
     * Initialization: Main Thread
     */
    void 
    ManagerImpl::initZeroconf(void) 
    {
    #ifdef USE_ZEROCONF
      _debugInit("Zeroconf Initialization");
      int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF);
    
      if (useZeroconf) {
        _DNSService->startScanServices();
      }
    #endif
    }
    
    /**
     * Init the volume for speakers/micro from 0 to 100 value
     * Initialization: Main Thread
     */
    void
    ManagerImpl::initVolume()
    {
      _debugInit("Initiate Volume");
      setSpkrVolume(getConfigInt(AUDIO, VOLUME_SPKR));
      setMicVolume(getConfigInt(AUDIO, VOLUME_MICRO));
    }
    
    /**
     * configuration function requests
     * Main Thread
     */
    bool 
    ManagerImpl::getZeroconf(const std::string& sequenceId)
    {
      bool returnValue = false;
    #ifdef USE_ZEROCONF
      int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF);
      if (useZeroconf && _gui != NULL) {
        TokenList arg;
        TokenList argTXT;
        std::string newService = "new service";
        std::string newTXT = "new txt record";
        if (!_DNSService->isStart()) { _DNSService->startScanServices(); }
        DNSServiceMap services = _DNSService->getServices();
        DNSServiceMap::iterator iter = services.begin();
        arg.push_back(newService);
        while(iter!=services.end()) {
          arg.push_front(iter->first);
          _gui->sendMessage("100",sequenceId,arg);
          arg.pop_front(); // remove the first, the name
    
          TXTRecordMap record = iter->second.getTXTRecords();
          TXTRecordMap::iterator iterTXT = record.begin();
          while(iterTXT!=record.end()) {
            argTXT.clear();
            argTXT.push_back(iter->first);
            argTXT.push_back(iterTXT->first);
            argTXT.push_back(iterTXT->second);
            argTXT.push_back(newTXT);
            _gui->sendMessage("101",sequenceId,argTXT);
            iterTXT++;
          }
          iter++;
        }
        returnValue = true;
      }
    #else
      (void)sequenceId;
    #endif
      return returnValue;
    }
    
    /**
     * Main Thread
     */
    bool 
    ManagerImpl::attachZeroconfEvents(const std::string& sequenceId, Pattern::Observer& observer)
    {
      bool returnValue = false;
      // don't need the _gui like getZeroconf function
      // because Observer is here
    #ifdef USE_ZEROCONF
      int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF);
      if (useZeroconf) {
        if (!_DNSService->isStart()) { _DNSService->startScanServices(); }
        _DNSService->attach(observer);
        returnValue = true;
      }
    #else
      (void)sequenceId;
      (void)observer;
    #endif
      return returnValue;
    }
    bool
    ManagerImpl::detachZeroconfEvents(Pattern::Observer& observer)
    {
      bool returnValue = false;
    #ifdef USE_ZEROCONF
      if (_DNSService) {
        _DNSService->detach(observer);
        returnValue = true;
      }
    #else
      (void)observer;
    #endif
      return returnValue;
    }
    
    /**
     * Main Thread
     */
    bool
    ManagerImpl::getEvents() {
      initRegisterVoIPLink();
      return true;
    }
    
    // TODO: rewrite this
    /**
     * Main Thread
     */
    bool 
    ManagerImpl::getCallStatus(const std::string& sequenceId)
    {
      if (!_gui) { return false; }
      ost::MutexLock m(_callAccountMapMutex);
      CallAccountMap::iterator iter = _callAccountMap.begin();
      TokenList tk;
      std::string code;
      std::string status;
      std::string destination;  
      std::string number;
    
      while (iter != _callAccountMap.end())
      {
        Call* call = getAccountLink(iter->second)->getCall(iter->first);
        Call::ConnectionState state = call->getConnectionState();
        if (state != Call::Connected) {
          switch(state) {
            case Call::Trying:       code="110"; status = "Trying";       break;
            case Call::Ringing:      code="111"; status = "Ringing";      break;
            case Call::Progressing:  code="125"; status = "Progressing";  break;
            case Call::Disconnected: code="125"; status = "Disconnected"; break;
            default: code=""; status= "";
          }
        } else {
          switch (call->getState()) {
            case Call::Active:       code="112"; status = "Established";  break;
            case Call::Hold:         code="114"; status = "Held";         break;
            case Call::Busy:         code="113"; status = "Busy";         break;
            case Call::Refused:      code="125"; status = "Refused";      break;
            case Call::Error:        code="125"; status = "Error";        break;
            case Call::Inactive:     code="125"; status = "Inactive";     break;
          }
        }
    
        // No Congestion
        // No Wrong Number
        // 116 <CSeq> <call-id> <acc> <destination> Busy
        destination = call->getPeerName();
        number = call->getPeerNumber();
        if (number!="") {
          destination.append(" <");
          destination.append(number);
          destination.append(">");
        }
        tk.push_back(iter->second);
        tk.push_back(destination);
        tk.push_back(status);
        _gui->sendCallMessage(code, sequenceId, iter->first, tk);
        tk.clear();
    
        iter++;
      }
      
      return true;
    }
    
    //THREAD=Main
    bool 
    ManagerImpl::getConfigAll(const std::string& sequenceId)
    {
      bool returnValue = false;
      Conf::ConfigTreeIterator iter = _config.createIterator();
      TokenList tk = iter.begin();
      if (tk.size()) {
        returnValue = true;
      }
      while (tk.size()) {
        _gui->sendMessage("100", sequenceId, tk);
        tk = iter.next();
      }
      return returnValue;
    }
    
    //THREAD=Main
    bool 
    ManagerImpl::getConfig(const std::string& section, const std::string& name, TokenList& arg)
    {
      return _config.getConfigTreeItemToken(section, name, arg);
    }
    
    //THREAD=Main
    // throw an Conf::ConfigTreeItemException if not found
    int 
    ManagerImpl::getConfigInt(const std::string& section, const std::string& name)
    {
      try {
        return _config.getConfigTreeItemIntValue(section, name);
      } catch (Conf::ConfigTreeItemException& e) {
        throw e;
      }
      return 0;
    }
    
    //THREAD=Main
    std::string 
    ManagerImpl::getConfigString(const std::string& section, const std::string&
    name)
    {
      try {
        return _config.getConfigTreeItemValue(section, name);
      } catch (Conf::ConfigTreeItemException& e) {
        throw e;
      }
      return "";
    }
    
    //THREAD=Main
    bool 
    ManagerImpl::setConfig(const std::string& section, const std::string& name, const std::string& value)
    {
      return _config.setConfigTreeItem(section, name, value);
    }
    
    //THREAD=Main
    bool 
    ManagerImpl::setConfig(const std::string& section, const std::string& name, int value)
    {
      std::ostringstream valueStream;
      valueStream << value;
      return _config.setConfigTreeItem(section, name, valueStream.str());
    }
    
    //THREAD=Main
    bool 
    ManagerImpl::getConfigList(const std::string& sequenceId, const std::string& name)
    {
      bool returnValue = false;
      TokenList tk;
      if (name=="codecdescriptor") {
    
        CodecMap map = _codecDescriptorMap.getMap();
        CodecMap::iterator iter = map.begin();
        while( iter != map.end() ) {
          tk.clear();
          std::ostringstream strType;
          strType << iter->first;
          tk.push_back(strType.str());
          if (iter->second) {
            tk.push_back(iter->second->getOfficialName());
          } else {
            tk.push_back(strType.str());
          }
          _gui->sendMessage("100", sequenceId, tk);
          iter++;
        }
        returnValue = true;
      } else if (name=="ringtones") {
        // add empty line
        std::ostringstream str;
        str << 1;
        tk.push_back(str.str());
        tk.push_back(""); // filepath
        _gui->sendMessage("100", sequenceId, tk);
    
        // share directory
        std::string path = std::string(PROGSHAREDIR) + DIR_SEPARATOR_STR + RINGDIR;
        int nbFile = 1;
        returnValue = getDirListing(sequenceId, path, &nbFile);
    
        // home directory
        path = std::string(HOMEDIR) + DIR_SEPARATOR_STR + "." + PROGDIR + DIR_SEPARATOR_STR + RINGDIR;
        getDirListing(sequenceId, path, &nbFile);
      } else if (name=="audiodevice") {
        returnValue = getAudioDeviceList(sequenceId, AudioLayer::InputDevice | AudioLayer::OutputDevice);
      } else if (name=="audiodevicein") {
        returnValue = getAudioDeviceList(sequenceId, AudioLayer::InputDevice);
      } else if (name=="audiodeviceout") {
        returnValue = getAudioDeviceList(sequenceId, AudioLayer::OutputDevice);
      } else if (name=="countrytones") {
        returnValue = getCountryTones(sequenceId);
      }
      return returnValue;
    }
    
    //THREAD=Main
    bool 
    ManagerImpl::getAudioDeviceList(const std::string& sequenceId, int ioDeviceMask) 
    {
      AudioLayer* audiolayer = getAudioDriver();
      if (audiolayer == 0) { return false; }
    
      bool returnValue = false;
      
      // TODO: test when there is an error on initializing...
      TokenList tk;
      AudioDevice* device = 0;
      int nbDevice = audiolayer->getDeviceCount();
      
      for (int index = 0; index < nbDevice; index++ ) {
        device = audiolayer->getAudioDeviceInfo(index, ioDeviceMask);
        if (device != 0) {
          tk.clear();
          std::ostringstream str; str << index; tk.push_back(str.str());
          tk.push_back(device->getName());
          tk.push_back(device->getApiName());
          std::ostringstream rate; rate << (int)(device->getRate()); tk.push_back(rate.str());
          _gui->sendMessage("100", sequenceId, tk);
    
          // don't forget to delete it after
          delete device; device = 0;
        }
      }
      returnValue = true;
      
      std::ostringstream rate; 
      rate << "VARIABLE";
      tk.clear();
      tk.push_back(rate.str());
      _gui->sendMessage("101", sequenceId, tk);
    
      return returnValue;
    }
    
    //THREAD=Main
    bool
    ManagerImpl::getCountryTones(const std::string& sequenceId)
    {
      // see ToneGenerator for the list...
      sendCountryTone(sequenceId, 1, "North America");
      sendCountryTone(sequenceId, 2, "France");
      sendCountryTone(sequenceId, 3, "Australia");
      sendCountryTone(sequenceId, 4, "United Kingdom");
      sendCountryTone(sequenceId, 5, "Spain");
      sendCountryTone(sequenceId, 6, "Italy");
      sendCountryTone(sequenceId, 7, "Japan");
    
      return true;
    }
    
    //THREAD=Main
    void 
    ManagerImpl::sendCountryTone(const std::string& sequenceId, int index, const std::string& name) {
      TokenList tk;
      std::ostringstream str; str << index; tk.push_back(str.str());
      tk.push_back(name);
      _gui->sendMessage("100", sequenceId, tk);
    }
    
    //THREAD=Main
    bool
    ManagerImpl::getDirListing(const std::string& sequenceId, const std::string& path, int *nbFile) {
      TokenList tk;
      try {
        ost::Dir dir(path.c_str());
        const char *cFileName = 0;
        std::string fileName;
        std::string filePathName;
        while ( (cFileName=dir++) != 0 ) {
          fileName = cFileName;
          filePathName = path + DIR_SEPARATOR_STR + cFileName;
          if (fileName.length() && fileName[0]!='.' && !ost::isDir(filePathName.c_str())) {
            tk.clear();
            std::ostringstream str;
            str << (*nbFile);
            tk.push_back(str.str());
            tk.push_back(filePathName);
            _gui->sendMessage("100", sequenceId, tk);
            (*nbFile)++;
          }
        }
        return true;
      } catch (...) {
        // error to open file dir
        return false;
      }
    }
    
    bool 
    ManagerImpl::getAccountList(const std::string& sequenceId) 
    {
      bool oneActive = false;
      TokenList tk;
    
      AccountMap::iterator iter = _accountMap.begin();
      while ( iter != _accountMap.end() ) {
        if ( iter->second != 0 ) {
          _debug("Account List: %s\n", iter->first.data()); 
          tk.push_back(iter->first);
          // we try to send one active account for default account on start
          // this is not the way it should be... 
          if ( iter->second->isEnabled() || iter->second->shouldInitOnStart()) {
            tk.push_back("Active");
            tk.push_back(getConfigString(iter->first,CONFIG_ACCOUNT_ALIAS));
             _gui->sendMessage("130", sequenceId, tk);
            oneActive = true;
          } else {
            tk.push_back("Inactive");
            tk.push_back(getConfigString(iter->first,CONFIG_ACCOUNT_ALIAS));
             _gui->sendMessage("131", sequenceId, tk);
          }
    
         tk.clear();
        }
        iter++;
      }
    
      return oneActive;
    }
    
    //THREAD=Main
    /*
     * Experimental...
     */
    bool
    ManagerImpl::setSwitch(const std::string& switchName, std::string& message) {
      AudioLayer* audiolayer = 0;
      if (switchName == "audiodriver" ) {
        // hangup all call here 
        audiolayer = getAudioDriver();
    
        int oldSampleRate = 0;
        if (audiolayer) { oldSampleRate = audiolayer->getSampleRate(); }
    
        selectAudioDriver();
        audiolayer = getAudioDriver();
    
        if (audiolayer) {
          std::string error = audiolayer->getErrorMessage();
          int newSampleRate = audiolayer->getSampleRate();
    
          if (!error.empty()) {
            message = error;
            return false;
          }
    
          if (newSampleRate != oldSampleRate) {
            _toneMutex.enterMutex();
    
            _debug("Unload Telephone Tone\n");
            delete _telephoneTone; _telephoneTone = 0;
            _debug("Unload DTMF Key\n");
            delete _dtmfKey; _dtmfKey = 0;
    
            _debug("Load Telephone Tone\n");
            std::string country = getConfigString(PREFERENCES, ZONE_TONE);
            _telephoneTone = new TelephoneTone(country, newSampleRate);
    
            _debugInit("Loading DTMF key");
            _dtmfKey = new DTMF(newSampleRate);
    
            _toneMutex.leaveMutex();
          }
    
          message = _("Change with success");
          playDtmf('9');
          getAudioDriver()->sleep(300); // in milliseconds
          playDtmf('1');
          getAudioDriver()->sleep(300); // in milliseconds
          playDtmf('1');
          return true;
        }
      } else if ( switchName == "echo" ) {
        audiolayer = getAudioDriver();
        if (audiolayer) {
          audiolayer->toggleEchoTesting();
          return true;
        }
      }
    
    
      return false;
    }
    
    // ACCOUNT handling
    bool
    ManagerImpl::associateCallToAccount(const CallID& callID, const AccountID& accountID)
    {
      if (getAccountFromCall(callID) == AccountNULL) { // nothing with the same ID
        if (  accountExists(accountID)  ) { // account id exist in AccountMap
          ost::MutexLock m(_callAccountMapMutex);
          _callAccountMap[callID] = accountID;
          return true;
        } else {
          return false; 
        }
      } else {
        return false;
      }
    }
    
    AccountID
    ManagerImpl::getAccountFromCall(const CallID& callID)
    {
      ost::MutexLock m(_callAccountMapMutex);
      CallAccountMap::iterator iter = _callAccountMap.find(callID);
      if ( iter == _callAccountMap.end()) {
        return AccountNULL;
      } else {
        return iter->second;
      }
    }
    
    bool
    ManagerImpl::removeCallAccount(const CallID& callID)
    {
      ost::MutexLock m(_callAccountMapMutex);
      if ( _callAccountMap.erase(callID) ) {
        return true;
      }
      return false;
    }
    
    CallID 
    ManagerImpl::getNewCallID() 
    {
      std::ostringstream random_id("s");
      random_id << (unsigned)rand();
      
      // when it's not found, it return ""
      // generate, something like s10000s20000s4394040
      while (getAccountFromCall(random_id.str()) != AccountNULL) {
        random_id.clear();
        random_id << "s";
        random_id << (unsigned)rand();
      }
      return random_id.str();
    }
    
    short
    ManagerImpl::loadAccountMap()
    {
      _debugStart("Load account:");
      short nbAccount = 0;
      Account* tmpAccount;
      
      // SIP Loading X account...
      short nbAccountSIP = ACCOUNT_SIP_COUNT_DEFAULT;
      for (short iAccountSIP = 0; iAccountSIP<nbAccountSIP; iAccountSIP++) {
        std::ostringstream accountName;
        accountName << "SIP" << iAccountSIP;
        
        tmpAccount = AccountCreator::createAccount(AccountCreator::SIP_ACCOUNT, accountName.str());
         if (tmpAccount!=0) {
           _debugMid(" %s", accountName.str().data());
           _accountMap[accountName.str()] = tmpAccount;
          nbAccount++;
        }
      }
    
      // IAX Loading X account...
      short nbAccountIAX = ACCOUNT_IAX_COUNT_DEFAULT;
      for (short iAccountIAX = 0; iAccountIAX<nbAccountIAX; iAccountIAX++) {
        std::ostringstream accountName;
        accountName << "IAX" << iAccountIAX;
        tmpAccount = AccountCreator::createAccount(AccountCreator::IAX_ACCOUNT, accountName.str());
        if (tmpAccount!=0) {
           _debugMid(" %s", accountName.str().data());
           _accountMap[accountName.str()] = tmpAccount;
          nbAccount++;
        }
      }
      _debugEnd("\n");
    
      return nbAccount;
    }
    
    void
    ManagerImpl::unloadAccountMap()
    {
      _debug("Unloading account map...\n");
      AccountMap::iterator iter = _accountMap.begin();
      while ( iter != _accountMap.end() ) {
        _debug("-> Deleting account %s\n", iter->first.c_str());
        delete iter->second; iter->second = 0;
        iter++;
      }
      _accountMap.clear();
    }
    
    bool
    ManagerImpl::accountExists(const AccountID& accountID)
    {
      AccountMap::iterator iter = _accountMap.find(accountID);
      if ( iter == _accountMap.end() ) {
        return false;
      }
      return true;
    }
    
    Account*
    ManagerImpl::getAccount(const AccountID& accountID)
    {
      AccountMap::iterator iter = _accountMap.find(accountID);
      if ( iter == _accountMap.end() ) {
        return 0;
      }
      return iter->second;
    }
    
    VoIPLink* 
    ManagerImpl::getAccountLink(const AccountID& accountID)
    {
      Account* acc = getAccount(accountID);
      if ( acc ) {
        return acc->getVoIPLink();
      }
      return 0;
    }
    
    void
    ManagerImpl::initConfigAccount() {
      AccountMap::iterator iter = _accountMap.begin();
      while ( iter != _accountMap.end() ) {
        if (iter->second!=0) {
          iter->second->initConfig(_config);
        }
        iter++;
      }
    }
    
    #ifdef TEST
    /** 
     * Test accountMap
     */
    bool ManagerImpl::testCallAccountMap()
    {
      if ( getAccountFromCall(1) != AccountNULL ) {
        _debug("TEST: getAccountFromCall with empty list failed\n");
      }
      if ( removeCallAccount(1) != false ) {
        _debug("TEST: removeCallAccount with empty list failed\n");
      }
      CallID newid = getNewCallID();
      if ( associateCallToAccount(newid, "acc0") == false ) {
        _debug("TEST: associateCallToAccount with new CallID empty list failed\n");
      }
      if ( associateCallToAccount(newid, "acc1") == true ) {
        _debug("TEST: associateCallToAccount with a known CallID failed\n");
      }
      if ( getAccountFromCall( newid ) != "acc0" ) {
        _debug("TEST: getAccountFromCall don't return the good account id\n");
      }
      CallID secondnewid = getNewCallID();
      if ( associateCallToAccount(secondnewid, "xxxx") == true ) {
        _debug("TEST: associateCallToAccount with unknown account id failed\n");
      }
      if ( removeCallAccount( newid ) != true ) {
        _debug("TEST: removeCallAccount don't remove the association\n");
      }
    
      return true;
    }
    
    /**
     * Test AccountMap
     */
    bool ManagerImpl::testAccountMap() 
    {
      if (loadAccountMap() != 2) {
        _debug("TEST: loadAccountMap didn't load 2 account\n");
      }
      if (accountExists("acc0") == false) {
        _debug("TEST: accountExists didn't find acc0\n");
      }
      if (accountExists("accZ") != false) {
        _debug("TEST: accountExists found an unknown account\n");
      }
      if (getAccount("acc0") == 0) {
        _debug("TEST: getAccount didn't find acc0\n");
      }
      if (getAccount("accZ") != 0) {
        _debug("TEST: getAccount found an unknown account\n");
      }
      unloadAccountMap();
      if ( accountExists("acc0") == true ) {
        _debug("TEST: accountExists found an account after unloadAccount\n");
      }
      return true;
    }
    #endif