Add support for multiple caller pins

In config.ini, add the [pins] section, with key/value entries
for the expected pin and the channel regex to switch to when
the caller enters the pin. Here's an example:

[pins]
12345 = DevOps Team
23456 = Sales Team
This commit is contained in:
Scott Hardin 2017-05-27 22:12:26 +02:00
parent d3213be149
commit 75cafbfefe
9 changed files with 82 additions and 57 deletions

View File

@ -3,6 +3,7 @@
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp> #include <boost/property_tree/ini_parser.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <boost/foreach.hpp>
using namespace config; using namespace config;
@ -50,4 +51,15 @@ std::string config::Configuration::getString(const std::string &property) {
return get<std::string>(impl->ptree, property); return get<std::string>(impl->ptree, property);
} }
// TODO: return set
std::unordered_map<std::string, std::string> config::Configuration::getChildren(const std::string &property) {
std::unordered_map<std::string, std::string> pins;
BOOST_FOREACH(boost::property_tree::ptree::value_type &v,
impl->ptree.get_child(property)) {
//pins[v.first.data()] = get<std::string>(impl->ptree, property + "." + v.second.data());
pins[v.first.data()] = v.second.data();
}
return pins;
}

View File

@ -5,6 +5,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <stdexcept> #include <stdexcept>
#include <unordered_map>
namespace config { namespace config {
@ -31,6 +32,8 @@ namespace config {
std::string getString(const std::string &property); std::string getString(const std::string &property);
std::unordered_map<std::string, std::string> getChildren(const std::string &property);
private: private:
ConfigurationImpl *impl; ConfigurationImpl *impl;
}; };

View File

@ -50,3 +50,9 @@ void mumble::MumbleChannelJoiner::findJoinChannel(mumble::MumbleCommunicator *mc
} }
} }
void mumble::MumbleChannelJoiner::joinOtherChannel(mumble::MumbleCommunicator *mc, std::string channelNameRegex) {
this->channelNameRegex = boost::regex(channelNameRegex);
findJoinChannel(mc);
}

View File

