app_mf.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2021, Naveen Albert
  5. *
  6. * Naveen Albert <asterisk@phreaknet.org>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*! \file
  19. *
  20. * \brief MF sender and receiver applications
  21. *
  22. * \author Naveen Albert <asterisk@phreaknet.org>
  23. *
  24. * \ingroup applications
  25. */
  26. /*** MODULEINFO
  27. <support_level>extended</support_level>
  28. ***/
  29. #include "asterisk.h"
  30. #include "asterisk/file.h"
  31. #include "asterisk/pbx.h"
  32. #include "asterisk/channel.h"
  33. #include "asterisk/dsp.h"
  34. #include "asterisk/app.h"
  35. #include "asterisk/module.h"
  36. #include "asterisk/indications.h"
  37. #include "asterisk/conversions.h"
  38. /*** DOCUMENTATION
  39. <application name="ReceiveMF" language="en_US">
  40. <since>
  41. <version>16.21.0</version>
  42. <version>18.7.0</version>
  43. <version>19.0.0</version>
  44. </since>
  45. <synopsis>
  46. Detects MF digits on a channel and saves them to a variable.
  47. </synopsis>
  48. <syntax>
  49. <parameter name="variable" required="true">
  50. <para>The input digits will be stored in the given
  51. <replaceable>variable</replaceable> name.</para>
  52. </parameter>
  53. <parameter name="timeout">
  54. <para>The number of seconds to wait for all digits, if greater
  55. than <literal>0</literal>. Can be floating point. Default
  56. is no timeout.</para>
  57. </parameter>
  58. <parameter name="options">
  59. <optionlist>
  60. <option name="d">
  61. <para>Delay audio by a frame to try to extra quelch.</para>
  62. </option>
  63. <option name="l">
  64. <para>Receive digits even if a key pulse (KP) has not yet
  65. been received. By default, this application will ignore
  66. all other digits until a KP has been received.</para>
  67. </option>
  68. <option name="k">
  69. <para>Do not return a character for the KP digit.</para>
  70. </option>
  71. <option name="m">
  72. <para>Mute conference.</para>
  73. </option>
  74. <option name="n">
  75. <para>Maximum number of digits, regardless of the sequence.</para>
  76. </option>
  77. <option name="o">
  78. <para>Enable override. Repeated KPs will clear all previous digits.</para>
  79. </option>
  80. <option name="q">
  81. <para>Quelch MF from in-band.</para>
  82. </option>
  83. <option name="r">
  84. <para>"Radio" mode (relaxed MF).</para>
  85. </option>
  86. <option name="s">
  87. <para>Do not return a character for ST digits.</para>
  88. </option>
  89. </optionlist>
  90. </parameter>
  91. </syntax>
  92. <description>
  93. <para>Reads a ST, STP, ST2P, or ST3P-terminated string of MF digits from
  94. the user in to the given <replaceable>variable</replaceable>.</para>
  95. <para>This application does not automatically answer the channel and
  96. should be preceded with <literal>Answer</literal> or
  97. <literal>Progress</literal> as needed.</para>
  98. <variablelist>
  99. <variable name="RECEIVEMFSTATUS">
  100. <para>This is the status of the read operation.</para>
  101. <value name="START" />
  102. <value name="ERROR" />
  103. <value name="HANGUP" />
  104. <value name="MAXDIGITS" />
  105. <value name="TIMEOUT" />
  106. </variable>
  107. </variablelist>
  108. </description>
  109. <see-also>
  110. <ref type="application">Read</ref>
  111. <ref type="application">SendMF</ref>
  112. <ref type="application">ReceiveSF</ref>
  113. </see-also>
  114. </application>
  115. <application name="SendMF" language="en_US">
  116. <since>
  117. <version>16.21.0</version>
  118. <version>18.7.0</version>
  119. <version>19.0.0</version>
  120. </since>
  121. <synopsis>
  122. Sends arbitrary MF digits on the current or specified channel.
  123. </synopsis>
  124. <syntax>
  125. <parameter name="digits" required="true">
  126. <para>List of digits 0-9,*#ABC to send; w for a half-second pause,
  127. also f or F for a flash-hook if the channel supports flash-hook,
  128. h or H for 250 ms of 2600 Hz,
  129. and W for a wink if the channel supports wink.</para>
  130. <para>Key pulse and start digits are not included automatically.
  131. * is used for KP, # for ST, A for STP, B for ST2P, and C for ST3P.</para>
  132. </parameter>
  133. <parameter name="timeout_ms" required="false">
  134. <para>Amount of time to wait in ms between tones. (defaults to 50ms).</para>
  135. </parameter>
  136. <parameter name="duration_ms" required="false">
  137. <para>Duration of each numeric digit (defaults to 55ms).</para>
  138. </parameter>
  139. <parameter name="duration_ms_kp" required="false">
  140. <para>Duration of KP digits (defaults to 120ms).</para>
  141. </parameter>
  142. <parameter name="duration_ms_st" required="false">
  143. <para>Duration of ST, STP, ST2P, and ST3P digits (defaults to 65ms).</para>
  144. </parameter>
  145. <parameter name="channel" required="false">
  146. <para>Channel where digits will be played</para>
  147. </parameter>
  148. </syntax>
  149. <description>
  150. <para>It will send all digits or terminate if it encounters an error.</para>
  151. </description>
  152. <see-also>
  153. <ref type="application">ReceiveMF</ref>
  154. <ref type="application">SendSF</ref>
  155. <ref type="application">SendDTMF</ref>
  156. </see-also>
  157. </application>
  158. <manager name="PlayMF" language="en_US">
  159. <since>
  160. <version>16.21.0</version>
  161. <version>18.7.0</version>
  162. <version>19.0.0</version>
  163. </since>
  164. <synopsis>
  165. Play MF digit on a specific channel.
  166. </synopsis>
  167. <syntax>
  168. <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
  169. <parameter name="Channel" required="true">
  170. <para>Channel name to send digit to.</para>
  171. </parameter>
  172. <parameter name="Digit" required="true">
  173. <para>The MF digit to play.</para>
  174. </parameter>
  175. <parameter name="Duration" required="false">
  176. <para>The duration, in milliseconds, of the digit to be played.</para>
  177. </parameter>
  178. </syntax>
  179. <description>
  180. <para>Plays an MF digit on the specified channel.</para>
  181. </description>
  182. </manager>
  183. ***/
  184. enum read_option_flags {
  185. OPT_DELAY = (1 << 0),
  186. OPT_MUTE = (1 << 1),
  187. OPT_QUELCH = (1 << 2),
  188. OPT_RELAXED = (1 << 3),
  189. OPT_LAX_KP = (1 << 4),
  190. OPT_PROCESS = (1 << 5),
  191. OPT_NO_KP = (1 << 6),
  192. OPT_NO_ST = (1 << 7),
  193. OPT_KP_OVERRIDE = (1 << 8),
  194. OPT_MAXDIGITS = (1 << 9),
  195. };
  196. enum {
  197. OPT_ARG_MAXDIGITS,
  198. /* Must be the last element */
  199. OPT_ARG_ARRAY_SIZE,
  200. };
  201. AST_APP_OPTIONS(read_app_options, {
  202. AST_APP_OPTION('d', OPT_DELAY),
  203. AST_APP_OPTION('l', OPT_LAX_KP),
  204. AST_APP_OPTION('k', OPT_NO_KP),
  205. AST_APP_OPTION('m', OPT_MUTE),
  206. AST_APP_OPTION_ARG('n', OPT_MAXDIGITS, OPT_ARG_MAXDIGITS),
  207. AST_APP_OPTION('o', OPT_KP_OVERRIDE),
  208. AST_APP_OPTION('p', OPT_PROCESS),
  209. AST_APP_OPTION('q', OPT_QUELCH),
  210. AST_APP_OPTION('r', OPT_RELAXED),
  211. AST_APP_OPTION('s', OPT_NO_ST),
  212. });
  213. static const char *readmf_name = "ReceiveMF";
  214. static const char sendmf_name[] = "SendMF";
  215. #define MF_BETWEEN_MS 50
  216. #define MF_DURATION 55
  217. #define MF_KP_DURATION 120
  218. #define MF_ST_DURATION 65
  219. /*!
  220. * \brief Detects MF digits on channel using DSP, terminated by ST, STP, ST2P, or ST3P
  221. *
  222. * \param chan channel on which to read digits
  223. * \param buf Buffer in which to store digits
  224. * \param buflen Size of buffer
  225. * \param timeout ms to wait for all digits before giving up
  226. * \param features Any additional DSP features to use
  227. * \param laxkp Receive digits even if KP not received
  228. * \param override Start over if we receive additional KPs
  229. * \param no_kp Don't include KP in the output
  230. * \param no_st Don't include start digits in the output
  231. * \param maxdigits If greater than 0, only read this many digits no matter what
  232. *
  233. * \retval 0 if successful
  234. * \retval -1 if unsuccessful (including hangup).
  235. */
  236. static int read_mf_digits(struct ast_channel *chan, char *buf, int buflen, int timeout, int features, int laxkp, int override, int no_kp, int no_st, int maxdigits) {
  237. struct ast_dsp *dsp;
  238. struct ast_frame *frame = NULL;
  239. struct timeval start;
  240. int remaining_time = timeout;
  241. int digits_read = 0;
  242. int is_start_digit = 0;
  243. char *str = buf;
  244. int res = 0;
  245. if (!(dsp = ast_dsp_new())) {
  246. ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
  247. pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "ERROR");
  248. return -1;
  249. }
  250. ast_dsp_set_features(dsp, DSP_FEATURE_DIGIT_DETECT);
  251. ast_dsp_set_digitmode(dsp, DSP_DIGITMODE_MF | features);
  252. start = ast_tvnow();
  253. *str = 0; /* start with empty output buffer */
  254. /* based on app_read and generic_fax_exec from res_fax */
  255. while (timeout == 0 || remaining_time > 0) {
  256. if (timeout > 0) {
  257. remaining_time = ast_remaining_ms(start, timeout);
  258. if (remaining_time <= 0) {
  259. pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "TIMEOUT");
  260. break;
  261. }
  262. }
  263. if ((maxdigits && digits_read >= maxdigits) || digits_read >= (buflen - 1)) { /* we don't have room to store any more digits (very unlikely to happen for a legitimate reason) */
  264. /* This result will probably not be usable, so status should not be START */
  265. pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "MAXDIGITS");
  266. break;
  267. }
  268. /* ast_waitfordigit only waits for DTMF frames, we need to do DSP on voice frames */
  269. if (ast_waitfor(chan, 1000) > 0) {
  270. frame = ast_read(chan);
  271. if (!frame) {
  272. ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));
  273. pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "HANGUP");
  274. break;
  275. } else if (frame->frametype == AST_FRAME_VOICE) {
  276. frame = ast_dsp_process(chan, dsp, frame);
  277. /* AST_FRAME_DTMF is used all over the DSP code for DTMF, MF, fax, etc.
  278. It's used because we can use the frame to store the digit detected.
  279. All this means is that we received something we care about. */
  280. if (frame->frametype == AST_FRAME_DTMF) {
  281. char result = frame->subclass.integer;
  282. if (digits_read == 0 && !laxkp && result != '*') {
  283. ast_debug(1, "Received MF digit, but no KP yet, ignoring: %c\n", result);
  284. ast_frfree(frame);
  285. continue;
  286. }
  287. ast_debug(1, "Received MF digit: %c\n", result);
  288. if (result == '*') {
  289. /* We received an additional KP, start over? */
  290. if (override && digits_read > 0) {
  291. ast_debug(1, "Received another KP, starting over\n");
  292. str = buf;
  293. *str = 0;
  294. digits_read = 1; /* we just detected a KP */
  295. } else {
  296. digits_read++;
  297. }
  298. /* if we were told not to include the KP digit in the output string, then skip it */
  299. if (no_kp) {
  300. ast_frfree(frame);
  301. continue;
  302. }
  303. } else {
  304. digits_read++;
  305. }
  306. is_start_digit = (strchr("#", result) || strchr("A", result) || strchr("B", result) || strchr("C", result));
  307. /* if we were told not to include the ST digit in the output string, then skip it */
  308. if (!no_st || !is_start_digit) {
  309. *str++ = result; /* won't write past allotted memory, because of buffer check at top of loop */
  310. *str = 0;
  311. }
  312. /* we received a ST digit (ST, STP, ST2P, or ST3P), so we're done */
  313. if (is_start_digit) {
  314. pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "START");
  315. ast_frfree(frame);
  316. break;
  317. }
  318. /* only free frame if it was a DSP match. The MF itself should not be muted. */
  319. ast_frfree(frame);
  320. }
  321. }
  322. } else {
  323. pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "HANGUP");
  324. res = -1;
  325. }
  326. }
  327. ast_dsp_free(dsp);
  328. ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, remaining_time: %d }\n", ast_channel_name(chan), timeout, remaining_time);
  329. return res;
  330. }
  331. static int read_mf_exec(struct ast_channel *chan, const char *data)
  332. {
  333. #define BUFFER_SIZE 256
  334. char tmp[BUFFER_SIZE] = "";
  335. int to = 0;
  336. double tosec;
  337. struct ast_flags flags = {0};
  338. char *optargs[OPT_ARG_ARRAY_SIZE];
  339. char *argcopy = NULL;
  340. int res, features = 0, maxdigits = 0;
  341. AST_DECLARE_APP_ARGS(arglist,
  342. AST_APP_ARG(variable);
  343. AST_APP_ARG(timeout);
  344. AST_APP_ARG(options);
  345. );
  346. if (ast_strlen_zero(data)) {
  347. ast_log(LOG_WARNING, "ReceiveMF requires an argument (variable)\n");
  348. return -1;
  349. }
  350. argcopy = ast_strdupa(data);
  351. AST_STANDARD_APP_ARGS(arglist, argcopy);
  352. if (!ast_strlen_zero(arglist.options)) {
  353. ast_app_parse_options(read_app_options, &flags, optargs, arglist.options);
  354. }
  355. if (!ast_strlen_zero(arglist.timeout)) {
  356. tosec = atof(arglist.timeout);
  357. if (tosec <= 0) {
  358. to = 0;
  359. } else {
  360. to = tosec * 1000.0;
  361. }
  362. }
  363. if (ast_strlen_zero(arglist.variable)) {
  364. ast_log(LOG_WARNING, "Invalid! Usage: ReceiveMF(variable[,timeout][,option])\n");
  365. return -1;
  366. }
  367. if (ast_test_flag(&flags, OPT_MAXDIGITS) && !ast_strlen_zero(optargs[OPT_ARG_MAXDIGITS])) {
  368. maxdigits = atoi(optargs[OPT_ARG_MAXDIGITS]);
  369. if (maxdigits <= 0) {
  370. ast_log(LOG_WARNING, "Invalid maximum number of digits, ignoring: '%s'\n", optargs[OPT_ARG_MAXDIGITS]);
  371. maxdigits = 0;
  372. }
  373. }
  374. if (ast_test_flag(&flags, OPT_DELAY)) {
  375. features |= DSP_DIGITMODE_MUTEMAX;
  376. }
  377. if (ast_test_flag(&flags, OPT_MUTE)) {
  378. features |= DSP_DIGITMODE_MUTECONF;
  379. }
  380. if (!ast_test_flag(&flags, OPT_QUELCH)) {
  381. features |= DSP_DIGITMODE_NOQUELCH;
  382. }
  383. if (ast_test_flag(&flags, OPT_RELAXED)) {
  384. features |= DSP_DIGITMODE_RELAXDTMF;
  385. }
  386. res = read_mf_digits(chan, tmp, BUFFER_SIZE, to, features, (ast_test_flag(&flags, OPT_LAX_KP)),
  387. (ast_test_flag(&flags, OPT_KP_OVERRIDE)), (ast_test_flag(&flags, OPT_NO_KP)), (ast_test_flag(&flags, OPT_NO_ST)), maxdigits);
  388. pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
  389. if (!ast_strlen_zero(tmp)) {
  390. ast_verb(3, "MF digits received: '%s'\n", tmp);
  391. } else if (!res) { /* if channel hung up, don't print anything out */
  392. ast_verb(3, "No MF digits received.\n");
  393. }
  394. return res;
  395. }
  396. static int sendmf_exec(struct ast_channel *chan, const char *vdata)
  397. {
  398. int res;
  399. char *data;
  400. int dinterval = 0, duration = 0, durationkp = 0, durationst = 0;
  401. struct ast_channel *chan_found = NULL;
  402. struct ast_channel *chan_dest = chan;
  403. struct ast_channel *chan_autoservice = NULL;
  404. AST_DECLARE_APP_ARGS(args,
  405. AST_APP_ARG(digits);
  406. AST_APP_ARG(dinterval);
  407. AST_APP_ARG(duration);
  408. AST_APP_ARG(durationkp);
  409. AST_APP_ARG(durationst);
  410. AST_APP_ARG(channel);
  411. );
  412. if (ast_strlen_zero(vdata)) {
  413. ast_log(LOG_WARNING, "SendMF requires an argument\n");
  414. return 0;
  415. }
  416. data = ast_strdupa(vdata);
  417. AST_STANDARD_APP_ARGS(args, data);
  418. if (ast_strlen_zero(args.digits)) {
  419. ast_log(LOG_WARNING, "The digits argument is required (0-9,*#ABC,wf)\n");
  420. return 0;
  421. }
  422. if (!ast_strlen_zero(args.dinterval)) {
  423. ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS);
  424. }
  425. if (!ast_strlen_zero(args.duration)) {
  426. ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS);
  427. }
  428. if (!ast_strlen_zero(args.durationkp)) {
  429. ast_app_parse_timelen(args.durationkp, &durationkp, TIMELEN_MILLISECONDS);
  430. }
  431. if (!ast_strlen_zero(args.durationst)) {
  432. ast_app_parse_timelen(args.durationst, &durationst, TIMELEN_MILLISECONDS);
  433. }
  434. if (!ast_strlen_zero(args.channel)) {
  435. chan_found = ast_channel_get_by_name(args.channel);
  436. if (!chan_found) {
  437. ast_log(LOG_WARNING, "No such channel: %s\n", args.channel);
  438. return 0;
  439. }
  440. chan_dest = chan_found;
  441. if (chan_found != chan) {
  442. chan_autoservice = chan;
  443. }
  444. }
  445. res = ast_mf_stream(chan_dest, chan_autoservice, NULL, args.digits, dinterval <= 0 ? MF_BETWEEN_MS : dinterval,
  446. duration <= 0 ? MF_DURATION : duration, durationkp <= 0 ? MF_KP_DURATION : durationkp,
  447. durationst <= 0 ? MF_ST_DURATION : durationst, 0);
  448. ast_channel_cleanup(chan_found);
  449. return chan_autoservice ? 0 : res;
  450. }
  451. static int manager_play_mf(struct mansession *s, const struct message *m)
  452. {
  453. const char *channel = astman_get_header(m, "Channel");
  454. const char *digit = astman_get_header(m, "Digit");
  455. const char *duration = astman_get_header(m, "Duration");
  456. struct ast_channel *chan;
  457. unsigned int duration_ms = MF_DURATION;
  458. if (!(chan = ast_channel_get_by_name(channel))) {
  459. astman_send_error(s, m, "Channel not found");
  460. return 0;
  461. }
  462. if (ast_strlen_zero(digit)) {
  463. astman_send_error(s, m, "No digit specified");
  464. chan = ast_channel_unref(chan);
  465. return 0;
  466. }
  467. /* Override default duration with KP or ST-specific default durations */
  468. if (!strcmp(digit, "*"))
  469. duration_ms = MF_KP_DURATION;
  470. if (!strcmp(digit, "#") || !strcmp(digit, "A") || !strcmp(digit, "B") || !strcmp(digit, "C"))
  471. duration_ms = MF_ST_DURATION;
  472. if (!ast_strlen_zero(duration) && (sscanf(duration, "%30u", &duration_ms) != 1)) {
  473. astman_send_error(s, m, "Could not convert Duration parameter");
  474. chan = ast_channel_unref(chan);
  475. return 0;
  476. }
  477. ast_mf_stream(chan, NULL, NULL, digit, 0, duration_ms, duration_ms, duration_ms, 1);
  478. chan = ast_channel_unref(chan);
  479. astman_send_ack(s, m, "MF successfully queued");
  480. return 0;
  481. }
  482. static int unload_module(void)
  483. {
  484. int res;
  485. res = ast_unregister_application(readmf_name);
  486. res |= ast_unregister_application(sendmf_name);
  487. res |= ast_manager_unregister("PlayMF");
  488. return res;
  489. }
  490. static int load_module(void)
  491. {
  492. int res;
  493. res = ast_register_application_xml(readmf_name, read_mf_exec);
  494. res |= ast_register_application_xml(sendmf_name, sendmf_exec);
  495. res |= ast_manager_register_xml("PlayMF", EVENT_FLAG_CALL, manager_play_mf);
  496. return res;
  497. }
  498. AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "MF Sender and Receiver Applications");