<p>N A has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/17695">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">app_sf: Add full tech-agnostic SF support<br><br>Adds tech-agnostic support for SF signaling<br>by adding SF sender and receiver applications<br>as well as Dial integration.<br><br>ASTERISK-29802 #close<br><br>Change-Id: I7ec50752e9a661af639425e5d1e339f17411bcad<br>---<br>M apps/app_dial.c<br>M apps/app_mf.c<br>A apps/app_sf.c<br>M include/asterisk/app.h<br>M main/app.c<br>5 files changed, 654 insertions(+), 6 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/95/17695/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_dial.c b/apps/app_dial.c</span><br><span>index f073af3..9b5782c 100644</span><br><span>--- a/apps/app_dial.c</span><br><span>+++ b/apps/app_dial.c</span><br><span>@@ -158,6 +158,8 @@</span><br><span>                                         <argument name="progress" /></span><br><span>                                         <argument name="mfprogress" /></span><br><span>                                       <argument name="mfwink" /></span><br><span style="color: hsl(120, 100%, 40%);">+                                    <argument name="sfprogress" /></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <argument name="sfwink" /></span><br><span>                                   <para>Send the specified DTMF strings <emphasis>after</emphasis> the called</span><br><span>                                        party has answered, but before the call gets bridged.  The</span><br><span>                                   <replaceable>called</replaceable> DTMF string is sent to the called party, and the</span><br><span>@@ -170,6 +172,11 @@</span><br><span>                                        If <replaceable>mfwink</replaceable> is specified, its MF is sent</span><br><span>                                        to the called party immediately after receiving a <literal>WINK</literal> message.</para></span><br><span>                                  <para>See <literal>SendMF</literal> for valid digits.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                  <para>If <replaceable>sfprogress</replaceable> is specified, its SF is sent</span><br><span style="color: hsl(120, 100%, 40%);">+                                 to the called party immediately after receiving a <literal>PROGRESS</literal> message.</span><br><span style="color: hsl(120, 100%, 40%);">+                                    If <replaceable>sfwink</replaceable> is specified, its SF is sent</span><br><span style="color: hsl(120, 100%, 40%);">+                                 to the called party immediately after receiving a <literal>WINK</literal> message.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                   <para>See <literal>SendSF</literal> for valid digits.</para></span><br><span>                                 </option></span><br><span>                              <option name="E"></span><br><span>                                    <para>Enable echoing of sent MF or SF digits back to caller (e.g. "hearpulsing").</span><br><span>@@ -1214,6 +1221,7 @@</span><br><span>    struct privacy_args *pa,</span><br><span>     const struct cause_args *num_in, int *result, char *dtmf_progress,</span><br><span>   char *mf_progress, char *mf_wink,</span><br><span style="color: hsl(120, 100%, 40%);">+     char *sf_progress, char *sf_wink,</span><br><span>    const int hearpulsing,</span><br><span>       const int ignore_cc,</span><br><span>         struct ast_party_id *forced_clid, struct ast_party_id *stored_clid,</span><br><span>@@ -1580,6 +1588,14 @@</span><br><span>                                                         ast_mf_stream(c, (hearpulsing ? NULL : in),</span><br><span>                                                  (hearpulsing ? in : NULL), mf_progress, 50, 55, 120, 65, 0);</span><br><span>                                                 }</span><br><span style="color: hsl(120, 100%, 40%);">+                                             if (!ast_strlen_zero(sf_progress)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                  ast_verb(3,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           "Sending SF '%s' to %s as result of "</span><br><span style="color: hsl(120, 100%, 40%);">+                                                               "receiving a PROGRESS message.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                                          sf_progress, (hearpulsing ? "parties" : "called party"));</span><br><span style="color: hsl(120, 100%, 40%);">+                                                 ast_sf_stream(c, (hearpulsing ? NULL : in),</span><br><span style="color: hsl(120, 100%, 40%);">+                                                   (hearpulsing ? in : NULL), sf_progress, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                                                }</span><br><span>                                            if (!ast_strlen_zero(dtmf_progress)) {</span><br><span>                                                       ast_verb(3,</span><br><span>                                                          "Sending DTMF '%s' to the called party as result of "</span><br><span>@@ -1602,6 +1618,14 @@</span><br><span>                                                     ast_mf_stream(c, (hearpulsing ? NULL : in),</span><br><span>                                                  (hearpulsing ? in : NULL), mf_wink, 50, 55, 120, 65, 0);</span><br><span>                                             }</span><br><span style="color: hsl(120, 100%, 40%);">+                                             if (!ast_strlen_zero(sf_wink)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                      ast_verb(3,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           "Sending SF '%s' to %s as result of "</span><br><span style="color: hsl(120, 100%, 40%);">+                                                               "receiving a WINK message.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                                              sf_wink, (hearpulsing ? "parties" : "called party"));</span><br><span style="color: hsl(120, 100%, 40%);">+                                                     ast_sf_stream(c, (hearpulsing ? NULL : in),</span><br><span style="color: hsl(120, 100%, 40%);">+                                                   (hearpulsing ? in : NULL), sf_wink, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                                            }</span><br><span>                                    }</span><br><span>                                    break;</span><br><span>                               case AST_CONTROL_VIDUPDATE:</span><br><span>@@ -2278,6 +2302,7 @@</span><br><span>  struct timeval calldurationlimit = { 0, };</span><br><span>   char *dtmfcalled = NULL, *dtmfcalling = NULL, *dtmf_progress = NULL;</span><br><span>         char *mf_progress = NULL, *mf_wink = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    char *sf_progress = NULL, *sf_wink = NULL;</span><br><span>   struct privacy_args pa = {</span><br><span>           .sentringing = 0,</span><br><span>            .privdb_val = 0,</span><br><span>@@ -2413,11 +2438,13 @@</span><br><span>   }</span><br><span> </span><br><span>        if (ast_test_flag64(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) {</span><br><span style="color: hsl(0, 100%, 40%);">-         mf_wink = opt_args[OPT_ARG_SENDDTMF];</span><br><span style="color: hsl(0, 100%, 40%);">-           dtmfcalled = strsep(&mf_wink, ":");</span><br><span style="color: hsl(0, 100%, 40%);">-               dtmfcalling = strsep(&mf_wink, ":");</span><br><span style="color: hsl(0, 100%, 40%);">-              dtmf_progress = strsep(&mf_wink, ":");</span><br><span style="color: hsl(0, 100%, 40%);">-            mf_progress = strsep(&mf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+            sf_wink = opt_args[OPT_ARG_SENDDTMF];</span><br><span style="color: hsl(120, 100%, 40%);">+         dtmfcalled = strsep(&sf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+             dtmfcalling = strsep(&sf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+            dtmf_progress = strsep(&sf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+          mf_progress = strsep(&sf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+            mf_wink = strsep(&sf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+                sf_progress = strsep(&sf_wink, ":");</span><br><span>   }</span><br><span> </span><br><span>        if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {</span><br><span>@@ -2894,7 +2921,8 @@</span><br><span>  }</span><br><span> </span><br><span>        peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result,</span><br><span style="color: hsl(0, 100%, 40%);">-              dtmf_progress, mf_progress, mf_wink, (ast_test_flag64(&opts, OPT_HEARPULSING) ? 1 : 0),</span><br><span style="color: hsl(120, 100%, 40%);">+           dtmf_progress, mf_progress, mf_wink, sf_progress, sf_wink,</span><br><span style="color: hsl(120, 100%, 40%);">+            (ast_test_flag64(&opts, OPT_HEARPULSING) ? 1 : 0),</span><br><span>               ignore_cc, &forced_clid, &stored_clid, &config);</span><br><span> </span><br><span>     if (!peer) {</span><br><span>diff --git a/apps/app_mf.c b/apps/app_mf.c</span><br><span>index 3caf8a4..336e8aa 100644</span><br><span>--- a/apps/app_mf.c</span><br><span>+++ b/apps/app_mf.c</span><br><span>@@ -106,6 +106,7 @@</span><br><span>          <see-also></span><br><span>                     <ref type="application">Read</ref></span><br><span>                     <ref type="application">SendMF</ref></span><br><span style="color: hsl(120, 100%, 40%);">+                    <ref type="application">ReceiveSF</ref></span><br><span>                </see-also></span><br><span>    </application></span><br><span>         <application name="SendMF" language="en_US"></span><br><span>@@ -142,6 +143,7 @@</span><br><span>                 </description></span><br><span>                 <see-also></span><br><span>                     <ref type="application">ReceiveMF</ref></span><br><span style="color: hsl(120, 100%, 40%);">+                 <ref type="application">SendSF</ref></span><br><span>                   <ref type="application">SendDTMF</ref></span><br><span>                 </see-also></span><br><span>    </application></span><br><span>diff --git a/apps/app_sf.c b/apps/app_sf.c</span><br><span>new file mode 100644</span><br><span>index 0000000..10849d4</span><br><span>--- /dev/null</span><br><span>+++ b/apps/app_sf.c</span><br><span>@@ -0,0 +1,438 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Naveen Albert</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief SF sender and receiver applications</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \ingroup applications</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>extended</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/file.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/pbx.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/channel.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/dsp.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/app.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/indications.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/conversions.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** DOCUMENTATION</span><br><span style="color: hsl(120, 100%, 40%);">+ <application name="ReceiveSF" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+             <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                      Detects SF digits on a channel and saves them to a variable.</span><br><span style="color: hsl(120, 100%, 40%);">+          </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+             <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+                        <parameter name="variable" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+                         <para>The input digits will be stored in the given</span><br><span style="color: hsl(120, 100%, 40%);">+                              <replaceable>variable</replaceable> name.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                    </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+                    <parameter name="digits" required="false"></span><br><span style="color: hsl(120, 100%, 40%);">+                          <para>Maximum number of digits to read. Default is unlimited.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                      </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+                    <parameter name="timeout"></span><br><span style="color: hsl(120, 100%, 40%);">+                            <para>The number of seconds to wait for all digits, if greater</span><br><span style="color: hsl(120, 100%, 40%);">+                          than <literal>0</literal>. Can be floating point. Default</span><br><span style="color: hsl(120, 100%, 40%);">+                         is no timeout.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                   </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+                    <parameter name="frequency"></span><br><span style="color: hsl(120, 100%, 40%);">+                          <para>The frequency for which to detect pulsed digits.</span><br><span style="color: hsl(120, 100%, 40%);">+                          Default is 2600 Hz.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                      </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+                    <parameter name="options"></span><br><span style="color: hsl(120, 100%, 40%);">+                            <optionlist></span><br><span style="color: hsl(120, 100%, 40%);">+                                    <option name="d"></span><br><span style="color: hsl(120, 100%, 40%);">+                                             <para>Delay audio by a frame to try to extra quelch.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                       </option></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <option name="e"></span><br><span style="color: hsl(120, 100%, 40%);">+                                             <para>Allow receiving extra pulses 11 through 16.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                  </option></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <option name="m"></span><br><span style="color: hsl(120, 100%, 40%);">+                                             <para>Mute conference.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                     </option></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <option name="q"></span><br><span style="color: hsl(120, 100%, 40%);">+                                             <para>Quelch SF from in-band.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                      </option></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <option name="r"></span><br><span style="color: hsl(120, 100%, 40%);">+                                             <para>"Radio" mode (relaxed SF).</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                 </option></span><br><span style="color: hsl(120, 100%, 40%);">+                               </optionlist></span><br><span style="color: hsl(120, 100%, 40%);">+                   </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+            </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+               <description></span><br><span style="color: hsl(120, 100%, 40%);">+                   <para>Reads SF digits from the user in to the given</span><br><span style="color: hsl(120, 100%, 40%);">+                     <replaceable>variable</replaceable>.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                 <para>This application does not automatically answer the channel and</span><br><span style="color: hsl(120, 100%, 40%);">+                    should be preceded with <literal>Answer</literal> or</span><br><span style="color: hsl(120, 100%, 40%);">+                      <literal>Progress</literal> as needed.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                       <variablelist></span><br><span style="color: hsl(120, 100%, 40%);">+                          <variable name="RECEIVESFSTATUS"></span><br><span style="color: hsl(120, 100%, 40%);">+                                     <para>This is the status of the read operation.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                                    <value name="START" /></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <value name="ERROR" /></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <value name="HANGUP" /></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <value name="MAXDIGITS" /></span><br><span style="color: hsl(120, 100%, 40%);">+                                    <value name="TIMEOUT" /></span><br><span style="color: hsl(120, 100%, 40%);">+                              </variable></span><br><span style="color: hsl(120, 100%, 40%);">+                     </variablelist></span><br><span style="color: hsl(120, 100%, 40%);">+         </description></span><br><span style="color: hsl(120, 100%, 40%);">+          <see-also></span><br><span style="color: hsl(120, 100%, 40%);">+                      <ref type="application">ReceiveMF</ref></span><br><span style="color: hsl(120, 100%, 40%);">+                 <ref type="application">SendMF</ref></span><br><span style="color: hsl(120, 100%, 40%);">+                    <ref type="application">SendSF</ref></span><br><span style="color: hsl(120, 100%, 40%);">+                    <ref type="application">Read</ref></span><br><span style="color: hsl(120, 100%, 40%);">+              </see-also></span><br><span style="color: hsl(120, 100%, 40%);">+     </application></span><br><span style="color: hsl(120, 100%, 40%);">+  <application name="SendSF" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+                <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                      Sends arbitrary SF digits on the current or specified channel.</span><br><span style="color: hsl(120, 100%, 40%);">+                </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+             <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+                        <parameter name="digits" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+                           <para>List of digits 0-9 to send; w for a half-second pause,</span><br><span style="color: hsl(120, 100%, 40%);">+                            also f or F for a flash-hook if the channel supports flash-hook,</span><br><span style="color: hsl(120, 100%, 40%);">+                              h or H for 250 ms of 2600 Hz, and W for a wink if the channel</span><br><span style="color: hsl(120, 100%, 40%);">+                         supports wink.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                   </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+                    <parameter name="frequency" required="false"></span><br><span style="color: hsl(120, 100%, 40%);">+                               <para>Frequency to use. (defaults to 2600 Hz).</para></span><br><span style="color: hsl(120, 100%, 40%);">+                     </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+                    <parameter name="channel" required="false"></span><br><span style="color: hsl(120, 100%, 40%);">+                         <para>Channel where digits will be played</para></span><br><span style="color: hsl(120, 100%, 40%);">+                  </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+            </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+               <description></span><br><span style="color: hsl(120, 100%, 40%);">+                   <para>It will send all digits or terminate if it encounters an error.</para></span><br><span style="color: hsl(120, 100%, 40%);">+              </description></span><br><span style="color: hsl(120, 100%, 40%);">+          <see-also></span><br><span style="color: hsl(120, 100%, 40%);">+                      <ref type="application">SendDTMF</ref></span><br><span style="color: hsl(120, 100%, 40%);">+                  <ref type="application">SendMF</ref></span><br><span style="color: hsl(120, 100%, 40%);">+                    <ref type="application">ReceiveMF</ref></span><br><span style="color: hsl(120, 100%, 40%);">+                 <ref type="application">ReceiveSF</ref></span><br><span style="color: hsl(120, 100%, 40%);">+         </see-also></span><br><span style="color: hsl(120, 100%, 40%);">+     </application></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum read_option_flags {</span><br><span style="color: hsl(120, 100%, 40%);">+       OPT_DELAY = (1 << 0),</span><br><span style="color: hsl(120, 100%, 40%);">+   OPT_MUTE = (1 << 1),</span><br><span style="color: hsl(120, 100%, 40%);">+    OPT_QUELCH = (1 << 2),</span><br><span style="color: hsl(120, 100%, 40%);">+  OPT_RELAXED = (1 << 3),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_EXTRAPULSES = (1 << 4),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_APP_OPTIONS(read_app_options, {</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_APP_OPTION('d', OPT_DELAY),</span><br><span style="color: hsl(120, 100%, 40%);">+       AST_APP_OPTION('e', OPT_EXTRAPULSES),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('m', OPT_MUTE),</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_APP_OPTION('q', OPT_QUELCH),</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_APP_OPTION('r', OPT_RELAXED),</span><br><span style="color: hsl(120, 100%, 40%);">+});</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const char *readsf_name = "ReceiveSF";</span><br><span style="color: hsl(120, 100%, 40%);">+static const char sendsf_name[] = "SendSF";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int read_sf_digits(struct ast_channel *chan, char *buf, int buflen, int timeout, int maxdigits, int freq, int features, int extrapulses) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Bell System Technical Journal 39 (Nov. 1960) */</span><br><span style="color: hsl(120, 100%, 40%);">+    #define SF_MIN_OFF 25</span><br><span style="color: hsl(120, 100%, 40%);">+ #define SF_ON 67</span><br><span style="color: hsl(120, 100%, 40%);">+      #define SF_BETWEEN 600</span><br><span style="color: hsl(120, 100%, 40%);">+        #define SF_MIN_DETECT 50</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_dsp *dsp = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_frame *frame = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct timeval start, pulsetimer, digittimer;</span><br><span style="color: hsl(120, 100%, 40%);">+ int remaining_time = timeout;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *str = buf;</span><br><span style="color: hsl(120, 100%, 40%);">+      int hits = 0, digits_read = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        unsigned short int sf_on = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!(dsp = ast_dsp_new())) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_WARNING, "Unable to allocate DSP!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "ERROR");</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_dsp_set_features(dsp, DSP_FEATURE_FREQ_DETECT);</span><br><span style="color: hsl(120, 100%, 40%);">+   /* tolerance is 46 to 76% make break at 8 to 12 pps */</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_dsp_set_freqmode(dsp, freq, SF_MIN_DETECT, 16, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      start = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+  *str = 0; /* start with empty output buffer */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      while (timeout == 0 || remaining_time > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+               if (timeout > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 remaining_time = ast_remaining_ms(start, timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (remaining_time <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "TIMEOUT");</span><br><span style="color: hsl(120, 100%, 40%);">+                            break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (digits_read >= (buflen - 1)) { /* we don't have room to store any more digits (very unlikely to happen for a legitimate reason) */</span><br><span style="color: hsl(120, 100%, 40%);">+                 /* This result will probably not be usable, so status should not be START */</span><br><span style="color: hsl(120, 100%, 40%);">+                  pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "MAXDIGITS");</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (ast_waitfor(chan, 1000) > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 frame = ast_read(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (!frame) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+                           pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "HANGUP");</span><br><span style="color: hsl(120, 100%, 40%);">+                             break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        } else if (frame->frametype == AST_FRAME_VOICE) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          frame = ast_dsp_process(chan, dsp, frame);</span><br><span style="color: hsl(120, 100%, 40%);">+                            if (frame->frametype == AST_FRAME_DTMF) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                  char result = frame->subclass.integer;</span><br><span style="color: hsl(120, 100%, 40%);">+                                     if (result == 'q') {</span><br><span style="color: hsl(120, 100%, 40%);">+                                          sf_on = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                                            pulsetimer = ast_tvnow(); /* reset the pulse timer */</span><br><span style="color: hsl(120, 100%, 40%);">+                                         /* now, we need at least a 33ms pause to register the pulse */</span><br><span style="color: hsl(120, 100%, 40%);">+                                        }</span><br><span style="color: hsl(120, 100%, 40%);">+                             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                                      if (sf_on) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                          int timeleft = ast_remaining_ms(pulsetimer, SF_MIN_OFF);</span><br><span style="color: hsl(120, 100%, 40%);">+                                              if (timeleft <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                       sf_on = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                                                    /* The pulse needs to end no more than 30ms after we detected it */</span><br><span style="color: hsl(120, 100%, 40%);">+                                                   if (timeleft > -30) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                              hits++;</span><br><span style="color: hsl(120, 100%, 40%);">+                                                               digittimer = ast_tvnow(); /* reset the digit timer */</span><br><span style="color: hsl(120, 100%, 40%);">+                                                         ast_debug(5, "Detected SF pulse (pulse #%d)\n", hits);</span><br><span style="color: hsl(120, 100%, 40%);">+                                                              if (!(dsp = ast_dsp_new())) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                  pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "ERROR");</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                      ast_frfree(frame);</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                    return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+                                                            }</span><br><span style="color: hsl(120, 100%, 40%);">+                                                             ast_dsp_set_features(dsp, DSP_FEATURE_FREQ_DETECT);</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           ast_dsp_set_freqmode(dsp, freq, SF_MIN_DETECT, 16, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                                                        } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                              ast_debug(5, "SF noise, ignoring, time elapsed was %d ms\n", timeleft);</span><br><span style="color: hsl(120, 100%, 40%);">+                                                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                                             }</span><br><span style="color: hsl(120, 100%, 40%);">+                                     } else if (hits > 0 && ast_remaining_ms(digittimer, SF_BETWEEN) <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                         /* has the digit finished? */</span><br><span style="color: hsl(120, 100%, 40%);">+                                         ast_debug(2, "Received SF digit: %d\n", hits);</span><br><span style="color: hsl(120, 100%, 40%);">+                                              digits_read++;</span><br><span style="color: hsl(120, 100%, 40%);">+                                                if (hits > 10) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                   if (extrapulses) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                            /* dahdi-base.c translates 11 to * and 12 to # */</span><br><span style="color: hsl(120, 100%, 40%);">+                                                             if (hits == 11) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                     hits = '*';</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           } else if (hits == 12) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                      hits = '#';</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           } else if (hits == 13) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                      hits = 'D';</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           } else if (hits == 14) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                      hits = 'C';</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           } else if (hits == 15) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                      hits = 'B';</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           } else if (hits == 16) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                      hits = 'A';</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                      ast_debug(3, "Got %d SF pulses, is someone playing with the phone?\n", hits);</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                       hits = 'A';</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           }</span><br><span style="color: hsl(120, 100%, 40%);">+                                                             *str++ = hits;</span><br><span style="color: hsl(120, 100%, 40%);">+                                                        } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                              ast_debug(2, "Got more than 10 pulses, truncating to 10\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                hits = 0; /* 10 dial pulses = digit 0 */</span><br><span style="color: hsl(120, 100%, 40%);">+                                                              *str++ = hits + '0';</span><br><span style="color: hsl(120, 100%, 40%);">+                                                          }</span><br><span style="color: hsl(120, 100%, 40%);">+                                             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                      if (hits == 10) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                             hits = 0; /* 10 dial pulses = digit 0 */</span><br><span style="color: hsl(120, 100%, 40%);">+                                                      }</span><br><span style="color: hsl(120, 100%, 40%);">+                                                     *str++ = hits + '0';</span><br><span style="color: hsl(120, 100%, 40%);">+                                          }</span><br><span style="color: hsl(120, 100%, 40%);">+                                             *str = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                                             hits = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                                             if (maxdigits > 0 && digits_read >= maxdigits) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                        pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "START");</span><br><span style="color: hsl(120, 100%, 40%);">+                                                      ast_frfree(frame);</span><br><span style="color: hsl(120, 100%, 40%);">+                                                    break;</span><br><span style="color: hsl(120, 100%, 40%);">+                                                }</span><br><span style="color: hsl(120, 100%, 40%);">+                                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                             }</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "HANGUP");</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (dsp) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_dsp_free(dsp);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, remaining_time: %d }\n", ast_channel_name(chan), timeout, remaining_time);</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int read_sf_exec(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+#define BUFFER_SIZE 256</span><br><span style="color: hsl(120, 100%, 40%);">+       char tmp[BUFFER_SIZE] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ double tosec;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags flags = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+ char *argcopy = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int features = 0, digits = 0, to = 0, freq = 2600;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_DECLARE_APP_ARGS(arglist,</span><br><span style="color: hsl(120, 100%, 40%);">+         AST_APP_ARG(variable);</span><br><span style="color: hsl(120, 100%, 40%);">+                AST_APP_ARG(digits);</span><br><span style="color: hsl(120, 100%, 40%);">+          AST_APP_ARG(timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+         AST_APP_ARG(freq);</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_APP_ARG(options);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (ast_strlen_zero(data)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_WARNING, "ReceiveSF requires an argument (variable)\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   argcopy = ast_strdupa(data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_STANDARD_APP_ARGS(arglist, argcopy);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!ast_strlen_zero(arglist.options)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_app_parse_options(read_app_options, &flags, NULL, arglist.options);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ast_strlen_zero(arglist.timeout)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              tosec = atof(arglist.timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (tosec <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  to = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+               } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      to = tosec * 1000.0;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ast_strlen_zero(arglist.digits) && (ast_str_to_int(arglist.digits, &digits) || digits <= 0)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_WARNING, "Invalid number of digits: %s\n", arglist.digits);</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ast_strlen_zero(arglist.freq) && (ast_str_to_int(arglist.freq, &freq) || freq <= 0)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_WARNING, "Invalid freq: %s\n", arglist.freq);</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_strlen_zero(arglist.variable)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_WARNING, "Invalid! Usage: ReceiveSF(variable[,timeout][,option])\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_test_flag(&flags, OPT_DELAY)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           features |= DSP_DIGITMODE_MUTEMAX;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_test_flag(&flags, OPT_MUTE)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            features |= DSP_DIGITMODE_MUTECONF;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ast_test_flag(&flags, OPT_QUELCH)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         features |= DSP_DIGITMODE_NOQUELCH;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_test_flag(&flags, OPT_RELAXED)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         features |= DSP_DIGITMODE_RELAXDTMF;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   read_sf_digits(chan, tmp, BUFFER_SIZE, to, digits, freq, features, ast_test_flag(&flags, OPT_EXTRAPULSES));</span><br><span style="color: hsl(120, 100%, 40%);">+       pbx_builtin_setvar_helper(chan, arglist.variable, tmp);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!ast_strlen_zero(tmp)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_verb(3, "SF digits received: '%s'\n", tmp);</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_verb(3, "No SF digits received.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int sendsf_exec(struct ast_channel *chan, const char *vdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        int res;</span><br><span style="color: hsl(120, 100%, 40%);">+      char *data;</span><br><span style="color: hsl(120, 100%, 40%);">+   int frequency = 2600;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan_found = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_channel *chan_dest = chan;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan_autoservice = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_APP_ARG(digits);</span><br><span style="color: hsl(120, 100%, 40%);">+          AST_APP_ARG(frequency);</span><br><span style="color: hsl(120, 100%, 40%);">+               AST_APP_ARG(channel);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (ast_strlen_zero(vdata)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_WARNING, "SendSF requires an argument\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   data = ast_strdupa(vdata);</span><br><span style="color: hsl(120, 100%, 40%);">+    AST_STANDARD_APP_ARGS(args, data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (ast_strlen_zero(args.digits)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_WARNING, "The digits argument is required (0-9,wf)\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!ast_strlen_zero(args.frequency) && (ast_str_to_int(args.frequency, &frequency) || frequency < 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_WARNING, "Invalid duration: %s\n", args.frequency);</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!ast_strlen_zero(args.channel)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         chan_found = ast_channel_get_by_name(args.channel);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!chan_found) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    ast_log(LOG_WARNING, "No such channel: %s\n", args.channel);</span><br><span style="color: hsl(120, 100%, 40%);">+                        return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             chan_dest = chan_found;</span><br><span style="color: hsl(120, 100%, 40%);">+               if (chan_found != chan) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     chan_autoservice = chan;</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     res = ast_sf_stream(chan_dest, chan_autoservice, NULL, args.digits, frequency, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_channel_cleanup(chan_found);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return chan_autoservice ? 0 : res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    res = ast_unregister_application(readsf_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        res |= ast_unregister_application(sendsf_name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    res = ast_register_application_xml(readsf_name, read_sf_exec);</span><br><span style="color: hsl(120, 100%, 40%);">+        res |= ast_register_application_xml(sendsf_name, sendsf_exec);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "SF Sender and Receiver Applications");</span><br><span>diff --git a/include/asterisk/app.h b/include/asterisk/app.h</span><br><span>index 55b7386..19f7a3f 100644</span><br><span>--- a/include/asterisk/app.h</span><br><span>+++ b/include/asterisk/app.h</span><br><span>@@ -942,6 +942,31 @@</span><br><span> void ast_unreplace_sigchld(void);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Send a string of SF digits to a channel</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan    The channel that will receive the SF digits</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param peer    (optional) Peer channel that will be autoserviced while the</span><br><span style="color: hsl(120, 100%, 40%);">+ *                primary channel is receiving SF</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan2   A second channel that will simultaneously receive SF digits.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                This option may only be used if is_external is 0.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param digits  This is a string of characters representing the SF digits</span><br><span style="color: hsl(120, 100%, 40%);">+ *                to be sent to the channel.  Valid characters are</span><br><span style="color: hsl(120, 100%, 40%);">+ *                "0123456789".  Note: You can pass arguments 'f' or</span><br><span style="color: hsl(120, 100%, 40%);">+ *                'F', if you want to Flash the channel (if supported by the</span><br><span style="color: hsl(120, 100%, 40%);">+ *                channel), or 'w' or 'W' to add a wink (if supported by the</span><br><span style="color: hsl(120, 100%, 40%);">+ *                channel).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param frequency  The frequency to use for signaling. 0 can be specified for</span><br><span style="color: hsl(120, 100%, 40%);">+ *                the default, which is 2600 Hz.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param is_external 1 if called by a thread that is not the channel's media</span><br><span style="color: hsl(120, 100%, 40%);">+ *                handler thread, 0 if called by the channel's media handler</span><br><span style="color: hsl(120, 100%, 40%);">+ *                thread.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 on success.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 on failure or a channel hung up.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sf_stream(struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *chan2, const char *digits, int frequency, int is_external);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Send a string of MF digits to a channel</span><br><span>  *</span><br><span>  * \param chan    The channel that will receive the MF digits.</span><br><span>diff --git a/main/app.c b/main/app.c</span><br><span>index 5be8457..05f9771 100644</span><br><span>--- a/main/app.c</span><br><span>+++ b/main/app.c</span><br><span>@@ -830,6 +830,145 @@</span><br><span>     return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int sf_stream(struct ast_channel *chan, struct ast_channel *chan2, const char *digits, int frequency, int is_external)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Bell System Technical Journal 39 (Nov. 1960) */</span><br><span style="color: hsl(120, 100%, 40%);">+    #define SF_ON 67</span><br><span style="color: hsl(120, 100%, 40%);">+      #define SF_OFF 33</span><br><span style="color: hsl(120, 100%, 40%);">+     #define SF_BETWEEN 600</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+      int res;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_silence_generator *silgen = NULL, *silgen2 = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *freq;</span><br><span style="color: hsl(120, 100%, 40%);">+   int (*my_sleep)(struct ast_channel *chan, int ms);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (is_external) {</span><br><span style="color: hsl(120, 100%, 40%);">+            my_sleep = external_sleep;</span><br><span style="color: hsl(120, 100%, 40%);">+    } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              my_sleep = ast_safe_sleep;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Need a quiet time before sending digits. */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (ast_opt_transmit_silence) {</span><br><span style="color: hsl(120, 100%, 40%);">+               silgen = ast_channel_start_silence_generator(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  silgen2 = ast_channel_start_silence_generator(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_autoservice_start(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     res = my_sleep(chan, 100);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_autoservice_stop(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+            goto sf_stream_cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   freq = ast_malloc(32);</span><br><span style="color: hsl(120, 100%, 40%);">+        /* pauses need to send audio, so send 0 Hz */</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(freq, 31, "%d/%d,%d/%d", frequency, SF_ON, 0, SF_OFF);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (ptr = digits; *ptr; ptr++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (*ptr == 'w') {</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* 'w' -- wait half a second */</span><br><span style="color: hsl(120, 100%, 40%);">+                       res = my_sleep(chan, 500);</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (*ptr == 'h' || *ptr == 'H') {</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* 'h' -- 2600 Hz for half a second, but</span><br><span style="color: hsl(120, 100%, 40%);">+                              only to far end of trunk, not near end */</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_playtones_start(chan, 0, "2600", 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_playtones_start(chan2, 0, "0", 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_autoservice_start(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+                 }</span><br><span style="color: hsl(120, 100%, 40%);">+                     res = my_sleep(chan, 250);</span><br><span style="color: hsl(120, 100%, 40%);">+                    ast_senddigit_mf_end(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_autoservice_stop(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_senddigit_mf_end(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+                  }</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (strchr("0123456789*#ABCDabcdwWfF", *ptr)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (*ptr == 'f' || *ptr == 'F') {</span><br><span style="color: hsl(120, 100%, 40%);">+                             /* ignore return values if not supported by channel */</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_indicate(chan, AST_CONTROL_FLASH);</span><br><span style="color: hsl(120, 100%, 40%);">+                        } else if (*ptr == 'W') {</span><br><span style="color: hsl(120, 100%, 40%);">+                             /* ignore return values if not supported by channel */</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_indicate(chan, AST_CONTROL_WINK);</span><br><span style="color: hsl(120, 100%, 40%);">+                 } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              /* Character represents valid SF */</span><br><span style="color: hsl(120, 100%, 40%);">+                           int beeps;</span><br><span style="color: hsl(120, 100%, 40%);">+                            if (*ptr == '*') {</span><br><span style="color: hsl(120, 100%, 40%);">+                                    beeps = 11;</span><br><span style="color: hsl(120, 100%, 40%);">+                           } else if (*ptr == '#') {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     beeps = 12;</span><br><span style="color: hsl(120, 100%, 40%);">+                           } else if (*ptr == 'D') {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     beeps = 13;</span><br><span style="color: hsl(120, 100%, 40%);">+                           } else if (*ptr == 'C') {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     beeps = 14;</span><br><span style="color: hsl(120, 100%, 40%);">+                           } else if (*ptr == 'B') {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     beeps = 15;</span><br><span style="color: hsl(120, 100%, 40%);">+                           } else if (*ptr == 'A') {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     beeps = 16;</span><br><span style="color: hsl(120, 100%, 40%);">+                           } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                                      beeps = (*ptr == '0') ? 10 : *ptr - '0';</span><br><span style="color: hsl(120, 100%, 40%);">+                              }</span><br><span style="color: hsl(120, 100%, 40%);">+                             while (beeps-- > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                      ast_playtones_start(chan, 0, freq, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                                        if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                          ast_playtones_start(chan2, 0, freq, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                                               ast_autoservice_start(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+                                 }</span><br><span style="color: hsl(120, 100%, 40%);">+                                     res = my_sleep(chan, SF_ON + SF_OFF);</span><br><span style="color: hsl(120, 100%, 40%);">+                                 ast_senddigit_mf_end(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+                                   if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                          ast_autoservice_stop(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+                                          ast_senddigit_mf_end(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+                                  }</span><br><span style="color: hsl(120, 100%, 40%);">+                                     if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                            break;</span><br><span style="color: hsl(120, 100%, 40%);">+                                        }</span><br><span style="color: hsl(120, 100%, 40%);">+                             }</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     /* pause between digits */</span><br><span style="color: hsl(120, 100%, 40%);">+                    ast_playtones_start(chan, 0, "0", 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_playtones_start(chan2, 0, "0", 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_autoservice_start(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+                 }</span><br><span style="color: hsl(120, 100%, 40%);">+                     res = my_sleep(chan, SF_BETWEEN);</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_autoservice_stop(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_senddigit_mf_end(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+                  }</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_senddigit_mf_end(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_log(LOG_WARNING, "Illegal SF character '%c' in string. (0-9A-DwWfFhH allowed)\n", *ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_free(freq);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+sf_stream_cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ if (silgen) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_channel_stop_silence_generator(chan, silgen);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (silgen2) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_channel_stop_silence_generator(chan2, silgen2);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int mf_stream(struct ast_channel *chan, struct ast_channel *chan2, const char *digits, int between, unsigned int duration,</span><br><span>        unsigned int durationkp, unsigned int durationst, int is_external)</span><br><span> {</span><br><span>@@ -1007,6 +1146,22 @@</span><br><span>     return res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sf_stream(struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *chan2, const char *digits, int frequency, int is_external)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (frequency <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              frequency = 2600;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!is_external && !chan2 && peer && ast_autoservice_start(peer)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     res = sf_stream(chan, chan2, digits, frequency, is_external);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!is_external && !chan2 && peer && ast_autoservice_stop(peer)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_mf_stream(struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *chan2, const char *digits,</span><br><span>     int between, unsigned int duration, unsigned int durationkp, unsigned int durationst, int is_external)</span><br><span> {</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/17695">change 17695</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/17695"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 18 </div>
<div style="display:none"> Gerrit-Change-Id: I7ec50752e9a661af639425e5d1e339f17411bcad </div>
<div style="display:none"> Gerrit-Change-Number: 17695 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: N A <mail@interlinked.x10host.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>