Select Git revision
managerimpl.cpp
-
Tristan Matthews authored
Check return codes of calls to system
Tristan Matthews authoredCheck return codes of calls to system
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
managerimpl.cpp 87.60 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/instant_messaging.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() :
hasTriedToRegister_(false), config_(), currentCallId_(),
currentCallMutex_(), audiodriver_(0), dtmfKey_(0), toneMutex_(),
telephoneTone_(0), audiofile_(0), speakerVolume_(0), micVolume_(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()
{
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 = NULL;
try {
parser = new Conf::YamlParser(path_.c_str());
parser->serializeEvents();
parser->composeEvents();
parser->constructNativeData();
} catch (Conf::YamlParserException &e) {
ERROR("Manager: %s", e.what());
fflush(stderr);
delete parser;
parser = NULL;
}
loadAccountMap(parser);
delete parser;
initVolume();
initAudioDriver();
audioLayerMutexLock();
if (audiodriver_) {
telephoneTone_ = new TelephoneTone(preferences.getZoneToneChoice(), audiodriver_->getSampleRate());
dtmfKey_ = new DTMF(8000);
}
audioLayerMutexUnlock();
history_->load_history(preferences.getHistoryLimit());
registerAccounts();
}
void ManagerImpl::terminate()
{
std::vector<std::string> callList(getCallList());
DEBUG("Manager: Hangup %zu remaining call", callList.size());
for (std::vector<std::string>::iterator iter = callList.begin(); iter != callList.end(); ++iter)
hangupCall(*iter);
unloadAccountMap();
delete SIPVoIPLink::instance();
delete dtmfKey_;
delete telephoneTone_;
telephoneTone_ = NULL;
audioLayerMutexLock();
delete audiodriver_;
audiodriver_ = NULL;
audioLayerMutexUnlock();
}
bool ManagerImpl::isCurrentCall(const std::string& callId) const
{
return currentCallId_ == callId;
}
bool ManagerImpl::hasCurrentCall() const
{
return not currentCallId_.empty();
}
std::string
ManagerImpl::getCurrentCallId() const
{
return currentCallId_;
}
void ManagerImpl::switchCall(const std::string& id)
{
ost::MutexLock m(currentCallMutex_);
DEBUG("----- Switch current call id to %s -----", id.c_str());
currentCallId_ = 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 abort, 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));
static const char * const SIP_SCHEME = "sip:";
static const char * const SIPS_SCHEME = "sips:";
Call::CallConfiguration callConfig = (to_cleaned.find(SIP_SCHEME) == 0 or to_cleaned.find(SIPS_SCHEME) == 0) ? Call::IPtoIP : Call::Classic;
associateConfigToCall(call_id, 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());
// 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);
processRemainingParticipants(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);
if (iter_conf != conferenceMap_.end()) {
Conference *conf = iter_conf->second;
if (conf) {
ParticipantSet participants(conf->getParticipantList());
for (ParticipantSet::const_iterator iter = participants.begin();
iter != participants.end(); ++iter)
hangupCall(*iter);
} else {
ERROR("Manager: No such conference %s", id.c_str());
return false;
}
}
switchCall("");
getMainBuffer()->stateInfo();
return true;
}
//THREAD=Main
void ManagerImpl::onHoldCall(const std::string& callId)
{
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 */
SIPVoIPLink::instance()-> onhold(callId);
} else {
/* Classic call, attached to an account */
std::string account_id(getAccountFromCall(callId));
if (account_id.empty()) {
DEBUG("Manager: Account ID %s or callid %s doesn't exists in call onHold", account_id.c_str(), callId.c_str());
return;
}
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();
}
//THREAD=Main
void ManagerImpl::offHoldCall(const std::string& callId)
{
std::string accountId;
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)
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();
getAccountLink(accountId)->offhold(callId);
}
}
dbus_.getCallManager()->callStateChanged(callId, isRec ? "UNHOLD_RECORD" : "UNHOLD_CURRENT");
if (participToConference(callId)) {
std::string currentAccountId(getAccountFromCall(callId));
Call *call = getAccountLink(currentAccountId)->getCall(callId);
if (call)
switchCall(call->getConfId());
} else
switchCall(callId);
addStream(callId);
getMainBuffer()->stateInfo();
}
//THREAD=Main
bool ManagerImpl::transferCall(const std::string& callId, const std::string& to)
{
if (participToConference(callId)) {
removeParticipant(callId);
Conference *conf = getConferenceFromCallID(callId);
processRemainingParticipants(callId, conf);
} else if (not isConference(getCurrentCallId()))
switchCall("");
// Direct IP to IP call
if (getConfigFromCall(callId) == Call::IPtoIP)
SIPVoIPLink::instance()->transfer(callId, to);
else {
std::string accountid(getAccountFromCall(callId));
if (accountid.empty())
return false;
getAccountLink(accountid)->transfer(callId, to);
}
// remove waiting call in case we make transfer without even answer
removeWaitingCall(callId);
getMainBuffer()->stateInfo();
return true;
}
void ManagerImpl::transferFailed()
{
dbus_.getCallManager()->transferFailed();
}
void ManagerImpl::transferSucceded()
{
dbus_.getCallManager()->transferSucceded();
}
bool ManagerImpl::attendedTransfer(const std::string& transferID, const std::string& targetID)
{
if (getConfigFromCall(transferID) == Call::IPtoIP)
return SIPVoIPLink::instance()->attendedTransfer(transferID, targetID);
// Classic call, attached to an account
std::string accountid(getAccountFromCall(transferID));
if (accountid.empty())
return false;
return getAccountLink(accountid)->attendedTransfer(transferID, targetID);
}
//THREAD=Main : Call:Incoming
void ManagerImpl::refuseCall(const std::string& id)
{
stopTone();
if (getCallList().size() <= 1) {
audioLayerMutexLock();
audiodriver_->stopStream();
audioLayerMutexUnlock();
}
/* Direct IP to IP call */
if (getConfigFromCall(id) == Call::IPtoIP)
SIPVoIPLink::instance()->refuse(id);
else {
/* Classic call, attached to an account */
std::string accountid = getAccountFromCall(id);
if (accountid.empty())
return;
getAccountLink(accountid)->refuse(id);
removeCallAccount(id);
}
removeWaitingCall(id);
dbus_.getCallManager()->callStateChanged(id, "HUNGUP");
// Disconnect streams
removeStream(id);
getMainBuffer()->stateInfo();
}
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 participants: %u", conferenceMap_.size());
ConferenceMap::iterator iter = conferenceMap_.find(conference_id);
Conference* conf = 0;
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(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)
{
ConferenceMap::iterator iter_conf = conferenceMap_.find(id);
if (iter_conf == conferenceMap_.end())
return;
Conference *conf = iter_conf->second;
bool isRec = conf->getState() == Conference::ACTIVE_ATTACHED_REC or
conf->getState() == Conference::ACTIVE_DETACHED_REC or
conf->getState() == Conference::HOLD_REC;
ParticipantSet participants(conf->getParticipantList());
for (ParticipantSet::const_iterator iter = participants.begin();
iter != participants.end(); ++iter) {
switchCall(*iter);
onHoldCall(*iter);
}
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() and iter_conf->second) {
Conference *conf = iter_conf->second;
bool isRec = conf->getState() == Conference::ACTIVE_ATTACHED_REC or
conf->getState() == Conference::ACTIVE_DETACHED_REC or
conf->getState() == Conference::HOLD_REC;
ParticipantSet participants(conf->getParticipantList());
for (ParticipantSet::const_iterator iter = participants.begin(); iter!= participants.end(); ++iter) {
Call *call = getAccountLink(getAccountFromCall(*iter))->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();
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
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) and (current_call_id != callId2)) {
// If currently in a conference
if (isConference(current_call_id))
detachParticipant(Call::DEFAULT_ID, current_call_id);
else
onHoldCall(current_call_id); // currently in a call
}
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());
return;
}
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());
return;
}
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 (std::vector<std::string>::const_iterator iter = participantList.begin(); iter != participantList.end(); ++iter) {
std::string numberaccount(*iter);
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;
}
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);
// Conference may have been deleted and set to 0 above
processRemainingParticipants(current_call_id, conf);
if (conf == 0) {
ERROR("Manager: Error: Call is not conferencing, cannot detach");
return;
}
}
dbus_.getCallManager()->conferenceChanged(conf->getConfID(), conf->getStateStr());
} 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);
if (iter == conferenceMap_.end() or iter->second == 0) {
DEBUG("Manager: Error: Conference is NULL");
return;
}
Conference *conf = iter->second;
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::const_iterator iter = conf_map.find(call->getConfId());
if (iter == conf_map.end() or iter->second == 0) {
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();
dbus_.getCallManager()->conferenceChanged(conf->getConfID(), conf->getStateStr());
}
void ManagerImpl::processRemainingParticipants(const std::string ¤t_call_id, Conference * &conf)
{
ParticipantSet participants(conf->getParticipantList());
size_t n = participants.size();
DEBUG("Manager: Process remaining %d participant(s) from conference %s",
n, conf->getConfID().c_str());
if (n > 1) {
// 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 (n == 1) {
ParticipantSet::iterator iter_participant = participants.begin();
// bind main participant to remaining conference call
if (iter_participant != participants.end()) {
// this call is no longer a conference participant
std::string currentAccountId(getAccountFromCall(*iter_participant));
Call *call = getAccountLink(currentAccountId)->getCall(*iter_participant);
if (call) {
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());
conf = 0;
} else {
DEBUG("Manager: No remaining participants, remove conference");
removeConference(conf->getConfID());
conf = 0;
switchCall("");
}
}
void ManagerImpl::joinConference(const std::string& conf_id1,
const std::string& conf_id2)
{
ConferenceMap::iterator iter(conferenceMap_.find(conf_id1));
if (iter == conferenceMap_.end()) {
ERROR("Manager: Error: Not a valid conference ID: %s", conf_id1.c_str());
return;
}
if (conferenceMap_.find(conf_id2) != conferenceMap_.end()) {
ERROR("Manager: Error: Not a valid conference ID: %s", conf_id2.c_str());
return;
}
if (iter->second) {
Conference *conf = iter->second;
ParticipantSet participants(conf->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 (call and participToConference(call_id)) {
DEBUG("Manager: Add stream to conference");
// bind to conference participant
ConferenceMap::iterator iter = conferenceMap_.find(call->getConfId());
if (iter != conferenceMap_.end() and iter->second) {
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()
{
DEBUG("Manager: Saving Configuration to XDG directory %s", path_.c_str());
audioPreference.setVolumemic(getMicVolume());
audioPreference.setVolumespkr(getSpkrVolume());
try {
Conf::YamlEmitter emitter(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();
} catch (const Conf::YamlEmitterException &e) {
ERROR("ConfigTree: %s", e.what());
}
}
//THREAD=Main
void ManagerImpl::sendDtmf(const std::string& id, char code)
{
std::string accountid(getAccountFromCall(id));
playDtmf(code);
getAccountLink(accountid)->carryingDTMFdigits(id, code);
}
//THREAD=Main | VoIPLink
void ManagerImpl::playDtmf(char code)
{
stopTone();
if (not voipPreferences.getPlayDtmf()) {
DEBUG("Manager: playDtmf: Do not have to play a tone...");
return;
}
// length in milliseconds
int pulselen = voipPreferences.getPulseLength();
if (pulselen == 0) {
DEBUG("Manager: playDtmf: Pulse length is not set...");
return;
}
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;
}
// 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;
}
// Multi-thread
bool ManagerImpl::incomingCallWaiting()
{
return nbIncomingWaitingCall_ > 0;
}
void ManagerImpl::addWaitingCall(const std::string& id)
{
ost::MutexLock m(waitingCallMutex_);
waitingCall_.insert(id);
nbIncomingWaitingCall_++;
}
void ManagerImpl::removeWaitingCall(const std::string& id)
{
ost::MutexLock m(waitingCallMutex_);
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
void ManagerImpl::incomingCall(Call* call, const std::string& accountId)
{
assert(call);
stopTone();
associateCallToAccount(call->getCallId(), accountId);
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)
call->setPeerNumber(peerNumber.substr(startIndex + 4));
}
if (not hasCurrentCall()) {
call->setConnectionState(Call::Ringing);
ringtone(accountId);
}
addWaitingCall(call->getCallId());
std::string from(call->getPeerName());
std::string number(call->getPeerNumber());
if (not from.empty() and not number.empty())
from += " ";
from += "<" + number + ">";
dbus_.getCallManager()->incomingCall(accountId, call->getCallId(), call->getDisplayName() + " " + from);
}
//THREAD=VoIP
void ManagerImpl::incomingMessage(const std::string& callID,
const std::string& from,
const std::string& message)
{
if (participToConference(callID)) {
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) {
ERROR("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;
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;
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));
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 {
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 != 0) {
removeParticipant(call_id);
processRemainingParticipants(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, there are no calls remaining");
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);
processRemainingParticipants(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()
{
dbus_.getConfigurationManager()->accountsChanged();
}
/**
* Multi Thread
*/
void ManagerImpl::playATone(Tone::TONEID toneId)
{
if (not voipPreferences.getPlayTones())
return;
audioLayerMutexLock();
if (audiodriver_ == NULL) {
ERROR("Manager: Error: Audio layer not initialized");
audioLayerMutexUnlock();
return;
}
audiodriver_->flushUrgent();
audiodriver_->startStream();
audioLayerMutexUnlock();
if (telephoneTone_ != 0) {
toneMutex_.enterMutex();
telephoneTone_->setCurrentTone(toneId);
toneMutex_.leaveMutex();
}
}
/**
* 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
*/
void ManagerImpl::playTone()
{
playATone(Tone::TONE_DIALTONE);
}
/**
* Multi Thread
*/
void ManagerImpl::playToneWithMessage()
{
playATone(Tone::TONE_CONGESTION);
}
/**
* 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
static const char * const RINGDIR = "ringtones";
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();
{
ost::MutexLock m(toneMutex_);
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 or 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());
}
} // leave mutex
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_;
}
///////////////////////////////////////////////////////////////////////////////
// Private functions
///////////////////////////////////////////////////////////////////////////////
/**
* Initialization: Main Thread
*/
std::string ManagerImpl::getConfigFile() const
{
std::string configdir = std::string(HOMEDIR) + DIR_SEPARATOR_STR + ".config"
+ DIR_SEPARATOR_STR + PACKAGE;
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");
}
static const char * const PROGNAME = "sflphoned";
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();
audioPreference.setPlugin(audioPlugin);
AlsaLayer *alsa = dynamic_cast<AlsaLayer*>(audiodriver_);
if (!alsa) {
ERROR("Can't find alsa device");
audioLayerMutexUnlock();
return ;
}
bool wasStarted = audiodriver_->isStarted();
// Recreate audio driver with new settings
delete audiodriver_;
audiodriver_ = audioPreference.createAudioLayer();
if (wasStarted)
audiodriver_->startStream();
audioLayerMutexUnlock();
}
/**
* Set audio output device
*/
void ManagerImpl::setAudioDevice(const int index, int streamType)
{
audioLayerMutexLock();
AlsaLayer *alsaLayer = dynamic_cast<AlsaLayer*>(audiodriver_);
if (!alsaLayer) {
ERROR("Can't find alsa device");
audioLayerMutexUnlock();
return ;
}
bool wasStarted = audiodriver_->isStarted();
switch (streamType) {
case SFL_PCM_PLAYBACK:
audioPreference.setCardout(index);
break;
case SFL_PCM_CAPTURE:
audioPreference.setCardin(index);
break;
case SFL_PCM_RINGTONE:
audioPreference.setCardring(index);
break;
default:
break;
}
// Recreate audio driver with new settings
delete audiodriver_;
audiodriver_ = audioPreference.createAudioLayer();
if (wasStarted)
audiodriver_->startStream();
audioLayerMutexUnlock();
}
/**
* Get list of supported audio output device
*/
std::vector<std::string> ManagerImpl::getAudioOutputDeviceList()
{
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()
{
std::vector<std::string> devices;
audioLayerMutexLock();
AlsaLayer *alsalayer = dynamic_cast<AlsaLayer *>(audiodriver_);
if (alsalayer)
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;
AlsaLayer *alsa = dynamic_cast<AlsaLayer*>(audiodriver_);
if (alsa) {
std::stringstream ssi, sso, ssr;
sso << alsa->getIndexOut();
v.push_back(sso.str());
ssi << alsa->getIndexIn();
v.push_back(ssi.str());
ssr << alsa->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() 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() const
{
return audioPreference.getIsAlwaysRecording();
}
void ManagerImpl::setIsAlwaysRecording(bool isAlwaysRec)
{
return audioPreference.setIsAlwaysRecording(isAlwaysRec);
}
void ManagerImpl::setRecordingCall(const std::string& id)
{
Recordable* rec = NULL;
ConferenceMap::const_iterator it(conferenceMap_.find(id));
if (it == conferenceMap_.end()) {
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());
Conference *conf = it->second;
if (conf) {
rec = conf;
if (rec->isRecording())
conf->setState(Conference::ACTIVE_ATTACHED);
else
conf->setState(Conference::ACTIVE_ATTACHED_REC);
}
}
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();
{
ost::MutexLock m(toneMutex_);
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());
}
} // release toneMutex
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();
{
ost::MutexLock m(toneMutex_);
delete audiofile_;
audiofile_ = NULL;
}
}
void ManagerImpl::setHistoryLimit(int days)
{
DEBUG("Manager: Set history limit");
preferences.setHistoryLimit(days);
saveConfig();
}
int ManagerImpl::getHistoryLimit() const
{
return preferences.getHistoryLimit();
}
int32_t ManagerImpl::getMailNotify() const
{
return preferences.getNotifyMails();
}
void ManagerImpl::setMailNotify()
{
DEBUG("Manager: Set mail notify");
preferences.getNotifyMails() ? preferences.setNotifyMails(true) : preferences.setNotifyMails(false);
saveConfig();
}
void ManagerImpl::setAudioManager(const std::string &api)
{
audioLayerMutexLock();
if (!audiodriver_) {
audioLayerMutexUnlock();
return;
}
if (api == audioPreference.getAudioApi()) {
DEBUG("Manager: Audio manager chosen already in use. No changes made. ");
audioLayerMutexUnlock();
return;
}
audioLayerMutexUnlock();
switchAudioManager();
saveConfig();
}
std::string ManagerImpl::getAudioManager() const
{
return audioPreference.getAudioApi();
}
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() const
{
return audioPreference.getPlugin();
}
std::string ManagerImpl::getNoiseSuppressState() const
{
return audioPreference.getNoiseReduce() ? "enabled" : "disabled";
}
void ManagerImpl::setNoiseSuppressState(const std::string &state)
{
audioPreference.setNoiseReduce(state == "enabled");
}
bool ManagerImpl::getEchoCancelState() const
{
return audioPreference.getEchoCancel();
}
void ManagerImpl::setEchoCancelState(const std::string &state)
{
audioPreference.setEchoCancel(state == "enabled");
}
int ManagerImpl::getEchoCancelTailLength() const
{
return audioPreference.getEchoCancelTailLength();
}
void ManagerImpl::setEchoCancelTailLength(int length)
{
audioPreference.setEchoCancelTailLength(length);
}
int ManagerImpl::getEchoCancelDelay() const
{
return audioPreference.getEchoCancelDelay();
}
void ManagerImpl::setEchoCancelDelay(int delay)
{
audioPreference.setEchoCancelDelay(delay);
}
/**
* Initialization: Main Thread
*/
void ManagerImpl::initAudioDriver()
{
audioLayerMutexLock();
audiodriver_ = audioPreference.createAudioLayer();
audioLayerMutexUnlock();
}
void ManagerImpl::switchAudioManager()
{
audioLayerMutexLock();
bool wasStarted = audiodriver_->isStarted();
delete audiodriver_;
audiodriver_ = audioPreference.switchAndCreateAudioLayer();
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: %d -> %d", currentSamplerate, samplerate);
bool wasActive = audiodriver_->isStarted();
mainBuffer_.setInternalSamplingRate(samplerate);
delete audiodriver_;
audiodriver_ = audioPreference.createAudioLayer();
unsigned int sampleRate = audiodriver_->getSampleRate();
delete telephoneTone_;
telephoneTone_ = new TelephoneTone(preferences.getZoneToneChoice(), sampleRate);
delete dtmfKey_;
dtmfKey_ = new DTMF(sampleRate);
if (wasActive)
audiodriver_->startStream();
audioLayerMutexUnlock();
}
/**
* Init the volume for speakers/micro from 0 to 100 value
* Initialization: Main Thread
*/
void ManagerImpl::initVolume()
{
setSpkrVolume(audioPreference.getVolumespkr());
setMicVolume(audioPreference.getVolumemic());
}
void ManagerImpl::setSpkrVolume(unsigned short spkr_vol)
{
speakerVolume_ = spkr_vol;
}
void ManagerImpl::setMicVolume(unsigned short mic_vol)
{
micVolume_ = mic_vol;
}
int ManagerImpl::getLocalIp2IpPort() const
{
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
{
return config_.getConfigTreeItemIntValue(section, name);
}
bool ManagerImpl::getConfigBool(const std::string& section,
const std::string& name) const
{
return config_.getConfigTreeItemValue(section, name) == Conf::TRUE_STR;
}
//THREAD=Main
std::string ManagerImpl::getConfigString(const std::string& section,
const std::string& name) const
{
return config_.getConfigTreeItemValue(section, name);
}
//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
AccountMap::const_iterator ip2ip_iter = accountMap_.find(IP2IP_PROFILE);
vector<string> v;
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()) {
for (AccountMap::const_iterator iter = accountMap_.begin(); iter != accountMap_.end(); ++iter) {
if (iter->first == IP2IP_PROFILE || iter->first.empty())
continue;
if (iter->second)
v.push_back(iter->second->getAccountID());
}
}
else {
for (vector<string>::const_iterator iter = account_order.begin(); iter != account_order.end(); ++iter) {
if (*iter == IP2IP_PROFILE or iter->empty())
continue;
AccountMap::const_iterator account_iter = accountMap_.find(*iter);
if (account_iter != accountMap_.end() and account_iter->second)
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::stringstream accountID;
accountID << "Account:" << time(NULL);
std::string newAccountID(accountID.str());
// Get the type
std::string 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
std::string accountList(preferences.getAccountOrder());
if (not accountList.empty()) {
newAccountID += "/";
// Prepend the new account
accountList.insert(0, newAccountID);
preferences.setAccountOrder(accountList);
} else {
newAccountID += "/";
accountList = newAccountID;
preferences.setAccountOrder(accountList);
}
DEBUG("AccountMap: %s", accountList.c_str());
newAccount->registerVoIPLink();
saveConfig();
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();
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;
}
void ManagerImpl::removeCallAccount(const std::string& callID)
{
ost::MutexLock m(callAccountMapMutex_);
callAccountMap_.erase(callID);
// Stop audio layer if there is no call anymore
if (callAccountMap_.empty()) {
audioLayerMutexLock();
if (audiodriver_)
audiodriver_->stopStream();
audioLayerMutexUnlock();
}
}
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() const
{
return unserialize(preferences.getAccountOrder());
}
void ManagerImpl::loadAccountMap(Conf::YamlParser *parser)
{
// 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)
SIPVoIPLink::instance()->createDefaultSipUdpTransport();
// 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()
{
for (AccountMap::iterator iter = accountMap_.begin(); iter != accountMap_.end(); ++iter) {
// Avoid removing the IP2IP account twice
if (not iter->first.empty()) {
delete iter->second;
iter->second = 0;
}
}
accountMap_.clear();
}
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;
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)
{
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)
{
addressbookPreference.setList(ManagerImpl::serialize(list));
saveConfig();
}
std::vector<std::string> ManagerImpl::getAddressbookList() 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();
}
bool ManagerImpl::associateConfigToCall(const std::string& callID,
Call::CallConfiguration config)
{
if (getConfigFromCall(callID) == 0) { // 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) 0;
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() const
{
return history_->get_history_serialized();
}
void ManagerImpl::setHistorySerialized(std::vector<std::string> history)
{
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() 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 = 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() const
{
std::vector<std::string> v;
vectorFromMapKeys(conferenceMap_, v);
return v;
}
std::vector<std::string> ManagerImpl::getParticipantList(const std::string& confID) const
{
std::vector<std::string> v;
ConferenceMap::const_iterator iter_conf = conferenceMap_.find(confID);
if (iter_conf != conferenceMap_.end()) {
const ParticipantSet participants(iter_conf->second->getParticipantList());
std::copy(participants.begin(), participants.end(), std::back_inserter(v));;
} else
WARN("Manager: Warning: Did not find conference %s", confID.c_str());
return v;
}