Select Git revision
codecmodel.h
-
Sébastien Blin authored
Change-Id: I8d5f968fbedbc884c91416246049a0ef4cd652eb
Sébastien Blin authoredChange-Id: I8d5f968fbedbc884c91416246049a0ef4cd652eb
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
managerimpl.cpp 102.70 KiB
/*
* Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc.
* Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>
* Author: Yan Morin <yan.morin@savoirfairelinux.com>
* Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com>
* Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
* Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
* Author: Guillaume Carmel-Archambault <guillaume.carmel-archambault@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 3 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.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include "config.h"
#include "managerimpl.h"
#include "account.h"
#include "dbus/callmanager.h"
#include "global.h"
#include "sip/sipaccount.h"
#include "im/InstantMessaging.h"
#include "iax/iaxaccount.h"
#include "numbercleaner.h"
#include "audio/alsa/alsalayer.h"
#include "audio/pulseaudio/pulselayer.h"
#include "audio/sound/tonelist.h"
#include "audio/sound/audiofile.h"
#include "audio/sound/dtmf.h"
#include "history/historymanager.h"
#include "sip/sipvoiplink.h"
#include "iax/iaxvoiplink.h"
#include "manager.h"
#include "dbus/configurationmanager.h"
#include "conference.h"
#include <cerrno>
#include <ctime>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>
#include <sys/types.h> // mkdir(2)
#include <sys/stat.h> // mkdir(2)
ManagerImpl::ManagerImpl (void) :
_hasTriedToRegister (false), _config(), _currentCallId2(),
_currentCallMutex(), _audiodriver (0),
_dtmfKey (0), _audioCodecFactory(), _toneMutex(),
_telephoneTone (0), _audiofile (0), _spkr_volume (0),
_mic_volume (0), _waitingCall(),
_waitingCallMutex(), _nbIncomingWaitingCall (0), _path (""),
_callAccountMap(),
_callAccountMapMutex(), _callConfigMap(), _accountMap(),
_history (new HistoryManager), _imModule(new sfl::InstantMessaging)
{
// initialize random generator for call id
srand (time (NULL));
}
// never call if we use only the singleton...
ManagerImpl::~ManagerImpl (void)
{
delete _imModule;
delete _history;
delete _audiofile;
}
void ManagerImpl::init (std::string config_file)
{
if (config_file.empty())
config_file = getConfigFile();
_path = config_file;
_debug ("Manager: configuration file path: %s", _path.c_str());
Conf::YamlParser *parser;
try {
parser = new Conf::YamlParser (_path.c_str());
parser->serializeEvents();
parser->composeEvents();
parser->constructNativeData();
} catch (Conf::YamlParserException &e) {
_error ("Manager: %s", e.what());
delete parser;
parser = NULL;
}
loadAccountMap(parser);
delete parser;
initVolume();
initAudioDriver();
selectAudioDriver();
// Initialize the list of supported audio codecs
_audioCodecFactory.init();
audioLayerMutexLock();
if (_audiodriver) {
unsigned int sampleRate = _audiodriver->getSampleRate();
_debug ("Manager: Load telephone tone");
std::string country(preferences.getZoneToneChoice());
_telephoneTone = new TelephoneTone (country, sampleRate);
_debug ("Manager: Loading DTMF key (%d)", sampleRate);
sampleRate = 8000;
_dtmfKey = new DTMF (sampleRate);
}
audioLayerMutexUnlock();
// Load the history
_history->load_history (preferences.getHistoryLimit());
// Init the instant messaging module
_imModule->init();
// Register accounts
registerAccounts();
}
void ManagerImpl::terminate ()
{
_debug ("Manager: Terminate ");
std::vector<std::string> callList(getCallList());
_debug ("Manager: Hangup %zu remaining call", callList.size());
std::vector<std::string>::iterator iter;
for (iter = callList.begin(); iter != callList.end(); ++iter)
hangupCall (*iter);
unloadAccountMap();
delete _dtmfKey;
delete _telephoneTone;
_telephoneTone = NULL;
audioLayerMutexLock();
delete _audiodriver;
_audiodriver = NULL;
_audioCodecFactory.deleteHandlePointer();
audioLayerMutexUnlock();
}
bool ManagerImpl::isCurrentCall (const std::string& callId)
{
return _currentCallId2 == callId;
}
bool ManagerImpl::hasCurrentCall ()
{
return not _currentCallId2.empty();
}
const std::string&
ManagerImpl::getCurrentCallId () const
{
return _currentCallId2;
}
void ManagerImpl::switchCall (const std::string& id)
{
ost::MutexLock m (_currentCallMutex);
_debug ("----- Switch current call id to %s -----", id.c_str());
_currentCallId2 = id;
}
///////////////////////////////////////////////////////////////////////////////
// Management of events' IP-phone user
///////////////////////////////////////////////////////////////////////////////
/* Main Thread */
bool ManagerImpl::outgoingCall (const std::string& account_id,
const std::string& call_id, const std::string& to, const std::string& conf_id)
{
if (call_id.empty()) {
_debug ("Manager: New outgoing call abbort, missing callid");
return false;
}
// Call ID must be unique
if (not getAccountFromCall (call_id).empty()) {
_error ("Manager: Error: Call id already exists in outgoing call");
return false;
}
_debug ("Manager: New outgoing call %s to %s", call_id.c_str(), to.c_str());
stopTone();
std::string current_call_id(getCurrentCallId());
std::string prefix;
if (hookPreference.getNumberEnabled())
prefix = hookPreference.getNumberAddPrefix();
std::string to_cleaned(NumberCleaner::clean(to, prefix));
Call::CallConfiguration callConfig;
/* Check what kind of call we are dealing with */
checkCallConfiguration (call_id, to_cleaned, &callConfig);
// in any cases we have to detach from current communication
if (hasCurrentCall()) {
_debug ("Manager: Has current call (%s) put it onhold", current_call_id.c_str());
// if this is not a conferenceand this and is not a conference participant
if (!isConference (current_call_id) && !participToConference (current_call_id))
onHoldCall (current_call_id);
else if (isConference (current_call_id) && !participToConference (call_id))
detachParticipant (Call::DEFAULT_ID, current_call_id);
}
if (callConfig == Call::IPtoIP) {
_debug ("Manager: Start IP2IP call");
/* We need to retrieve the sip voiplink instance */
if (SIPVoIPLink::instance()->SIPNewIpToIpCall(call_id, to_cleaned)) {
switchCall (call_id);
return true;
} else
callFailure (call_id);
return false;
}
_debug ("Manager: Selecting account %s", account_id.c_str());
// Is this account exist
if (!accountExists (account_id)) {
_error ("Manager: Error: Account doesn't exist in new outgoing call");
return false;
}
if(!associateCallToAccount (call_id, account_id))
_warn("Manager: Warning: Could not associate call id %s to account id %s", call_id.c_str(), account_id.c_str());
try {
Call *call = getAccountLink(account_id)->newOutgoingCall (call_id, to_cleaned);
switchCall (call_id);
call->setConfId(conf_id);
} catch (const VoipLinkException &e) {
callFailure (call_id);
_error ("Manager: %s", e.what());
return false;
}
getMainBuffer()->stateInfo();
return true;
}
//THREAD=Main : for outgoing Call
bool ManagerImpl::answerCall (const std::string& call_id)
{
_debug ("Manager: Answer call %s", call_id.c_str());
// If sflphone is ringing
stopTone();
// store the current call id
std::string current_call_id(getCurrentCallId());
// Retreive call coresponding to this id
std::string account_id = getAccountFromCall (call_id);
Call *call = getAccountLink (account_id)->getCall (call_id);
if (call == NULL) {
_error("Manager: Error: Call is null");
}
// in any cases we have to detach from current communication
if (hasCurrentCall()) {
_debug ("Manager: Currently conversing with %s", current_call_id.c_str());
if (!isConference(current_call_id) && !participToConference (current_call_id)) {
// if it is not a conference and is not a conference participant
_debug ("Manager: Answer call: Put the current call (%s) on hold", current_call_id.c_str());
onHoldCall (current_call_id);
} else if (isConference (current_call_id) && !participToConference (call_id)) {
// if we are talking to a conference and we are answering an incoming call
_debug ("Manager: Detach main participant from conference");
detachParticipant (Call::DEFAULT_ID, current_call_id);
}
}
try {
getAccountLink (account_id)->answer (call);
}
catch (const VoipLinkException &e) {
_error("Manager: Error: %s", e.what());
}
// if it was waiting, it's waiting no more
removeWaitingCall (call_id);
// if we dragged this call into a conference already
if (participToConference (call_id))
switchCall (call->getConfId());
else
switchCall (call_id);
// Connect streams
addStream (call_id);
getMainBuffer()->stateInfo();
// Start recording if set in preference
if (audioPreference.getIsAlwaysRecording())
setRecordingCall(call_id);
// update call state on client side
if (audioPreference.getIsAlwaysRecording())
_dbus.getCallManager()->callStateChanged (call_id, "RECORD");
else
_dbus.getCallManager()->callStateChanged(call_id, "CURRENT");
return true;
}
//THREAD=Main
void ManagerImpl::hangupCall (const std::string& callId)
{
_info ("Manager: Hangup call %s", callId.c_str());
// First stop audio layer if there is no call anymore
if (getCallList().empty()) {
audioLayerMutexLock();
if(_audiodriver == NULL) {
audioLayerMutexUnlock();
_error("Manager: Error: Audio layer was not instantiated");
return;
}
_debug ("Manager: stop audio stream, there is no call remaining");
_audiodriver->stopStream();
audioLayerMutexUnlock();
}
// store the current call id
std::string currentCallId(getCurrentCallId());
stopTone();
/* Broadcast a signal over DBus */
_debug ("Manager: Send DBUS call state change (HUNGUP) for id %s", callId.c_str());
_dbus.getCallManager()->callStateChanged (callId, "HUNGUP");
if (not isValidCall(callId) and not getConfigFromCall(callId) == Call::IPtoIP) {
_error("Manager: Error: Could not hang up call, call not valid");
return;
}
// Disconnect streams
removeStream(callId);
if (participToConference (callId)) {
Conference *conf = getConferenceFromCallID (callId);
if (conf != NULL) {
// remove this participant
removeParticipant (callId);
processRemainingParticipant (currentCallId, conf);
}
} else {
// we are not participating to a conference, current call switched to ""
if (!isConference (currentCallId))
switchCall ("");
}
if (getConfigFromCall (callId) == Call::IPtoIP) {
/* Direct IP to IP call */
try {
SIPVoIPLink::instance()->hangup (callId);
}
catch (const VoipLinkException &e)
{
_error("%s", e.what());
}
}
else {
std::string accountId (getAccountFromCall (callId));
getAccountLink (accountId)->hangup (callId);
removeCallAccount (callId);
}
getMainBuffer()->stateInfo();
}
bool ManagerImpl::hangupConference (const std::string& id)
{
_debug ("Manager: Hangup conference %s", id.c_str());
ConferenceMap::iterator iter_conf = _conferencemap.find (id);
std::string currentAccountId;
if (iter_conf != _conferencemap.end()) {
Conference *conf = iter_conf->second;
ParticipantSet participants = conf->getParticipantList();
ParticipantSet::iterator iter_participant = participants.begin();
while (iter_participant != participants.end()) {
_debug ("Manager: Hangup conference participant %s", (*iter_participant).c_str());
hangupCall (*iter_participant);
iter_participant++;
}
}
switchCall ("");
getMainBuffer()->stateInfo();
return true;
}
//THREAD=Main
bool ManagerImpl::onHoldCall (const std::string& callId)
{
bool returnValue = false;
_debug ("Manager: Put call %s on hold", callId.c_str());
stopTone();
std::string current_call_id = getCurrentCallId();
try {
if (getConfigFromCall (callId) == Call::IPtoIP) {
/* Direct IP to IP call */
returnValue = SIPVoIPLink::instance ()-> onhold (callId);
}
else {
/* Classic call, attached to an account */
std::string account_id(getAccountFromCall (callId));
if (account_id == "") {
_debug ("Manager: Account ID %s or callid %s doesn't exists in call onHold", account_id.c_str(), callId.c_str());
return false;
}
returnValue = getAccountLink (account_id)->onhold (callId);
}
}
catch (const VoipLinkException &e){
_error("Manager: Error: %s", e.what());
}
// Unbind calls in main buffer
removeStream (callId);
// Remove call from teh queue if it was still there
removeWaitingCall (callId);
// keeps current call id if the action is not holding this call or a new outgoing call
// this could happen in case of a conference
if (current_call_id == callId)
switchCall ("");
_dbus.getCallManager()->callStateChanged (callId, "HOLD");
getMainBuffer()->stateInfo();
return returnValue;
}
//THREAD=Main
bool ManagerImpl::offHoldCall (const std::string& callId)
{
std::string accountId;
bool returnValue = true;
std::string codecName;
_debug ("Manager: Put call %s off hold", callId.c_str());
stopTone();
std::string currentCallId = getCurrentCallId();
//Place current call on hold if it isn't
if (hasCurrentCall()) {
// if this is not a conference and this and is not a conference participant
if (!isConference (currentCallId) && !participToConference (currentCallId)) {
_debug ("Manager: Has current call (%s), put on hold", currentCallId.c_str());
onHoldCall (currentCallId);
} else if (isConference (currentCallId) && !participToConference (callId))
detachParticipant (Call::DEFAULT_ID, currentCallId);
}
bool isRec = false;
/* Direct IP to IP call */
if (getConfigFromCall (callId) == Call::IPtoIP)
returnValue = SIPVoIPLink::instance ()-> offhold (callId);
else {
/* Classic call, attached to an account */
accountId = getAccountFromCall (callId);
_debug ("Manager: Setting offhold, Account %s, callid %s", accountId.c_str(), callId.c_str());
Call * call = getAccountLink (accountId)->getCall (callId);
if (call)
{
isRec = call->isRecording();
returnValue = getAccountLink (accountId)->offhold (callId);
}
}
_dbus.getCallManager()->callStateChanged (callId, isRec ? "UNHOLD_RECORD" : "UNHOLD_CURRENT");
if (participToConference (callId)) {
std::string currentAccountId;
currentAccountId = getAccountFromCall (callId);
Call *call = getAccountLink (currentAccountId)->getCall (callId);
if (call)
switchCall (call->getConfId());
} else
switchCall (callId);
addStream(callId);
getMainBuffer()->stateInfo();
return returnValue;
}
//THREAD=Main
bool ManagerImpl::transferCall (const std::string& callId, const std::string& to)
{
bool returnValue = false;;
_info ("Manager: Transfer call %s", callId.c_str());
std::string currentCallId = getCurrentCallId();
if (participToConference(callId)) {
Conference *conf = getConferenceFromCallID(callId);
if (conf == NULL)
_error("Manager: Error: Could not find conference from call id");
removeParticipant (callId);
processRemainingParticipant (callId, conf);
}
else if (!isConference(currentCallId))
switchCall("");
// Direct IP to IP call
if (getConfigFromCall (callId) == Call::IPtoIP)
returnValue = SIPVoIPLink::instance ()-> transfer (callId, to);
else {
std::string accountid (getAccountFromCall (callId));
if (accountid == "") {
_warn ("Manager: Call doesn't exists");
return false;
}
returnValue = getAccountLink (accountid)->transfer (callId, to);
}
// remove waiting call in case we make transfer without even answer
removeWaitingCall (callId);
getMainBuffer()->stateInfo();
return returnValue;
}
void ManagerImpl::transferFailed ()
{
_dbus.getCallManager()->transferFailed();
}
void ManagerImpl::transferSucceded ()
{
_dbus.getCallManager()->transferSucceded();
}
bool ManagerImpl::attendedTransfer(const std::string& transferID, const std::string& targetID)
{
bool returnValue = false;
_debug("Manager: Attended transfer");
// Direct IP to IP call
if (getConfigFromCall (transferID) == Call::IPtoIP)
returnValue = SIPVoIPLink::instance ()-> attendedTransfer(transferID, targetID);
else { // Classic call, attached to an account
std::string accountid = getAccountFromCall (transferID);
if (accountid.empty()) {
_warn ("Manager: Call doesn't exists");
return false;
}
returnValue = getAccountLink (accountid)->attendedTransfer (transferID, targetID);
}
getMainBuffer()->stateInfo();
return returnValue;
}
//THREAD=Main : Call:Incoming
bool ManagerImpl::refuseCall (const std::string& id)
{
std::string accountid;
bool returnValue;
_debug ("Manager: Refuse call %s", id.c_str());
std::string current_call_id = getCurrentCallId();
stopTone();
int nbCalls = getCallList().size();
if (nbCalls <= 1) {
_debug (" refuseCall: stop audio stream, there is only %d call(s) remaining", nbCalls);
audioLayerMutexLock();
_audiodriver->stopStream();
audioLayerMutexUnlock();
}
/* Direct IP to IP call */
if (getConfigFromCall (id) == Call::IPtoIP)
returnValue = SIPVoIPLink::instance ()-> refuse (id);
else {
/* Classic call, attached to an account */
accountid = getAccountFromCall (id);
if (accountid.empty()) {
_warn ("Manager: Call doesn't exists");
return false;
}
returnValue = getAccountLink (accountid)->refuse (id);
removeCallAccount (id);
}
// if the call was outgoing or established, we didn't refuse it
// so the method did nothing
if (returnValue) {
removeWaitingCall (id);
_dbus.getCallManager()->callStateChanged (id, "HUNGUP");
}
// Disconnect streams
removeStream(id);
getMainBuffer()->stateInfo();
return returnValue;
}
Conference*
ManagerImpl::createConference (const std::string& id1, const std::string& id2)
{
typedef std::pair<std::string, Conference*> ConferenceEntry;
_debug ("Manager: Create conference with call %s and %s", id1.c_str(), id2.c_str());
Conference* conf = new Conference;
conf->add (id1);
conf->add (id2);
// Add conference to map
_conferencemap.insert (ConferenceEntry (conf->getConfID(), conf));
// broadcast a signal over dbus
_dbus.getCallManager()->conferenceCreated (conf->getConfID());
return conf;
}
void ManagerImpl::removeConference (const std::string& conference_id)
{
_debug ("Manager: Remove conference %s", conference_id.c_str());
_debug ("Manager: number of participant: %d", (int) _conferencemap.size());
ConferenceMap::iterator iter = _conferencemap.find (conference_id);
Conference* conf = NULL;
if (iter != _conferencemap.end())
conf = iter->second;
if (conf == NULL) {
_error ("Manager: Error: Conference not found");
return;
}
// broadcast a signal over dbus
_dbus.getCallManager()->conferenceRemoved (conference_id);
// We now need to bind the audio to the remain participant
// Unbind main participant audio from conference
getMainBuffer()->unBindAll (Call::DEFAULT_ID);
ParticipantSet participants = conf->getParticipantList();
// bind main participant audio to remaining conference call
ParticipantSet::iterator iter_p = participants.begin();
if (iter_p != participants.end())
getMainBuffer()->bindCallID (*iter_p, Call::DEFAULT_ID);
// Then remove the conference from the conference map
if (_conferencemap.erase (conference_id) == 1)
_debug ("Manager: Conference %s removed successfully", conference_id.c_str());
else
_error ("Manager: Error: Cannot remove conference: %s", conference_id.c_str());
delete conf;
}
Conference*
ManagerImpl::getConferenceFromCallID (const std::string& call_id)
{
std::string account_id;
account_id = getAccountFromCall (call_id);
Call *call = getAccountLink (account_id)->getCall (call_id);
ConferenceMap::const_iterator iter = _conferencemap.find (call->getConfId());
if (iter != _conferencemap.end())
return iter->second;
else
return NULL;
}
void ManagerImpl::holdConference (const std::string& id)
{
_debug ("Manager: Hold conference()");
ConferenceMap::iterator iter_conf = _conferencemap.find (id);
bool isRec = false;
std::string currentAccountId;
if (iter_conf != _conferencemap.end()) {
Conference *conf = iter_conf->second;
if(conf->getState() == Conference::ACTIVE_ATTACHED_REC) {
isRec = true;
} else if (conf->getState() == Conference::ACTIVE_DETACHED_REC) {
isRec = true;
} else if (conf->getState() == Conference::HOLD_REC) {
isRec = true;
}
ParticipantSet participants = conf->getParticipantList();
ParticipantSet::iterator iter_participant = participants.begin();
while (iter_participant != participants.end()) {
_debug (" holdConference: participant %s", (*iter_participant).c_str());
currentAccountId = getAccountFromCall (*iter_participant);
switchCall (*iter_participant);
onHoldCall (*iter_participant);
iter_participant++;
}
conf->setState(isRec ? Conference::HOLD_REC : Conference::HOLD);
_dbus.getCallManager()->conferenceChanged (conf->getConfID(), conf->getStateStr());
}
}
void ManagerImpl::unHoldConference (const std::string& id)
{
ConferenceMap::iterator iter_conf = _conferencemap.find (id);
if (iter_conf != _conferencemap.end()) {
bool isRec = false;
Conference *conf = iter_conf->second;
if (conf->getState() == Conference::ACTIVE_ATTACHED_REC or
conf->getState() == Conference::ACTIVE_DETACHED_REC or
conf->getState() == Conference::HOLD_REC)
isRec = true;
ParticipantSet participants(conf->getParticipantList());
for (ParticipantSet::const_iterator iter = participants.begin(); iter!= participants.end(); ++iter) {
_debug (" unholdConference: participant %s", (*iter).c_str());
std::string currentAccountId(getAccountFromCall(*iter));
Call *call = getAccountLink(currentAccountId)->getCall(*iter);
// if one call is currently recording, the conference is in state recording
isRec |= call->isRecording();
offHoldCall (*iter);
}
conf->setState(isRec ? Conference::ACTIVE_ATTACHED_REC : Conference::ACTIVE_ATTACHED);
_dbus.getCallManager()->conferenceChanged (conf->getConfID(), conf->getStateStr());
}
}
bool ManagerImpl::isConference (const std::string& id)
{
return (_conferencemap.find(id) != _conferencemap.end());
}
bool ManagerImpl::participToConference (const std::string& call_id)
{
std::string accountId(getAccountFromCall(call_id));
Call *call = getAccountLink (accountId)->getCall(call_id);
if (call == NULL) {
_error("Manager: Error call is NULL in particip to conference");
return false;
}
if (call->getConfId().empty())
return false;
return true;
}
void ManagerImpl::addParticipant (const std::string& callId, const std::string& conferenceId)
{
_debug ("Manager: Add participant %s to %s", callId.c_str(), conferenceId.c_str());
ConferenceMap::iterator iter = _conferencemap.find (conferenceId);
if (iter == _conferencemap.end()) {
_error("Manager: Error: Conference id is not valid");
return;
}
std::string currentAccountId(getAccountFromCall (callId));
Call *call = getAccountLink (currentAccountId)->getCall (callId);
if (call == NULL) {
_error("Manager: Error: Call id is not valid");
return;
}
// store the current call id (it will change in offHoldCall or in answerCall)
std::string current_call_id(getCurrentCallId());
// detach from prior communication and switch to this conference
if (current_call_id != callId) {
if (isConference (current_call_id))
detachParticipant (Call::DEFAULT_ID, current_call_id);
else
onHoldCall (current_call_id);
}
// TODO: remove this ugly hack => There should be different calls when double clicking
// a conference to add main participant to it, or (in this case) adding a participant
// toconference
switchCall ("");
// Add main participant
addMainParticipant (conferenceId);
Conference* conf = iter->second;
switchCall (conf->getConfID());
// Add coresponding IDs in conf and call
call->setConfId (conf->getConfID());
conf->add (callId);
// Connect new audio streams together
getMainBuffer()->unBindAll (callId);
std::map<std::string, std::string> callDetails = getCallDetails (callId);
std::string callState = callDetails.find("CALL_STATE")->second;
if (callState == "HOLD") {
conf->bindParticipant (callId);
offHoldCall (callId);
} else if (callState == "INCOMING") {
conf->bindParticipant (callId);
answerCall (callId);
} else if (callState == "CURRENT") {
conf->bindParticipant (callId);
}
ParticipantSet participants(conf->getParticipantList());
if (participants.empty())
_error("Manager: Error: Participant list is empty for this conference");
// reset ring buffer for all conference participant
// flush conference participants only
for (ParticipantSet::const_iterator iter_p = participants.begin();
iter_p != participants.end(); ++iter_p)
getMainBuffer()->flush(*iter_p);
getMainBuffer()->flush (Call::DEFAULT_ID);
// Connect stream
addStream(callId);
}
void ManagerImpl::addMainParticipant (const std::string& conference_id)
{
if (hasCurrentCall()) {
std::string current_call_id(getCurrentCallId());
if (isConference(current_call_id))
detachParticipant(Call::DEFAULT_ID, current_call_id);
else
onHoldCall(current_call_id);
}
audioLayerMutexLock();
_debug("Manager: Add Main Participant");
ConferenceMap::const_iterator iter = _conferencemap.find (conference_id);
if (iter != _conferencemap.end()) {
Conference *conf = iter->second;
ParticipantSet participants = conf->getParticipantList();
for (ParticipantSet::const_iterator iter_p = participants.begin();
iter_p != participants.end(); ++iter_p)
getMainBuffer()->bindCallID(*iter_p, Call::DEFAULT_ID);
// Reset ringbuffer's readpointers
for (ParticipantSet::const_iterator iter_p = participants.begin();
iter_p != participants.end(); ++iter_p)
getMainBuffer()->flush(*iter_p);
getMainBuffer()->flush(Call::DEFAULT_ID);
if (conf->getState() == Conference::ACTIVE_DETACHED)
conf->setState(Conference::ACTIVE_ATTACHED);
else if (conf->getState() == Conference::ACTIVE_DETACHED_REC)
conf->setState(Conference::ACTIVE_ATTACHED_REC);
else
_warn("Manager: Warning: Invalid conference state while adding main participant");
_dbus.getCallManager()->conferenceChanged (conference_id, conf->getStateStr());
}
audioLayerMutexUnlock();
switchCall (conference_id);
}
void ManagerImpl::joinParticipant (const std::string& callId1, const std::string& callId2)
{
_debug ("Manager: Join participants %s, %s", callId1.c_str(), callId2.c_str());
std::map<std::string, std::string> call1Details = getCallDetails (callId1);
std::map<std::string, std::string> call2Details = getCallDetails (callId2);
std::string current_call_id = getCurrentCallId();
_debug ("Manager: Current Call ID %s", current_call_id.c_str());
// detach from the conference and switch to this conference
if ( (current_call_id != callId1) && (current_call_id != callId2)) {
if (isConference (current_call_id)) {
// If currently in a conference
detachParticipant (Call::DEFAULT_ID, current_call_id);
}
else {
// If currently in a call
onHoldCall (current_call_id);
}
}
Conference *conf = createConference (callId1, callId2);
// Set corresponding conference ids for call 1
std::string currentAccountId1 = getAccountFromCall (callId1);
Call *call1 = getAccountLink (currentAccountId1)->getCall (callId1);
if(call1 == NULL) {
_error("Manager: Could not find call %s", callId1.c_str());
}
call1->setConfId (conf->getConfID());
getMainBuffer()->unBindAll(callId1);
// Set corresponding conderence details
std::string currentAccountId2 = getAccountFromCall (callId2);
Call *call2 = getAccountLink (currentAccountId2)->getCall (callId2);
if(call2 == NULL) {
_error("Manager: Could not find call %s", callId2.c_str());
}
call2->setConfId (conf->getConfID());
getMainBuffer()->unBindAll(callId2);
// Process call1 according to its state
std::string call1_state_str = call1Details.find ("CALL_STATE")->second;
_debug ("Manager: Process call %s state: %s", callId1.c_str(), call1_state_str.c_str());
if (call1_state_str == "HOLD") {
conf->bindParticipant (callId1);
offHoldCall (callId1);
} else if (call1_state_str == "INCOMING") {
conf->bindParticipant (callId1);
answerCall (callId1);
} else if (call1_state_str == "CURRENT")
conf->bindParticipant (callId1);
else if (call1_state_str == "RECORD")
conf->bindParticipant(callId1);
else if (call1_state_str == "INACTIVE") {
conf->bindParticipant (callId1);
answerCall (callId1);
} else
_warn ("Manager: Call state not recognized");
// Process call2 according to its state
std::string call2_state_str = call2Details.find ("CALL_STATE")->second;
_debug ("Manager: Process call %s state: %s", callId2.c_str(), call2_state_str.c_str());
if (call2_state_str == "HOLD") {
conf->bindParticipant (callId2);
offHoldCall (callId2);
} else if (call2_state_str == "INCOMING") {
conf->bindParticipant (callId2);
answerCall (callId2);
} else if (call2_state_str == "CURRENT")
conf->bindParticipant (callId2);
else if (call2_state_str == "RECORD")
conf->bindParticipant (callId2);
else if (call2_state_str == "INACTIVE") {
conf->bindParticipant (callId2);
answerCall (callId2);
} else
_warn ("Manager: Call state not recognized");
// Switch current call id to this conference
switchCall (conf->getConfID());
conf->setState(Conference::ACTIVE_ATTACHED);
// set recording sampling rate
audioLayerMutexLock();
if (_audiodriver)
conf->setRecordingSmplRate(_audiodriver->getSampleRate());
audioLayerMutexUnlock();
getMainBuffer()->stateInfo();
}
void ManagerImpl::createConfFromParticipantList(const std::vector< std::string > &participantList)
{
// we must at least have 2 participant for a conference
if (participantList.size() <= 1) {
_error("Manager: Error: Participant number must be higher or equal to 2");
return;
}
Conference *conf = new Conference();
int successCounter = 0;
for (size_t i = 0; i != participantList.size(); i++) {
std::string numberaccount(participantList[i]);
std::string tostr(numberaccount.substr(0, numberaccount.find(",")));
std::string account(numberaccount.substr(numberaccount.find(",")+1, numberaccount.size()));
std::string generatedCallID(getNewCallID());
// Manager methods may behave differently if the call id particip to a conference
conf->add(generatedCallID);
switchCall("");
// Create call
bool callSuccess = outgoingCall(account, generatedCallID, tostr, conf->getConfID());
// If not able to create call remove this participant from the conference
if (!callSuccess)
conf->remove(generatedCallID);
else {
_dbus.getCallManager()->newCallCreated(account, generatedCallID, tostr);
successCounter++;
}
}
// Create the conference if and only if at least 2 calls have been successfully created
if (successCounter >= 2 ) {
_conferencemap.insert(std::pair<std::string, Conference *> (conf->getConfID(), conf));
_dbus.getCallManager()->conferenceCreated (conf->getConfID());
audioLayerMutexLock();
if(_audiodriver)
conf->setRecordingSmplRate(_audiodriver->getSampleRate());
audioLayerMutexUnlock();
getMainBuffer()->stateInfo();
} else
delete conf;
}
void ManagerImpl::detachParticipant (const std::string& call_id,
const std::string& current_id)
{
_debug ("Manager: Detach participant %s (current id: %s)", call_id.c_str(), current_id.c_str());
std::string current_call_id(getCurrentCallId());
if (call_id != Call::DEFAULT_ID) {
std::string currentAccountId(getAccountFromCall (call_id));
Call *call = getAccountLink (currentAccountId)->getCall (call_id);
if (call == NULL) {
_error("Manager: Error: Could not find call %s", call_id.c_str());
return;
}
// TODO: add conference_id as a second parameter
ConferenceMap::iterator iter = _conferencemap.find(call->getConfId());
Conference *conf = getConferenceFromCallID(call_id);
if (conf == NULL) {
_error ("Manager: Error: Call is not conferencing, cannot detach");
return;
}
std::map<std::string, std::string> call_details(getCallDetails(call_id));
std::map<std::string, std::string>::iterator iter_details(call_details.find ("CALL_STATE"));
if (iter_details == call_details.end()) {
_error ("Manager: Error: Could not find CALL_STATE");
return;
}
if (iter_details->second == "RINGING")
removeParticipant (call_id);
else {
onHoldCall (call_id);
removeParticipant (call_id);
processRemainingParticipant (current_call_id, conf);
}
} else {
_debug ("Manager: Unbind main participant from conference %d");
getMainBuffer()->unBindAll (Call::DEFAULT_ID);
if (!isConference(current_call_id)) {
_error("Manager: Warning: Current call id (%s) is not a conference", current_call_id.c_str());
return;
}
ConferenceMap::iterator iter = _conferencemap.find (current_call_id);
Conference *conf = iter->second;
if (conf == NULL) {
_debug("Manager: Error: Conference is NULL");
return;
}
if (conf->getState() == Conference::ACTIVE_ATTACHED)
conf->setState(Conference::ACTIVE_DETACHED);
else if(conf->getState() == Conference::ACTIVE_ATTACHED_REC)
conf->setState(Conference::ACTIVE_DETACHED_REC);
else
_warn("Manager: Warning: Undefined behavior, invalid conference state in detach participant");
_dbus.getCallManager()->conferenceChanged (conf->getConfID(), conf->getStateStr());
switchCall ("");
}
}
void ManagerImpl::removeParticipant (const std::string& call_id)
{
_debug ("Manager: Remove participant %s", call_id.c_str());
// this call is no more a conference participant
const std::string currentAccountId(getAccountFromCall (call_id));
Call *call = getAccountLink (currentAccountId)->getCall (call_id);
ConferenceMap conf_map = _conferencemap;
ConferenceMap::iterator iter = conf_map.find (call->getConfId());
if (iter == conf_map.end()) {
_error ("Manager: Error: No conference with id %s, cannot remove participant", call->getConfId().c_str());
return;
}
Conference *conf = iter->second;
_debug ("Manager: Remove participant %s", call_id.c_str());
conf->remove(call_id);
call->setConfId ("");
removeStream(call_id);
getMainBuffer()->stateInfo();
}
void ManagerImpl::processRemainingParticipant (const std::string ¤t_call_id, Conference *conf)
{
_debug ("Manager: Process remaining %d participant(s) from conference %s",
conf->getNbParticipants(), conf->getConfID().c_str());
if (conf->getNbParticipants() > 1) {
ParticipantSet participants(conf->getParticipantList());
// Reset ringbuffer's readpointers
for (ParticipantSet::const_iterator iter_p = participants.begin();
iter_p != participants.end();
++iter_p)
getMainBuffer()->flush (*iter_p);
getMainBuffer()->flush (Call::DEFAULT_ID);
} else if (conf->getNbParticipants() == 1) {
_debug ("Manager: Only one remaining participant");
ParticipantSet participants = conf->getParticipantList();
ParticipantSet::iterator iter_participant = participants.begin();
// bind main participant to remaining conference call
if (iter_participant != participants.end()) {
// this call is no more a conference participant
std::string currentAccountId(getAccountFromCall (*iter_participant));
Call *call = getAccountLink(currentAccountId)->getCall(*iter_participant);
call->setConfId ("");
// if we are not listening to this conference
if (current_call_id != conf->getConfID())
onHoldCall (call->getCallId());
else
switchCall (*iter_participant);
}
removeConference (conf->getConfID());
} else {
_debug ("Manager: No remaining participant, remove conference");
removeConference (conf->getConfID());
switchCall ("");
}
}
void ManagerImpl::joinConference (const std::string& conf_id1,
const std::string& conf_id2)
{
_debug ("Manager: Join conference %s, %s", conf_id1.c_str(), conf_id2.c_str());
ConferenceMap::iterator iter(_conferencemap.find(conf_id1));
Conference *conf1;
if (iter != _conferencemap.end())
conf1 = iter->second;
else {
_error ("Manager: Error: Not a valid conference ID");
return;
}
iter = _conferencemap.find (conf_id2);
Conference *conf2;
if (iter != _conferencemap.end())
conf2 = iter->second;
else {
_error ("Manager: Error: Not a valid conference ID");
return;
}
ParticipantSet participants(conf1->getParticipantList());
for (ParticipantSet::const_iterator iter_p = participants.begin();
iter_p != participants.end(); ++iter_p) {
detachParticipant (*iter_p, "");
addParticipant (*iter_p, conf_id2);
}
}
void ManagerImpl::addStream (const std::string& call_id)
{
_debug ("Manager: Add audio stream %s", call_id.c_str());
std::string currentAccountId(getAccountFromCall(call_id));
Call *call(getAccountLink(currentAccountId)->getCall(call_id));
if (participToConference(call_id)) {
_debug ("Manager: Add stream to conference");
// bind to conference participant
ConferenceMap::iterator iter = _conferencemap.find (call->getConfId());
if (iter != _conferencemap.end()) {
Conference* conf = iter->second;
conf->bindParticipant (call_id);
ParticipantSet participants(conf->getParticipantList());
// reset ring buffer for all conference participant
for (ParticipantSet::const_iterator iter_p = participants.begin();
iter_p != participants.end(); ++iter_p)
getMainBuffer()->flush (*iter_p);
getMainBuffer()->flush (Call::DEFAULT_ID);
}
} else {
_debug ("Manager: Add stream to call");
// bind to main
getMainBuffer()->bindCallID (call_id);
audioLayerMutexLock();
_audiodriver->flushUrgent();
_audiodriver->flushMain();
audioLayerMutexUnlock();
}
getMainBuffer()->stateInfo();
}
void ManagerImpl::removeStream (const std::string& call_id)
{
_debug ("Manager: Remove audio stream %s", call_id.c_str());
getMainBuffer()->unBindAll (call_id);
getMainBuffer()->stateInfo();
}
//THREAD=Main
void ManagerImpl::saveConfig (void)
{
_debug ("Manager: Saving Configuration to XDG directory %s", _path.c_str());
audioPreference.setVolumemic (getMicVolume());
audioPreference.setVolumespkr (getSpkrVolume());
try {
// emitter = new Conf::YamlEmitter("sequenceEmitter.yml");
emitter = new Conf::YamlEmitter (_path.c_str());
for (AccountMap::iterator iter = _accountMap.begin(); iter != _accountMap.end(); ++iter) {
// Skip the "" account ID (which refer to the IP2IP account)
if (iter->first.empty())
continue;
else
iter->second->serialize (emitter);
}
preferences.serialize (emitter);
voipPreferences.serialize (emitter);
addressbookPreference.serialize (emitter);
hookPreference.serialize (emitter);
audioPreference.serialize (emitter);
shortcutPreferences.serialize (emitter);
emitter->serializeData();
delete emitter;
} catch (Conf::YamlEmitterException &e) {
_error ("ConfigTree: %s", e.what());
}
}
//THREAD=Main
bool ManagerImpl::sendDtmf (const std::string& id, char code)
{
std::string accountid(getAccountFromCall(id));
playDtmf (code);
return getAccountLink (accountid)->carryingDTMFdigits (id, code);
}
//THREAD=Main | VoIPLink
bool ManagerImpl::playDtmf (char code)
{
stopTone();
if (not voipPreferences.getPlayDtmf()) {
_debug ("Manager: playDtmf: Do not have to play a tone...");
return false;
}
// length in milliseconds
int pulselen = voipPreferences.getPulseLength();
if (pulselen == 0) {
_debug ("Manager: playDtmf: Pulse length is not set...");
return false;
}
audioLayerMutexLock();
// numbers of int = length in milliseconds / 1000 (number of seconds)
// = number of seconds * SAMPLING_RATE by SECONDS
// fast return, no sound, so no dtmf
if (_audiodriver == NULL || _dtmfKey == NULL) {
_debug ("Manager: playDtmf: Error no audio layer...");
audioLayerMutexUnlock();
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) _audiodriver->getSampleRate()) / 1000);
// this buffer is for mono
// TODO <-- this should be global and hide if same size
SFLDataFormat *buf = new SFLDataFormat[size];
// 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->flushUrgent();
_audiodriver->startStream();
_audiodriver->putUrgent (buf, size * sizeof (SFLDataFormat));
}
audioLayerMutexUnlock();
// TODO Cache the DTMF
delete[] buf;
return true;
}
// Multi-thread
bool ManagerImpl::incomingCallWaiting ()
{
return (_nbIncomingWaitingCall > 0) ? true : false;
}
void ManagerImpl::addWaitingCall (const std::string& id)
{
_info ("Manager: Add waiting call %s (%d calls)", id.c_str(), _nbIncomingWaitingCall);
ost::MutexLock m(_waitingCallMutex);
_waitingCall.insert (id);
_nbIncomingWaitingCall++;
}
void ManagerImpl::removeWaitingCall (const std::string& id)
{
_info ("Manager: Remove waiting call %s (%d calls)", id.c_str(), _nbIncomingWaitingCall);
ost::MutexLock m(_waitingCallMutex);
// should return more than 1 if it erase a call
if (_waitingCall.erase (id))
_nbIncomingWaitingCall--;
}
bool ManagerImpl::isWaitingCall (const std::string& id)
{
return _waitingCall.find(id) != _waitingCall.end();
}
///////////////////////////////////////////////////////////////////////////////
// Management of event peer IP-phone
////////////////////////////////////////////////////////////////////////////////
// SipEvent Thread
bool ManagerImpl::incomingCall (Call* call, const std::string& accountId)
{
assert(call);
stopTone();
_debug ("Manager: Incoming call %s for account %s", call->getCallId().data(), accountId.c_str());
associateCallToAccount (call->getCallId(), accountId);
// If account is null it is an ip to ip call
if (accountId.empty())
associateConfigToCall (call->getCallId(), Call::IPtoIP);
else {
// strip sip: which is not required and bring confusion with ip to ip calls
// when placing new call from history (if call is IAX, do nothing)
std::string peerNumber(call->getPeerNumber());
size_t startIndex = peerNumber.find ("sip:");
if (startIndex != std::string::npos) {
std::string strippedPeerNumber = peerNumber.substr (startIndex + 4);
call->setPeerNumber (strippedPeerNumber);
}
}
if (!hasCurrentCall()) {
_debug ("Manager: Has no current call start ringing");
call->setConnectionState (Call::Ringing);
ringtone (accountId);
} else
_debug ("Manager: has current call, beep in current audio stream");
addWaitingCall (call->getCallId());
std::string from(call->getPeerName());
std::string number(call->getPeerNumber());
std::string display_name(call->getDisplayName());
if (not from.empty() and not number.empty()) {
from.append (" <");
from.append (number);
from.append (">");
} else if (from.empty()) {
from.append ("<");
from.append (number);
from.append (">");
}
/* Broadcast a signal over DBus */
_debug ("Manager: From: %s, Number: %s, Display Name: %s", from.c_str(), number.c_str(), display_name.c_str());
std::string display(display_name);
display.append(" ");
display.append(from);
_dbus.getCallManager()->incomingCall(accountId, call->getCallId(), display);
return true;
}
//THREAD=VoIP
void ManagerImpl::incomingMessage (const std::string& callID,
const std::string& from,
const std::string& message)
{
if (participToConference (callID)) {
_debug ("Manager: Particip to a conference, send message to everyone");
Conference *conf = getConferenceFromCallID (callID);
ParticipantSet participants = conf->getParticipantList();
for (ParticipantSet::const_iterator iter_p = participants.begin();
iter_p != participants.end(); ++iter_p) {
if (*iter_p == callID)
continue;
std::string accountId(getAccountFromCall(*iter_p));
_debug ("Manager: Send message to %s, (%s)", (*iter_p).c_str(), accountId.c_str());
Account *account = getAccount (accountId);
if (!account) {
_debug ("Manager: Failed to get account while sending instant message");
return;
}
account->getVoIPLink()->sendTextMessage (_imModule, callID, message, from);
}
// in case of a conference we must notify client using conference id
_dbus.getCallManager()->incomingMessage (conf->getConfID(), from, message);
} else
_dbus.getCallManager()->incomingMessage (callID, from, message);
}
//THREAD=VoIP
bool ManagerImpl::sendTextMessage (const std::string& callID, const std::string& message, const std::string& from)
{
if (isConference (callID)) {
_debug ("Manager: Is a conference, send instant message to everyone");
ConferenceMap::iterator it = _conferencemap.find (callID);
if (it == _conferencemap.end())
return false;
Conference *conf = it->second;
if (!conf)
return false;
const ParticipantSet participants = conf->getParticipantList();
for (ParticipantSet::const_iterator iter_p = participants.begin();
iter_p != participants.end(); ++iter_p) {
std::string accountId = getAccountFromCall (*iter_p);
Account *account = getAccount (accountId);
if (!account) {
_debug ("Manager: Failed to get account while sending instant message");
return false;
}
account->getVoIPLink()->sendTextMessage (_imModule, *iter_p, message, from);
}
return true;
}
if (participToConference (callID)) {
_debug ("Manager: Particip to a conference, send instant message to everyone");
Conference *conf = getConferenceFromCallID (callID);
if (!conf)
return false;
const ParticipantSet participants(conf->getParticipantList());
for (ParticipantSet::const_iterator iter_p = participants.begin();
iter_p != participants.end(); ++iter_p) {
const std::string accountId(getAccountFromCall(*iter_p));
const Account *account = getAccount (accountId);
if (!account) {
_debug ("Manager: Failed to get account while sending instant message");
return false;
}
account->getVoIPLink()->sendTextMessage (_imModule, *iter_p, message, from);
}
} else {
const Account *account = getAccount (getAccountFromCall(callID));
if (!account) {
_debug ("Manager: Failed to get account while sending instant message");
return false;
}
account->getVoIPLink()->sendTextMessage (_imModule, callID, message, from);
}
return true;
}
//THREAD=VoIP CALL=Outgoing
void ManagerImpl::peerAnsweredCall (const std::string& id)
{
_debug ("Manager: Peer answered call %s", id.c_str());
// The if statement is usefull only if we sent two calls at the same time.
if (isCurrentCall(id))
stopTone();
// Connect audio streams
addStream(id);
audioLayerMutexLock();
_audiodriver->flushMain();
_audiodriver->flushUrgent();
audioLayerMutexUnlock();
if (audioPreference.getIsAlwaysRecording()) {
setRecordingCall(id);
_dbus.getCallManager()->callStateChanged (id, "RECORD");
}
else
_dbus.getCallManager()->callStateChanged(id, "CURRENT");
}
//THREAD=VoIP Call=Outgoing
void ManagerImpl::peerRingingCall (const std::string& id)
{
_debug ("Manager: Peer call %s ringing", id.c_str());
if (isCurrentCall (id))
ringback();
_dbus.getCallManager()->callStateChanged (id, "RINGING");
}
//THREAD=VoIP Call=Outgoing/Ingoing
void ManagerImpl::peerHungupCall (const std::string& call_id)
{
_debug ("Manager: Peer hungup call %s", call_id.c_str());
if (participToConference (call_id)) {
Conference *conf = getConferenceFromCallID (call_id);
if (conf != NULL) {
removeParticipant (call_id);
processRemainingParticipant (getCurrentCallId(), conf);
}
} else {
if (isCurrentCall (call_id)) {
stopTone();
switchCall("");
}
}
/* Direct IP to IP call */
if (getConfigFromCall (call_id) == Call::IPtoIP)
SIPVoIPLink::instance ()->hangup (call_id);
else {
const std::string account_id(getAccountFromCall (call_id));
getAccountLink(account_id)->peerHungup(call_id);
}
/* Broadcast a signal over DBus */
_dbus.getCallManager()->callStateChanged (call_id, "HUNGUP");
removeWaitingCall (call_id);
removeCallAccount (call_id);
removeStream (call_id);
if (getCallList().empty()) {
_debug ("Manager: Stop audio stream, ther is only %d call(s) remaining", getCallList().size());
audioLayerMutexLock();
_audiodriver->stopStream();
audioLayerMutexUnlock();
}
}
//THREAD=VoIP
void ManagerImpl::callBusy (const std::string& id)
{
_debug ("Manager: Call %s busy", id.c_str());
_dbus.getCallManager()->callStateChanged (id, "BUSY");
if (isCurrentCall (id)) {
playATone (Tone::TONE_BUSY);
switchCall ("");
}
removeCallAccount (id);
removeWaitingCall (id);
}
//THREAD=VoIP
void ManagerImpl::callFailure (const std::string& call_id)
{
_dbus.getCallManager()->callStateChanged (call_id, "FAILURE");
if (isCurrentCall (call_id)) {
playATone (Tone::TONE_BUSY);
switchCall ("");
}
if (participToConference (call_id)) {
_debug ("Manager: Call %s participating to a conference failed", call_id.c_str());
Conference *conf = getConferenceFromCallID (call_id);
if (conf == NULL) {
_error("Manager: Could not retreive conference from call id %s", call_id.c_str());
return;
}
// remove this participant
removeParticipant (call_id);
processRemainingParticipant (getCurrentCallId(), conf);
}
removeCallAccount (call_id);
removeWaitingCall (call_id);
}
//THREAD=VoIP
void ManagerImpl::startVoiceMessageNotification (const std::string& accountId,
int nb_msg)
{
_dbus.getCallManager()->voiceMailNotify (accountId, nb_msg);
}
void ManagerImpl::connectionStatusNotification ()
{
if (_dbus.isConnected())
_dbus.getConfigurationManager()->accountsChanged();
}
/**
* Multi Thread
*/
bool ManagerImpl::playATone (Tone::TONEID toneId)
{
if (not voipPreferences.getPlayTones())
return false;
audioLayerMutexLock();
if (_audiodriver == NULL) {
_error("Manager: Error: Audio layer not initialized");
audioLayerMutexUnlock();
return false;
}
_audiodriver->flushUrgent();
_audiodriver->startStream();
audioLayerMutexUnlock();
if (_telephoneTone != 0) {
_toneMutex.enterMutex();
_telephoneTone->setCurrentTone (toneId);
_toneMutex.leaveMutex();
}
return true;
}
/**
* Multi Thread
*/
void ManagerImpl::stopTone ()
{
if (not voipPreferences.getPlayTones())
return;
_toneMutex.enterMutex();
if (_telephoneTone != NULL)
_telephoneTone->setCurrentTone (Tone::TONE_NULL);
if (_audiofile) {
std::string filepath(_audiofile->getFilePath());
_dbus.getCallManager()->recordPlaybackStopped(filepath);
delete _audiofile;
_audiofile = NULL;
}
_toneMutex.leaveMutex();
}
/**
* Multi Thread
*/
bool ManagerImpl::playTone ()
{
playATone(Tone::TONE_DIALTONE);
return true;
}
/**
* Multi Thread
*/
bool ManagerImpl::playToneWithMessage ()
{
playATone (Tone::TONE_CONGESTION);
return true;
}
/**
* Multi Thread
*/
void ManagerImpl::congestion ()
{
playATone (Tone::TONE_CONGESTION);
}
/**
* Multi Thread
*/
void ManagerImpl::ringback ()
{
playATone (Tone::TONE_RINGTONE);
}
/**
* Multi Thread
*/
void ManagerImpl::ringtone (const std::string& accountID)
{
Account *account = getAccount (accountID);
if (!account) {
_warn ("Manager: Warning: invalid account in ringtone");
return;
}
if (!account->getRingtoneEnabled()) {
ringback();
return;
}
std::string ringchoice = account->getRingtonePath();
if (ringchoice.find (DIR_SEPARATOR_STR) == std::string::npos) {
// check inside global share directory
ringchoice = std::string (PROGSHAREDIR) + DIR_SEPARATOR_STR
+ RINGDIR + DIR_SEPARATOR_STR + ringchoice;
}
audioLayerMutexLock();
if (!_audiodriver) {
_error ("Manager: Error: no audio layer in ringtone");
audioLayerMutexUnlock();
return;
}
int samplerate = _audiodriver->getSampleRate();
audioLayerMutexUnlock();
_toneMutex.enterMutex();
if (_audiofile) {
_dbus.getCallManager()->recordPlaybackStopped(_audiofile->getFilePath());
delete _audiofile;
_audiofile = NULL;
}
try {
if (ringchoice.find (".wav") != std::string::npos)
_audiofile = new WaveFile(ringchoice, samplerate);
else {
sfl::Codec *codec;
if (ringchoice.find (".ul") != std::string::npos || ringchoice.find (".au") != std::string::npos)
codec = _audioCodecFactory.getCodec(PAYLOAD_CODEC_ULAW);
else
throw AudioFileException("Couldn't guess an appropriate decoder");
_audiofile = new RawFile(ringchoice, static_cast<sfl::AudioCodec *>(codec), samplerate);
}
}
catch (AudioFileException &e) {
_error("Manager: Exception: %s", e.what());
}
_toneMutex.leaveMutex();
audioLayerMutexLock();
// start audio if not started AND flush all buffers (main and urgent)
_audiodriver->startStream();
audioLayerMutexUnlock();
}
AudioLoop*
ManagerImpl::getTelephoneTone ()
{
if (_telephoneTone) {
ost::MutexLock m (_toneMutex);
return _telephoneTone->getCurrentTone();
}
else
return NULL;
}
AudioLoop*
ManagerImpl::getTelephoneFile ()
{
ost::MutexLock m (_toneMutex);
return _audiofile;
}
void ManagerImpl::notificationIncomingCall (void)
{
audioLayerMutexLock();
if(_audiodriver == NULL) {
_error("Manager: Error: Audio layer not initialized");
audioLayerMutexUnlock();
return;
}
_debug ("ManagerImpl: Notification incoming call");
// Enable notification only if more than one call
if (hasCurrentCall()) {
std::ostringstream frequency;
frequency << "440/" << 160;
Tone tone (frequency.str(), _audiodriver->getSampleRate());
unsigned int nbSample = tone.getSize();
SFLDataFormat buf[nbSample];
tone.getNext (buf, nbSample);
/* Put the data in the urgent ring buffer */
_audiodriver->flushUrgent();
_audiodriver->putUrgent (buf, sizeof (SFLDataFormat) * nbSample);
}
audioLayerMutexUnlock();
}
///////////////////////////////////////////////////////////////////////////////
// Private functions
///////////////////////////////////////////////////////////////////////////////
/**
* Initialization: Main Thread
*/
std::string ManagerImpl::getConfigFile (void) const
{
std::string configdir = std::string (HOMEDIR) + DIR_SEPARATOR_STR + ".config"
+ DIR_SEPARATOR_STR + PROGDIR;
if (XDG_CONFIG_HOME != NULL) {
std::string xdg_env = std::string (XDG_CONFIG_HOME);
if (not xdg_env.empty())
configdir = xdg_env;
}
if (mkdir (configdir.data(), 0700) != 0) {
// If directory creation failed
if (errno != EEXIST)
_debug ("Cannot create directory: %m");
}
return configdir + DIR_SEPARATOR_STR + PROGNAME + ".yml";
}
std::vector<std::string> ManagerImpl::unserialize (std::string s)
{
std::vector<std::string> list;
std::string temp;
while (s.find ("/", 0) != std::string::npos) {
size_t pos = s.find ("/", 0);
temp = s.substr (0, pos);
s.erase (0, pos + 1);
list.push_back (temp);
}
return list;
}
std::string ManagerImpl::serialize (const std::vector<std::string> &v)
{
std::string res;
for (std::vector<std::string>::const_iterator iter = v.begin(); iter != v.end(); ++iter)
res += *iter + "/";
return res;
}
std::string ManagerImpl::getCurrentCodecName (const std::string& id)
{
std::string accountid = getAccountFromCall (id);
VoIPLink* link = getAccountLink (accountid);
Call* call = link->getCall (id);
std::string codecName;
if (call) {
Call::CallState state = call->getState();
if (state == Call::Active or state == Call::Conferencing)
codecName = link->getCurrentCodecName(call);
}
return codecName;
}
/**
* Set input audio plugin
*/
void ManagerImpl::setAudioPlugin (const std::string& audioPlugin)
{
audioLayerMutexLock();
int layerType = _audiodriver -> getLayerType();
audioPreference.setPlugin (audioPlugin);
if (CHECK_INTERFACE (layerType , ALSA)) {
_debug ("Set input audio plugin");
_audiodriver -> setErrorMessage (-1);
_audiodriver -> openDevice (_audiodriver->getIndexIn(), _audiodriver->getIndexOut(),
_audiodriver->getIndexRing(), _audiodriver -> getSampleRate(),
_audiodriver -> getFrameSize(), SFL_PCM_BOTH, audioPlugin);
if (_audiodriver -> getErrorMessage() != -1)
notifyErrClient (_audiodriver -> getErrorMessage());
}
audioLayerMutexUnlock();
}
/**
* Set audio output device
*/
void ManagerImpl::setAudioDevice (const int index, int streamType)
{
_debug ("Manager: Set audio device: %d", index);
audioLayerMutexLock();
if(_audiodriver == NULL) {
_warn ("Manager: Error: No audio driver");
audioLayerMutexUnlock();
return;
}
_audiodriver -> setErrorMessage (-1);
AlsaLayer *alsaLayer = dynamic_cast<AlsaLayer*>(_audiodriver);
if (!alsaLayer) {
_error("Cannot set audio output for non-alsa device");
audioLayerMutexUnlock();
return ;
}
const std::string alsaplugin(alsaLayer->getAudioPlugin());
_debug ("Manager: Set ALSA plugin: %s", alsaplugin.c_str());
switch (streamType) {
case SFL_PCM_PLAYBACK:
_debug ("Manager: Set output device");
_audiodriver->openDevice (_audiodriver->getIndexIn(), index, _audiodriver->getIndexRing(),
_audiodriver->getSampleRate(), _audiodriver->getFrameSize(),
SFL_PCM_PLAYBACK, alsaplugin);
audioPreference.setCardout (index);
break;
case SFL_PCM_CAPTURE:
_debug ("Manager: Set input device");
_audiodriver->openDevice (index, _audiodriver->getIndexOut(), _audiodriver->getIndexRing(),
_audiodriver->getSampleRate(), _audiodriver->getFrameSize(),
SFL_PCM_CAPTURE, alsaplugin);
audioPreference.setCardin (index);
break;
case SFL_PCM_RINGTONE:
_debug ("Manager: Set ringtone device");
_audiodriver->openDevice (_audiodriver->getIndexOut(), _audiodriver->getIndexOut(), index,
_audiodriver->getSampleRate(), _audiodriver->getFrameSize(),
SFL_PCM_RINGTONE, alsaplugin);
audioPreference.setCardring (index);
break;
default:
_warn ("Unknown stream type");
}
if (_audiodriver -> getErrorMessage() != -1)
notifyErrClient (_audiodriver -> getErrorMessage());
audioLayerMutexUnlock();
}
/**
* Get list of supported audio output device
*/
std::vector<std::string> ManagerImpl::getAudioOutputDeviceList (void)
{
_debug ("Manager: Get audio output device list");
std::vector<std::string> devices;
audioLayerMutexLock();
AlsaLayer *alsalayer = dynamic_cast<AlsaLayer*>(_audiodriver);
if (alsalayer)
devices = alsalayer->getSoundCardsInfo(SFL_PCM_PLAYBACK);
audioLayerMutexUnlock();
return devices;
}
/**
* Get list of supported audio input device
*/
std::vector<std::string> ManagerImpl::getAudioInputDeviceList (void)
{
std::vector<std::string> devices;
audioLayerMutexLock();
AlsaLayer *alsalayer = dynamic_cast<AlsaLayer *>(_audiodriver);
if (alsalayer == NULL) {
_error("Manager: Error: Audio layer not initialized");
audioLayerMutexUnlock();
return devices;
}
devices = alsalayer->getSoundCardsInfo (SFL_PCM_CAPTURE);
audioLayerMutexUnlock();
return devices;
}
/**
* Get string array representing integer indexes of output and input device
*/
std::vector<std::string> ManagerImpl::getCurrentAudioDevicesIndex ()
{
audioLayerMutexLock();
std::vector<std::string> v;
if (_audiodriver == NULL) {
_error("Manager: Error: Audio layer not initialized");
audioLayerMutexUnlock();
return v;
}
std::stringstream ssi, sso, ssr;
sso << _audiodriver->getIndexOut();
v.push_back (sso.str());
ssi << _audiodriver->getIndexIn();
v.push_back (ssi.str());
ssr << _audiodriver->getIndexRing();
v.push_back (ssr.str());
audioLayerMutexUnlock();
return v;
}
int ManagerImpl::isRingtoneEnabled (const std::string& id)
{
Account *account = getAccount (id);
if (!account) {
_warn ("Manager: Warning: invalid account in ringtone enabled");
return 0;
}
return account->getRingtoneEnabled();
}
void ManagerImpl::ringtoneEnabled (const std::string& id)
{
Account *account = getAccount (id);
if (!account) {
_warn ("Manager: Warning: invalid account in ringtone enabled");
return;
}
account->getRingtoneEnabled() ? account->setRingtoneEnabled (false) : account->setRingtoneEnabled (true);
}
std::string ManagerImpl::getRecordPath (void) const
{
return audioPreference.getRecordpath();
}
void ManagerImpl::setRecordPath (const std::string& recPath)
{
_debug ("Manager: Set record path %s", recPath.c_str());
audioPreference.setRecordpath (recPath);
}
bool ManagerImpl::getIsAlwaysRecording(void) const
{
return audioPreference.getIsAlwaysRecording();
}
void ManagerImpl::setIsAlwaysRecording(bool isAlwaysRec)
{
return audioPreference.setIsAlwaysRecording(isAlwaysRec);
}
void ManagerImpl::setRecordingCall (const std::string& id)
{
Recordable* rec = NULL;
if (not isConference (id)) {
_debug ("Manager: Set recording for call %s", id.c_str());
std::string accountid(getAccountFromCall(id));
rec = getAccountLink(accountid)->getCall(id);
} else {
_debug ("Manager: Set recording for conference %s", id.c_str());
ConferenceMap::const_iterator it(_conferencemap.find(id));
Conference *conf = it->second;
if (rec->isRecording())
conf->setState(Conference::ACTIVE_ATTACHED);
else
conf->setState(Conference::ACTIVE_ATTACHED_REC);
rec = conf;
}
if (rec == NULL) {
_error("Manager: Error: Could not find recordable instance %s", id.c_str());
return;
}
rec->setRecording();
_dbus.getCallManager()->recordPlaybackFilepath(id, rec->getFileName());
}
bool ManagerImpl::isRecording (const std::string& id)
{
const std::string accountid(getAccountFromCall (id));
Recordable* rec = getAccountLink (accountid)->getCall (id);
return rec and rec->isRecording();
}
bool ManagerImpl::startRecordedFilePlayback(const std::string& filepath)
{
_debug("Manager: Start recorded file playback %s", filepath.c_str());
audioLayerMutexLock();
if (!_audiodriver) {
_error("Manager: Error: No audio layer in start recorded file playback");
audioLayerMutexUnlock();
return false;
}
int sampleRate = _audiodriver->getSampleRate();
audioLayerMutexUnlock();
_toneMutex.enterMutex();
if (_audiofile) {
_dbus.getCallManager()->recordPlaybackStopped(_audiofile->getFilePath());
delete _audiofile;
_audiofile = NULL;
}
try {
_audiofile = new WaveFile(filepath, sampleRate);
}
catch (const AudioFileException &e) {
_error("Manager: Exception: %s", e.what());
}
_toneMutex.leaveMutex();
audioLayerMutexLock();
_audiodriver->startStream();
audioLayerMutexUnlock();
return true;
}
void ManagerImpl::stopRecordedFilePlayback(const std::string& filepath)
{
_debug("Manager: Stop recorded file playback %s", filepath.c_str());
audioLayerMutexLock();
_audiodriver->stopStream();
audioLayerMutexUnlock();
_toneMutex.enterMutex();
delete _audiofile;
_audiofile = NULL;
_toneMutex.leaveMutex();
}
void ManagerImpl::setHistoryLimit (int days)
{
_debug ("Manager: Set history limit");
preferences.setHistoryLimit (days);
saveConfig();
}
int ManagerImpl::getHistoryLimit (void) const
{
return preferences.getHistoryLimit();
}
int32_t ManagerImpl::getMailNotify (void) const
{
return preferences.getNotifyMails();
}
void ManagerImpl::setMailNotify (void)
{
_debug ("Manager: Set mail notify");
preferences.getNotifyMails() ? preferences.setNotifyMails (true) : preferences.setNotifyMails (false);
saveConfig();
}
void ManagerImpl::setAudioManager (int32_t api)
{
_debug ("Manager: Setting audio manager ");
audioLayerMutexLock();
if (!_audiodriver) {
audioLayerMutexUnlock();
return;
}
int layerType = _audiodriver->getLayerType();
if (layerType == api) {
_debug ("Manager: Audio manager chosen already in use. No changes made. ");
audioLayerMutexUnlock();
return;
}
audioLayerMutexUnlock();
preferences.setAudioApi(api);
switchAudioManager();
saveConfig();
}
int32_t ManagerImpl::getAudioManager (void) const
{
return preferences.getAudioApi();
}
void ManagerImpl::notifyErrClient (int32_t errCode)
{
_debug ("Manager: NOTIFY ERR NUMBER %d" , errCode);
_dbus.getConfigurationManager()->errorAlert(errCode);
}
int ManagerImpl::getAudioDeviceIndex (const std::string &name)
{
int soundCardIndex = 0;
audioLayerMutexLock();
if (_audiodriver == NULL) {
_error("Manager: Error: Audio layer not initialized");
audioLayerMutexUnlock();
return soundCardIndex;
}
AlsaLayer *alsalayer = dynamic_cast<AlsaLayer *> (_audiodriver);
if (alsalayer)
soundCardIndex = alsalayer -> soundCardGetIndex (name);
audioLayerMutexUnlock();
return soundCardIndex;
}
std::string ManagerImpl::getCurrentAudioOutputPlugin (void) const
{
return audioPreference.getPlugin();
}
std::string ManagerImpl::getNoiseSuppressState (void) const
{
// noise suppress disabled by default
return audioPreference.getNoiseReduce() ? "enabled" : "disabled";
}
void ManagerImpl::setNoiseSuppressState (const std::string &state)
{
_debug ("Manager: Set noise suppress state: %s", state.c_str());
bool isEnabled = (state == "enabled");
audioPreference.setNoiseReduce (isEnabled);
audioLayerMutexLock();
if (_audiodriver)
_audiodriver->setNoiseSuppressState (isEnabled);
audioLayerMutexUnlock();
}
std::string ManagerImpl::getEchoCancelState() const
{
// echo canceller disabled by default
return audioPreference.getEchoCancel() ? "enabled" : "disabled";
}
void ManagerImpl::setEchoCancelState(const std::string &state)
{
audioPreference.setEchoCancel(state == "enabled");
}
int ManagerImpl::getEchoCancelTailLength(void) const
{
return audioPreference.getEchoCancelTailLength();
}
void ManagerImpl::setEchoCancelTailLength(int length)
{
audioPreference.setEchoCancelTailLength(length);
}
int ManagerImpl::getEchoCancelDelay(void) const
{
return audioPreference.getEchoCancelDelay();
}
void ManagerImpl::setEchoCancelDelay(int delay)
{
audioPreference.setEchoCancelDelay(delay);
}
/**
* Initialization: Main Thread
*/
bool ManagerImpl::initAudioDriver (void)
{
_debug ("Manager: AudioLayer Creation");
audioLayerMutexLock();
if (preferences.getAudioApi() == ALSA) {
_audiodriver = new AlsaLayer (this);
_audiodriver->setMainBuffer (&_mainBuffer);
} else if (preferences.getAudioApi() == PULSEAUDIO) {
if (system("ps -C pulseaudio") == 0) {
_audiodriver = new PulseLayer (this);
_audiodriver->setMainBuffer (&_mainBuffer);
} else {
_audiodriver = new AlsaLayer (this);
preferences.setAudioApi (ALSA);
_audiodriver->setMainBuffer (&_mainBuffer);
}
} else
_debug ("Error - Audio API unknown");
if (_audiodriver == NULL) {
_debug ("Manager: Init audio driver error");
audioLayerMutexUnlock();
return false;
} else {
int error = _audiodriver->getErrorMessage();
if (error == -1) {
_debug ("Manager: Init audio driver: %d", error);
audioLayerMutexUnlock();
return false;
}
}
audioLayerMutexUnlock();
return true;
}
/**
* Initialization: Main Thread and gui
*/
void ManagerImpl::selectAudioDriver (void)
{
audioLayerMutexLock();
if (_audiodriver == NULL) {
_debug("Manager: Audio layer not initialized");
audioLayerMutexUnlock();
return;
}
/* Retrieve the global devices info from the user config */
std::string alsaPlugin(audioPreference.getPlugin());
int numCardIn = audioPreference.getCardin();
int numCardOut = audioPreference.getCardout();
int numCardRing = audioPreference.getCardring();
int sampleRate = getMainBuffer()->getInternalSamplingRate();
int frameSize = audioPreference.getFramesize();
/* Only for the ALSA layer, we check the sound card information */
AlsaLayer *alsalayer = dynamic_cast<AlsaLayer*>(_audiodriver);
if (alsalayer) {
if (!alsalayer->soundCardIndexExist (numCardIn, SFL_PCM_CAPTURE)) {
_debug (" Card with index %d doesn't exist or cannot capture. Switch to 0.", numCardIn);
numCardIn = ALSA_DFT_CARD_ID;
audioPreference.setCardin (ALSA_DFT_CARD_ID);
}
if (!alsalayer->soundCardIndexExist (numCardOut, SFL_PCM_PLAYBACK)) {
_debug (" Card with index %d doesn't exist or cannot playback. Switch to 0.", numCardOut);
numCardOut = ALSA_DFT_CARD_ID;
audioPreference.setCardout (ALSA_DFT_CARD_ID);
}
if (!alsalayer->soundCardIndexExist (numCardRing, SFL_PCM_RINGTONE)) {
_debug (" Card with index %d doesn't exist or cannot ringtone. Switch to 0.", numCardRing);
numCardRing = ALSA_DFT_CARD_ID;
audioPreference.setCardring (ALSA_DFT_CARD_ID);
}
}
_audiodriver->setErrorMessage (-1);
/* Open the audio devices */
_audiodriver->openDevice (numCardIn, numCardOut, numCardRing, sampleRate, frameSize,
SFL_PCM_BOTH, alsaPlugin);
/* Notify the error if there is one */
if (_audiodriver-> getErrorMessage() != -1)
notifyErrClient (_audiodriver -> getErrorMessage());
audioLayerMutexUnlock();
}
void ManagerImpl::switchAudioManager (void)
{
_debug ("Manager: Switching audio manager ");
audioLayerMutexLock();
if (_audiodriver == NULL) {
audioLayerMutexUnlock();
return;
}
bool wasStarted = _audiodriver->isStarted();
int type = _audiodriver->getLayerType();
int samplerate = _mainBuffer.getInternalSamplingRate();
int framesize = audioPreference.getFramesize();
_debug ("Manager: samplerate: %d, framesize %d", samplerate, framesize);
std::string alsaPlugin(audioPreference.getPlugin());
int numCardIn = audioPreference.getCardin();
int numCardOut = audioPreference.getCardout();
int numCardRing = audioPreference.getCardring();
_debug ("Manager: Deleting current layer... ");
delete _audiodriver;
_audiodriver = NULL;
switch (type) {
case ALSA:
_debug ("Manager: Creating Pulseaudio layer...");
_audiodriver = new PulseLayer (this);
_audiodriver->setMainBuffer (&_mainBuffer);
break;
case PULSEAUDIO:
_debug ("Manager: Creating ALSA layer...");
_audiodriver = new AlsaLayer (this);
_audiodriver->setMainBuffer (&_mainBuffer);
break;
default:
_warn ("Manager: Error: audio layer unknown");
break;
}
_audiodriver->setErrorMessage (-1);
_audiodriver->openDevice (numCardIn, numCardOut, numCardRing, samplerate, framesize,
SFL_PCM_BOTH, alsaPlugin);
if (_audiodriver->getErrorMessage() != -1)
notifyErrClient (_audiodriver -> getErrorMessage());
_debug ("Manager: Current device: %d ", type);
if (wasStarted)
_audiodriver->startStream();
audioLayerMutexUnlock();
}
void ManagerImpl::audioSamplingRateChanged (int samplerate)
{
audioLayerMutexLock();
if (!_audiodriver) {
_debug("Manager: No Audio driver initialized");
audioLayerMutexUnlock();
return;
}
// Only modify internal sampling rate if new sampling rate is higher
int currentSamplerate = _mainBuffer.getInternalSamplingRate();
if (currentSamplerate >= samplerate) {
_debug("Manager: No need to update audio layer sampling rate");
audioLayerMutexUnlock();
return;
}
else
_debug ("Manager: Audio sampling rate changed");
int type = _audiodriver->getLayerType();
int framesize = audioPreference.getFramesize();
_debug ("Manager: New samplerate: %d, New framesize %d", samplerate, framesize);
std::string alsaPlugin(audioPreference.getPlugin());
int numCardIn = audioPreference.getCardin();
int numCardOut = audioPreference.getCardout();
int numCardRing = audioPreference.getCardring();
_debug ("Manager: Deleting current layer...");
bool wasActive = _audiodriver->isStarted();
delete _audiodriver;
_audiodriver = 0;
switch (type) {
case PULSEAUDIO:
_debug ("Manager: Creating Pulseaudio layer...");
_audiodriver = new PulseLayer (this);
_audiodriver->setMainBuffer (&_mainBuffer);
break;
case ALSA:
_debug ("Manager: Creating ALSA layer...");
_audiodriver = new AlsaLayer (this);
_audiodriver->setMainBuffer (&_mainBuffer);
break;
default:
_error ("Manager: Error: audio layer unknown");
audioLayerMutexUnlock();
return;
}
_audiodriver->setErrorMessage (-1);
_audiodriver->openDevice (numCardIn, numCardOut, numCardRing, samplerate, framesize,
SFL_PCM_BOTH, alsaPlugin);
if (_audiodriver -> getErrorMessage() != -1)
notifyErrClient (_audiodriver -> getErrorMessage());
_debug ("Manager: Current device: %d ", type);
_mainBuffer.setInternalSamplingRate(samplerate);
unsigned int sampleRate = _audiodriver->getSampleRate();
delete _telephoneTone;
_debug ("Manager: Load telephone tone");
std::string country = preferences.getZoneToneChoice();
_telephoneTone = new TelephoneTone (country, sampleRate);
delete _dtmfKey;
_debug ("Manager: Loading DTMF key with sample rate %d", sampleRate);
_dtmfKey = new DTMF (sampleRate);
// Restart audio layer if it was active
if (wasActive)
_audiodriver->startStream();
audioLayerMutexUnlock();
}
/**
* Init the volume for speakers/micro from 0 to 100 value
* Initialization: Main Thread
*/
void ManagerImpl::initVolume ()
{
_debug ("Initiate Volume");
setSpkrVolume (audioPreference.getVolumespkr());
setMicVolume (audioPreference.getVolumemic());
}
void ManagerImpl::setSpkrVolume (unsigned short spkr_vol)
{
/* Set the manager sound volume */
_spkr_volume = spkr_vol;
audioLayerMutexLock();
/* Only for PulseAudio */
PulseLayer *pulselayer = dynamic_cast<PulseLayer*> (_audiodriver);
if (pulselayer and pulselayer->getLayerType() == PULSEAUDIO)
pulselayer->setPlaybackVolume (spkr_vol);
audioLayerMutexUnlock();
}
void ManagerImpl::setMicVolume (unsigned short mic_vol)
{
_mic_volume = mic_vol;
}
int ManagerImpl::getLocalIp2IpPort (void) const
{
// The SIP port used for default account (IP to IP) calls=
return preferences.getPortNum();
}
//THREAD=Main
bool ManagerImpl::getConfig (const std::string& section,
const std::string& name, TokenList& arg) const
{
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) const
{
try {
return _config.getConfigTreeItemIntValue (section, name);
} catch (const Conf::ConfigTreeItemException& e) {
throw;
}
return 0;
}
bool ManagerImpl::getConfigBool (const std::string& section,
const std::string& name) const
{
try {
return _config.getConfigTreeItemValue (section, name) == Conf::TRUE_STR;
} catch (const Conf::ConfigTreeItemException& e) {
throw;
}
return false;
}
//THREAD=Main
std::string ManagerImpl::getConfigString (const std::string& section,
const std::string& name) const
{
try {
return _config.getConfigTreeItemValue (section, name);
} catch (const Conf::ConfigTreeItemException& e) {
throw;
}
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());
}
void ManagerImpl::setAccountsOrder (const std::string& order)
{
_debug ("Manager: Set accounts order : %s", order.c_str());
// Set the new config
preferences.setAccountOrder (order);
saveConfig();
}
std::vector<std::string> ManagerImpl::getAccountList () const
{
using std::vector;
using std::string;
vector<string> account_order(loadAccountOrder());
// The IP2IP profile is always available, and first in the list
vector<string> v;
AccountMap::const_iterator ip2ip_iter = _accountMap.find (IP2IP_PROFILE);
if (ip2ip_iter->second)
v.push_back (ip2ip_iter->second->getAccountID());
else
_error ("Manager: could not find IP2IP profile in getAccount list");
// If no order has been set, load the default one
// ie according to the creation date.
if (account_order.empty()) {
_debug ("Manager: account order is empty");
for (AccountMap::const_iterator iter = _accountMap.begin(); iter != _accountMap.end(); ++iter) {
if (iter->second != NULL and iter->first != IP2IP_PROFILE and not iter->first.empty()) {
_debug ("PUSHING BACK %s", iter->first.c_str());
v.push_back (iter->second->getAccountID());
}
}
}
else {
// otherwise, load the custom one
// ie according to the saved order
_debug ("Manager: Load account list according to preferences");
for (vector<string>::const_iterator iter = account_order.begin(); iter != account_order.end(); ++iter) {
// This account has not been loaded, so we ignore it
AccountMap::const_iterator account_iter = _accountMap.find (*iter);
if (account_iter != _accountMap.end()) {
if (account_iter->second and (account_iter->first not_eq IP2IP_PROFILE) and not account_iter->first.empty()) {
// If the account is valid
v.push_back (account_iter->second->getAccountID());
}
}
}
}
return v;
}
std::map<std::string, std::string> ManagerImpl::getAccountDetails (
const std::string& accountID) const
{
// Default account used to get default parameters if requested by client (to build new account)
static const SIPAccount DEFAULT_ACCOUNT("default");
if (accountID.empty()) {
_debug ("Manager: Returning default account settings");
return DEFAULT_ACCOUNT.getAccountDetails();
}
AccountMap::const_iterator iter = _accountMap.find(accountID);
Account * account = 0;
if (iter != _accountMap.end())
account = iter->second;
if (account)
return account->getAccountDetails();
else {
_debug ("Manager: Get account details on a non-existing accountID %s. Returning default", accountID.c_str());
return DEFAULT_ACCOUNT.getAccountDetails();
}
}
// method to reduce the if/else mess.
// Even better, switch to XML !
void ManagerImpl::setAccountDetails (const std::string& accountID,
const std::map<std::string, std::string>& details)
{
_debug ("Manager: Set account details for %s", accountID.c_str());
Account* account = getAccount(accountID);
if (account == NULL) {
_error ("Manager: Error: Could not find account %s", accountID.c_str());
return;
}
account->setAccountDetails (details);
// Serialize configuration to disk once it is done
saveConfig();
if (account->isEnabled())
account->registerVoIPLink();
else
account->unregisterVoIPLink();
// Update account details to the client side
_dbus.getConfigurationManager()->accountsChanged();
}
std::string ManagerImpl::addAccount (
const std::map<std::string, std::string>& details)
{
/** @todo Deal with both the _accountMap and the Configuration */
std::string accountType, account_list;
std::stringstream accountID;
accountID << "Account:" << time (NULL);
std::string newAccountID(accountID.str());
// Get the type
accountType = (*details.find (CONFIG_ACCOUNT_TYPE)).second;
_debug ("Manager: Adding account %s", newAccountID.c_str());
/** @todo Verify the uniqueness, in case a program adds accounts, two in a row. */
Account* newAccount = NULL;
if (accountType == "SIP") {
newAccount = new SIPAccount(newAccountID);
} else if (accountType == "IAX") {
newAccount = new IAXAccount(newAccountID);
} else {
_error ("Unknown %s param when calling addAccount(): %s",
CONFIG_ACCOUNT_TYPE, accountType.c_str());
return "";
}
_accountMap[newAccountID] = newAccount;
newAccount->setAccountDetails (details);
// Add the newly created account in the account order list
account_list = preferences.getAccountOrder();
if (not account_list.empty()) {
newAccountID += "/";
// Prepend the new account
account_list.insert (0, newAccountID);
preferences.setAccountOrder (account_list);
} else {
newAccountID += "/";
account_list = newAccountID;
preferences.setAccountOrder (account_list);
}
_debug ("AccountMap: %s", account_list.c_str());
newAccount->registerVoIPLink();
saveConfig();
if (_dbus.isConnected())
_dbus.getConfigurationManager()->accountsChanged();
return accountID.str();
}
void ManagerImpl::removeAccount (const std::string& accountID)
{
// Get it down and dying
Account* remAccount = getAccount (accountID);
if (remAccount != NULL) {
remAccount->unregisterVoIPLink();
_accountMap.erase (accountID);
// http://projects.savoirfairelinux.net/issues/show/2355
// delete remAccount;
}
_config.removeSection (accountID);
saveConfig();
_debug ("REMOVE ACCOUNT");
if (_dbus.isConnected())
_dbus.getConfigurationManager()->accountsChanged();
}
// ACCOUNT handling
bool ManagerImpl::associateCallToAccount (const std::string& callID,
const std::string& accountID)
{
if (getAccountFromCall(callID).empty() and accountExists(accountID)) {
// account id exist in AccountMap
ost::MutexLock m (_callAccountMapMutex);
_callAccountMap[callID] = accountID;
_debug ("Manager: Associate Call %s with Account %s", callID.data(), accountID.data());
return true;
}
return false;
}
std::string ManagerImpl::getAccountFromCall (const std::string& callID)
{
ost::MutexLock m (_callAccountMapMutex);
CallAccountMap::iterator iter = _callAccountMap.find (callID);
return (iter == _callAccountMap.end()) ? "" : iter->second;
}
bool ManagerImpl::removeCallAccount (const std::string& callID)
{
ost::MutexLock m (_callAccountMapMutex);
return _callAccountMap.erase (callID);
}
bool ManagerImpl::isValidCall(const std::string& callID)
{
ost::MutexLock m(_callAccountMapMutex);
return _callAccountMap.find (callID) != _callAccountMap.end();
}
std::string ManagerImpl::getNewCallID ()
{
std::ostringstream random_id ("s");
random_id << (unsigned) rand();
// when it's not found, it return ""
// generate, something like s10000s20000s4394040
while (not getAccountFromCall (random_id.str()).empty()) {
random_id.clear();
random_id << "s";
random_id << (unsigned) rand();
}
return random_id.str();
}
std::vector<std::string> ManagerImpl::loadAccountOrder (void) const
{
const std::string account_list(preferences.getAccountOrder());
_debug ("Manager: Load account order %s", account_list.c_str());
return unserialize (account_list);
}
void ManagerImpl::loadAccountMap(Conf::YamlParser *parser)
{
SIPVoIPLink *link = SIPVoIPLink::instance();
// build a default IP2IP account with default parameters
Account *ip2ip = new SIPAccount(IP2IP_PROFILE);
_accountMap[IP2IP_PROFILE] = ip2ip;
// If configuration file parsed, load saved preferences
if (parser) {
Conf::Sequence *seq = parser->getAccountSequence()->getSequence();
for (Conf::Sequence::const_iterator iter = seq->begin(); iter != seq->end(); ++iter) {
Conf::MappingNode *map = (Conf::MappingNode *) (*iter);
std::string accountid;
map->getValue ("id", &accountid);
if (accountid == "IP2IP") {
ip2ip->unserialize (map);
break;
}
}
}
// Initialize default UDP transport according to
// IP to IP settings (most likely using port 5060)
link->createDefaultSipUdpTransport();
// Call this method to create TLS listener
link->createDefaultSipTlsListener();
// Force IP2IP settings to be loaded to be loaded
// No registration in the sense of the REGISTER method is performed.
ip2ip->registerVoIPLink();
if (!parser)
return;
// build preferences
preferences.unserialize (parser->getPreferenceNode());
voipPreferences.unserialize (parser->getVoipPreferenceNode());
addressbookPreference.unserialize (parser->getAddressbookNode());
hookPreference.unserialize (parser->getHookNode());
audioPreference.unserialize (parser->getAudioNode());
shortcutPreferences.unserialize (parser->getShortcutNode());
Conf::Sequence *seq = parser->getAccountSequence()->getSequence();
// Each element in sequence is a new account to create
for (Conf::Sequence::const_iterator iter = seq->begin(); iter != seq->end(); ++iter) {
Conf::MappingNode *map = (Conf::MappingNode *) (*iter);
std::string accountType;
map->getValue ("type", &accountType);
std::string accountid;
map->getValue ("id", &accountid);
std::string accountAlias;
map->getValue ("alias", &accountAlias);
if (accountid.empty() or accountAlias.empty() or accountid == IP2IP_PROFILE)
continue;
Account *a;
if (accountType == "IAX")
a = new IAXAccount(accountid);
else // assume SIP
a = new SIPAccount(accountid);
_accountMap[accountid] = a;
a->unserialize (map);
}
}
void ManagerImpl::unloadAccountMap ()
{
_debug ("Manager: Unload account map");
AccountMap::iterator iter;
for (iter = _accountMap.begin(); iter != _accountMap.end(); ++iter) {
// Avoid removing the IP2IP account twice
if (iter->first != "")
delete iter->second;
}
_accountMap.clear();
delete SIPVoIPLink::instance();
}
bool ManagerImpl::accountExists (const std::string& accountID)
{
return _accountMap.find (accountID) != _accountMap.end();
}
Account*
ManagerImpl::getAccount (const std::string& accountID) const
{
AccountMap::const_iterator iter = _accountMap.find(accountID);
if (iter != _accountMap.end())
return iter->second;
_debug ("Manager: Did not found account %s, returning IP2IP account", accountID.c_str());
return getAccount(IP2IP_PROFILE);
}
std::string ManagerImpl::getAccountIdFromNameAndServer (
const std::string& userName, const std::string& server) const
{
_info ("Manager : username = %s , server = %s", userName.c_str(), server.c_str());
// Try to find the account id from username and server name by full match
for (AccountMap::const_iterator iter = _accountMap.begin(); iter != _accountMap.end(); ++iter) {
SIPAccount *account = dynamic_cast<SIPAccount *> (iter->second);
if (account and account->isEnabled() and account->fullMatch (userName, server)) {
_debug ("Manager: Matching account id in request is a fullmatch %s@%s", userName.c_str(), server.c_str());
return iter->first;
}
}
// We failed! Then only match the hostname
for (AccountMap::const_iterator iter = _accountMap.begin(); iter != _accountMap.end(); ++iter) {
SIPAccount *account = dynamic_cast<SIPAccount *> (iter->second);
if (account and account->isEnabled() and account->hostnameMatch (server)) {
_debug ("Manager: Matching account id in request with hostname %s", server.c_str());
return iter->first;
}
}
// We failed! Then only match the username
for (AccountMap::const_iterator iter = _accountMap.begin(); iter != _accountMap.end(); ++iter) {
SIPAccount *account = dynamic_cast<SIPAccount *> (iter->second);
if (account and account->isEnabled() and account->userMatch (userName)) {
_debug ("Manager: Matching account id in request with username %s", userName.c_str());
return iter->first;
}
}
_debug ("Manager: Username %s or server %s doesn't match any account, using IP2IP", userName.c_str(), server.c_str());
return "";
}
std::map<std::string, int32_t> ManagerImpl::getAddressbookSettings () const
{
std::map<std::string, int32_t> settings;
settings["ADDRESSBOOK_ENABLE"] = addressbookPreference.getEnabled();
settings["ADDRESSBOOK_MAX_RESULTS"] = addressbookPreference.getMaxResults();
settings["ADDRESSBOOK_DISPLAY_CONTACT_PHOTO"] = addressbookPreference.getPhoto();
settings["ADDRESSBOOK_DISPLAY_PHONE_BUSINESS"] = addressbookPreference.getBusiness();
settings["ADDRESSBOOK_DISPLAY_PHONE_HOME"] = addressbookPreference.getHome();
settings["ADDRESSBOOK_DISPLAY_PHONE_MOBILE"] = addressbookPreference.getMobile();
return settings;
}
void ManagerImpl::setAddressbookSettings (
const std::map<std::string, int32_t>& settings)
{
_debug ("Manager: Update addressbook settings");
addressbookPreference.setEnabled (settings.find ("ADDRESSBOOK_ENABLE")->second == 1);
addressbookPreference.setMaxResults (settings.find ("ADDRESSBOOK_MAX_RESULTS")->second);
addressbookPreference.setPhoto (settings.find ("ADDRESSBOOK_DISPLAY_CONTACT_PHOTO")->second == 1);
addressbookPreference.setBusiness (settings.find ("ADDRESSBOOK_DISPLAY_PHONE_BUSINESS")->second == 1);
addressbookPreference.setHone (settings.find ("ADDRESSBOOK_DISPLAY_PHONE_HOME")->second == 1);
addressbookPreference.setMobile (settings.find ("ADDRESSBOOK_DISPLAY_PHONE_MOBILE")->second == 1);
// Write it to the configuration file
// TODO save config is called for updateAddressbookSettings, updateHookSettings, setHistoryLimit each called
// when closing preference window (in this order)
// saveConfig();
}
void ManagerImpl::setAddressbookList (const std::vector<std::string>& list)
{
_debug ("Manager: Set addressbook list");
std::string s = ManagerImpl::serialize (list);
_debug("Manager: New addressbook list: %s", s.c_str());
addressbookPreference.setList (s);
saveConfig();
}
std::vector<std::string> ManagerImpl::getAddressbookList (void) const
{
return unserialize (addressbookPreference.getList());
}
std::map<std::string, std::string> ManagerImpl::getHookSettings () const
{
std::map<std::string, std::string> settings;
settings["URLHOOK_IAX2_ENABLED"] = hookPreference.getIax2Enabled() ? "true" : "false";
settings["PHONE_NUMBER_HOOK_ADD_PREFIX"] = hookPreference.getNumberAddPrefix();
settings["PHONE_NUMBER_HOOK_ENABLED"] = hookPreference.getNumberEnabled() ? "true" : "false";
settings["URLHOOK_SIP_ENABLED"] = hookPreference.getSipEnabled() ? "true" : "false";
settings["URLHOOK_COMMAND"] = hookPreference.getUrlCommand();
settings["URLHOOK_SIP_FIELD"] = hookPreference.getUrlSipField();
return settings;
}
void ManagerImpl::setHookSettings (const std::map<std::string, std::string>& settings)
{
hookPreference.setIax2Enabled (settings.find ("URLHOOK_IAX2_ENABLED")->second == "true");
hookPreference.setNumberAddPrefix (settings.find ("PHONE_NUMBER_HOOK_ADD_PREFIX")->second);
hookPreference.setNumberEnabled (settings.find ("PHONE_NUMBER_HOOK_ENABLED")->second == "true");
hookPreference.setSipEnabled (settings.find ("URLHOOK_SIP_ENABLED")->second == "true");
hookPreference.setUrlCommand (settings.find ("URLHOOK_COMMAND")->second);
hookPreference.setUrlSipField (settings.find ("URLHOOK_SIP_FIELD")->second);
// Write it to the configuration file
// TODO save config is called for updateAddressbookSettings, updateHookSettings, setHistoryLimit each called
// when closing preference window (in this order)
// saveConfig();
}
void ManagerImpl::checkCallConfiguration (const std::string& id,
const std::string &to, Call::CallConfiguration *callConfig)
{
Call::CallConfiguration config;
if (to.find (SIP_SCHEME) == 0 or to.find (SIPS_SCHEME) == 0) {
_debug ("Manager: Sip scheme detected (sip: or sips:), sending IP2IP Call");
config = Call::IPtoIP;
} else
config = Call::Classic;
associateConfigToCall (id, config);
*callConfig = config;
}
bool ManagerImpl::associateConfigToCall (const std::string& callID,
Call::CallConfiguration config)
{
if (getConfigFromCall (callID) == CallConfigNULL) { // nothing with the same ID
_callConfigMap[callID] = config;
_debug ("Manager: Associate call %s with config %d", callID.c_str(), config);
return true;
} else
return false;
}
Call::CallConfiguration ManagerImpl::getConfigFromCall (const std::string& callID) const
{
CallConfigMap::const_iterator iter = _callConfigMap.find (callID);
if (iter == _callConfigMap.end())
return (Call::CallConfiguration) CallConfigNULL;
else
return iter->second;
}
bool ManagerImpl::removeCallConfig (const std::string& callID)
{
return _callConfigMap.erase (callID);
}
std::map<std::string, std::string> ManagerImpl::getCallDetails (const std::string& callID)
{
// We need here to retrieve the call information attached to the call ID
// To achieve that, we need to get the voip link attached to the call
// But to achieve that, we need to get the account the call was made with
// So first we fetch the account
const std::string accountid(getAccountFromCall (callID));
// Then the VoIP link this account is linked with (IAX2 or SIP)
Call *call = NULL;
if (Account *account = getAccount (accountid)) {
VoIPLink *link = account->getVoIPLink();
if (link)
call = link->getCall (callID);
}
std::map<std::string, std::string> call_details;
if (call) {
std::ostringstream type;
type << call->getCallType();
call_details["ACCOUNTID"] = accountid;
call_details["PEER_NUMBER"] = call->getPeerNumber();
call_details["PEER_NAME"] = call->getPeerName();
call_details["DISPLAY_NAME"] = call->getDisplayName();
call_details["CALL_STATE"] = call->getStateStr();
call_details["CALL_TYPE"] = type.str();
} else {
_error ("Manager: Error: getCallDetails()");
call_details["ACCOUNTID"] = "";
call_details["PEER_NUMBER"] = "Unknown";
call_details["PEER_NAME"] = "Unknown";
call_details["DISPLAY_NAME"] = "Unknown";
call_details["CALL_STATE"] = "UNKNOWN";
call_details["CALL_TYPE"] = "0";
}
return call_details;
}
std::vector<std::string> ManagerImpl::getHistorySerialized(void) const
{
_debug("Manager: Get history serialized");
return _history->get_history_serialized();
}
void ManagerImpl::setHistorySerialized(std::vector<std::string> history)
{
_debug("Manager: Set history serialized");
_history->set_serialized_history (history, preferences.getHistoryLimit());;
_history->save_history();
}
namespace {
template <typename M, typename V>
void vectorFromMapKeys(const M &m, V &v)
{
for (typename M::const_iterator it = m.begin(); it != m.end(); ++it )
v.push_back(it->first);
}
}
std::vector<std::string> ManagerImpl::getCallList (void) const
{
std::vector<std::string> v;
vectorFromMapKeys(_callAccountMap, v);
return v;
}
std::map<std::string, std::string> ManagerImpl::getConferenceDetails (
const std::string& confID) const
{
std::map<std::string, std::string> conf_details;
ConferenceMap::const_iterator iter_conf;
iter_conf = _conferencemap.find (confID);
if (iter_conf != _conferencemap.end()) {
conf_details["CONFID"] = confID;
conf_details["CONF_STATE"] = iter_conf->second->getStateStr();
}
return conf_details;
}
std::vector<std::string> ManagerImpl::getConferenceList (void) const
{
_debug ("ManagerImpl::getConferenceList");
std::vector<std::string> v;
vectorFromMapKeys(_conferencemap, v);
return v;
}
std::vector<std::string> ManagerImpl::getParticipantList (
const std::string& confID) const
{
_debug ("ManagerImpl: Get participant list %s", confID.c_str());
ConferenceMap::const_iterator iter_conf = _conferencemap.find (confID);
Conference *conf = NULL;
if (iter_conf != _conferencemap.end())
conf = iter_conf->second;
std::vector<std::string> v;
if (conf) {
ParticipantSet participants = conf->getParticipantList();
std::copy(participants.begin(), participants.end(), std::back_inserter(v));;
} else
_warn ("Manager: Warning: Did not found conference %s", confID.c_str());
return v;
}