]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
[Core] switch_ivr: Restrict the misuse of uuid_hold API. Add switch_hold unit-test.
authorAndrey Volk <andywolk@gmail.com>
Thu, 12 Aug 2021 15:02:41 +0000 (18:02 +0300)
committerAndrey Volk <andywolk@gmail.com>
Sat, 23 Oct 2021 19:00:52 +0000 (22:00 +0300)
src/mod/applications/mod_commands/mod_commands.c
src/switch_ivr.c
tests/unit/.gitignore
tests/unit/Makefile.am
tests/unit/conf_hold/freeswitch.xml [new file with mode: 0644]
tests/unit/conf_hold/gw/holdtest.xml [new file with mode: 0644]
tests/unit/switch_hold.c [new file with mode: 0644]

index 955f80ae33c203656acc68c155346a739134f272..b5f7f5ff8ed24a6e85fd961860c8717b373e6b9c 100644 (file)
@@ -7896,6 +7896,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
        switch_console_set_complete("add uuid_flush_dtmf ::console::list_uuid");
        switch_console_set_complete("add uuid_getvar ::console::list_uuid");
        switch_console_set_complete("add uuid_hold ::console::list_uuid");
+       switch_console_set_complete("add uuid_hold off ::console::list_uuid");
+       switch_console_set_complete("add uuid_hold toggle ::console::list_uuid");
        switch_console_set_complete("add uuid_send_info ::console::list_uuid");
        switch_console_set_complete("add uuid_jitterbuffer ::console::list_uuid");
        switch_console_set_complete("add uuid_kill ::console::list_uuid");
index 4f9d2d0cbd69f5dac6334bc1536a6dbdbadbf93d..ddd3a8e529734073523d51c6d61bc49cacb1ad4f 100644 (file)
@@ -1530,6 +1530,16 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_hold(switch_core_session_t *session,
        const char *other_uuid;
        switch_event_t *event;
 
+       if (channel) {
+               switch_channel_callstate_t callstate;
+
+               callstate = switch_channel_get_callstate(channel);
+               if (callstate == CCS_HELD) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Call is already on hold. No need to hold again.\n");
+                       return SWITCH_STATUS_FALSE;
+               }
+       }
+
        msg.message_id = SWITCH_MESSAGE_INDICATE_HOLD;
        msg.string_arg = message;
        msg.from = __FILE__;
