Select Git revision
-
Simon Désaulniers authored
Two instructions were mutually misordered.
Simon Désaulniers authoredTwo instructions were mutually misordered.
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