Skip to content
Snippets Groups Projects
  • yanmorin's avatar
    94f514e6
    · 94f514e6
    yanmorin authored
    RPM spec fixe
    Add name to constant, else it won't compile with zeroconf enabled
    94f514e6
    History
    yanmorin authored
    RPM spec fixe
    Add name to constant, else it won't compile with zeroconf enabled
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);
  }
}