@@ -1557,13 +1567,14 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_hold(switch_core_session_t *session,
 SWITCH_DECLARE(switch_status_t) switch_ivr_hold_uuid(const char *uuid, const char *message, switch_bool_t moh)
 {
        switch_core_session_t *session;
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
 
        if ((session = switch_core_session_locate(uuid))) {
-               switch_ivr_hold(session, message, moh);
+               status = switch_ivr_hold(session, message, moh);
                switch_core_session_rwunlock(session);
        }
 
-       return SWITCH_STATUS_SUCCESS;
+       return status;
 }
 
 SWITCH_DECLARE(switch_status_t) switch_ivr_hold_toggle_uuid(const char *uuid, const char *message, switch_bool_t moh)
@@ -1571,21 +1582,22 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_hold_toggle_uuid(const char *uuid, co
        switch_core_session_t *session;
        switch_channel_t *channel;
        switch_channel_callstate_t callstate;
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
 
        if ((session = switch_core_session_locate(uuid))) {
                if ((channel = switch_core_session_get_channel(session))) {
                        callstate = switch_channel_get_callstate(channel);
 
-                       if (callstate == CCS_ACTIVE) {
-                               switch_ivr_hold(session, message, moh);
+                       if (callstate == CCS_ACTIVE || callstate == CCS_UNHELD) {
+                               status = switch_ivr_hold(session, message, moh);
                        } else if (callstate == CCS_HELD) {
-                               switch_ivr_unhold(session);
+                               status = switch_ivr_unhold(session);
                        }
                }
                switch_core_session_rwunlock(session);
        }
 
-       return SWITCH_STATUS_SUCCESS;
+       return status;
 }
 
 SWITCH_DECLARE(switch_status_t) switch_ivr_unhold(switch_core_session_t *session)
@@ -1596,6 +1608,16 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_unhold(switch_core_session_t *session
        switch_core_session_t *b_session;
        switch_event_t *event;
 
+       if (channel) {
+               switch_channel_callstate_t callstate;
+
+               callstate = switch_channel_get_callstate(channel);
+               if (callstate != CCS_HELD) {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Call is not on hold. No need to unhold.\n");
+                       return SWITCH_STATUS_FALSE;
+               }
+       }
+
        msg.message_id = SWITCH_MESSAGE_INDICATE_UNHOLD;
        msg.from = __FILE__;
 
@@ -1624,13 +1646,14 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_unhold(switch_core_session_t *session
 SWITCH_DECLARE(switch_status_t) switch_ivr_unhold_uuid(const char *uuid)
 {
        switch_core_session_t *session;
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
 
        if ((session = switch_core_session_locate(uuid))) {
-               switch_ivr_unhold(session);
+               status = switch_ivr_unhold(session);
                switch_core_session_rwunlock(session);
        }
 
-       return SWITCH_STATUS_SUCCESS;
+       return status;
 }
 
 
index 66ea66aa069aa10c4504217e601f8175371965e2..4c7b7466c41ebd981b189c5d42b33a0cd9f595ae 100644 (file)
@@ -22,6 +22,7 @@ switch_core_video
 switch_eavesdrop
 switch_event
 switch_hash
+switch_hold
 switch_ivr_async
 switch_ivr_originate
 switch_ivr_play_say
index ee2e92b175e90177e8a06e651940fd9bf6bc4531..3f41d71dda70b09dbe05510020dbc2ec9baec32b 100644 (file)
@@ -4,6 +4,7 @@ noinst_PROGRAMS = switch_event switch_hash switch_ivr_originate switch_utils swi
                           switch_ivr_play_say switch_core_codec switch_rtp switch_xml
 noinst_PROGRAMS += switch_core_video switch_core_db switch_vad switch_packetizer switch_core_session test_sofia switch_ivr_async switch_core_asr switch_log
 
+noinst_PROGRAMS+= switch_hold
 AM_LDFLAGS += -avoid-version -no-undefined $(SWITCH_AM_LDFLAGS) $(openssl_LIBS)
 AM_LDFLAGS += $(FREESWITCH_LIBS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS)
 
diff --git a/tests/unit/conf_hold/freeswitch.xml b/tests/unit/conf_hold/freeswitch.xml
new file mode 100644 (file)
index 0000000..883e384
--- /dev/null
@@ -0,0 +1,157 @@
+<?xml version="1.0"?>
+<document type="freeswitch/xml">
+  <X-PRE-PROCESS cmd="exec-set" data="test=echo 1234"/>
+  <X-PRE-PROCESS cmd="set" data="default_password=$${test}"/>
+  <X-PRE-PROCESS cmd="set" data="core_video_blank_image=$${conf_dir}/freeswitch-logo.png"/>
+  <section name="configuration" description="Various Configuration">
+    <configuration name="modules.conf" description="Modules">
+               <modules>
+                       <load module="mod_sofia"/>
+                       <load module="mod_console"/>
+                       <load module="mod_loopback"/>
+                       <load module="mod_commands"/>
+                       <load module="mod_dptools"/>
+                       <load module="mod_dialplan_xml"/>
+                       <load module="mod_tone_stream"/>
+                       <load module="mod_commands"/>
+                       <load module="mod_sndfile"/>
+      </modules>
+    </configuration>
+
+    <configuration name="console.conf" description="Console Logger">
+      <mappings>
+        <map name="all" value="console,debug,info,notice,warning,err,crit,alert"/>
+      </mappings>
+      <settings>
+        <param name="colorize" value="true"/>
+        <param name="loglevel" value="debug"/>
+      </settings>
+    </configuration>
+
+    <configuration name="timezones.conf" description="Timezones">
+      <timezones>
+          <zone name="GMT" value="GMT0" />
+      </timezones>
+    </configuration>
+
+    <configuration name="sofia.conf" description="SofiaSIP">
+        <profiles>
+    <profile name="external">
+        <gateways>
+
+           <gateway name="hold_unhold_test">
+                <param name="username" value="not-used"/>
+                <param name="password" value="not-used"/>
+                <param name="proxy" value="$${local_ip_v4}:61068"/>
+                <param name="register" value="false"/>
+                <param name="retry-seconds" value="30"/>
+                <param name="dtmf-type" value="rfc2833"/>
+                <variables>   
+                 <variable name="rtp_secure_media"  value="false"  direction="outbound"/>  
+                </variables>
+            </gateway>
+        </gateways>
+
+      <domains>
+        <domain name="all" alias="false" parse="true"/>
+      </domains>
+
+      <settings>
+        <param name="debug" value="1"/>
+        <param name="shutdown-on-fail" value="true"/>
+        <param name="p-asserted-id-parse" value="verbatim"/>
+        <param name="username" value="SignalWire-STACK"/>
+        <param name="user-agent-string" value="SignalWire STACK Unit Test"/>
+        <param name="sip-trace" value="yes"/>
+        <param name="sip-capture" value="no"/>
+        <param name="rfc2833-pt" value="101"/>
+        <param name="sip-port" value="61068"/>
+        <param name="dialplan" value="XML"/>
+        <param name="context" value="default"/>
+        <param name="dtmf-duration" value="2000"/>
+        <param name="inbound-codec-prefs" value="PCMU"/>
+        <param name="outbound-codec-prefs" value="PCMU"/>
+        <param name="rtp-timer-name" value="soft"/>
+        <param name="local-network-acl" value="localnet.auto"/>
+        <param name="manage-presence" value="false"/>
+        <param name="inbound-codec-negotiation" value="generous"/>
+        <param name="nonce-ttl" value="60"/>
+        <param name="inbound-late-negotiation" value="true"/>
+        <param name="inbound-zrtp-passthru" value="false"/>
+        <param name="rtp-ip" value="$${local_ip_v4}"/>
+        <param name="sip-ip" value="$${local_ip_v4}"/>
+        <param name="ext-rtp-ip" value="$${local_ip_v4}"/>
+        <param name="ext-sip-ip" value="$${local_ip_v4}"/>
+        <param name="rtp-timeout-sec" value="300"/>
+        <param name="rtp-hold-timeout-sec" value="1800"/>
+        <param name="session-timeout" value="600"/>
+        <param name="minimum-session-expires" value="90"/>
+        <param name="tls" value="false"/>
+      </settings>
+  </profile>
+
+    <profile name="internal">
+        <gateways>
+    </gateways>
+
+      <domains>
+        <domain name="all" alias="false" parse="true"/>
+      </domains>
+
+      <settings>
+        <param name="debug" value="1"/>
+        <param name="shutdown-on-fail" value="true"/>
+        <param name="p-asserted-id-parse" value="verbatim"/>
+        <param name="username" value="SignalWire-STACK"/>
+        <param name="user-agent-string" value="SignalWire STACK Unit Test"/>
+        <param name="sip-trace" value="yes"/>
+        <param name="sip-capture" value="no"/>
+        <param name="rfc2833-pt" value="101"/>
+        <param name="sip-port" value="61069"/>
+        <param name="dialplan" value="XML"/>
+        <param name="context" value="default"/>
+        <param name="dtmf-duration" value="2000"/>
+        <param name="inbound-codec-prefs" value="PCMU"/>
+        <param name="outbound-codec-prefs" value="PCMU"/>
+        <param name="rtp-timer-name" value="soft"/>
+        <param name="local-network-acl" value="localnet.auto"/>
+        <param name="manage-presence" value="false"/>
+        <param name="inbound-codec-negotiation" value="generous"/>
+        <param name="nonce-ttl" value="60"/>
+        <param name="inbound-late-negotiation" value="true"/>
+        <param name="inbound-zrtp-passthru" value="false"/>
+        <param name="rtp-ip" value="$${local_ip_v4}"/>
+        <param name="sip-ip" value="$${local_ip_v4}"/>
+        <param name="ext-rtp-ip" value="$${local_ip_v4}"/>
+        <param name="ext-sip-ip" value="$${local_ip_v4}"/>
+        <param name="rtp-timeout-sec" value="300"/>
+        <param name="rtp-hold-timeout-sec" value="1800"/>
+        <param name="session-timeout" value="600"/>
+        <param name="minimum-session-expires" value="90"/>
+        <param name="tls" value="false"/>
+      </settings>
+  </profile>
+
+  </profiles>
+   </configuration>
+
+   <configuration name="switch.conf" description="Switch">
+     <param name="rtp-start-port" value="20000"/>
+     <param name="rtp-end-port" value="30000"/>
+     <param name="threaded-system-exec" value="true"/>
+   </configuration>
+  </section>
+
+  <section name="dialplan" description="Regex/XML Dialplan">
+    <context name="default">
+               <extension name="one">
+                       <condition field="destination_number" expression="^\+15553332900$">
+                               <action application="set" data="absolute_codec_string=PCMU@20i"/>
+                               <action application="info"/>
+                               <action application="answer"/>
+                               <action application="park""/>
+                       </condition>
+               </extension>
+    </context>
+  </section>
+</document>
diff --git a/tests/unit/conf_hold/gw/holdtest.xml b/tests/unit/conf_hold/gw/holdtest.xml
new file mode 100644 (file)
index 0000000..d919ddb
--- /dev/null
@@ -0,0 +1,14 @@
+<include/>
+    <gateway name="hold_unhold_test">
+      <param name="username" value="not-used"/>
+      <param name="password" value="not-used"/>
+      <param name="proxy" value="127.0.0.1"/>
+      <param name="register" value="false"/>
+      <param name="retry-seconds" value="30"/>
+      <param name="dtmf-type" value="rfc2833"/>
+      <variables>   
+        <variable name="rtp_secure_media"  value="false"  direction="outbound"/>
+      </variables>
+    </gateway>
+</include>
+
diff --git a/tests/unit/switch_hold.c b/tests/unit/switch_hold.c
new file mode 100644 (file)
index 0000000..9da8ccf
--- /dev/null
@@ -0,0 +1,105 @@
+#include <switch.h>
+#include <test/switch_test.h>
+
+FST_CORE_DB_BEGIN("./conf_hold")
+{
+FST_SUITE_BEGIN(switch_hold)
+{
+       FST_SETUP_BEGIN()
+       {
+               fst_requires_module("mod_sofia");
+               fst_requires_module("mod_commands");
+       }
+       FST_SETUP_END()
+
+       FST_TEARDOWN_BEGIN()
+       {
+       }
+       FST_TEARDOWN_END()
+
+       FST_TEST_BEGIN(hold_unhold_restriction)
+       {
+               switch_core_session_t *session = NULL;
+               switch_status_t status;
+               switch_call_cause_t cause;
+
+               status = switch_ivr_originate(NULL, &session, &cause, "{ignore_early_media=true}sofia/gateway/hold_unhold_test/+15553332900", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL);
+               fst_requires(session);
+               fst_check(status == SWITCH_STATUS_SUCCESS);
+
+               if (session) {
+                       const char *uuid = switch_core_session_get_uuid(session);
+                       switch_channel_t *channel = NULL;
+
+                       channel = switch_core_session_get_channel(session);
+                       fst_requires(channel);
+
+                       if (uuid) {
+                               char *off_uuid = switch_mprintf("off %s", uuid);
+                               char *toggle_uuid = switch_mprintf("toggle %s", uuid);
+
+                               switch_stream_handle_t stream = { 0 };
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "firing the api.\n");
+
+                               SWITCH_STANDARD_STREAM(stream);
+                               switch_api_execute("uuid_hold", off_uuid, NULL, &stream);
+                               fst_check_string_equals(stream.data, "-ERR Operation failed\n");
+                               switch_safe_free(stream.data);
+                               switch_sleep(200000);
+
+                               SWITCH_STANDARD_STREAM(stream);
+                               switch_api_execute("uuid_hold", uuid, NULL, &stream);
+                               fst_check_string_equals(stream.data, "+OK Success\n");
+                               switch_safe_free(stream.data);
+                               switch_sleep(200000);
+
+                               SWITCH_STANDARD_STREAM(stream);
+                               switch_api_execute("uuid_hold", uuid, NULL, &stream);
+                               fst_check_string_equals(stream.data, "-ERR Operation failed\n");
+                               switch_safe_free(stream.data);
+                               switch_sleep(200000);
+
+                               SWITCH_STANDARD_STREAM(stream);
+                               switch_api_execute("uuid_hold", uuid, NULL, &stream);
+                               fst_check_string_equals(stream.data, "-ERR Operation failed\n");
+                               switch_safe_free(stream.data);
+                               switch_sleep(200000);
+
+                               SWITCH_STANDARD_STREAM(stream);
+                               switch_api_execute("uuid_hold", toggle_uuid, NULL, &stream);
+                               fst_check_string_equals(stream.data, "+OK Success\n");
+                               switch_safe_free(stream.data);
+                               switch_sleep(200000);
+
+                               SWITCH_STANDARD_STREAM(stream);
+                               switch_api_execute("uuid_hold", off_uuid, NULL, &stream);
+                               fst_check_string_equals(stream.data, "-ERR Operation failed\n");
+                               switch_safe_free(stream.data);
+                               switch_sleep(200000);
+
+                               SWITCH_STANDARD_STREAM(stream);
+                               switch_api_execute("uuid_hold", toggle_uuid, NULL, &stream);
+                               fst_check_string_equals(stream.data, "+OK Success\n");
+                               switch_safe_free(stream.data);
+                               switch_sleep(200000);
+
+                               SWITCH_STANDARD_STREAM(stream);
+                               switch_api_execute("uuid_hold", uuid, NULL, &stream);
+                               fst_check_string_equals(stream.data, "-ERR Operation failed\n");
+                               switch_safe_free(stream.data);
+                               switch_sleep(200000);
+
+                               switch_safe_free(off_uuid);
+                               switch_safe_free(toggle_uuid);
+                       }
+                       
+                       switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+                       switch_core_session_rwunlock(session);
+               }
+       }
+       FST_TEST_END()
+}
+FST_SUITE_END()
+}
+FST_CORE_END()
+