@ -22,6 +22,7 @@ namespace mumble {
void checkChannel(std::string channel_name, int channel_id); void checkChannel(std::string channel_name, int channel_id);
void maybeJoinChannel(mumble::MumbleCommunicator *mc); void maybeJoinChannel(mumble::MumbleCommunicator *mc);
void findJoinChannel(mumble::MumbleCommunicator *mc); void findJoinChannel(mumble::MumbleCommunicator *mc);
void joinOtherChannel(mumble::MumbleCommunicator *mc, std::string channelNameRegex);
private: private:
log4cpp::Category &logger; log4cpp::Category &logger;

View File

@ -121,8 +121,8 @@ namespace sip {
class _Account : public pj::Account { class _Account : public pj::Account {
public: public:
_Account(sip::PjsuaCommunicator &comm) _Account(sip::PjsuaCommunicator &comm, int max_calls)
: communicator(comm) { } : communicator(comm) { this->max_calls = max_calls; }
virtual void onRegState(pj::OnRegStateParam &prm) override; virtual void onRegState(pj::OnRegStateParam &prm) override;
@ -131,7 +131,8 @@ namespace sip {
private: private:
sip::PjsuaCommunicator &communicator; sip::PjsuaCommunicator &communicator;
bool available = true; int active_calls = 0;
int max_calls;
friend class _Call; friend class _Call;
}; };
@ -166,7 +167,7 @@ namespace sip {
* if no pin is set, go ahead and turn off mute/deaf * if no pin is set, go ahead and turn off mute/deaf
* otherwise, wait for pin to be entered * otherwise, wait for pin to be entered
*/ */
if ( communicator.caller_pin.length() == 0 ) { if ( communicator.pins.size() == 0 ) {
// No PIN set... enter DTMF root menu and turn off mute/deaf // No PIN set... enter DTMF root menu and turn off mute/deaf
communicator.dtmf_mode = DTMF_MODE_ROOT; communicator.dtmf_mode = DTMF_MODE_ROOT;
// turning off mute automatically turns off deaf // turning off mute automatically turns off deaf
@ -185,7 +186,11 @@ namespace sip {
} else if (ci.state == PJSIP_INV_STATE_DISCONNECTED) { } else if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
auto &acc = dynamic_cast<_Account &>(account); auto &acc = dynamic_cast<_Account &>(account);
if (not acc.available) { /*
* Not sure why we check acc.available, but with multi-call
* functionality, this check doesn't work.
*/
//if (not acc.available) {
auto msgText = "Call from " + address + " finished."; auto msgText = "Call from " + address + " finished.";
communicator.calls[ci.id].mixer->clear(); communicator.calls[ci.id].mixer->clear();
@ -198,8 +203,9 @@ namespace sip {
communicator.calls[ci.id].onDisconnect(); communicator.calls[ci.id].onDisconnect();
acc.available = true; //acc.available = true;
} acc.active_calls--;
//}
delete this; delete this;
} else { } else {
@ -221,7 +227,7 @@ namespace sip {
communicator.calls[ci.id].media->startTransmit(*aud_med); communicator.calls[ci.id].media->startTransmit(*aud_med);
aud_med->startTransmit(*communicator.calls[ci.id].media); aud_med->startTransmit(*communicator.calls[ci.id].media);
} else if (ci.media[0].status == PJSUA_CALL_MEDIA_NONE) { } else if (ci.media[0].status == PJSUA_CALL_MEDIA_NONE) {
dynamic_cast<_Account &>(account).available = true; dynamic_cast<_Account &>(account).active_calls++;
} }
} }
@ -293,6 +299,7 @@ namespace sip {
pj::CallOpParam param; pj::CallOpParam param;
auto ci = getInfo(); auto ci = getInfo();
std::string chanName;
/* /*
* DTMF CALLER MENU * DTMF CALLER MENU
@ -308,11 +315,15 @@ namespace sip {
/* /*
* When user presses '#', test PIN entry * When user presses '#', test PIN entry
*/ */
if ( communicator.caller_pin.length() > 0 ) { if ( communicator.pins.size() > 0 ) {
if ( communicator.got_dtmf == communicator.caller_pin ) { if ( communicator.pins.count(communicator.got_dtmf) > 0 ) {
communicator.logger.info("Caller entered correct PIN"); communicator.logger.info("Caller entered correct PIN");
communicator.dtmf_mode = DTMF_MODE_ROOT; communicator.dtmf_mode = DTMF_MODE_ROOT;
communicator.calls[ci.id].joinAuthChannel(); communicator.logger.notice("MYDEBUG: %s:%s",
communicator.got_dtmf.c_str(),
communicator.pins[communicator.got_dtmf].c_str());
communicator.calls[ci.id].joinOtherChannel(
communicator.pins[communicator.got_dtmf]);
this->playAudioFile(communicator.file_entering_channel); this->playAudioFile(communicator.file_entering_channel);
communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_MUTE, false); communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_MUTE, false);
@ -386,12 +397,23 @@ namespace sip {
this->playAudioFile(communicator.file_mute_off); this->playAudioFile(communicator.file_mute_off);
communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_MUTE, false); communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_MUTE, false);
break; break;
case '0': case '9':
if ( communicator.pins.size() > 0 ) {
communicator.dtmf_mode = DTMF_MODE_UNAUTH;
communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_DEAF, true);
communicator.calls[ci.id].joinDefaultChannel();
this->playAudioFile(communicator.file_prompt_pin);
} else {
// we should have a 'not supported' message
}
break;
case '0': // block these for the menu itself
case '*':
default:
// play menu // play menu
communicator.logger.info("Unsupported DTMF digit '%s' in state STAR", prm.digit.c_str());
this->playAudioFile(communicator.file_menu); this->playAudioFile(communicator.file_menu);
break; break;
default:
communicator.logger.info("Unsupported DTMF digit '%s' in state STAR", prm.digit.c_str());
} }
/* /*
* In any case, switch back to root after one digit * In any case, switch back to root after one digit
@ -422,17 +444,13 @@ namespace sip {
if (communicator.uriValidator.validateUri(uri)) { if (communicator.uriValidator.validateUri(uri)) {
if (available) { if (active_calls < max_calls) {
param.statusCode = PJSIP_SC_OK; param.statusCode = PJSIP_SC_OK;
available = false; active_calls++;
} else { } else {
/* communicator.logger.notice("BUSY - reject incoming call from %s.", uri.c_str());
* EXPERIMENT WITH MULTI-LINE
*/
communicator.logger.info("MULTI-LINE Incoming call from %s.", uri.c_str());
param.statusCode = PJSIP_SC_OK; param.statusCode = PJSIP_SC_OK;
available = false; param.statusCode = PJSIP_SC_BUSY_EVERYWHERE;
//param.statusCode = PJSIP_SC_BUSY_EVERYWHERE;
} }
call->answer(param); call->answer(param);
@ -450,6 +468,7 @@ sip::PjsuaCommunicator::PjsuaCommunicator(IncomingConnectionValidator &validator
uriValidator(validator) { uriValidator(validator) {
logWriter.reset(new sip::_LogWriter(pjsuaLogger)); logWriter.reset(new sip::_LogWriter(pjsuaLogger));
max_calls = maxCalls;
endpoint.libCreate(); endpoint.libCreate();
@ -551,7 +570,7 @@ void sip::PjsuaCommunicator::registerAccount(string host, string user, string pa
accountConfig.sipConfig.authCreds.push_back(cred); accountConfig.sipConfig.authCreds.push_back(cred);
logger.info("Registering account for URI: %s.", uri.c_str()); logger.info("Registering account for URI: %s.", uri.c_str());
account.reset(new _Account(*this)); account.reset(new _Account(*this, max_calls));
account->create(accountConfig); account->create(accountConfig);
} }

View File

@ -80,7 +80,8 @@ namespace sip {
std::function<void()> onConnect; std::function<void()> onConnect;
std::function<void()> onDisconnect; std::function<void()> onDisconnect;
std::function<void()> onCallerAuth; std::function<void()> onCallerAuth;
std::function<void()> joinAuthChannel; std::function<void()> joinAuthChannel; // DEPRECATE ?
std::function<void(std::string channelNameRegex)> joinOtherChannel;
std::function<void()> joinDefaultChannel; std::function<void()> joinDefaultChannel;
}; };
@ -114,6 +115,7 @@ namespace sip {
std::string file_mute_on; std::string file_mute_on;
std::string file_mute_off; std::string file_mute_off;
std::string file_menu; std::string file_menu;
int max_calls;
// TODO: move these to private? // TODO: move these to private?
std::string got_dtmf; std::string got_dtmf;
@ -126,6 +128,8 @@ namespace sip {
call calls[MY_MAX_CALLS]; call calls[MY_MAX_CALLS];
std::unordered_map<std::string, std::string> pins;
private: private:
log4cpp::Category &logger; log4cpp::Category &logger;
log4cpp::Category &pjsuaLogger; log4cpp::Category &pjsuaLogger;

View File

@ -60,6 +60,11 @@ int main(int argc, char *argv[]) {
sip::PjsuaCommunicator pjsuaCommunicator(connectionValidator, conf.getInt("sip.frameLength"), max_calls); sip::PjsuaCommunicator pjsuaCommunicator(connectionValidator, conf.getInt("sip.frameLength"), max_calls);
try {
pjsuaCommunicator.pins = conf.getChildren("pins");
} catch (...) {
}
mumble::MumbleCommunicatorConfig mumbleConf; mumble::MumbleCommunicatorConfig mumbleConf;
mumbleConf.host = conf.getString("mumble.host"); mumbleConf.host = conf.getString("mumble.host");
mumbleConf.port = conf.getInt("mumble.port"); mumbleConf.port = conf.getInt("mumble.port");
@ -79,19 +84,6 @@ int main(int argc, char *argv[]) {
mumbleConf.comment = ""; mumbleConf.comment = "";
} }
try {
mumbleConf.authchan = conf.getString("mumble.channelAuthExpression");
} catch (...) {
mumbleConf.authchan = "";
}
/* default to <no pin> */
try {
pjsuaCommunicator.caller_pin = conf.getString("app.caller_pin");
} catch (...) {
pjsuaCommunicator.caller_pin = "";
}
try { pjsuaCommunicator.file_welcome = conf.getString("files.welcome"); try { pjsuaCommunicator.file_welcome = conf.getString("files.welcome");
} catch (...) { } catch (...) {
pjsuaCommunicator.file_welcome = "welcome.wav"; pjsuaCommunicator.file_welcome = "welcome.wav";
@ -137,23 +129,10 @@ int main(int argc, char *argv[]) {
pjsuaCommunicator.file_menu = "menu.wav"; pjsuaCommunicator.file_menu = "menu.wav";
} }
/* If the channelUnauthExpression is set, use this as the default
* channel and use channelNameExpression for the authchan. Otherwise,
* use the channelNameExpression for the default.
*/
std::string defaultChan = conf.getString("mumble.channelNameExpression"); std::string defaultChan = conf.getString("mumble.channelNameExpression");
std::string authChan = "";
if ( pjsuaCommunicator.caller_pin.size() > 0 ) {
try {
authChan = conf.getString("mumble.channelAuthExpression");
} catch (...) {
// defaultChan = conf.getString("mumble.channelNameExpression");
}
}
mumble::MumbleChannelJoiner mumbleChannelJoiner(defaultChan); mumble::MumbleChannelJoiner mumbleChannelJoiner(defaultChan);
mumble::MumbleChannelJoiner mumbleAuthChannelJoiner(authChan); mumble::MumbleChannelJoiner mumbleOtherChannelJoiner(defaultChan);
for (int i = 0; i<max_calls; i++) { for (int i = 0; i<max_calls; i++) {
@ -223,11 +202,12 @@ int main(int argc, char *argv[]) {
&mumbleChannelJoiner, &mumbleChannelJoiner,
mumcom); mumcom);
// PJ notifies Mumble that Caller Auth is done // PJ notifies Mumble to join other channel
pjsuaCommunicator.calls[i].joinAuthChannel = std::bind( pjsuaCommunicator.calls[i].joinOtherChannel = std::bind(
&mumble::MumbleChannelJoiner::findJoinChannel, &mumble::MumbleChannelJoiner::joinOtherChannel,
&mumbleAuthChannelJoiner, &mumbleOtherChannelJoiner,
mumcom); mumcom,
_1);
// Passing audio from Mumble to SIP // Passing audio from Mumble to SIP
mumcom->onIncomingPcmSamples = std::bind( mumcom->onIncomingPcmSamples = std::bind(

View File

@ -1,3 +1,3 @@
Press star one for status.
Press star five to mute. Press star five to mute.
Press star six to un-mute. Press star six to un-mute.
Press star nine to change channel.

Binary file not shown.