Browse Source

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
Scott Hardin 6 years ago
parent
commit
75cafbfefe
9 changed files with 82 additions and 57 deletions
  1. 12 0
      Configuration.cpp
  2. 3 0
      Configuration.hpp
  3. 6 0
      MumbleChannelJoiner.cpp
  4. 1 0
      MumbleChannelJoiner.hpp
  5. 42 23
      PjsuaCommunicator.cpp
  6. 5 1
      PjsuaCommunicator.hpp
  7. 12 32
      main.cpp
  8. 1 1
      media/menu.msg
  9. BIN
      media/menu.wav

+ 12 - 0
Configuration.cpp

@@ -3,6 +3,7 @@
 #include <boost/property_tree/ptree.hpp>
 #include <boost/property_tree/ini_parser.hpp>
 #include <boost/format.hpp>
+#include <boost/foreach.hpp>
 
 using namespace config;
 
@@ -50,4 +51,15 @@ std::string config::Configuration::getString(const std::string &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;
+}
+
 

+ 3 - 0
Configuration.hpp

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

+ 6 - 0
MumbleChannelJoiner.cpp

@@ -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);
+}
+
+

+ 1 - 0
MumbleChannelJoiner.hpp

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

+ 42 - 23
PjsuaCommunicator.cpp

@@ -121,8 +121,8 @@ namespace sip {
 
     class _Account : public pj::Account {
     public:
-        _Account(sip::PjsuaCommunicator &comm)
-                : communicator(comm) { }
+        _Account(sip::PjsuaCommunicator &comm, int max_calls)
+                : communicator(comm) { this->max_calls = max_calls; }
 
         virtual void onRegState(pj::OnRegStateParam &prm) override;
 
@@ -131,7 +131,8 @@ namespace sip {
     private:
         sip::PjsuaCommunicator &communicator;
 
-        bool available = true;
+        int active_calls = 0;
+        int max_calls;
 
         friend class _Call;
     };
@@ -166,7 +167,7 @@ namespace sip {
              * if no pin is set, go ahead and turn off mute/deaf
              * 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
                 communicator.dtmf_mode = DTMF_MODE_ROOT;
                 // turning off mute automatically turns off deaf
@@ -185,7 +186,11 @@ namespace sip {
         } else if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
             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.";
 
                 communicator.calls[ci.id].mixer->clear();
@@ -198,8 +203,9 @@ namespace sip {
 
                 communicator.calls[ci.id].onDisconnect();
 
-                acc.available = true;
-            }
+                //acc.available = true;
+                acc.active_calls--;
+            //}
 
             delete this;
         } else {
@@ -221,7 +227,7 @@ namespace sip {
             communicator.calls[ci.id].media->startTransmit(*aud_med);
             aud_med->startTransmit(*communicator.calls[ci.id].media);
         } 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;
 
         auto ci = getInfo();
+        std::string chanName;
 
         /*
          * DTMF CALLER MENU
@@ -308,11 +315,15 @@ namespace sip {
                         /*
                          * When user presses '#', test PIN entry
                          */
-                        if ( communicator.caller_pin.length() > 0 ) {
-                            if ( communicator.got_dtmf == communicator.caller_pin ) {
+                        if ( communicator.pins.size() > 0 ) {
+                            if ( communicator.pins.count(communicator.got_dtmf) > 0 ) {
                                 communicator.logger.info("Caller entered correct PIN");
                                 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);
                                 communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_MUTE, false);
@@ -386,12 +397,23 @@ namespace sip {
                         this->playAudioFile(communicator.file_mute_off);
                         communicator.calls[ci.id].sendUserState(mumlib::UserState::SELF_MUTE, false);
                         break;
-                    case '0':
-                        // play menu
-                        this->playAudioFile(communicator.file_menu);
+                    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
                         communicator.logger.info("Unsupported DTMF digit '%s' in state STAR", prm.digit.c_str());
+                        this->playAudioFile(communicator.file_menu);
+                        break;
                 }
                 /*
                  * In any case, switch back to root after one digit
@@ -422,17 +444,13 @@ namespace sip {
 
         if (communicator.uriValidator.validateUri(uri)) {
 
-            if (available) {
+            if (active_calls < max_calls) {
                 param.statusCode = PJSIP_SC_OK;
-                available = false;
+                active_calls++;
             } else {
-                /*
-                 * EXPERIMENT WITH MULTI-LINE
-                 */
-                communicator.logger.info("MULTI-LINE Incoming call from %s.", uri.c_str());
+                communicator.logger.notice("BUSY - reject incoming call from %s.", uri.c_str());
                 param.statusCode = PJSIP_SC_OK;
-                available = false;
-                //param.statusCode = PJSIP_SC_BUSY_EVERYWHERE;
+                param.statusCode = PJSIP_SC_BUSY_EVERYWHERE;
             }
 
             call->answer(param);
@@ -450,6 +468,7 @@ sip::PjsuaCommunicator::PjsuaCommunicator(IncomingConnectionValidator &validator
           uriValidator(validator) {
 
     logWriter.reset(new sip::_LogWriter(pjsuaLogger));
+    max_calls = maxCalls;
 
 
     endpoint.libCreate();
@@ -551,7 +570,7 @@ void sip::PjsuaCommunicator::registerAccount(string host, string user, string pa
     accountConfig.sipConfig.authCreds.push_back(cred);
 
     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);
 }
 

+ 5 - 1
PjsuaCommunicator.hpp

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

+ 12 - 32
main.cpp

@@ -60,6 +60,11 @@ int main(int argc, char *argv[]) {
 
     sip::PjsuaCommunicator pjsuaCommunicator(connectionValidator, conf.getInt("sip.frameLength"), max_calls);
 
+    try {
+        pjsuaCommunicator.pins = conf.getChildren("pins");
+    } catch (...) {
+    }
+
     mumble::MumbleCommunicatorConfig mumbleConf;
     mumbleConf.host = conf.getString("mumble.host");
     mumbleConf.port = conf.getInt("mumble.port");
@@ -79,19 +84,6 @@ int main(int argc, char *argv[]) {
         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");
     } catch (...) {
         pjsuaCommunicator.file_welcome = "welcome.wav";
@@ -137,23 +129,10 @@ int main(int argc, char *argv[]) {
         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 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 mumbleAuthChannelJoiner(authChan);
+    mumble::MumbleChannelJoiner mumbleOtherChannelJoiner(defaultChan);
 
     for (int i = 0; i<max_calls; i++) {
 
@@ -223,11 +202,12 @@ int main(int argc, char *argv[]) {
                 &mumbleChannelJoiner,
                 mumcom);
 
-        // PJ notifies Mumble that Caller Auth is done
-        pjsuaCommunicator.calls[i].joinAuthChannel = std::bind(
-                &mumble::MumbleChannelJoiner::findJoinChannel,
-                &mumbleAuthChannelJoiner,
-                mumcom);
+        // PJ notifies Mumble to join other channel
+        pjsuaCommunicator.calls[i].joinOtherChannel = std::bind(
+                &mumble::MumbleChannelJoiner::joinOtherChannel,
+                &mumbleOtherChannelJoiner,
+                mumcom,
+                _1);
 
         // Passing audio from Mumble to SIP
         mumcom->onIncomingPcmSamples = std::bind(

+ 1 - 1
media/menu.msg

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

BIN
media/menu.wav