Skip to content
Snippets Groups Projects
Select Git revision
  • ac2ac77bbbffbf4856aacaac0f337229bcce044c
  • master default protected
  • release/202005
  • release/202001
  • release/201912
  • release/201911
  • release/releaseWindowsTestOne
  • release/windowsReleaseTest
  • release/releaseTest
  • release/releaseWindowsTest
  • release/201910
  • release/qt/201910
  • release/windows-test/201910
  • release/201908
  • release/201906
  • release/201905
  • release/201904
  • release/201903
  • release/201902
  • release/201901
  • release/201812
  • 4.0.0
  • 2.2.0
  • 2.1.0
  • 2.0.1
  • 2.0.0
  • 1.4.1
  • 1.4.0
  • 1.3.0
  • 1.2.0
  • 1.1.0
31 results

CallView.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    managerimpl.cpp 37.07 KiB
    /**
     *  Copyright (C) 2004-2005 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/ringbuffer.h"
    #include "audio/tonelist.h"
    
    #include "sipvoiplink.h"
    #include "voIPLink.h"
    #include "call.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)))
    
    #define DFT_VOIP_LINK 0
    
    ManagerImpl::ManagerImpl (void)
    {
      // Init private variables 
      //_error = new Error();
    
      _hasZeroconf = false;
    #ifdef USE_ZEROCONF
      _hasZeroconf = true;
      _DNSService = new DNSService();
    #endif
    
      // setup
      _path = ""; 
      _exist = 0;
      _setupLoaded = false;
      _gui = NULL;
    
      // SOUND:
      _codecMap = CodecDescriptorMap().getMap();
      _audiodriverPA = NULL;
    
      // Initialize after by init() -> initVolume()
      _spkr_volume = 0;
      _mic_volume  = 0; 
      _mic_volume_before_mute = 0;
    
      // Call
      _currentCallId = 0;
      _nbIncomingWaitingCall=0;
      _registerState = UNREGISTERED;
      _hasTriedToRegister = false;
      // initialize random generator for call id
      srand (time(NULL));
    }
    
    // never call if we use only the singleton...
    ManagerImpl::~ManagerImpl (void) 
    {
      terminate();
    
    #ifdef USE_ZEROCONF
      delete _DNSService; _DNSService = NULL;
    #endif
    
      _debug("%s stop correctly.\n", PROGNAME);
    }
    
    void 
    ManagerImpl::init() 
    {
      initVolume();
    
      if (_exist == 0) {
        _debug("Cannot create config file in your home directory\n");
      }
    
      _debugInit("Load Telephone Tone");
      std::string country = getConfigString(PREFERENCES, ZONE_TONE);
      _telephoneTone = new TelephoneTone(country);
    
      try {
        selectAudioDriver();
      }
      catch (const portaudio::PaException &e) {
          getAudioDriver()->setErrorMessage(e.paErrorText());
      }
      catch (const portaudio::PaCppException &e) {
          getAudioDriver()->setErrorMessage(e.what());
      }
      catch (const std::runtime_error &e) {
          getAudioDriver()->setErrorMessage(e.what());
      }
      catch (...) {
          displayError("An unknown exception occured while selecting audio driver.");
          throw;
      }
      initAudioCodec();
    
      _debugInit("Adding new VoIP Link");
      // Set a sip voip link by default
      _voIPLinkVector.push_back(new SipVoIPLink());
    
      // 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();
    
      _debug("Removing VoIP Links...\n");
      for(VoIPLinkVector::iterator pos = _voIPLinkVector.begin();
          pos != _voIPLinkVector.end();
          pos++) {
        delete *pos;
        *pos = NULL;
      }
      _voIPLinkVector.clear();
    
      _debug("Removing calls\n");
      _mutex.enterMutex();
      for(CallVector::iterator pos = _callVector.begin();
          pos != _callVector.end();
          pos++) {
        delete *pos;   *pos = NULL;
      }
      _callVector.clear();
      _mutex.leaveMutex();
    
      unloadAudioCodec();
    
      _debug("Unload Audio Driver\n");
      delete _audiodriverPA; _audiodriverPA = NULL;
    
      _debug("Unload Telephone Tone\n");
      delete _telephoneTone; _telephoneTone = 0;
    }
    
    void
    ManagerImpl::setGui (GuiFramework* gui)
    {
    	_gui = gui;
    }
    
    /**
     * Multi Thread with _mutex for callVector
     */
    Call *
    ManagerImpl::pushBackNewCall (CALLID id, enum CallType type)
    {
      ost::MutexLock m(_mutex);
      Call* call = new Call(id, type, _voIPLinkVector.at(DFT_VOIP_LINK));
      // Set the wanted voip-link (first of the list)
      _callVector.push_back(call);
      return call;
    }
    
    /**
     * Multi Thread with _mutex for callVector
     */
    Call*
    ManagerImpl::getCall (CALLID id)
    {
      //_debug("%10d: Getting call\n", id);
      Call* call = NULL;
      unsigned int size = _callVector.size();
      for (unsigned int i = 0; i < size; i++) {
        call = _callVector.at(i);
        if (call && call->getId() == id) {
          break;
        } else {
          call = NULL;
        }
      }
      return call;
    }
    
    /**
     * Multi Thread with _mutex for callVector
     */
    void
    ManagerImpl::deleteCall (CALLID id)
    {
      //_debug("%10d: Deleting call\n", id);
      CallVector::iterator iter = _callVector.begin();
      while(iter!=_callVector.end()) {
        Call *call = *iter;
        if (call != NULL && call->getId() == id) {
          if (call->getFlagNotAnswered() && call->isIncomingType() && call->getState() != Call::NotExist) {
            decWaitingCall();
          }
          delete (*iter); *iter = NULL; 
          call = NULL;
          _callVector.erase(iter);
          return;
        }
        iter++;
      }
    }
    
    void
    ManagerImpl::setCurrentCallId(CALLID id)
    {
      //_debug("%10d: Setting current callid, old one was: %d\n", id, _currentCallId);
      _currentCallId = id;
    }
    
    void
    ManagerImpl::removeCallFromCurrent(CALLID id)
    {
      if ( _currentCallId == id ) {
        //_debug("%10d: Setting current callid, old one was: %d\n", 0, _currentCallId);
        _currentCallId = 0;
      }
    }
    
    
    ///////////////////////////////////////////////////////////////////////////////
    // Management of events' IP-phone user
    ///////////////////////////////////////////////////////////////////////////////
    /**
     * Main thread
     */
    int 
    ManagerImpl::outgoingCall (const std::string& to)
    {
      CALLID id = generateNewCallId();
      _debug("%10d: Outgoing Call\n", id);
      Call *call = pushBackNewCall(id, Outgoing);
      ost::MutexLock m(_mutex);
      call->setState(Call::Progressing);
      call->setCallerIdNumber(to);
      if (call->outgoingCall(to) == 0) {
        return id;
      } else {
        return 0;
      }
    }
    
    /**
     * User action (main thread)
     * Every Call
     */
    int 
    ManagerImpl::hangupCall (CALLID id)
    {
      _debug("%10d: Hangup Call\n", id);
      stopTone();
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call == NULL) {
        return -1;
      }
      int result = -1;
      if (call->getState() != Call::Error) { 
        result = call->hangup();
      }
      deleteCall(id);
      // current call id or no line selected
      if (id == _currentCallId || _currentCallId == 0) {
        removeCallFromCurrent(id);
      }
      return result;
    }
    
    /**
     * User action (main thread)
     * Every Call
     * -1 : call not found
     *  0 : already in this state...
     */
    int
    ManagerImpl::cancelCall (CALLID id)
    {
      _debug("%10d: Cancel Call\n", id);
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call == NULL) { 
        return -1; 
      }
      int result = call->cancel();
      deleteCall(id);
      stopTone();
      return result;
    }
    
    /**
     * User action (main thread)
     * Incoming Call
     */
    int 
    ManagerImpl::answerCall (CALLID id)
    {
      _debug("%10d: Answer Call\n", id);
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call == NULL) {
        return -1;
      }
      if (call->getFlagNotAnswered()) {
        decWaitingCall();
        call->setFlagNotAnswered(false);
      }
      if (call->getState() != Call::OnHold) {
        switchCall(id);
      }
      stopTone(); // before answer, don't stop the audio stream after open it
      return call->answer();
    }
    
    /**
     * User action (main thread)
     * Every Call
     * @return 0 if it fails, -1 if not present
     */
    int 
    ManagerImpl::onHoldCall (CALLID id)
    {
      _debug("%10d: On Hold Call\n", id);
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call == NULL) {
        return -1;
      }
      removeCallFromCurrent(id);
      if ( call->getState() == Call::OnHold || call->isNotAnswered()) {
        return 1;
      }
      return call->onHold();
    }
    
    /**
     * User action (main thread)
     * Every Call
     */
    int 
    ManagerImpl::offHoldCall (CALLID id)
    {
      _debug("%10d: Off Hold Call\n", id);
      ost::MutexLock m(_mutex);
      stopTone();
      Call* call = getCall(id);
      if (call == 0) {
        return -1;
      }
      if (call->getState() == Call::OffHold) {
        return 1;
      }
      setCurrentCallId(id);
      int returnValue = call->offHold();
      // start audio if it's ok
      if (returnValue != -1) {
        try {
          getAudioDriver()->startStream();
        } catch(...) {
          _debugException("Off hold could not start audio stream");
        }
      }
      return returnValue;
    }
    
    /**
     * User action (main thread)
     * Every Call
     */
    int 
    ManagerImpl::transferCall (CALLID id, const std::string& to)
    {
      _debug("%10d: Transfer Call to %s\n", id, to.c_str());
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call == 0) {
        return -1;
      }
      removeCallFromCurrent(id);
      return call->transfer(to);
    }
    
    /**
     * User action (main thread)
     * All Call
     */
    void
    ManagerImpl::mute() {
      _mic_volume_before_mute = _mic_volume;
      setMicVolume(0);
    }
    
    /**
     * User action (main thread)
     * All Call
     */
    void
    ManagerImpl::unmute() {
      if ( _mic_volume == 0 ) {
        setMicVolume(_mic_volume_before_mute);
      }
    }
    
    /**
     * User action (main thread)
     * Call Incoming
     */
    int 
    ManagerImpl::refuseCall (CALLID id)
    {
      _debug("%10d: Refuse Call\n", id);
      ost::MutexLock m(_mutex);
      Call *call = getCall(id);
      if (call == NULL) {
        return -1;
      }
    
      if ( call->getState() != Call::Progressing ) {
        return -1;
      }
    
      int refuse = call->refuse();
      removeCallFromCurrent(id);
      deleteCall(id);
      stopTone();
      return refuse;
    }
    
    /**
     * User action (main thread)
     */
    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;
    }
    
    /**
     * Main Thread
     */
    bool
    ManagerImpl::initRegisterVoIPLink() 
    {
      int returnValue = true;
      _debugInit("Initiate VoIP Link Registration\n");
      if (_hasTriedToRegister == false) {
        if ( _voIPLinkVector.at(DFT_VOIP_LINK)->init() ) { 
          // we call here, because it's long...
          // If network is available and exosip is start..
          if (getConfigInt(SIGNALISATION, AUTO_REGISTER) && _exist == 1) {
            registerVoIPLink();
            _hasTriedToRegister = true;
          }
        } else {
          returnValue = false;
        }
      }
      return returnValue;
    }
    
    
    /**
     * Initialize action (main thread)
     * Note that Registration is only send if STUN is not activated
     * @return true if setRegister is call without failure, else return false
     */
    bool
    ManagerImpl::registerVoIPLink (void)
    {
      _debug("Register VoIP Link\n");
      int returnValue = false;
      if (_voIPLinkVector.at(DFT_VOIP_LINK)->setRegister() >= 0) {
        returnValue = true;
        _registerState = REGISTERED;
      } else {
        _registerState = FAILED;
      }
      return returnValue;
    }
    
    /**
     * Terminate action (main thread)
     * @return true if the unregister method is send correctly
     */
    bool 
    ManagerImpl::unregisterVoIPLink (void)
    {
      _debug("Unregister VoIP Link\n");
    	if (_voIPLinkVector.at(DFT_VOIP_LINK)->setUnregister() == 0) {
    		return true;
    	} else {
    		return false;
    	}
    }
    
    /**
     * User action (main thread)
     */
    bool 
    ManagerImpl::sendDtmf (CALLID id, char code)
    {
      int sendType = getConfigInt(SIGNALISATION, SEND_DTMF_AS);
      int returnValue = false;
      switch (sendType) {
      case 0: // SIP INFO
        playDtmf(code);
        _voIPLinkVector.at(DFT_VOIP_LINK)->carryingDTMFdigits(id, code);
        returnValue = true;
        break;
    
      case 1: // Audio way
        break;
      case 2: // rfc 2833
        break;
      default: // unknown - error config?
        break;
      }
      return returnValue;
    }
    
    /**
     * User action (main thread)
     */
    bool
    ManagerImpl::playDtmf(char code)
    {
      stopTone();
    
      // 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
      int size = pulselen * (SAMPLING_RATE/1000);
    
      // this buffer is for mono
      int16* _buf = new int16[size];
      bool returnValue = false;
    
      // Handle dtmf
      _key.startTone(code);
    
      // copy the sound...
      if ( _key.generateDTMF(_buf, size) ) {
        int k;
    
        // allocation of more space, for stereo conversion
        int16* buf_ctrl_vol = new int16[size*CHANNELS];
    
        // Control volume and format mono->stereo
        for (int j = 0; j < size; j++) {
          k = j<<1; // fast multiplication by two
          buf_ctrl_vol[k] = buf_ctrl_vol[k+1] = _buf[j];
        }
    
        AudioLayer *audiolayer = getAudioDriver();
    
        // Put buffer to urgentRingBuffer 
        // put the size in bytes...
        // so size * CHANNELS * 2 (bytes for the int16)
        int nbInt16InChar = sizeof(int16)/sizeof(char);
        audiolayer->putUrgent(buf_ctrl_vol, size * CHANNELS * nbInt16InChar);
    
        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");
        }
        delete[] buf_ctrl_vol; buf_ctrl_vol = 0;
        returnValue = true;
      }
      delete[] _buf; _buf = 0;
      return returnValue;
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    // Management of event peer IP-phone 
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Multi-thread
     */
    bool
    ManagerImpl::incomingCallWaiting() {
      ost::MutexLock m(_incomingCallMutex);
      return (_nbIncomingWaitingCall > 0) ? true : false;
    }
    
    void
    ManagerImpl::incWaitingCall() {
      ost::MutexLock m(_incomingCallMutex);
      _nbIncomingWaitingCall++;
      //_debug("incWaitingCall: %d\n", _nbIncomingWaitingCall);
    }
    
    void
    ManagerImpl::decWaitingCall() {
      ost::MutexLock m(_incomingCallMutex);
      _nbIncomingWaitingCall--;
      //_debug("decWaitingCall: %d\n", _nbIncomingWaitingCall);
    }
    
    
    /**
     * SipEvent Thread
     * Set the call info for incoming call
     */
    void
    ManagerImpl::callSetInfo(CALLID id, const std::string& name, const std::string& number)
    {
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call != 0) {
        call->setCallerIdName(name);
        call->setCallerIdNumber(number);
      }
    }
    
    /**
     * SipEvent Thread
     * ask if it can close the call
     */
    bool
    ManagerImpl::callCanBeClosed(CALLID id) {
      bool returnValue = false;
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call != NULL && call->getState() != Call::Progressing) {
        returnValue = true;
      }
      return returnValue;
    }
    
    /**
     * SipEvent Thread
     * ask if it can answer the call
     */
    bool
    ManagerImpl::callCanBeAnswered(CALLID id) {
      bool returnValue = false;
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call != NULL && ( call->getFlagNotAnswered() || 
           (call->getState()!=Call::OnHold && call->getState()!=Call::OffHold) )) {
        returnValue = true;
      }
      return returnValue;
    }
    
    /**
     * SipEvent Thread
     * ask if it can start the sound thread
     */
    bool
    ManagerImpl::callIsOnHold(CALLID id) {
      bool returnValue = false;
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call != NULL && (call->getState()==Call::OnHold)) {
        returnValue = true;
      }
      return returnValue;
    }
    
    /**
     * SipEvent Thread
     */
    int 
    ManagerImpl::incomingCall (CALLID id, const std::string& name, const std::string& number)
    {
      _debug("%10d: Incoming call\n", id);
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call == NULL) {
        return -1;
      }
      call->setType(Incoming);
      call->setState(Call::Progressing);
    
      if ( _currentCallId == 0 ) {
        call->setFlagNotAnswered(false);
        ringtone();
        switchCall(id);
      } else {
        incWaitingCall();
      }
    
      // TODO: Account not yet implemented
      std::string accountId = "acc1";
      std::string from = name;     call->setCallerIdName(name);
      call->setCallerIdNumber(number);
      if ( !number.empty() ) {
        from.append(" <");
        from.append(number);
        from.append(">");
      }
      return _gui->incomingCall(id, accountId, from);
    }
    
    /**
     * SipEvent Thread
     * for outgoing message, send by SipEvent
     */
    void 
    ManagerImpl::incomingMessage(const std::string& message) {
      if (_gui) {
        _gui->incomingMessage(message);
      }
    }
    
    /**
     * SipEvent Thread
     * for outgoing call, send by SipEvent
     */
    void 
    ManagerImpl::peerAnsweredCall (CALLID id)
    {
      _debug("%10d: Peer Answered Call\n", id);
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call != 0) {
        call->setFlagNotAnswered(false);
        call->setState(Call::Answered);
    
        stopTone();
        // switch current call
        switchCall(id);
        if (_gui) _gui->peerAnsweredCall(id);
      }
    }
    
    /**
     * SipEvent Thread
     * for outgoing call, send by SipEvent
     */
    int
    ManagerImpl::peerRingingCall (CALLID id)
    {
      _debug("%10d: Peer Ringing Call\n", id);
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call != 0) {
        call->setState(Call::Ringing);
    
        // ring
        ringback();
        if (_gui) _gui->peerRingingCall(id);
      }
      return 1;
    }
    
    /**
     * SipEvent Thread
     * for outgoing call, send by SipEvent
     */
    int 
    ManagerImpl::peerHungupCall (CALLID id)
    {
      _debug("%10d: Peer Hungup Call\n", id);
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if ( call == NULL ) {
        return -1;
      }
      if ( _currentCallId == id ) {
        stopTone();
      }
    
      if (_gui) _gui->peerHungupCall(id);
      deleteCall(id);
      call->setState(Call::Hungup);
    
      removeCallFromCurrent(id);
      return 1;
    }
    
    /**
     * Multi Thread
     */
    void
    ManagerImpl::callBusy(CALLID id) {
      _debug("%10d: Call is busy\n", id);
      playATone(Tone::TONE_BUSY);
      ost::MutexLock m(_mutex);
      Call* call = getCall(id);
      if (call != 0) {
        call->setState(Call::Busy);
      }
      deleteCall(id);
      call->setState(Call::Hungup);
    
      removeCallFromCurrent(id);
    }
    
    /**
     * Multi Thread
     */
    void
    ManagerImpl::callFailure(CALLID id) {
      _debug("%10d: Call failed\n", id);
      playATone(Tone::TONE_BUSY);
      _mutex.enterMutex();
      Call* call = getCall(id);
      if (call != 0) {
        call->setState(Call::Error);
      }
      _mutex.leaveMutex();
      if (_gui) {
        _gui->callFailure(id);
      }
      deleteCall(id);
      call->setState(Call::Hungup);
    
      removeCallFromCurrent(id);
    }
    
    /**
     * SipEvent Thread
     * for outgoing call, send by SipEvent
     */
    void 
    ManagerImpl::displayTextMessage (CALLID id, const std::string& message)
    {
      if(_gui) {
        _gui->displayTextMessage(id, message);
      }
    }
    
    /**
     * SipEvent Thread
     * for outgoing call, send by SipEvent
     */
    void 
    ManagerImpl::displayErrorText (CALLID id, const std::string& message)
    {
      if(_gui) {
        _gui->displayErrorText(id, message);
      } else {
        std::cerr << message << std::endl;
      }
    }
    
    /**
     * SipEvent Thread
     * for outgoing call, send by SipEvent
     */
    void 
    ManagerImpl::displayError (const std::string& voIPError)
    {
      if(_gui) {
        _gui->displayError(voIPError);
      }
    }
    
    /**
     * SipEvent Thread
     * for outgoing call, send by SipEvent
     */
    void 
    ManagerImpl::displayStatus (const std::string& status)
    {
      if(_gui) {
        _gui->displayStatus(status);
      }
    }
    
    /**
     * SipEvent Thread
     * for outgoing call, send by SipEvent
     */
    void 
    ManagerImpl::displayConfigError (const std::string& message)
    {
      if(_gui) {
        _gui->displayConfigError(message);
      }
    }
    
    /**
     * SipEvent Thread
     * for outgoing call, send by SipEvent
     */
    void
    ManagerImpl::startVoiceMessageNotification (const std::string& nb_msg)
    {
      if (_gui) _gui->sendVoiceNbMessage(nb_msg);
    }
    
    /**
     * SipEvent Thread
     * for outgoing call, send by SipEvent
     */
    void
    ManagerImpl::stopVoiceMessageNotification (void)
    {
      if (_gui) _gui->sendVoiceNbMessage(std::string("0"));
    }
    
    /**
     * SipEvent Thread
     */
    void 
    ManagerImpl::registrationSucceed()
    {
      if (_gui) _gui->sendRegistrationState(true);
    }
    
    /**
     * SipEvent Thread
     */
    void 
    ManagerImpl::registrationFailed()
    {
      if (_gui) _gui->sendRegistrationState(false);
    }
    
    /**
     * Multi Thread
     */
    bool 
    ManagerImpl::playATone(Tone::TONEID toneId) {
      _toneMutex.enterMutex();
      _telephoneTone->setCurrentTone(toneId);
      _toneMutex.leaveMutex();
    
      try {
        getAudioDriver()->startStream();
      } catch(...) {
        _debugException("Off hold could not start audio stream");
      }
      return true;
    }
    
    /**
     * Multi Thread
     */
    void 
    ManagerImpl::stopTone() {
      try {
        getAudioDriver()->stopStream();
      } catch(...) {
        _debugException("Stop tone and stop stream");
      }
    
      _toneMutex.enterMutex();
      _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);
    }
    
    /**
     * Multi Thread
     */
    void
    ManagerImpl::congestion () {
      playATone(Tone::TONE_CONGESTION);
    }
    
    /**
     * Multi Thread
     */
    void
    ManagerImpl::ringback () {
      playATone(Tone::TONE_RINGTONE);
    }
    
    /**
     * Multi Thread
     */
    void
    ManagerImpl::ringtone() 
    {
      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; 
      }
      _toneMutex.enterMutex(); 
      bool loadFile = _audiofile.loadFile(ringchoice);
      _toneMutex.leaveMutex(); 
      if (loadFile) {
        _toneMutex.enterMutex(); 
        _audiofile.start();
        _toneMutex.leaveMutex(); 
        try {
          getAudioDriver()->startStream();
        } catch(...) {
          _debugException("Audio file couldn't start audio stream");
        }
      } else {
        ringback();
      }
    }
    
    AudioLoop*
    ManagerImpl::getTelephoneTone()
    {
      if(_telephoneTone) {
        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) {
        std::ostringstream frequency;
        frequency << "440/" << FRAME_PER_BUFFER;
    
        Tone tone(frequency.str());
        unsigned int nbInt16 = tone.getSize();
        int16 buf[nbInt16];
        tone.getNext(buf, tone.getMonoSize());
        audiolayer->putUrgent(buf, sizeof(int16)*nbInt16);
      }
    
    }
    
    /**
     * Multi Thread
     */
    void
    ManagerImpl::getStunInfo (StunAddress4& stunSvrAddr) 
    {
      StunAddress4 mappedAddr;
      struct in_addr in;
      char* addr;
      char to[16];
      bzero (to, 16);
    
      int fd3, fd4;
      bool ok = stunOpenSocketPair(stunSvrAddr,
                                        &mappedAddr,
                                        &fd3,
                                        &fd4);
      if (ok) {
        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);
      } else {
        _debug("Opening a stun socket pair failed\n");
      }
    }
    
    bool
    ManagerImpl::useStun (void) 
    {
      if (getConfigInt(SIGNALISATION, USE_STUN)) {
          return true;
      } else {
          return false;
      }
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    // Private functions
    ///////////////////////////////////////////////////////////////////////////////
    
    /**
     * Multi Thread
     */
    CALLID 
    ManagerImpl::generateNewCallId (void)
    {
      CALLID random_id = (unsigned)rand();
    
      // Check if already a call with this id exists 
      _mutex.enterMutex();
      while (getCall(random_id) != NULL && random_id != 0) {
        random_id = rand();
      }
      _mutex.leaveMutex();
      // If random_id is not attributed, returns it.
      return random_id;
    }
    
    /**
     * 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_str(FULL_NAME, EMPTY_FIELD);
      fill_config_str(USER_PART, EMPTY_FIELD);
      fill_config_str(AUTH_USER_NAME, EMPTY_FIELD);
      fill_config_str(PASSWORD, EMPTY_FIELD);
      fill_config_str(HOST_PART, EMPTY_FIELD);
      fill_config_str(PROXY, EMPTY_FIELD);
      fill_config_int(AUTO_REGISTER, 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);
      fill_config_str(STUN_SERVER, DFT_STUN_SERVER);
      fill_config_int(USE_STUN, NO_STR);
    
      section = AUDIO;
      fill_config_int(DRIVER_NAME, DFT_DRIVER_STR);
      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);
    
      _exist = createSettingsPath();
      _setupLoaded = (_exist == 2 ) ? false : true;
    }
    
    /**
     * Initialization: Main Thread
     */
    void
    ManagerImpl::initAudioCodec (void)
    {
      _debugInit("Load Audio Codecs");
      // TODO: need to be more dynamic...
      _codecDescVector.push_back(new CodecDescriptor(getConfigString(AUDIO, CODEC1)));
      _codecDescVector.push_back(new CodecDescriptor(getConfigString(AUDIO, CODEC2)));
      _codecDescVector.push_back(new CodecDescriptor(getConfigString(AUDIO, CODEC3)));
    }
    
    /**
     * Terminate: Main Thread
     */
    void 
    ManagerImpl::unloadAudioCodec()
    {
      _debug("Unload Audio Codecs\n");
      CodecDescriptorVector::iterator iter = _codecDescVector.begin();
      while(iter!=_codecDescVector.end()) {
        delete *iter; *iter = NULL;
        iter++;
      }
      _codecDescVector.clear();
    }
    
    
    /**
     * Initialization: Main Thread
     */
    void
    ManagerImpl::selectAudioDriver (void)
    {
    #if defined(AUDIO_PORTAUDIO)
      try {
        _debugInit("AudioLayer Creation");
        _audiodriverPA = new AudioLayer();
        int noDevice = getConfigInt(AUDIO, DRIVER_NAME);
        _debugInit(" AudioLayer Device Count");
        int nbDevice = portaudio::System::instance().deviceCount();
        if (nbDevice == 0) {
          throw std::runtime_error("Portaudio detect no sound card.");
        } else if (noDevice >= nbDevice) {
          _debug(" Portaudio auto-select device #0 because device #%d is not found\n", noDevice);
          _setupLoaded = false;
          noDevice = 0;
        }
        _debugInit(" AudioLayer Opening Device");
        _audiodriverPA->openDevice(noDevice);
      } catch(...) {
        throw;
      }
    #else
    # error You must define one AUDIO driver to use.
    #endif
    }
    
    /**
     * 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\n");
      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;
      }
    #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;
      }
    #endif
      return returnValue;
    }
    bool
    ManagerImpl::detachZeroconfEvents(Pattern::Observer& observer)
    {
      bool returnValue = false;
    #ifdef USE_ZEROCONF
      if (_DNSService) {
        _DNSService->detach(observer);
        returnValue = true;
      }
    #endif
      return returnValue;
    }
    
    /**
     * Main Thread
     */
    bool
    ManagerImpl::getEvents() {
      initRegisterVoIPLink();
      return true;
    }
    
    /**
     * Main Thread
     */
    bool 
    ManagerImpl::getCallStatus(const std::string& sequenceId)
    {
      // TODO: implement account
      std::string accountId = "acc1"; 
      std::string code;
      std::string status;
      TokenList tk;
      Call* call;
    
      if (_gui!=NULL) {
        ost::MutexLock m(_mutex);
        CallVector::iterator iter = _callVector.begin();
        while(iter!=_callVector.end()){
          call = (*iter);
          switch( call->getState() ) {
           case Call::Progressing: code="110"; status="Trying";        break;
           case Call::Ringing:     code="111"; status = "Ringing";     break;
           case Call::Answered:    code="112"; status = "Established"; break;
           case Call::Busy:        code="113"; status = "Busy";        break;
           case Call::OnHold:      code="114"; status = "Held";        break;
           case Call::OffHold:     code="115"; status = "Unheld";      break;
           default:                code="125"; status="Other";    
          }
    
          // No Congestion
          // No Wrong Number
          // 116 <CSeq> <call-id> <acc> <destination> Busy
          std::string destination = call->getCallerIdName();
          std::string number = call->getCallerIdNumber();
          if (number!="") {
            destination.append(" <");
            destination.append(number);
            destination.append(">");
          }
          tk.push_back(accountId);
          tk.push_back(destination);
          tk.push_back(status);
          _gui->sendCallMessage(code, sequenceId, (*iter)->getId(), tk);
          iter++;
          tk.clear();
        }
      }
      return true;
    }
    
    /**
     * Main Thread
     */
    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;
    }
    
    /**
     * Main Thread
     */
    bool 
    ManagerImpl::getConfig(const std::string& section, const std::string& name, TokenList& arg)
    {
      return _config.getConfigTreeItemToken(section, name, arg);
    }
    
    /**
     * Main Thread
     */
    // 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;
    }
    
    /**
     * Main Thread
     */
    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 "";
    }
    
    /**
     * Main Thread
     */
    bool 
    ManagerImpl::setConfig(const std::string& section, const std::string& name, const std::string& value)
    {
      return _config.setConfigTreeItem(section, name, value);
    }
    
    /**
     * Main Thread
     */
    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());
    }
    
    /**
     * Main Thread
     */
    bool 
    ManagerImpl::getConfigList(const std::string& sequenceId, const std::string& name)
    {
      bool returnValue = false;
      TokenList tk;
      if (name=="codecdescriptor") {
    
        CodecMap::iterator iter = _codecMap.begin();
        while( iter != _codecMap.end() ) {
          tk.clear();
          std::ostringstream strType;
          strType << iter->first;
          tk.push_back(strType.str());
          tk.push_back(iter->second);
          _gui->sendMessage("100", sequenceId, tk);
          iter++;
        }
        returnValue = true;
      } else if (name=="ringtones") {
        std::string path = std::string(PROGSHAREDIR) + DIR_SEPARATOR_STR + RINGDIR;
        int nbFile = 0;
        returnValue = getDirListing(sequenceId, path, &nbFile);
    
        path = std::string(HOMEDIR) + DIR_SEPARATOR_STR + "." + PROGDIR + DIR_SEPARATOR_STR + RINGDIR;
        getDirListing(sequenceId, path, &nbFile);
      } else if (name=="audiodevice") {
        returnValue = getAudioDeviceList(sequenceId);
      } else if (name=="countrytones") {
        returnValue = getCountryTones(sequenceId);
      }
      return returnValue;
    }
    
    /**
     * User request Main Thread (list)
     */
    bool 
    ManagerImpl::getAudioDeviceList(const std::string& sequenceId) 
    {
      bool returnValue = false;
      try {
        // TODO: test when there is an error on initializing...
        TokenList tk;
        portaudio::System& sys = portaudio::System::instance();
    
        const char *hostApiName;
        const char *deviceName;
    
        for (int index = 0; index < sys.deviceCount(); index++ ) {
          portaudio::Device& device = sys.deviceByIndex(index);
          hostApiName = device.hostApi().name();
          deviceName  = device.name();
    
          tk.clear();
          std::ostringstream str; str << index; tk.push_back(str.str());
          tk.push_back(deviceName);
          tk.push_back(std::string(hostApiName));
          _gui->sendMessage("100", sequenceId, tk);
        }
        returnValue = true;
      } catch (...) {
        returnValue = false;
      }
      return returnValue;
    }
    
    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;
    }
    
    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);
    }
    
    /**
     * User action : main thread
     */
    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 = NULL;
        std::string fileName;
        std::string filePathName;
        while ( (cFileName=dir++) != NULL ) {
          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;
      }
    }
    
    /**
     * Multi Thread
     */
    void 
    ManagerImpl::switchCall(CALLID id)
    {
      // we can only switch the current call id if we 
      // it's not selected yet..
      if (_currentCallId == 0 ) {
        setCurrentCallId(id);
      }
    }