Project 'savoirfairelinux/ring-client-uwp' was moved to 'savoirfairelinux/jami-client-uwp'. Please update any links and bookmarks that may still have the old path.
Select Git revision
AccountListItemsViewModel.cpp
-
Andreas Traczyk authored
- seperates contacts by account - implements the trust request API - introduces localization strings and transifex update mechanism - adds lodepng to remove windows photobooth png metadata - changes icons - adds default colored initial avatars - changes logo images - complete overhaul on the ui/ux Change-Id: I43914ff63a48bb43a6997721773df5029a1650b5
Andreas Traczyk authored- seperates contacts by account - implements the trust request API - introduces localization strings and transifex update mechanism - adds lodepng to remove windows photobooth png metadata - changes icons - adds default colored initial avatars - changes logo images - complete overhaul on the ui/ux Change-Id: I43914ff63a48bb43a6997721773df5029a1650b5
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