Project 'savoirfairelinux/ring-daemon' was moved to 'savoirfairelinux/jami-daemon'. Please update any links and bookmarks that may still have the old path.
Select Git revision
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
managerimpl.cpp 34.36 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 <sys/types.h> // mkdir(2)
#include <sys/stat.h> // mkdir(2)
#include <cc++/thread.h>
#include <cc++/file.h>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include "sipvoiplink.h"
#include "manager.h"
#include "audio/audiocodec.h"
#include "audio/audiolayer.h"
#include "audio/ringbuffer.h"
#include "audio/tonegenerator.h"
#include "audio/tonelist.h"
#include "call.h"
#include "error.h"
#include "user_cfg.h"
#include "voIPLink.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)
{
// initialize random generator
srand (time(NULL));
// Init private variables
_error = new Error();
_tone = new ToneGenerator();
_hasZeroconf = false;
#ifdef USE_ZEROCONF
_hasZeroconf = true;
_DNSService = new DNSService();
#endif
_nCodecs = 0;
_currentCallId = 0;
_startTime = 0;
_endTime = 0;
_path = "";
_exist = 0;
_setupLoaded = false;
_gui = NULL;
_audiodriverPA = NULL;
// Initialize after by init() -> initVolume()
_spkr_volume = 0;
_mic_volume = 0;
_mic_volume_before_mute = 0;
_toneType = ZT_TONE_NULL;
_nbIncomingWaitingCall=0;
_codecMap = CodecDescriptorMap().getMap();
_registerState = UNREGISTERED;
}
// never call if we use only the singleton...
ManagerImpl::~ManagerImpl (void)
{
terminate();
#ifdef USE_ZEROCONF
delete _DNSService; _DNSService = NULL;
#endif
delete _tone; _tone = NULL;
delete _error; _error = NULL;
_debug("%s stop correctly\n", PROGNAME);
}
void
ManagerImpl::init()
{
_debugInit("Volume Initialisation");
initVolume();
if (_exist == 0) {
_debug("Cannot create config file in your home directory\n");
}
try {
_debugInit("Audio Driver Selection");
selectAudioDriver();
}
catch (const portaudio::PaException &e)
{
displayError(e.paErrorText());
throw e;
}
catch (const portaudio::PaCppException &e)
{
displayError(e.what());
throw e;
}
catch (const std::runtime_error &e)
{
displayError(e.what());
throw e;
}
catch (...)
{
displayError("An unknown exception occured.");
throw;
}
_debugInit("Audio Codec Initialization");
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();
std::string country = getConfigString(PREFERENCES, ZONE_TONE);
_telephoneTone = new TelephoneTone(country);
}
void ManagerImpl::terminate()
{
delete _telephoneTone; _telephoneTone = 0;
for(VoIPLinkVector::iterator pos = _voIPLinkVector.begin();
pos != _voIPLinkVector.end();
pos++) {
delete *pos;
*pos = NULL;
}
_voIPLinkVector.clear();
_mutex.enterMutex();
for(CallVector::iterator pos = _callVector.begin();
pos != _callVector.end();
pos++) {
delete *pos; *pos = NULL;
}
_callVector.clear();
_mutex.leaveMutex();
unloadAudioCodec();
delete _audiodriverPA; _audiodriverPA = NULL;
}
void
ManagerImpl::setGui (GuiFramework* gui)
{
_gui = gui;
}
/**
* Multi Thread with _mutex for callVector
*/
Call *
ManagerImpl::pushBackNewCall (CALLID id, enum CallType type)
{
Call* call = new Call(id, type, _voIPLinkVector.at(DFT_VOIP_LINK));
// Set the wanted voip-link (first of the list)
ost::MutexLock m(_mutex);
_callVector.push_back(call);
return call;
}
/**
* Multi Thread with _mutex for callVector
*/
Call*
ManagerImpl::getCall (CALLID 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)
{
CallVector::iterator iter = _callVector.begin();
while(iter!=_callVector.end()) {
Call *call = *iter;
if (call != NULL && call->getId() == id) {
if (call->getFlagNotAnswered()) {
decWaitingCall();
call->setFlagNotAnswered(false);
}
delete (*iter); *iter = NULL;
call = NULL;
_callVector.erase(iter);
return;
}
iter++;
}
}
///////////////////////////////////////////////////////////////////////////////
// Management of events' IP-phone user
///////////////////////////////////////////////////////////////////////////////
/**
* Main thread
*/
int
ManagerImpl::outgoingCall (const std::string& to)
{
CALLID id = generateNewCallId();
Call *call = pushBackNewCall(id, Outgoing);
_debug("Outgoing Call with identifiant %d\n", id);
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)
{
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) {
stopTone(); // stop tone, like a 700 error: number not found Not Found
}
return result;
}
/**
* User action (main thread)
* Every Call
* -1 : call not found
* 0 : already in this state...
*/
int
ManagerImpl::cancelCall (CALLID 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)
{
ost::MutexLock m(_mutex);
Call* call = getCall(id);
if (call == NULL) {
return -1;
}
if (call->getFlagNotAnswered()) {
decWaitingCall();
call->setFlagNotAnswered(false);
}
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)
{
ost::MutexLock m(_mutex);
Call* call = getCall(id);
if (call == NULL) {
return -1;
}
if ( call->getState() == Call::OnHold || call->isNotAnswered()) {
return 1;
}
setCurrentCallId(0);
return call->onHold();
}
/**
* User action (main thread)
* Every Call
*/
int
ManagerImpl::offHoldCall (CALLID id)
{
stopTone();
ost::MutexLock m(_mutex);
Call* call = getCall(id);
if (call == NULL) {
return -1;
}
if (call->getState() == Call::OffHold) {
return 1;
}
setCurrentCallId(id);
int returnValue = call->offHold();
if (returnValue) {
getAudioDriver()->startStream();
}
return returnValue;
}
/**
* User action (main thread)
* Every Call
*/
int
ManagerImpl::transferCall (CALLID id, const std::string& to)
{
ost::MutexLock m(_mutex);
Call* call = getCall(id);
if (call == NULL) {
return -1;
}
setCurrentCallId(0);
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)
{
ost::MutexLock m(_mutex);
Call *call = getCall(id);
if (call == NULL) {
return -1;
}
if ( call->getState() != Call::Progressing ) {
return -1;
}
int refuse = call->refuse();
setCurrentCallId(0);
deleteCall(id);
stopTone();
return refuse;
}
/**
* User action (main thread)
*/
bool
ManagerImpl::saveConfig (void)
{
setConfig(AUDIO, VOLUME_SPKR, getSpkrVolume());
setConfig(AUDIO, VOLUME_MICRO, getMicVolume());
_setupLoaded = _config.saveConfigTree(_path.data());
return _setupLoaded;
}
/**
* Main Thread
*/
void
ManagerImpl::initRegisterVoIPLink()
{
if (_hasTriedToRegister == false) {
_voIPLinkVector.at(DFT_VOIP_LINK)->init(); // we call here, because it's long...
if (_voIPLinkVector.at(DFT_VOIP_LINK)->checkNetwork()) {
// If network is available
if (getConfigInt(SIGNALISATION, AUTO_REGISTER) && _exist == 1) {
registerVoIPLink();
}
}
_hasTriedToRegister = true;
}
}
/**
* Initialize action (main thread)
* Note that Registration is only send if STUN is not activated
* @return 1 if setRegister is call without failure, else return 0
*/
int
ManagerImpl::registerVoIPLink (void)
{
int returnValue = 0;
// Cyrille always want to register to receive call | 2005-10-24 10:50
//if ( !useStun() ) {
if (_voIPLinkVector.at(DFT_VOIP_LINK)->setRegister() >= 0) {
returnValue = 1;
_registerState = REGISTERED;
} else {
_registerState = FAILED;
}
//} else {
// _registerState = UNREGISTERED;
//}
return returnValue;
}
/**
* Terminate action (main thread)
* @return 1 if the unregister method is send correctly
*/
int
ManagerImpl::unregisterVoIPLink (void)
{
if (_voIPLinkVector.at(DFT_VOIP_LINK)->setUnregister() == 0) {
return 1;
} else {
return 0;
}
}
/**
* 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();
_toneMutex.enterMutex();
audiolayer->urgentRingBuffer().flush();
// Put buffer to urgentRingBuffer
// put the size in bytes...
// so size * CHANNELS * 2 (bytes for the int16)
int nbInt16InChar = sizeof(int16)/sizeof(char);
int toSend = audiolayer->urgentRingBuffer().AvailForPut();
if (toSend > size * CHANNELS * nbInt16InChar ) {
toSend = size * CHANNELS * nbInt16InChar;
}
audiolayer->urgentRingBuffer().Put(buf_ctrl_vol, toSend);
// We activate the stream if it's not active yet.
if (!audiolayer->isStreamActive()) {
audiolayer->startStream();
audiolayer->sleep(pulselen);
audiolayer->urgentRingBuffer().flush();
audiolayer->stopStream();
} else {
audiolayer->sleep(pulselen); // in milliseconds
}
_toneMutex.leaveMutex();
//setZonetone(false);
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 != NULL) {
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->getState() != Call::OnHold &&
call->getState() != Call::OffHold) {
returnValue = true;
}
return returnValue;
}
/**
* SipEvent Thread
*/
int
ManagerImpl::incomingCall (CALLID id)
{
ost::MutexLock m(_mutex);
Call* call = getCall(id);
if (call == NULL) {
return -1;
}
call->setType(Incoming);
call->setState(Call::Progressing);
if ( _currentCallId == 0 ) {
switchCall(id);
call->setFlagNotAnswered(false);
ringtone();
} else {
incWaitingCall();
}
// TODO: Account not yet implemented
std::string accountId = "acc1";
std::string from = call->getCallerIdName();
std::string number = call->getCallerIdNumber();
if ( number.length() ) {
from.append(" <");
from.append(number);
from.append(">");
}
return _gui->incomingCall(id, accountId, from);
}
/**
* SipEvent Thread
* for outgoing call, send by SipEvent
*/
void
ManagerImpl::peerAnsweredCall (CALLID id)
{
ost::MutexLock m(_mutex);
Call* call = getCall(id);
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)
{
ost::MutexLock m(_mutex);
Call* call = getCall(id);
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)
{
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);
setCurrentCallId(0);
return 1;
}
/**
* 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"));
}
/**
* Multi Thread
*/
bool
ManagerImpl::playATone(Tone::TONEID toneId) {
ost::MutexLock m(_toneMutex);
//_toneType = tone;
//_tone->toneHandle(_toneType, getConfigString(PREFERENCES, ZONE_TONE));
_telephoneTone->setCurrentTone(toneId);
getAudioDriver()->startStream();
return true;
}
/**
* Multi Thread
*/
void
ManagerImpl::stopTone() {
_toneMutex.enterMutex();
_telephoneTone->setCurrentTone(Tone::TONE_NULL);
// if ( _toneType != ZT_TONE_NULL ) {
// _toneType = ZT_TONE_NULL;
// _tone->stopTone();
// }
_toneMutex.leaveMutex();
getAudioDriver()->stopStream();
}
/**
* 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::callBusy(CALLID id) {
playATone(Tone::TONE_BUSY);
Call* call = getCall(id);
if (call != NULL) {
call->setState(Call::Busy);
}
}
/**
* Multi Thread
*/
void
ManagerImpl::callFailure(CALLID id) {
playATone(Tone::TONE_BUSY);
Call* call = getCall(id);
if (call != NULL) {
getCall(id)->setState(Call::Error);
}
if (_gui) {
_gui->callFailure(id);
}
}
/**
* Multi Thread
*/
void
ManagerImpl::ringtone()
{
_toneMutex.enterMutex();
_toneType = ZT_TONE_FILE;
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;
}
int play = _tone->playRingtone(ringchoice.c_str());
_toneMutex.leaveMutex();
if (play!=1) {
ringback();
}
}
/**
* Use Urgent Buffer
* By AudioRTP thread
*/
void
ManagerImpl::notificationIncomingCall (void) {
int16* buf_ctrl_vol;
int16* buffer = new int16[SAMPLING_RATE];
int size = SAMPLES_SIZE(FRAME_PER_BUFFER); //SAMPLING_RATE/2;
int k;
//int spkrVolume;
_tone->generateSin(440, 0, buffer);
// Volume Control
buf_ctrl_vol = new int16[size*CHANNELS];
// spkrVolume = getSpkrVolume();
for (int j = 0; j < size; j++) {
k = j<<1; // fast multiply by two
buf_ctrl_vol[k] = buf_ctrl_vol[k+1] = buffer[j];
// * spkrVolume/100;
}
getAudioDriver()->putUrgent(buf_ctrl_vol, SAMPLES_SIZE(FRAME_PER_BUFFER));
delete[] buf_ctrl_vol; buf_ctrl_vol = NULL;
delete[] buffer; buffer = NULL;
}
/**
* 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);
_debug("Got port pair at %d\n", mappedAddr.port);
_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("address firewall = %s\n",_firewallAddr.data());
} else {
_debug("Opened 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
while (getCall(random_id) != NULL && random_id != 0) {
random_id = rand();
}
// 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)
{
// 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()
{
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()
{
setSpkrVolume(getConfigInt(AUDIO, VOLUME_SPKR));
setMicVolume(getConfigInt(AUDIO, VOLUME_MICRO));
}
/**
* configuration function requests
* Main Thread
*/
bool
ManagerImpl::getZeroconf(const std::string& )
{
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& , Pattern::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& )
{
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)
{
ost::MutexLock m(_mutex);
// TODO: implement account
std::string accountId = "acc1";
std::string code;
std::string status;
TokenList tk;
Call* call;
if (_gui!=NULL) {
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 = "Holded";
break;
case Call::OffHold:
code="115";
status = "Unholded";
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)
{
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);
}
return true;
}
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)
{
CALLID currentCallId = getCurrentCallId();
if (currentCallId!=0 && id!=currentCallId) {
onHoldCall(currentCallId);
}
setCurrentCallId(id);
}
// EOF