├── .github └── workflows │ ├── daily-latest.yml │ ├── daily.yml │ ├── main-latest.yml │ └── main.yml ├── LICENSE ├── README.md ├── apps ├── app_acts.c ├── app_assert.c ├── app_audichron.c ├── app_callback.c ├── app_callerid.c ├── app_ccsa.c ├── app_coin.c ├── app_dahdimonitor.c ├── app_dialtone.c ├── app_featureprocess.c ├── app_frame.c ├── app_george.c ├── app_keyprefetch.c ├── app_loopdisconnect.c ├── app_loopplayback.c ├── app_mail.c ├── app_memory.c ├── app_mwi.c ├── app_partialplayback.c ├── app_playdigits.c ├── app_predial.c ├── app_pulsar.c ├── app_randomplayback.c ├── app_remoteaccess.c ├── app_saytelnumber.c ├── app_selective.c ├── app_softmodem.c ├── app_streamsilence.c ├── app_tonetest.c ├── app_verify.c ├── app_wakeupcall.c └── app_wrappers.c ├── configs └── samples │ ├── irc.conf.sample │ ├── res_alarmsystem.conf.sample │ ├── res_smdr_whozz.conf.sample │ └── verify.conf.sample ├── funcs ├── func_dbchan.c ├── func_dtmf_flash.c ├── func_dtmf_trace.c ├── func_message_sub.c ├── func_nanpa.c ├── func_notchfilter.c ├── func_numpeer.c ├── func_query.c ├── func_resonance.c └── func_tech.c ├── patches ├── af_wanpipe.diff ├── agi_record_noisefirst.diff ├── alsa.diff ├── app_confbridge_Fix_bridge_shutdown_race_condition.patch ├── ast_rtoutpulsing1.diff ├── ast_rtoutpulsing2.diff ├── asterisk-prevent-duplicate-processes.diff ├── blueboxing.diff ├── coindsp.patch ├── core_local_bug_fix_for_dial_string_parsing.patch ├── dahdi_irq_handler.diff ├── dahdi_pci_module.diff ├── dahdi_rtoutpulse.diff ├── dahdicleanup.diff ├── disablenewexten.diff ├── freepbx_installvercheck.diff ├── hearpulsing-ast.diff ├── hearpulsing-dahlin.diff ├── hearpulsing-dahtool.patch ├── kewl.diff ├── kewl2.diff ├── loader_deprecated.patch ├── modfinal.diff ├── pciradio_Kbuild.diff ├── prefixinclude.diff ├── pulsar.patch ├── returnif.patch ├── sipcustparams.patch ├── sipfaxcontrol.diff ├── srtp.diff ├── tor2_Kbuild.diff └── translate_c_Prefer_better_codecs_on_ties.patch ├── phreaknet.1 ├── phreaknet.1.md ├── phreaknet.sh ├── phreakscript_autocomplete.sh ├── phreakscript_update.sh ├── res ├── res_alarmsystem.c ├── res_coindetect.c ├── res_deadlock.c ├── res_dialpulse.c ├── res_digitmap.c ├── res_irc.c ├── res_msp.c ├── res_pbx_validate.c ├── res_phreaknet.c ├── res_pjsip_presence.c └── res_smdr_whozz.c └── testsuite └── tests ├── apps ├── assert │ ├── configs │ │ └── ast1 │ │ │ └── extensions.conf │ └── test-config.yaml ├── coin │ ├── configs │ │ └── ast1 │ │ │ ├── 10c.ulaw │ │ │ ├── 25c.ulaw │ │ │ ├── 5c.ulaw │ │ │ └── extensions.conf │ └── test-config.yaml ├── dialtone │ ├── configs │ │ └── ast1 │ │ │ └── extensions.conf │ └── test-config.yaml └── verify │ ├── configs │ └── ast1 │ │ ├── extensions.conf │ │ ├── iax.conf │ │ └── verify.conf │ └── test-config.yaml └── funcs └── func_dbchan ├── configs └── ast1 │ └── extensions.conf └── test-config.yaml /.github/workflows/daily-latest.yml: -------------------------------------------------------------------------------- 1 | name: Daily CI (latest) 2 | 3 | on: 4 | # Retest daily to catch kernel breakage ASAP 5 | schedule: 6 | - cron: '0 4 * * *' 7 | 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | jobs: 12 | dahdi-kernel-next: 13 | runs-on: ubuntu-24.04 14 | name: DAHDI, next kernel 15 | container: debian:12 16 | steps: 17 | - name: Install packages 18 | run: | 19 | apt-get -y update 20 | apt-get -y upgrade 21 | apt-get -y install git gcc make perl-modules flex bison wget libssl-dev libelf-dev bc 22 | - name: Clone kernel 23 | run: | 24 | cd /usr/src 25 | git clone --depth 1 git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 26 | - name: Build kernel 27 | run: | 28 | cd /usr/src/linux-next 29 | make -j$(nproc) kernelversion 30 | make -j$(nproc) x86_64_defconfig 31 | make -j$(nproc) modules_prepare 32 | make -j$(nproc) 33 | make -j$(nproc) modules 34 | - name: Checkout 35 | uses: actions/checkout@v4 36 | - name: Build DAHDI 37 | run: | 38 | ./phreaknet.sh make 39 | GIT_REPO_PATH=${GITHUB_WORKSPACE} KSRC=/usr/src/linux-next phreaknet dahdi --drivers 40 | -------------------------------------------------------------------------------- /.github/workflows/daily.yml: -------------------------------------------------------------------------------- 1 | name: Daily CI 2 | 3 | on: 4 | # Retest daily to catch kernel breakage ASAP 5 | schedule: 6 | - cron: '0 4 * * *' 7 | 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | jobs: 12 | fedora-42: 13 | runs-on: ubuntu-24.04 14 | name: Fedora 42 15 | container: fedora:42 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Build DAHDI and Asterisk 19 | run: | 20 | ./phreaknet.sh make 21 | GIT_REPO_PATH=${GITHUB_WORKSPACE} phreaknet install --fast --dahdi --autokvers --drivers --devmode --sip 22 | fedora-42-master: 23 | runs-on: ubuntu-24.04 24 | name: Fedora 42 Master 25 | container: fedora:42 26 | steps: 27 | - uses: actions/checkout@v4 28 | - name: Build DAHDI and Asterisk 29 | run: | 30 | ./phreaknet.sh make 31 | GIT_REPO_PATH=${GITHUB_WORKSPACE} phreaknet install --fast --dahdi --autokvers --drivers --devmode --sip --version=master 32 | -------------------------------------------------------------------------------- /.github/workflows/main-latest.yml: -------------------------------------------------------------------------------- 1 | name: Main CI (latest) 2 | 3 | on: 4 | push: 5 | branches: [ master, dev ] 6 | pull_request: 7 | branches: [ master ] 8 | # Retest weekly to ensure nothing has broken 9 | schedule: 10 | - cron: '0 4 * * 6' 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | jobs: 16 | dahdi-kernel-mainline: 17 | runs-on: ubuntu-24.04 18 | name: DAHDI, mainline kernel 19 | container: debian:12 20 | steps: 21 | - name: Install packages 22 | run: | 23 | apt-get -y update 24 | apt-get -y upgrade 25 | apt-get -y install git gcc make perl-modules flex bison wget libssl-dev libelf-dev bc 26 | - name: Clone kernel 27 | run: | 28 | cd /usr/src 29 | git clone --depth 1 git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 30 | - name: Build kernel 31 | run: | 32 | cd /usr/src/linux 33 | make -j$(nproc) kernelversion 34 | make -j$(nproc) x86_64_defconfig 35 | make -j$(nproc) modules_prepare 36 | make -j$(nproc) 37 | make -j$(nproc) modules 38 | - name: Checkout 39 | uses: actions/checkout@v4 40 | - name: Build DAHDI 41 | run: | 42 | ./phreaknet.sh make 43 | GIT_REPO_PATH=${GITHUB_WORKSPACE} KSRC=/usr/src/linux phreaknet dahdi --drivers 44 | dahdi-kernel-next: 45 | runs-on: ubuntu-24.04 46 | name: DAHDI, next kernel 47 | container: debian:12 48 | steps: 49 | - name: Install packages 50 | run: | 51 | apt-get -y update 52 | apt-get -y upgrade 53 | apt-get -y install git gcc make perl-modules flex bison wget libssl-dev libelf-dev bc 54 | - name: Clone kernel 55 | run: | 56 | cd /usr/src 57 | git clone --depth 1 git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 58 | - name: Build kernel 59 | run: | 60 | cd /usr/src/linux-next 61 | make -j$(nproc) kernelversion 62 | make -j$(nproc) x86_64_defconfig 63 | make -j$(nproc) modules_prepare 64 | make -j$(nproc) 65 | make -j$(nproc) modules 66 | - name: Checkout 67 | uses: actions/checkout@v4 68 | - name: Build DAHDI 69 | run: | 70 | ./phreaknet.sh make 71 | GIT_REPO_PATH=${GITHUB_WORKSPACE} KSRC=/usr/src/linux-next phreaknet dahdi --drivers 72 | -------------------------------------------------------------------------------- /apps/app_assert.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2021, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Dialplan assertion application 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup applications 26 | */ 27 | 28 | /*** MODULEINFO 29 | extended 30 | ***/ 31 | 32 | #include "asterisk.h" 33 | 34 | #include "asterisk/file.h" 35 | #include "asterisk/pbx.h" 36 | #include "asterisk/channel.h" 37 | #include "asterisk/app.h" 38 | #include "asterisk/module.h" 39 | 40 | /*** DOCUMENTATION 41 | 42 | 43 | Asserts that an expression is true. 44 | 45 | 46 | 47 | The expression to evaluate. 48 | 49 | 50 | 51 | 54 | 55 | 56 | 57 | 58 | Evaluates expression and continues dialplan 59 | execution if the expression is true and ends the call with a warning 60 | if it is false (unless the d option is provided). 61 | This application can be used to verify functional correctness 62 | of dialplans (e.g. dialplan test cases), similar to the assert 63 | function in C. For instance, if a certain property is expected to always hold at some 64 | point in your dialplan, this application can be used to enforce that. 65 | 66 | 67 | RaiseException 68 | 69 | 70 | ***/ 71 | 72 | enum option_flags { 73 | OPT_NOHANGUP = (1 << 0), 74 | }; 75 | 76 | AST_APP_OPTIONS(app_options, { 77 | AST_APP_OPTION('d', OPT_NOHANGUP), 78 | }); 79 | 80 | static const char *app = "Assert"; 81 | 82 | static int assert_exec(struct ast_channel *chan, const char *data) 83 | { 84 | char *argcopy = NULL; 85 | struct ast_flags flags = {0}; 86 | int value = 0; 87 | 88 | AST_DECLARE_APP_ARGS(arglist, 89 | AST_APP_ARG(expression); 90 | AST_APP_ARG(options); 91 | ); 92 | 93 | if (ast_strlen_zero(data)) { 94 | ast_log(LOG_WARNING, "%s requires an argument (variable)\n", app); 95 | return -1; 96 | } 97 | 98 | argcopy = ast_strdupa(data); 99 | 100 | AST_STANDARD_APP_ARGS(arglist, argcopy); 101 | 102 | if (!ast_strlen_zero(arglist.options)) { 103 | ast_app_parse_options(app_options, &flags, NULL, arglist.options); 104 | } 105 | 106 | if (ast_strlen_zero(arglist.expression)) { 107 | ast_log(LOG_WARNING, "%s requires an argument (variable)\n", app); 108 | return -1; 109 | } 110 | 111 | value = atoi(arglist.expression); /* already parsed, so it should already be an integer anyways */ 112 | 113 | if (!value) { 114 | AST_DECLARE_APP_ARGS(extendata, 115 | AST_APP_ARG(expr); 116 | AST_APP_ARG(rest); 117 | ); 118 | const char *exprparse = NULL; 119 | char *datacopy = NULL; 120 | char *context, *exten; 121 | int priority; 122 | 123 | /* so, what was the expression? Let's find out */ 124 | struct ast_exten *e; 125 | struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */ 126 | 127 | ast_channel_lock(chan); 128 | context = ast_strdupa(ast_channel_context(chan)); 129 | exten = ast_strdupa(ast_channel_exten(chan)); 130 | priority = ast_channel_priority(chan); 131 | ast_channel_unlock(chan); 132 | 133 | ast_rdlock_contexts(); 134 | e = pbx_find_extension(chan, NULL, &q, context, exten, priority, NULL, "", E_MATCH); 135 | ast_unlock_contexts(); 136 | if (!e) { 137 | ast_log(LOG_WARNING, "Couldn't find current execution location\n"); /* should never happen */ 138 | return -1; 139 | } 140 | exprparse = ast_get_extension_app_data(e); 141 | datacopy = ast_strdupa(exprparse); 142 | AST_STANDARD_APP_ARGS(extendata, datacopy); 143 | 144 | ast_log(LOG_WARNING, "Assertion failed at %s,%s,%d: %s\n", context, exten, priority, extendata.expr); 145 | 146 | if (!ast_test_flag(&flags, OPT_NOHANGUP)) { 147 | return -1; 148 | } 149 | } 150 | 151 | return 0; 152 | } 153 | 154 | static int unload_module(void) 155 | { 156 | return ast_unregister_application(app); 157 | } 158 | 159 | static int load_module(void) 160 | { 161 | return ast_register_application_xml(app, assert_exec); 162 | } 163 | 164 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Dialplan assertion test application"); 165 | -------------------------------------------------------------------------------- /apps/app_loopdisconnect.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2022, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Perform a loop disconnect 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup applications 26 | */ 27 | 28 | /*** MODULEINFO 29 | dahdi 30 | extended 31 | ***/ 32 | 33 | #include "asterisk.h" 34 | 35 | #include 36 | 37 | #include "asterisk/lock.h" 38 | #include "asterisk/file.h" 39 | #include "asterisk/channel.h" 40 | #include "asterisk/pbx.h" 41 | #include "asterisk/module.h" 42 | 43 | /*** DOCUMENTATION 44 | 45 | 46 | Performs a loop disconnect on an FXS channel. 47 | 48 | 49 | 50 | Performs a loop disconnect (open switching interval) on an FXS channel. 51 | This will not hangup the channel or do anything besides the loop disconnect. 52 | Providing a loop disconnect can be used to force compatible equipment that will 53 | recognize a loop disconnect to release the line, such as answering machines. 54 | This application will wait for the loop disconnect to finish before continuing. 55 | 56 | 57 | POLARITY 58 | 59 | 60 | ***/ 61 | 62 | static char *app = "LoopDisconnect"; 63 | 64 | static inline int dahdi_wait_event(int fd) 65 | { 66 | /* Avoid the silly dahdi_waitevent which ignores a bunch of events */ 67 | int i, j = 0; 68 | i = DAHDI_IOMUX_SIGEVENT; 69 | if (ioctl(fd, DAHDI_IOMUX, &i) == -1) { 70 | return -1; 71 | } 72 | if (ioctl(fd, DAHDI_GETEVENT, &j) == -1) { 73 | return -1; 74 | } 75 | return j; 76 | } 77 | 78 | static int kewl_exec(struct ast_channel *chan, const char *data) 79 | { 80 | int res, x; 81 | struct dahdi_params dahdip; 82 | 83 | if (strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) { 84 | ast_log(LOG_WARNING, "%s is not a DAHDI channel\n", ast_channel_name(chan)); 85 | return -1; 86 | } 87 | 88 | memset(&dahdip, 0, sizeof(dahdip)); 89 | 90 | if (ioctl(ast_channel_fd(chan, 0), DAHDI_GET_PARAMS, &dahdip)) { 91 | ast_log(LOG_WARNING, "Unable to get parameters of %s: %s\n", ast_channel_name(chan), strerror(errno)); 92 | return -1; 93 | } 94 | 95 | if (!(dahdip.sigtype & __DAHDI_SIG_FXO)) { 96 | ast_log(LOG_WARNING, "%s is not an FXS channel\n", ast_channel_name(chan)); 97 | return -1; 98 | } 99 | 100 | x = DAHDI_KEWL; 101 | res = ioctl(ast_channel_fd(chan, 0), DAHDI_HOOK, &x); 102 | 103 | if (res && errno != EINPROGRESS) { 104 | ast_log(LOG_WARNING, "Unable to do loop disconnect on channel %s: %s\n", ast_channel_name(chan), strerror(errno)); 105 | return -1; 106 | } 107 | 108 | if (res) { 109 | /* Wait for the event to finish */ 110 | dahdi_wait_event(ast_channel_fd(chan, 0)); 111 | } 112 | 113 | ast_verb(3, "Loop Disconnect on channel %s\n", ast_channel_name(chan)); 114 | /* Actually wait for the loop disconnect to finish. */ 115 | return ast_safe_sleep(chan, 600); /* Actual timing will depend on the kewl times, but these are defined in DAHDI's kernel.h */ 116 | } 117 | 118 | static int unload_module(void) 119 | { 120 | return ast_unregister_application(app); 121 | } 122 | 123 | static int load_module(void) 124 | { 125 | return ast_register_application_xml(app, kewl_exec); 126 | } 127 | 128 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Loop Disconnect application"); 129 | -------------------------------------------------------------------------------- /apps/app_loopplayback.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2021, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Trivial application to loop playback of a sound file 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup applications 26 | */ 27 | 28 | /*** MODULEINFO 29 | extended 30 | ***/ 31 | 32 | #include "asterisk.h" 33 | 34 | #include "asterisk/file.h" 35 | #include "asterisk/pbx.h" 36 | #include "asterisk/module.h" 37 | #include "asterisk/app.h" 38 | #include "asterisk/conversions.h" 39 | 40 | /*** DOCUMENTATION 41 | 42 | 43 | Loops audio playback indefinitely or a certain number of times 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Number of times to play specified file(s). 52 | If omitted, playback will loop "forever", i.e. 53 | until the channel hangs up or it is redirected to 54 | a different application. Default is 0 (indefinitely). 55 | 56 | 57 | 58 | Plays back given filenames (do not put extension of wav/alaw, etc). 59 | If the file is non-existent it will fail. 60 | Options available with Playback are not available with 61 | this application. 62 | This application does not automatically answer the channel and should 63 | be preceded by Progress or Answer as 64 | appropriate. 65 | This application sets the following channel variable upon completion: 66 | 67 | 68 | The status of the playback attempt as a text string. 69 | 70 | 71 | 72 | 73 | 74 | 75 | Playback 76 | ControlPlayback 77 | 78 | 79 | ***/ 80 | 81 | static char *app = "LoopPlayback"; 82 | 83 | static int playback_exec(struct ast_channel *chan, const char *data) 84 | { 85 | int res = 0, mres = 0, loops = 0, plays = 0; 86 | char *tmp; 87 | 88 | AST_DECLARE_APP_ARGS(args, 89 | AST_APP_ARG(filenames); 90 | AST_APP_ARG(loops); 91 | ); 92 | 93 | if (ast_strlen_zero(data)) { 94 | ast_log(LOG_WARNING, "LoopPlayback requires an argument (filename)\n"); 95 | return -1; 96 | } 97 | 98 | tmp = ast_strdupa(data); 99 | AST_STANDARD_APP_ARGS(args, tmp); 100 | 101 | if (!ast_strlen_zero(args.loops) && (ast_str_to_int(args.loops, &loops) || loops < 0)) { 102 | ast_log(LOG_WARNING, "Invalid number of loops: %s\n", args.loops); 103 | return -1; 104 | } 105 | 106 | /* do not automatically answer the channel */ 107 | 108 | ast_stopstream(chan); 109 | 110 | while (!res && !mres && (loops == 0 || plays++ < loops)) { 111 | char *front, *files; 112 | char *back = ast_strdup(args.filenames); /* because we could be iterating for a LONG time, strdupa is a BAD idea, we could blow the stack */ 113 | files = back; /* duplicate the pointer so we don't modify it, since we need to free memory */ 114 | 115 | if (loops > 0) { 116 | ast_debug(1, "Looping playback of file(s) '%s', iteration %d of %d\n", back, plays, loops); 117 | } 118 | 119 | while (!res && (front = strsep(&files, "&"))) { 120 | res = ast_streamfile(chan, front, ast_channel_language(chan)); 121 | if (!res) { 122 | res = ast_waitstream(chan, ""); 123 | ast_stopstream(chan); 124 | } 125 | if (res) { 126 | if (!ast_check_hangup(chan)) { 127 | ast_log(LOG_WARNING, "LoopPlayback failed on %s for %s\n", ast_channel_name(chan), (char *)data); 128 | } 129 | res = 0; 130 | mres = 1; 131 | } 132 | } 133 | ast_free(back); 134 | } 135 | 136 | pbx_builtin_setvar_helper(chan, "LOOPPLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS"); 137 | return res; 138 | } 139 | 140 | static int unload_module(void) 141 | { 142 | int res; 143 | 144 | res = ast_unregister_application(app); 145 | 146 | return res; 147 | } 148 | 149 | static int load_module(void) 150 | { 151 | return ast_register_application_xml(app, playback_exec); 152 | } 153 | 154 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Playback Loop Application"); 155 | -------------------------------------------------------------------------------- /apps/app_memory.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2021, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Attempts to reclaim unused heap memory 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup applications 26 | */ 27 | 28 | /*** MODULEINFO 29 | no 30 | extended 31 | ***/ 32 | 33 | #include "asterisk.h" 34 | 35 | #include "asterisk/logger.h" 36 | #include "asterisk/channel.h" 37 | #include "asterisk/pbx.h" 38 | #include "asterisk/module.h" 39 | #include "asterisk/app.h" 40 | 41 | /*** DOCUMENTATION 42 | 43 | 44 | Attempts to reclaim unused heap memory. 45 | 46 | 47 | Attempts to release free memory from the heap. 48 | This application is typically used before the 49 | Systemapplication or the SHELL 50 | function if system memory conditions prevent these from succeeding 51 | ordinarily. 52 | This application may be used to diagnose this memory issue and 53 | prevent these calls from failing until the cause of the memory issue 54 | is found. You should also build Asterisk with MALLOC_DEBUG 55 | to troubleshoot memory issues. 56 | 57 | 58 | 59 | Some memory was released back to the system. 60 | 61 | 62 | No memory could be released back to the system. 63 | 64 | 65 | This application is not supported on this system. 66 | 67 | 68 | 69 | 70 | 71 | ***/ 72 | 73 | static char *app = "MallocTrim"; 74 | 75 | static int malloc_trim_exec(struct ast_channel *chan, const char *data) 76 | { 77 | #ifdef HAVE_MALLOC_TRIM 78 | 79 | extern int malloc_trim(size_t __pad) __THROW; 80 | 81 | if (malloc_trim(0)) { 82 | pbx_builtin_setvar_helper(chan, "MALLOCTRIMSTATUS", "SUCCESS"); 83 | } else { 84 | pbx_builtin_setvar_helper(chan, "MALLOCTRIMSTATUS", "FAILURE"); 85 | } 86 | return 0; 87 | 88 | #else 89 | 90 | pbx_builtin_setvar_helper(chan, "MALLOCTRIMSTATUS", "UNSUPPORTED"); 91 | return 0; 92 | 93 | #endif 94 | } 95 | 96 | static int unload_module(void) 97 | { 98 | return ast_unregister_application(app); 99 | } 100 | 101 | static int load_module(void) 102 | { 103 | return ast_register_application_xml(app, malloc_trim_exec); 104 | } 105 | 106 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Frees unused heap memory"); 107 | -------------------------------------------------------------------------------- /apps/app_partialplayback.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2023, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Application to partially play a sound file 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup applications 26 | */ 27 | 28 | /*** MODULEINFO 29 | extended 30 | ***/ 31 | 32 | #include "asterisk.h" 33 | 34 | #include "asterisk/file.h" 35 | #include "asterisk/pbx.h" 36 | #include "asterisk/module.h" 37 | #include "asterisk/app.h" 38 | #include "asterisk/mod_format.h" /* expose ast_filestream */ 39 | 40 | /*** DOCUMENTATION 41 | 42 | 43 | Play a file, between optionally specified start and end offsets. 44 | 45 | 46 | 47 | File to play. Do not include extension. 48 | 49 | 50 | Starting time. Default is start of file. 51 | 52 | 53 | Ending time. Default is end of file. 54 | 55 | 56 | 57 | Plays back a given filename (do not include extension). 58 | This application does not automatically answer the channel. 59 | This application sets the following channel variable upon completion: 60 | 61 | 62 | The status of the playback attempt. 63 | 64 | 65 | 66 | 67 | 68 | 69 | Playback 70 | ControlPlayback 71 | stream file 72 | control stream file 73 | ControlPlayback 74 | 75 | 76 | ***/ 77 | 78 | static char *app = "PartialPlayback"; 79 | 80 | static int playback_exec(struct ast_channel *chan, const char *data) 81 | { 82 | int res = 0; 83 | int start = 0, end = 0; 84 | char *tmp; 85 | const char *orig_chan_name = NULL; 86 | 87 | AST_DECLARE_APP_ARGS(args, 88 | AST_APP_ARG(filename); 89 | AST_APP_ARG(start); 90 | AST_APP_ARG(end); 91 | ); 92 | 93 | if (ast_strlen_zero(data)) { 94 | ast_log(LOG_WARNING, "%s requires an argument (filename)\n", app); 95 | return -1; 96 | } 97 | 98 | tmp = ast_strdupa(data); 99 | AST_STANDARD_APP_ARGS(args, tmp); 100 | 101 | if (ast_strlen_zero(args.filename)) { 102 | ast_log(LOG_ERROR, "%s requires a filename\n", app); 103 | return -1; 104 | } 105 | 106 | if (!ast_strlen_zero(args.start)) { 107 | start = atoi(args.start); 108 | if (start < 0) { 109 | ast_log(LOG_ERROR, "Invalid start time: %s\n", args.start); 110 | return -1; 111 | } 112 | } 113 | if (!ast_strlen_zero(args.end)) { 114 | end = atoi(args.end); 115 | } 116 | 117 | if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_MASQ_NOSTREAM)) { 118 | orig_chan_name = ast_strdupa(ast_channel_name(chan)); 119 | } 120 | 121 | ast_stopstream(chan); 122 | res = ast_streamfile(chan, args.filename, ast_channel_language(chan)); 123 | if (res) { 124 | goto end; 125 | } 126 | 127 | if (start) { 128 | /* Seek to starting position - based on waitstream_control in file.c */ 129 | int eoftest; 130 | /* We're fast forwarding from the beginning, so this is a convenient wrapper 131 | * to seek to the right spot, using ms instead of samples. */ 132 | ast_stream_fastforward(ast_channel_stream(chan), start); 133 | eoftest = fgetc(ast_channel_stream(chan)->f); 134 | if (feof(ast_channel_stream(chan)->f)) { 135 | ast_stream_rewind(ast_channel_stream(chan), start); 136 | } else { 137 | ungetc(eoftest, ast_channel_stream(chan)->f); 138 | } 139 | } 140 | 141 | if (end) { 142 | /* Play until we get to end time */ 143 | while (ast_channel_stream(chan)) { 144 | /* Based on waitstream_core in file.c */ 145 | int res; 146 | int ms; 147 | struct ast_frame *fr; 148 | off_t offset; 149 | int ms_len; 150 | 151 | if (orig_chan_name && strcasecmp(orig_chan_name, ast_channel_name(chan))) { 152 | ast_stopstream(chan); 153 | res = -1; 154 | break; 155 | } 156 | ms = ast_sched_wait(ast_channel_sched(chan)); 157 | if (ms < 0 && !ast_channel_timingfunc(chan)) { 158 | ast_stopstream(chan); 159 | break; 160 | } 161 | if (ms < 0) { 162 | ms = 1000; 163 | } 164 | res = ast_waitfor(chan, ms); 165 | if (res < 0) { 166 | ast_log(LOG_WARNING, "ast_waitfor failed (%s)\n", strerror(errno)); 167 | break; 168 | } 169 | fr = ast_read(chan); 170 | if (!fr) { 171 | ast_debug(3, "Channel %s did not return a frame, must've hung up\n", ast_channel_name(chan)); 172 | break; 173 | } 174 | switch (fr->frametype) { 175 | case AST_FRAME_CONTROL: 176 | switch (fr->subclass.integer) { 177 | case AST_CONTROL_HANGUP: 178 | goto end; 179 | default: 180 | break; 181 | } 182 | default: 183 | break; 184 | } 185 | /* Is our time yet come? */ 186 | offset = ast_tellstream(ast_channel_stream(chan)); 187 | ms_len = offset / (ast_format_get_sample_rate(ast_channel_stream(chan)->fmt->format) / 1000); 188 | if (ms_len >= end) { 189 | ast_debug(1, "Stopping stream because we reached %d ms\n", end); 190 | break; 191 | } 192 | ast_sched_runq(ast_channel_sched(chan)); 193 | } 194 | } else { 195 | /* Play the remainder */ 196 | res = ast_waitstream(chan, ""); 197 | } 198 | ast_stopstream(chan); 199 | 200 | end: 201 | if (res && !ast_check_hangup(chan)) { 202 | ast_log(LOG_WARNING, "%s failed on %s for %s\n", app, ast_channel_name(chan), args.filename); 203 | } 204 | 205 | pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", res ? "FAILED" : "SUCCESS"); 206 | return res; 207 | } 208 | 209 | static int unload_module(void) 210 | { 211 | return ast_unregister_application(app); 212 | } 213 | 214 | static int load_module(void) 215 | { 216 | return ast_register_application_xml(app, playback_exec); 217 | } 218 | 219 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Partial playback application"); 220 | -------------------------------------------------------------------------------- /apps/app_pulsar.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2022, Naveen Albert 5 | * 6 | * app_pulsar.c adapted from the old app_rpsim.c, copyrighted by: 7 | * Copyright (C) 2006, Russ Price 8 | * 9 | * See http://www.asterisk.org for more information about 10 | * the Asterisk project. Please do not directly contact 11 | * any of the maintainers of this project for assistance; 12 | * the project provides a web site, mailing lists and IRC 13 | * channels for your use. 14 | * 15 | * This program is free software, distributed under the terms of 16 | * the GNU General Public License Version 2. See the LICENSE file 17 | * at the top of the source tree. 18 | */ 19 | 20 | /* 21 | * This moudle is a rewrite of Russ Price's app_rpsim (from 2006 22 | * for Asterisk 1.4) and pulsar.agi (with multi-protocol support). 23 | */ 24 | 25 | /*! \file 26 | * 27 | * \brief Simulated revertive pulse signaling 28 | * 29 | * \author Naveen Albert 30 | * 31 | * \ingroup applications 32 | */ 33 | 34 | /*** MODULEINFO 35 | extended 36 | ***/ 37 | 38 | #include "asterisk.h" 39 | 40 | #include "asterisk/file.h" 41 | #include "asterisk/pbx.h" 42 | #include "asterisk/channel.h" 43 | #include "asterisk/app.h" 44 | #include "asterisk/module.h" 45 | 46 | /*** DOCUMENTATION 47 | 48 | 49 | Simulates revertive pulsing for a 4-digit number. 50 | 51 | 52 | 53 | The 4-digit station number. 54 | 55 | 56 | The type of pulsing to use. 57 | Valid options are panel, 1xb, 58 | and 5xb. 59 | Default is panel. 60 | 61 | 62 | 63 | 67 | 68 | 69 | 70 | 71 | Mimics the revertive pulsing sounds characteristic of 72 | Panel, Number 1 Crossbar, or Number 5 Crossbar pulsing. 73 | This application uses material sourced from Evan Doorbell 74 | tapes. The pulsar sounds directory should be 75 | placed directly into the main sounds directory. 76 | Sounds can be obtained from the original tarball 77 | at https://octothorpe.info/site/pulsar. 78 | This application does not automatically answer the channel 79 | and should be preceded by Progress 80 | or Answer as appropriate. 81 | 82 | 83 | ***/ 84 | 85 | enum pulsar_option_flags { 86 | OPT_BSIDE = (1 << 0), 87 | }; 88 | 89 | AST_APP_OPTIONS(pulsar_option_flags, { 90 | AST_APP_OPTION('b', OPT_BSIDE), 91 | }); 92 | 93 | static const char *app = "RevertivePulse"; 94 | 95 | static int valid_rp_digits(char *digits) 96 | { 97 | int i; 98 | for (i = 0; i < 4; i++) { 99 | if (!digits[i] || !isdigit(digits[i])) { 100 | return 0; 101 | } 102 | } 103 | return (!digits[i]); /* there shouldn't be anything left */ 104 | } 105 | 106 | static void translate(int number, int b_side, int *data) 107 | { 108 | if( number > 9999) { 109 | number %= 10000; /* should never happen */ 110 | } 111 | data[0] = (number / 2000) + 1; 112 | data[1] = (number % 2000) / 500 + (b_side ? 4 : 0) + 1; 113 | data[2] = (number % 500) / 100 + 1; 114 | data[3] = (number % 100) / 10 + 1; 115 | data[4] = (number % 10) + 1; 116 | } 117 | 118 | #define BUFFER_SIZE 21 /* pulsar/panel/middleX is the longest possible string: 20 chars + null terminator = 21 */ 119 | 120 | static int pulsar_stream(struct ast_channel *chan, char *file) 121 | { 122 | int res; 123 | 124 | res = ast_streamfile(chan, file, ast_channel_language(chan)); 125 | if (!res) { 126 | res = ast_waitstream(chan, ""); 127 | ast_stopstream(chan); 128 | } else { 129 | ast_log(LOG_WARNING, "Failed to play revertive pulse file '%s'\n", file); 130 | } 131 | return res; 132 | } 133 | 134 | static int pulsar_exec(struct ast_channel *chan, const char *data) 135 | { 136 | char *argcopy = NULL, *protocol = "panel"; 137 | struct ast_flags flags = {0}; 138 | char buf[BUFFER_SIZE]; 139 | int digit, res, bside = 0; 140 | int translated[5]; 141 | 142 | AST_DECLARE_APP_ARGS(args, 143 | AST_APP_ARG(digits); 144 | AST_APP_ARG(type); 145 | AST_APP_ARG(options); 146 | ); 147 | 148 | if (ast_strlen_zero(data)) { 149 | ast_log(LOG_WARNING, "%s requires an argument (variable)\n", app); 150 | return -1; 151 | } 152 | 153 | argcopy = ast_strdupa(data); 154 | AST_STANDARD_APP_ARGS(args, argcopy); 155 | 156 | if (!ast_strlen_zero(args.options)) { 157 | ast_app_parse_options(pulsar_option_flags, &flags, NULL, args.options); 158 | if (ast_test_flag(&flags, OPT_BSIDE)) { 159 | bside = 1; 160 | } 161 | } 162 | 163 | if (ast_strlen_zero(args.digits)) { 164 | ast_log(LOG_WARNING, "%s requires digits\n", app); 165 | return 0; /* RP isn't critical, it's just ear candy, so don't hang up */ 166 | } 167 | if (!valid_rp_digits(args.digits)) { 168 | ast_log(LOG_WARNING, "Digits '%s' invalid for revertive pulsing protocol\n", args.digits); 169 | return 0; 170 | } 171 | if (!ast_strlen_zero(args.type)) { 172 | if (!strcasecmp(args.type, "1xb")) { 173 | protocol = "1xb"; 174 | } else if (!strcasecmp(args.type, "5xb")) { 175 | protocol = "5xb"; 176 | } else if (strcasecmp(args.type, "panel")) { 177 | ast_log(LOG_WARNING, "Invalid RP protocol: '%s'. Defaulting to panel.\n", args.type); 178 | } 179 | } 180 | translate(atoi(args.digits), bside, translated); 181 | ast_stopstream(chan); 182 | 183 | ast_debug(1, "Simulating revertive pulsing digits '%s' with '%s' protocol\n", args.digits, protocol); 184 | snprintf(buf, BUFFER_SIZE, "pulsar/%s/begin", protocol); 185 | if (!ast_fileexists(buf, NULL, NULL)) { 186 | ast_log(LOG_WARNING, "Revertive pulse audio file '%s' does not exist, aborting\n", buf); 187 | return 0; /* if this file does not exist, the other ones probably don't, so bail out now */ 188 | } 189 | if (pulsar_stream(chan, buf)) { 190 | return -1; 191 | } 192 | for (digit = 0; digit < 5; digit++) { 193 | int i, midpulses; 194 | 195 | snprintf(buf, BUFFER_SIZE, "pulsar/%s/start%d", protocol, digit); 196 | res = pulsar_stream(chan, buf); 197 | if (!res && translated[digit] > 1) { 198 | snprintf(buf, BUFFER_SIZE, "pulsar/%s/first%d", protocol, digit); 199 | res = pulsar_stream(chan, buf); 200 | } 201 | if (res) { 202 | return -1; 203 | } 204 | midpulses = translated[digit] - 2; 205 | snprintf(buf, BUFFER_SIZE, "pulsar/%s/middle%d", protocol, digit); 206 | for (i = 0; i < midpulses; i++) { 207 | if (pulsar_stream(chan, buf)) { 208 | return -1; 209 | } 210 | } 211 | snprintf(buf, BUFFER_SIZE, "pulsar/%s/last%d", protocol, digit); 212 | if (pulsar_stream(chan, buf)) { 213 | return -1; 214 | } 215 | } 216 | snprintf(buf, BUFFER_SIZE, "pulsar/%s/end", protocol); 217 | res = pulsar_stream(chan, buf); 218 | 219 | return res; 220 | } 221 | 222 | static int unload_module(void) 223 | { 224 | return ast_unregister_application(app); 225 | } 226 | 227 | static int load_module(void) 228 | { 229 | return ast_register_application_xml(app, pulsar_exec); 230 | } 231 | 232 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Simulated Revertive Pulsing"); 233 | -------------------------------------------------------------------------------- /apps/app_randomplayback.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2022, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Trivial application to randomly play a sound file 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup applications 26 | */ 27 | 28 | /*** MODULEINFO 29 | extended 30 | ***/ 31 | 32 | #include "asterisk.h" 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | #include "asterisk/file.h" 39 | #include "asterisk/pbx.h" 40 | #include "asterisk/module.h" 41 | #include "asterisk/app.h" 42 | #include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */ 43 | 44 | /*** DOCUMENTATION 45 | 46 | 47 | Plays a random file with a particular directory and/or file prefix 48 | 49 | 50 | 51 | Directory/file prefix that must match. 52 | 53 | 54 | 55 | Plays back a random file with the provided prefix which contains 56 | a specific directory, optionally followed by a file prefix. If there 57 | is no file prefix, be sure to end with a trailing slash to search 58 | in a directory of the given name as opposed to a trailing file prefix. 59 | Knowledge of actual specific file candidates is not necessary. 60 | A random file matching this full prefix will be played. 61 | This application does not automatically answer the channel and should 62 | be preceded by Progress or Answer as 63 | appropriate. 64 | 65 | 66 | Playback 67 | ControlPlayback 68 | 69 | 70 | ***/ 71 | 72 | static char *app = "RandomPlayback"; 73 | 74 | /*! 75 | * \brief Traverses a directory and returns number of files or a specific file with specified prefix 76 | * 77 | * \param chan Channel 78 | * \param directoryprefix Directory name and file prefix (optional) 79 | * \param filename 0 if getting the file count, otherwise positive 1-indexed file number to retrieve 80 | * \param buffer Buffer to fill. Do not allocate beforehand. 81 | * 82 | * \retval -1 Failure 83 | * \retval 0 No files in directory (file count) 84 | * \retval non-zero Number of files in directory (file count) or file number that matched (retrieval) 85 | */ 86 | static int traverse_directory(struct ast_channel *chan, char *directoryprefix, int filenum, char **buffer) 87 | { 88 | char *fullpath; 89 | char *dir, *base; 90 | struct dirent* dent; 91 | DIR* srcdir; 92 | int files = 0, baselen = 0; 93 | RAII_VAR(struct ast_str *, media_dir, ast_str_create(64), ast_free); 94 | RAII_VAR(struct ast_str *, variant_dir, ast_str_create(64), ast_free); 95 | 96 | if (!media_dir || !variant_dir) { 97 | return -1; 98 | } 99 | 100 | if (directoryprefix[0] == '/') { /* absolute path */ 101 | ast_str_set(&media_dir, 0, "%s", directoryprefix); 102 | } else if (directoryprefix[0]) { 103 | ast_str_set(&media_dir, 0, "%s/sounds/%s/%s", ast_config_AST_DATA_DIR, chan ? ast_channel_language(chan) : "en", directoryprefix); 104 | } else { 105 | ast_str_set(&media_dir, 0, "%s/sounds/%s", ast_config_AST_DATA_DIR, chan ? ast_channel_language(chan) : "en"); 106 | } 107 | 108 | fullpath = ast_str_buffer(media_dir); 109 | baselen = strlen(fullpath); 110 | if (!fullpath[0]) { 111 | return -1; 112 | } 113 | if (fullpath[baselen - 1] == '/') { /* ends in trailing slash... but dirname ignores trailing slash */ 114 | dir = fullpath; 115 | dir[baselen - 1] = '\0'; 116 | base = ""; 117 | } else { 118 | dir = dirname(ast_strdupa(fullpath)); 119 | base = basename(ast_strdupa(fullpath)); 120 | } 121 | 122 | ast_debug(1, "'%s' -> '%s' -> dirname '%s', basename '%s'\n", directoryprefix, fullpath, dir, base); 123 | 124 | baselen = strlen(base); 125 | srcdir = opendir(dir); 126 | if (!srcdir) { 127 | ast_debug(1, "Failed to open '%s'\n", dir); 128 | return -1; 129 | } 130 | 131 | while((dent = readdir(srcdir)) != NULL) { 132 | struct stat st; 133 | 134 | if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) { 135 | continue; /* parent directory */ 136 | } 137 | if (*base && strncmp(dent->d_name, base, baselen)) { 138 | continue; /* doesn't match required file prefix */ 139 | } 140 | 141 | ast_str_reset(variant_dir); 142 | ast_str_set(&variant_dir, 0, "%s/%s", dir, dent->d_name); 143 | 144 | if (stat(ast_str_buffer(variant_dir), &st) < 0) { 145 | ast_log(LOG_ERROR, "Failed to stat %s\n", ast_str_buffer(variant_dir)); 146 | continue; 147 | } 148 | if (S_ISDIR(st.st_mode)) { 149 | continue; /* don't care about subdirectories */ 150 | } 151 | files++; 152 | if (filenum && filenum == files) { 153 | int slen = ast_str_strlen(variant_dir) + 1; 154 | *buffer = ast_malloc(slen); 155 | if (!*buffer) { 156 | files = -1; 157 | break; 158 | } 159 | ast_copy_string(*buffer, ast_str_buffer(variant_dir), slen); 160 | break; 161 | } 162 | } 163 | 164 | closedir(srcdir); 165 | return files; 166 | } 167 | 168 | static int dir_file_count(struct ast_channel *chan, char *directoryprefix) 169 | { 170 | return traverse_directory(chan, directoryprefix, 0, NULL); 171 | } 172 | 173 | static char *dir_file_match(struct ast_channel *chan, char *directoryprefix, int filenum) 174 | { 175 | char *buf = NULL; 176 | traverse_directory(chan, directoryprefix, filenum, &buf); 177 | return buf; 178 | } 179 | 180 | static int playback_exec(struct ast_channel *chan, const char *data) 181 | { 182 | int numfiles, rand; 183 | int res = 0; 184 | char *tmp, *buf; 185 | 186 | AST_DECLARE_APP_ARGS(args, 187 | AST_APP_ARG(dirprefix); 188 | ); 189 | 190 | if (ast_strlen_zero(data)) { 191 | ast_log(LOG_WARNING, "RandomPlayback requires an argument (filename)\n"); 192 | return -1; 193 | } 194 | 195 | tmp = ast_strdupa(data); 196 | AST_STANDARD_APP_ARGS(args, tmp); 197 | /* do not auto-answer channel */ 198 | 199 | numfiles = dir_file_count(chan, args.dirprefix); 200 | if (numfiles == -1) { 201 | ast_log(LOG_WARNING, "Failed to calculate number of files prefixed with '%s'\n", args.dirprefix); 202 | return -1; 203 | } 204 | if (!numfiles) { 205 | ast_log(LOG_WARNING, "No files prefixed with '%s'\n", args.dirprefix); 206 | return 0; 207 | } 208 | rand = 1 + (ast_random() % numfiles); 209 | buf = dir_file_match(chan, args.dirprefix, rand); 210 | if (!buf) { 211 | ast_log(LOG_WARNING, "Failed to find random file # %d prefixed with '%s'\n", rand, args.dirprefix); 212 | return -1; 213 | } 214 | 215 | ast_debug(1, "Lucky file was #%d/%d: '%s'\n", rand, numfiles, buf); 216 | 217 | tmp = buf + strlen(buf) - 1; 218 | while (tmp != buf && tmp[0] != '.') { 219 | tmp--; 220 | } 221 | tmp[0] = '\0'; /* get rid of the file extension, for ast_streamfile */ 222 | 223 | ast_stopstream(chan); 224 | res = ast_streamfile(chan, buf, ast_channel_language(chan)); 225 | if (!res) { 226 | res = ast_waitstream(chan, ""); 227 | ast_stopstream(chan); 228 | } 229 | 230 | ast_free(buf); 231 | return res; 232 | } 233 | 234 | static int unload_module(void) 235 | { 236 | int res; 237 | 238 | res = ast_unregister_application(app); 239 | 240 | return res; 241 | } 242 | 243 | static int load_module(void) 244 | { 245 | return ast_register_application_xml(app, playback_exec); 246 | } 247 | 248 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Random Playback Application"); 249 | -------------------------------------------------------------------------------- /apps/app_streamsilence.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2021, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Stream silent audio 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup applications 26 | */ 27 | 28 | /*** MODULEINFO 29 | extended 30 | ***/ 31 | 32 | #include "asterisk.h" 33 | 34 | #include "asterisk/logger.h" 35 | #include "asterisk/channel.h" 36 | #include "asterisk/pbx.h" 37 | #include "asterisk/module.h" 38 | #include "asterisk/app.h" 39 | 40 | /*** DOCUMENTATION 41 | 42 | 43 | Streams silence to a channel. 44 | 45 | 46 | 47 | The maximum amount of time, in seconds, this application should stream silent 48 | audio before dialplan execution continues automatically to the next priority. 49 | By default, there is no timeout. 50 | 51 | 52 | 53 | Streams silent audio to a channel, for up to a provided number of seconds. 54 | This application will send silent audio to a channel, as opposed to applications 55 | like Wait which do not. This guarantees that audiohooks will 56 | function properly, even if the channel is not bridged to something that is continously 57 | sending frames. 58 | 59 | 60 | Wait 61 | 62 | 63 | 64 | 65 | Stream silent audio on a channel. 66 | 67 | 68 | 69 | 1 to enable or empty to disable. 70 | 71 | 72 | 73 | Streams silent audio on a channel until disabled. 74 | This ensures that audiohooks can be processed constantly 75 | even if the channel receives no other frames. 76 | 77 | 78 | ChanSpy 79 | 80 | 81 | ***/ 82 | 83 | static char *app = "StreamSilence"; 84 | 85 | static int streamsilence_exec(struct ast_channel *chan, const char *data) 86 | { 87 | struct timeval start = ast_tvnow(); 88 | struct ast_silence_generator *g; 89 | int timeout_ms = 0; 90 | char *appdata; 91 | AST_DECLARE_APP_ARGS(args, 92 | AST_APP_ARG(timeout); 93 | ); 94 | 95 | appdata = ast_strdupa(S_OR(data, "")); 96 | AST_STANDARD_APP_ARGS(args, appdata); 97 | 98 | if (!ast_strlen_zero(args.timeout)) { 99 | ast_app_parse_timelen(args.timeout, &timeout_ms, TIMELEN_SECONDS); 100 | } 101 | if (timeout_ms > 0) { 102 | ast_debug(1, "Waiting for %d ms\n", timeout_ms); 103 | } 104 | g = ast_channel_start_silence_generator(chan); 105 | while (timeout_ms == 0 || ast_remaining_ms(start, timeout_ms)) { 106 | if (ast_safe_sleep(chan, 1000)) { 107 | ast_channel_stop_silence_generator(chan, g); 108 | return -1; /* channel hung up */ 109 | } 110 | } 111 | ast_channel_stop_silence_generator(chan, g); 112 | return 0; 113 | } 114 | 115 | /*! \brief Static structure for datastore information */ 116 | static const struct ast_datastore_info streamsilence_datastore = { 117 | .type = "streamsilence", 118 | }; 119 | 120 | /*! \internal \brief Disable silence stream on the channel */ 121 | static int remove_streamsilence(struct ast_channel *chan) 122 | { 123 | struct ast_datastore *datastore = NULL; 124 | struct ast_silence_generator *g; 125 | SCOPED_CHANNELLOCK(chan_lock, chan); 126 | 127 | datastore = ast_channel_datastore_find(chan, &streamsilence_datastore, NULL); 128 | if (!datastore) { 129 | ast_log(AST_LOG_WARNING, "Cannot remove STREAM_SILENCE from %s: STREAM_SILENCE not currently enabled\n", 130 | ast_channel_name(chan)); 131 | return -1; 132 | } 133 | g = datastore->data; 134 | if (g) { 135 | ast_channel_stop_silence_generator(chan, g); 136 | /* no need to call ast_free, stop_silence_generator frees g for us */ 137 | g = NULL; 138 | } 139 | if (ast_channel_datastore_remove(chan, datastore)) { 140 | ast_log(AST_LOG_WARNING, "Failed to remove STREAM_SILENCE datastore from channel %s\n", 141 | ast_channel_name(chan)); 142 | return -1; 143 | } 144 | ast_datastore_free(datastore); 145 | 146 | return 0; 147 | } 148 | 149 | static int streamsilence_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) 150 | { 151 | struct ast_datastore *datastore = NULL; 152 | struct ast_silence_generator *g; 153 | 154 | if (!chan) { 155 | ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd); 156 | return -1; 157 | } 158 | if (ast_strlen_zero(value)) { 159 | return remove_streamsilence(chan); 160 | } 161 | if (!strchr(value, '1')) { 162 | ast_log(LOG_WARNING, "Invalid argument: %s\n", value); 163 | return -1; 164 | } 165 | 166 | ast_channel_lock(chan); 167 | if (!(datastore = ast_channel_datastore_find(chan, &streamsilence_datastore, NULL))) { 168 | /* Allocate a new datastore to hold the reference to this audiohook information */ 169 | if (!(datastore = ast_datastore_alloc(&streamsilence_datastore, NULL))) { 170 | ast_channel_unlock(chan); 171 | return 0; 172 | } 173 | g = ast_channel_start_silence_generator(chan); 174 | datastore->data = g; 175 | ast_channel_datastore_add(chan, datastore); 176 | } else { 177 | ast_debug(1, "Silence generator already active, ignoring\n"); 178 | } 179 | ast_channel_unlock(chan); 180 | 181 | return 0; 182 | } 183 | 184 | static struct ast_custom_function streamsilence_function = { 185 | .name = "STREAM_SILENCE", 186 | .write = streamsilence_write, 187 | }; 188 | 189 | static int unload_module(void) 190 | { 191 | int res; 192 | 193 | res = ast_custom_function_unregister(&streamsilence_function); 194 | res |= ast_unregister_application(app); 195 | 196 | return res; 197 | } 198 | 199 | static int load_module(void) 200 | { 201 | int res; 202 | 203 | res = ast_custom_function_register(&streamsilence_function); 204 | res = ast_register_application_xml(app, streamsilence_exec); 205 | 206 | return res; 207 | } 208 | 209 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Streams silent audio to a channel"); 210 | -------------------------------------------------------------------------------- /apps/app_tonetest.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2021, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Tone test module 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup applications 26 | */ 27 | 28 | /*** MODULEINFO 29 | extended 30 | ***/ 31 | 32 | #include "asterisk.h" 33 | 34 | #include 35 | 36 | #include "asterisk/module.h" 37 | #include "asterisk/frame.h" 38 | #include "asterisk/format_cache.h" 39 | #include "asterisk/channel.h" 40 | #include "asterisk/dsp.h" 41 | #include "asterisk/pbx.h" 42 | #include "asterisk/audiohook.h" 43 | #include "asterisk/app.h" 44 | #include "asterisk/indications.h" 45 | #include "asterisk/conversions.h" 46 | 47 | /*** DOCUMENTATION 48 | 49 | 50 | Tone sweep test 51 | 52 | 53 | 54 | Starting frequency. Default is 100. 55 | 56 | 57 | Ending frequency. Default is 3500. 58 | 59 | 60 | Total time for sweep, in seconds. Default is 61 | 10 seconds. 62 | 63 | 64 | Volume reduction factor. Higher number equals 65 | softer tone sweep. Default is 1 (loudest). 66 | 67 | 68 | 69 | Generates an ascending or descending tone sweep (chirp) between two frequencies. 70 | 71 | 72 | Milliwatt 73 | PlayTones 74 | 75 | 76 | ***/ 77 | 78 | #define TONE_AMPLITUDE_MAX 0x7fff /* Max signed linear amplitude */ 79 | 80 | static char *chirpapp = "ToneSweep"; 81 | 82 | static void tone_sample_gen(short *slin_buf, int samples, int rate, int freq, short amplitude) 83 | { 84 | int idx; 85 | double sample_step = 2.0 * M_PI * freq / rate;/* radians per step */ 86 | 87 | for (idx = 0; idx < samples; ++idx) { 88 | slin_buf[idx] = amplitude * sin(sample_step * idx); 89 | } 90 | } 91 | 92 | static int tone_freq_sweep(struct ast_channel *chan, short amplitude, int start, int end, int incr, int timeout) 93 | { 94 | int res, freq, samples = 160, len = samples * 2, result = 0; 95 | struct timeval timerstart; 96 | if (ast_set_write_format(chan, ast_format_slin)) { 97 | ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", ast_channel_name(chan)); 98 | return -1; 99 | } 100 | /* Sweep frequencies loop. */ 101 | for (freq = start; freq != end; freq += incr) { 102 | timerstart = ast_tvnow(); 103 | while (ast_remaining_ms(timerstart, timeout) > 0) { 104 | if (ast_waitfor(chan, 1000) > 0) { 105 | short slin_buf[samples + AST_FRIENDLY_OFFSET]; 106 | struct ast_frame wf = { 107 | .frametype = AST_FRAME_VOICE, 108 | .offset = AST_FRIENDLY_OFFSET, 109 | .subclass.format = ast_format_slin, 110 | .datalen = len, 111 | .samples = samples, 112 | .src = __FUNCTION__, 113 | }; 114 | ast_debug(5, "%d Hz tone at amplitude %d.\n", freq, amplitude); 115 | tone_sample_gen(slin_buf + AST_FRIENDLY_OFFSET, samples, DEFAULT_SAMPLE_RATE, freq, amplitude); 116 | wf.data.ptr = slin_buf + AST_FRIENDLY_OFFSET; 117 | res = ast_write(chan, &wf); 118 | ast_frfree(&wf); 119 | if (res < 0) { 120 | return -1; 121 | } 122 | } else { 123 | return -1; 124 | } 125 | } 126 | } 127 | 128 | return result; 129 | } 130 | 131 | static int chirp_exec(struct ast_channel *chan, const char *data) 132 | { 133 | char *appdata; 134 | int mssleeptime, dir; 135 | double tmpduration; 136 | int start = 100, end = 3500, vol = 1, duration = 10000; /* 10 seconds, default */ 137 | AST_DECLARE_APP_ARGS(args, 138 | AST_APP_ARG(start); 139 | AST_APP_ARG(end); 140 | AST_APP_ARG(duration); 141 | AST_APP_ARG(vol); 142 | ); 143 | 144 | appdata = ast_strdupa(S_OR(data, "")); 145 | AST_STANDARD_APP_ARGS(args, appdata); 146 | 147 | if (!ast_strlen_zero(args.start) && ast_str_to_int(args.start, &start)) { 148 | if (start <= 0) { 149 | ast_log(LOG_WARNING, "Start frequency must be positive: %s\n", args.start); 150 | return -1; 151 | } 152 | } 153 | if (!ast_strlen_zero(args.end) && ast_str_to_int(args.end, &end)) { 154 | if (end <= 0) { 155 | ast_log(LOG_WARNING, "End frequency must be positive: %s\n", args.end); 156 | return -1; 157 | } 158 | } 159 | if (!ast_strlen_zero(args.duration) && sscanf(args.duration, "%30lf", &tmpduration) == 1) { 160 | duration = (int) (1000 * tmpduration); 161 | if (duration <= 0) { 162 | ast_log(LOG_WARNING, "Duration must be positive: %s\n", args.duration); 163 | return -1; 164 | } 165 | } 166 | if (!ast_strlen_zero(args.vol) && ast_str_to_int(args.vol, &vol)) { 167 | if (vol <= 0) { 168 | ast_log(LOG_WARNING, "Volume reduction factor: %s\n", args.vol); 169 | return -1; 170 | } 171 | } 172 | 173 | mssleeptime = (duration / abs(end - start)); 174 | 175 | if (end < start) { 176 | dir = -1; 177 | } else if (end > start) { 178 | dir = 1; 179 | } else { 180 | ast_log(LOG_WARNING, "Start and end frequencies must be different: %d\n", start); 181 | return -1; 182 | } 183 | 184 | ast_debug(1, "Starting tone sweep from %d to %d Hz for %d s (%d ms each Hz)\n", start, end, duration, mssleeptime); 185 | tone_freq_sweep(chan, TONE_AMPLITUDE_MAX / vol, start, end, 1 * dir, mssleeptime); 186 | 187 | return 0; 188 | } 189 | 190 | static int unload_module(void) 191 | { 192 | int res; 193 | 194 | res = ast_unregister_application(chirpapp); 195 | 196 | return res; 197 | } 198 | 199 | static int load_module(void) 200 | { 201 | int res; 202 | 203 | res = ast_register_application_xml(chirpapp, chirp_exec); 204 | 205 | return res; 206 | } 207 | 208 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Tone test module"); 209 | -------------------------------------------------------------------------------- /apps/app_wrappers.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2024, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Trivial application wrappers 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup applications 26 | */ 27 | 28 | /*** MODULEINFO 29 | extended 30 | ***/ 31 | 32 | #include "asterisk.h" 33 | 34 | #include "asterisk/pbx.h" 35 | #include "asterisk/module.h" 36 | #include "asterisk/app.h" 37 | #include "asterisk/indications.h" 38 | #include "asterisk/causes.h" 39 | 40 | /*** DOCUMENTATION 41 | 42 | 43 | Dial a destination, play in-band signalling if needed, and hang up the channel 44 | 45 | 46 | 47 | Arguments to the Dial application 48 | 49 | 50 | 51 | This application is a simple wrapper which calls Dial, plays the appropriate 52 | precise tone plan tones if needed, and then hangs up the channel. 53 | This is meant to simplify the very common idiom of dialing a destination, 54 | branching depending on the DIALSTATUS, and then hanging up. 55 | Dialplan execution will not continue after calling this application. 56 | 57 | 58 | Dial 59 | 60 | 61 | ***/ 62 | 63 | static char *dial_app = "InbandDial"; 64 | 65 | static int dial_exec(struct ast_channel *chan, const char *data) 66 | { 67 | struct ast_tone_zone_sound *ts = NULL; 68 | char dialstatus[64] = ""; 69 | char hangupcause[16] = ""; 70 | int cause = 0; 71 | 72 | if (ast_strlen_zero(data)) { 73 | ast_log(LOG_ERROR, "%s requires arguments\n", dial_app); 74 | return -1; 75 | } 76 | 77 | /* Even if Dial returns -1, we want to proceed for logging purposes and to hang up directly */ 78 | ast_pbx_exec_application(chan, "Dial", data); 79 | 80 | ast_channel_lock(chan); 81 | ast_copy_string(dialstatus, S_OR(pbx_builtin_getvar_helper(chan, "DIALSTATUS"), ""), sizeof(dialstatus)); 82 | ast_copy_string(hangupcause, S_OR(pbx_builtin_getvar_helper(chan, "HANGUPCAUSE"), ""), sizeof(hangupcause)); 83 | ast_channel_unlock(chan); 84 | 85 | if (!ast_strlen_zero(hangupcause)) { 86 | ast_verb(4, "Call ended, DIALSTATUS: %s (HANGUPCAUSE: %s)\n", dialstatus, hangupcause); 87 | } else { 88 | ast_verb(4, "Call ended, DIALSTATUS: %s\n", dialstatus); 89 | } 90 | 91 | if (!strcmp(dialstatus, "ANSWER")) { 92 | /* Normal answer and clearing */ 93 | } else if (!strcmp(dialstatus, "NOANSWER")) { 94 | /* Remote end disconnected without providing answer supervision. Continue. */ 95 | } else if (!strcmp(dialstatus, "CANCEL")) { 96 | /* Caller hung up before answer. Continue. */ 97 | } else if (!strcmp(dialstatus, "BUSY")) { 98 | /* Out of band busy, make it in band */ 99 | ts = ast_get_indication_tone(ast_channel_zone(chan), "busy"); /* Play busy tone */ 100 | } else if (!strcmp(dialstatus, "CHANUNAVAIL") || !strcmp(dialstatus, "CONGESTION")) { 101 | ts = ast_get_indication_tone(ast_channel_zone(chan), "congestion"); /* Play reorder tone */ 102 | } else { 103 | ast_log(LOG_WARNING, "Channel %s exited Dial with unexpected DIALSTATUS '%s'\n", ast_channel_name(chan), dialstatus); 104 | } 105 | 106 | if (ts) { 107 | int res = ast_playtones_start(chan, 0, ts->data, 0); 108 | ts = ast_tone_zone_sound_unref(ts); 109 | if (res) { 110 | ast_log(LOG_WARNING, "Unable to start tones on channel %s\n", ast_channel_name(chan)); 111 | } else { 112 | /* Stream the tone until the caller disconnects, up to a maximum of 3 minutes. 113 | * At that point, if the caller hasn't disconnected, something could be wrong, 114 | * so we shouldn't wait forever for the channel to clear. */ 115 | if (ast_safe_sleep(chan, 180 * 1000)) { 116 | return -1; 117 | } 118 | } 119 | } 120 | 121 | /* Based on pbx_builtin_hangup */ 122 | ast_set_hangupsource(chan, "dialplan/dialinband", 0); 123 | 124 | if (!ast_strlen_zero(hangupcause)) { 125 | cause = ast_str2cause(hangupcause); 126 | if (cause <= 0) { 127 | if (sscanf(hangupcause, "%30d", &cause) != 1 || cause <= 0) { 128 | ast_log(LOG_WARNING, "Invalid hangup cause: \"%s\"\n", data); 129 | cause = 0; 130 | } 131 | } 132 | } 133 | 134 | ast_channel_lock(chan); 135 | if (cause <= 0) { 136 | cause = ast_channel_hangupcause(chan); 137 | if (cause <= 0) { 138 | cause = AST_CAUSE_NORMAL_CLEARING; 139 | } 140 | } 141 | ast_channel_hangupcause_set(chan, cause); 142 | ast_softhangup_nolock(chan, AST_SOFTHANGUP_EXPLICIT); 143 | ast_channel_unlock(chan); 144 | 145 | return -1; 146 | } 147 | 148 | static int unload_module(void) 149 | { 150 | int res; 151 | 152 | res = ast_unregister_application(dial_app); 153 | 154 | return res; 155 | } 156 | 157 | static int load_module(void) 158 | { 159 | return ast_register_application_xml(dial_app, dial_exec); 160 | } 161 | 162 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Trivial Dialplan Application Wrappers"); 163 | -------------------------------------------------------------------------------- /configs/samples/irc.conf.sample: -------------------------------------------------------------------------------- 1 | ; 2 | ; IRC Configuration 3 | ; 4 | ; This file is used by res_irc 5 | ; 6 | ;[general] ; IRC server details. Currently, only one server is supported. 7 | ; 8 | ;hostname = irc.libera.chat ; Hostname of IRC server 9 | ;port = 6667 ; IRC port - default is 6667 (depends on whether it's plain text or SSL/TLS, check with your IRC server) 10 | ;username = jsmith ; IRC nickname/username 11 | ;password = password123 ; IRC password 12 | ;autojoin = #asterisk ; Comma-separated list of channels to join on startup. Default is none. 13 | ;tls = yes ; Whether to secure the connection with TLS or not. Default is no, but this is recommended if supported by your server. 14 | ; Don't forget to use the appropriate port for TLS connections, per your IRC server. 15 | ;tlsverify = no ; Whether to verify the peer's SSL certificate. If you receive verify warnings, disabling this may work. Default is no. 16 | ;sasl = yes ; Whether or not to use SASL authentication. Currently, only plain-text SASL auth is supported. Default is no. 17 | ;events = yes ; Whether or not to emit AMI events on incoming messages from IRC channels. Default is no. 18 | -------------------------------------------------------------------------------- /configs/samples/res_alarmsystem.conf.sample: -------------------------------------------------------------------------------- 1 | ; res_alarmsystem.conf 2 | 3 | ; This configuration allows configuring an alarm system using Asterisk. There are two roles 4 | ; that exist, client and server. Clients receive input from one or more sensors and 5 | ; report alarm events to a server. Typically, these will be different Asterisk servers, 6 | ; with clients at individual sites and a single, centralized server receiving events from them, 7 | ; but they could also both run on the same server if desired. 8 | ; However, it is RECOMMENDED that the server role not be at a location with sensors. 9 | ; This way, if someone were to sabotage the alarm system at the client side prior to 10 | ; the breach event, the alarm server will still be aware that something has gone wrong. 11 | 12 | ; WARNING WARNING WARNING This alarm system implementation comes with absolutely no warranty, 13 | ; use it at your own risk! 14 | 15 | ; *** Server configuration 16 | 17 | ;[general] 18 | ;bindport=4589 ; UDP port to which alarm server will bind, if any servers are enabled. 19 | ;bindaddr=0.0.0.0 20 | 21 | ;[myserver] ; Defines an alarm server to which alarm clients can report. 22 | ;type = server 23 | ;ip_loss_tolerance = 60 ; Number of seconds the server will tolerate not receiving pings from clients before considering 24 | ; IP connectivity to a client to have been lost (which will trigger the internet_lost alarm). 25 | ;contexts = myserver-contexts ; A config section which defines dialplan contexts to execute for each alarm event 26 | ;logfile = /var/log/asterisk/alarm_server.log ; Log file for this server 27 | 28 | ;[clients] ; Special section defining clients authorized to report to this server 29 | ;A101 = 1D7B ; one entry for each client, with client ID as the key and client PIN as the value 30 | 31 | ; *** Client configuration 32 | 33 | ;[myclient] ; Each alarm client is defined in its own section 34 | ;type = client 35 | ;client_id = A101 ; Unique, telenumeric ID (0-9,A-D) for this client. Should be unique across all clients that report to a server. 36 | ; Note that there is no security mechanism to restrict reporting aside from the client ID. 37 | ; Therefore, if the alarm server is exposed to the Internet, you may wish to use long, hard-to-guess client IDs 38 | ; to prevent spoofed reports, or lock down your firewall accordingly. 39 | ;client_pin=1D794B61 ; PIN, if required by server for authentication 40 | ;server_ip = 127.0.0.1:4589 ; IP/port to reach alarm server over IP 41 | ;server_dialstr = DAHDI/g1/*70w18005551212 ; A dial string for "POTS phone failover" to reach alarm server if unable to by IP. If you need to use dial options, use a Local channel to encapsulate the Dial() call. 42 | ; The server should call AlarmEventReceiver() (NOT AlarmReceiver() !!!) when receiving such a call. 43 | ; NOTE: When reporting an alarm trigger, the line will stay open until disarm_delay has been reached, to avoid making multiple calls in succession 44 | ;phone_hangup_delay = 45 ; Number of seconds to keep phone failover line open upon reporting event for reporting further events in that time. 45 | ; It is recommended this setting be at least 10-15 seconds, so that if phone failover is being used, 46 | ; a single phone call is sufficient to report sensor trigger and alarm disarm events, rather than dialing up a second time to report this event. 47 | ; You may want to tweak this based on the cost of each call, cost per minute, and the acceptable amount of delay in setting up a call. Default is 45. 48 | ;ping_interval = 4 ; How often to ping the server 49 | ;egress_delay = 15 ; number of seconds grace period to exit without re-triggering alarm 50 | ;contexts = myclient-contexts ; A config section which defines dialplan contexts to execute for each alarm event 51 | ;logfile = /var/log/asterisk/alarm_myclient.log ; Log file to which to log alarm events. 52 | 53 | ;[myclient-contexts] 54 | ;type = contexts 55 | ; In this section, the key is the name of the alarm event for which the specified dialplan will be executed, 56 | ; and the value is the dialplan [exten@]context to execute. If exten is omitted, s will be used. The priority will always be 1. 57 | ; 58 | ; okay = okay@myclientalarm ; Normal initialization 59 | ; triggered = triggered@myclientalarm ; Sensor has triggered alarm 60 | ; restored = restored@myclientalarm ; Sensor restored to normal (will not auto-disarm pending alarm) 61 | ; disarmed = disarmed@myclientalarm ; Alarm disarmed by user 62 | ; tempdisarmed = tempdisarmed@myclientalarm ; Alarm temporarily disarmed by user 63 | ; breach = breach@myclientalarm ; Breach (failure to disarm active alarm) has occured 64 | ; internet_lost = iplost@myclientalarm ; Internet connectivity to alarm peer lost 65 | ; internet_restored = iprestored@myclientalarm ; Internet connectivity to alarm peer restored 66 | 67 | ;[door] ; Section defining a door sensor 68 | ;type = sensor 69 | ;sensor_id = 1 ; Unique, numeric ID for this sensor. Should be unique across all sensors belongng to all clients that report to a server. 70 | ;client = myclient ; Client associated with this sensor 71 | ;device = DAHDI/23 ; if specified, then arg2 to AlarmSensor is optional since we can use the channel to determine which sensor was activated 72 | ; (This way, the same context can be specified for all sensors, using immediate=yes in chan_dahdi.conf) 73 | ;disarm_delay = 45 ; Number of seconds grace period permitted to disarm an active alarm after this sensor triggers before it is considered a breach. 74 | ; Default is 60. 75 | 76 | ;[window] ; Section defining a window sensor, which does not trigger alarms (reporting only) 77 | ;type = sensor 78 | ;sensor_id = 2 79 | ;client = myclient 80 | ;device = DAHDI/24 81 | ;disarm_delay = 0 ; Do not require the system to be disarmed when this sensor is triggered (reporting is informational only) 82 | 83 | ;[keypad] ; Section defining alarm keypad settings. An alarm keypad can be instantiated by using AlarmKeypad() 84 | ;type = keypad 85 | ;client = myclient ; Client associated with these keypad settings 86 | ;keypad_device = PJSIP/Polycom ; dial string for alarm keypad endpoints to autodial when alarm is triggered. Use a Local channel for predial options to autoanswer. 87 | ;pin = 1234 ; Hardcoded PIN which must be entered to disarm the alarm. Multiple PINs can be permitted by providing multiple comma-separated PINs. 88 | ;audio = custom/siren ; An optional audio file to play while waiting for the alarm to be disarmed. By default, a tone is played. 89 | ;cid_num = DISARM SYSTEM NOW ; Caller ID number to use for outgoing calls to the keypad device 90 | ;cid_name = ALARM PANEL ; Caller ID name to use for outgoing calls to the keypad device 91 | -------------------------------------------------------------------------------- /configs/samples/res_smdr_whozz.conf.sample: -------------------------------------------------------------------------------- 1 | ; res_smdr_whozz.conf - SMDR for "WHOZZ Calling?" call accounting devices 2 | 3 | ; TIPS: Both serial and parallel wiring of the phone line to your WHOZZ Calling? device are supported. 4 | ; It is recommended that you wire in parallel if possible to avoid putting the unit in the voice path. 5 | ; Additionally, please note that the WHOZZ Calling? units do NOT support pulse dialing, 6 | ; and pulse-to-tone converters may degrade the quality of the voice path. You may wish 7 | ; to drive a slave / follower line to follow hook state changes and receive dialed digits (but not audio) 8 | ; and connect that to the unit instead. 9 | ; As always, USE AT YOUR OWN RISK. 10 | 11 | ;[general] 12 | ;device=/dev/ttyS0 ; Device name of serial port to which WHOZZ Calling? unit is connected (9600/N/1) 13 | 14 | ;[line-1] 15 | ;line = 1 ; Line number as configured on WHOZZ Calling? unit. 16 | ;device=DAHDI/1 ; Optional. Device name of associated FXO port connected to this same line. 17 | ; If configured, if calls are made using this device, they will be ignored by this module. 18 | ; This allows this module to create CDRs for calls made directly on a phone line connected 19 | ; to an Asterisk FXO port, when the call was NOT made through Asterisk, 20 | ; but allows Asterisk to create the CDR record if the call was made through Asterisk. 21 | ; This avoids duplicate CDR records for the same call. 22 | ; Optional variables to set, for example, for outgoing calls, you could do a lookup to determine if a call is local, long distance, etc. based on dialed number. 23 | ;setvar=CDR(trunkgrp)=pots-1 24 | ;setvar=CDR(direction)=${WHOZZ_DIRECTION} 25 | ;setvar=CDR(line)=${WHOZZ_LINE} 26 | ;setvar=CHANNEL(amaflags)=BILLING 27 | 28 | ;[line-2] 29 | ;line = 2 30 | -------------------------------------------------------------------------------- /funcs/func_dtmf_flash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2022, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Function to intercept long DTMF digits and spawn hook flash 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup functions 26 | */ 27 | 28 | /*** MODULEINFO 29 | extended 30 | ***/ 31 | 32 | #include "asterisk.h" 33 | 34 | #include "asterisk/module.h" 35 | #include "asterisk/channel.h" 36 | #include "asterisk/app.h" 37 | #include "asterisk/pbx.h" 38 | #include "asterisk/framehook.h" 39 | 40 | /*** DOCUMENTATION 41 | 42 | 43 | Intercepts long DTMF frames on a channel and treats them as a hook flash instead 44 | 45 | 46 | 47 | RX or TX 48 | 49 | 50 | Digit to intercept. Default is * (star). 51 | 52 | 53 | Required length (in milliseconds) of digit to intercept. Default is 750. 54 | 55 | 56 | 57 | Listens for long DTMF digits and treats a special digit of a certain duration as a hook flash on the channel instead. 58 | This can be useful for IP devices that do not have the capability of sending a hook flash signal natively. 59 | This functionality requires that DTMF emulation be used for the device. This means that a DTMF START event is processed when a user begins holding down a DTMF key and the DTMF END event is not processed until the user has let go of the key. If fixed durations are used for DTMF, this functionality will likely not work for your endpoint. 60 | 61 | same => n,Set(LONG_DTMF_INTERCEPT(RX,*,750)=) ; provide hook flash capability via long "*" on IP phones 62 | 63 | 64 | 65 | ***/ 66 | 67 | enum direction { 68 | TX = 0, 69 | RX, 70 | }; 71 | 72 | struct dtmf2flash_data { 73 | enum direction fdirection; 74 | char digit; 75 | unsigned int duration; 76 | }; 77 | 78 | static void datastore_destroy_cb(void *data) { 79 | ast_free(data); 80 | } 81 | 82 | static const struct ast_datastore_info dtmf2flash_datastore = { 83 | .type = "dmtfflash", 84 | .destroy = datastore_destroy_cb 85 | }; 86 | 87 | static void hook_destroy_cb(void *framedata) 88 | { 89 | ast_free(framedata); 90 | } 91 | 92 | static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data) 93 | { 94 | char digit; 95 | long int len; 96 | struct dtmf2flash_data *framedata = data; 97 | 98 | if (!frame) { 99 | return frame; 100 | } 101 | if (!((event == AST_FRAMEHOOK_EVENT_WRITE && framedata->fdirection == TX) || 102 | (event == AST_FRAMEHOOK_EVENT_READ && framedata->fdirection == RX))) { 103 | return frame; 104 | } 105 | if (frame->frametype != AST_FRAME_DTMF_END) { 106 | return frame; 107 | } 108 | 109 | len = frame->len; 110 | digit = frame->subclass.integer; 111 | 112 | if (digit != framedata->digit) { 113 | return frame; /* not the right digit */ 114 | } 115 | if (len < framedata->duration) { 116 | return frame; /* too short to matter */ 117 | } 118 | 119 | ast_verb(3, "Got long DTMF digit '%c' (%ld ms), processing hook flash for %s\n", digit, len, ast_channel_name(chan)); 120 | 121 | ast_frfree(frame); 122 | frame = &ast_null_frame; 123 | { 124 | struct ast_frame f = { AST_FRAME_CONTROL, { AST_CONTROL_FLASH, } }; 125 | ast_queue_frame(chan, &f); 126 | } 127 | return frame; 128 | } 129 | 130 | static int dtmf2flash_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value) 131 | { 132 | struct dtmf2flash_data *framedata; 133 | struct ast_datastore *datastore = NULL; 134 | struct ast_framehook_interface interface = { 135 | .version = AST_FRAMEHOOK_INTERFACE_VERSION, 136 | .event_cb = hook_event_cb, 137 | .destroy_cb = hook_destroy_cb, 138 | }; 139 | int i = 0; 140 | char *parse; 141 | 142 | AST_DECLARE_APP_ARGS(args, 143 | AST_APP_ARG(direction); 144 | AST_APP_ARG(digit); 145 | AST_APP_ARG(duration); 146 | ); 147 | 148 | parse = ast_strdupa(S_OR(data, "")); 149 | AST_STANDARD_APP_ARGS(args, parse); 150 | 151 | if (!(framedata = ast_calloc(1, sizeof(*framedata)))) { 152 | return 0; 153 | } 154 | 155 | interface.data = framedata; 156 | 157 | if (!strcasecmp(args.direction, "TX")) { 158 | framedata->fdirection = TX; 159 | } else { 160 | framedata->fdirection = RX; 161 | } 162 | framedata->digit = ast_strlen_zero(args.digit) ? '*' : args.digit[0]; 163 | framedata->duration = ast_strlen_zero(args.duration) ? 750 : atoi(args.duration); 164 | 165 | ast_channel_lock(chan); 166 | i = ast_framehook_attach(chan, &interface); 167 | if (i >= 0) { 168 | int *id; 169 | if ((datastore = ast_channel_datastore_find(chan, &dtmf2flash_datastore, NULL))) { 170 | id = datastore->data; 171 | ast_framehook_detach(chan, *id); 172 | ast_channel_datastore_remove(chan, datastore); 173 | ast_datastore_free(datastore); 174 | } 175 | 176 | if (!(datastore = ast_datastore_alloc(&dtmf2flash_datastore, NULL))) { 177 | ast_framehook_detach(chan, i); 178 | ast_channel_unlock(chan); 179 | return 0; 180 | } 181 | 182 | if (!(id = ast_calloc(1, sizeof(int)))) { 183 | ast_datastore_free(datastore); 184 | ast_framehook_detach(chan, i); 185 | ast_channel_unlock(chan); 186 | return 0; 187 | } 188 | 189 | *id = i; /* Store off the id. The channel is still locked so it is safe to access this ptr. */ 190 | datastore->data = id; 191 | ast_channel_datastore_add(chan, datastore); 192 | ast_debug(1, "Set up interception of long DTMF of digit '%c' with duration %u\n", framedata->digit, framedata->duration); 193 | } 194 | ast_channel_unlock(chan); 195 | 196 | return 0; 197 | } 198 | 199 | static struct ast_custom_function dtmf2flash_function = { 200 | .name = "DTMF_FLASH", 201 | .write = dtmf2flash_helper, 202 | }; 203 | 204 | static int unload_module(void) 205 | { 206 | return ast_custom_function_unregister(&dtmf2flash_function); 207 | } 208 | 209 | static int load_module(void) 210 | { 211 | int res = ast_custom_function_register(&dtmf2flash_function); 212 | return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS; 213 | } 214 | 215 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Function to intercept long DTMF digits and spawn hook flash"); 216 | -------------------------------------------------------------------------------- /funcs/func_dtmf_trace.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2023, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Per-channel DTMF tracing function 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup functions 26 | * 27 | * \note This is meant to be a simpler and more granular version 28 | * of what the DTMF log level does. In addition to being global, 29 | * the DTMF log absolutely spams the console with several lines per digit, 30 | * making it quite unsuitable for busy systems. 31 | * The output of this framehook is one line per digit, per channel. 32 | */ 33 | 34 | /*** MODULEINFO 35 | extended 36 | ***/ 37 | 38 | #include "asterisk.h" 39 | 40 | #include "asterisk/module.h" 41 | #include "asterisk/channel.h" 42 | #include "asterisk/pbx.h" 43 | #include "asterisk/framehook.h" 44 | #include "asterisk/term.h" 45 | 46 | /*** DOCUMENTATION 47 | 48 | 49 | View DTMF digits as they are sent or received on a channel. 50 | 51 | 52 | 53 | TX or RX to limit the direction. 54 | Default is both directions. 55 | 56 | 57 | 58 | Subsequent function invocations will replace earlier ones. 59 | Examples: 60 | 61 | exten => 1,1,Set(DTMF_TRACE(RX)=1) 62 | 63 | 64 | exten => 1,1,Set(DTMF_TRACE(TX)=0) 65 | 66 | 67 | 68 | ***/ 69 | 70 | struct dtmf_trace_data { 71 | unsigned int tx:1; 72 | unsigned int rx:1; 73 | }; 74 | 75 | static void datastore_destroy_cb(void *data) { 76 | ast_free(data); 77 | } 78 | 79 | static const struct ast_datastore_info dtmf_trace_datastore = { 80 | .type = "dtmftrace", 81 | .destroy = datastore_destroy_cb 82 | }; 83 | 84 | static void hook_destroy_cb(void *framedata) 85 | { 86 | ast_free(framedata); 87 | } 88 | 89 | static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data) 90 | { 91 | struct dtmf_trace_data *framedata = data; 92 | 93 | if ((event != AST_FRAMEHOOK_EVENT_WRITE) && (event != AST_FRAMEHOOK_EVENT_READ)) { 94 | return frame; 95 | } 96 | if (frame && frame->frametype == AST_FRAME_DTMF_END) { 97 | if (event == AST_FRAMEHOOK_EVENT_READ && !framedata->rx) { 98 | ast_debug(4, "Digit %c from %s does not match filter\n", frame->subclass. integer, ast_channel_name(chan)); 99 | } else if (event == AST_FRAMEHOOK_EVENT_WRITE && !framedata->tx) { 100 | ast_debug(4, "Digit %c from %s does not match filter\n", frame->subclass. integer, ast_channel_name(chan)); 101 | } else { 102 | ast_verbose(COLORIZE_FMT "[%c] %s %s\n", COLORIZE(COLOR_BRGREEN, 0, "DTMF"), frame->subclass.integer, event == AST_FRAMEHOOK_EVENT_READ ? "<==" : "==>", ast_channel_name(chan)); 103 | } 104 | } 105 | return frame; 106 | } 107 | 108 | static int dtmf_trace_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value) 109 | { 110 | struct dtmf_trace_data *framedata; 111 | struct ast_datastore *datastore = NULL; 112 | struct ast_framehook_interface interface = { 113 | .version = AST_FRAMEHOOK_INTERFACE_VERSION, 114 | .event_cb = hook_event_cb, 115 | .destroy_cb = hook_destroy_cb, 116 | }; 117 | int i = 0; 118 | int enabled; 119 | int newtx = 0, newrx = 0; 120 | 121 | if (!chan) { 122 | ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd); 123 | return -1; 124 | } 125 | 126 | if (ast_strlen_zero(value)) { 127 | ast_log(LOG_ERROR, "No value provided to %s\n", cmd); 128 | return -1; 129 | } 130 | 131 | enabled = ast_true(value) ? 1 : 0; 132 | if (!ast_strlen_zero(data)) { 133 | if (strcasestr(data, "TX")) { 134 | newtx = 1; 135 | } 136 | if (strcasestr(data, "RX")) { 137 | newrx = 1; 138 | } 139 | } else { 140 | newtx = newrx = 1; 141 | } 142 | 143 | if (!(framedata = ast_calloc(1, sizeof(*framedata)))) { 144 | return -1; 145 | } 146 | 147 | interface.data = framedata; 148 | 149 | ast_channel_lock(chan); 150 | i = ast_framehook_attach(chan, &interface); 151 | if (i >= 0) { 152 | int *id; 153 | if ((datastore = ast_channel_datastore_find(chan, &dtmf_trace_datastore, NULL))) { 154 | id = datastore->data; 155 | ast_framehook_detach(chan, *id); 156 | ast_channel_datastore_remove(chan, datastore); 157 | ast_datastore_free(datastore); 158 | } 159 | 160 | if (!enabled) { 161 | ast_framehook_detach(chan, i); 162 | ast_channel_unlock(chan); 163 | return 0; 164 | } 165 | 166 | if (!(datastore = ast_datastore_alloc(&dtmf_trace_datastore, NULL))) { 167 | ast_framehook_detach(chan, i); 168 | ast_channel_unlock(chan); 169 | return 0; 170 | } 171 | 172 | if (!(id = ast_calloc(1, sizeof(int)))) { 173 | ast_datastore_free(datastore); 174 | ast_framehook_detach(chan, i); 175 | ast_channel_unlock(chan); 176 | return 0; 177 | } 178 | 179 | *id = i; /* Store off the id. The channel is still locked so it is safe to access this ptr. */ 180 | datastore->data = id; 181 | framedata->tx = newtx; 182 | framedata->rx = newrx; 183 | ast_debug(2, "DTMF trace now TX=%d,RX=%d\n", newtx, newrx); 184 | ast_channel_datastore_add(chan, datastore); 185 | } 186 | ast_channel_unlock(chan); 187 | 188 | return 0; 189 | } 190 | 191 | static struct ast_custom_function dtmf_trace_function = { 192 | .name = "DTMF_TRACE", 193 | .write = dtmf_trace_helper, 194 | }; 195 | 196 | static int unload_module(void) 197 | { 198 | return ast_custom_function_unregister(&dtmf_trace_function); 199 | } 200 | 201 | static int load_module(void) 202 | { 203 | int res = ast_custom_function_register(&dtmf_trace_function); 204 | return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS; 205 | } 206 | 207 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "DTMF Trace"); 208 | -------------------------------------------------------------------------------- /funcs/func_message_sub.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2024, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Message intercept hook 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup functions 26 | */ 27 | 28 | /*** MODULEINFO 29 | extended 30 | ***/ 31 | 32 | #include "asterisk.h" 33 | 34 | #include "asterisk/frame.h" 35 | #include "asterisk/pbx.h" 36 | #include "asterisk/channel.h" 37 | #include "asterisk/app.h" 38 | #include "asterisk/module.h" 39 | #include "asterisk/message.h" 40 | 41 | /*** DOCUMENTATION 42 | 43 | 44 | Add an intercept subroutine to run when text messages are received on a channel 45 | 46 | 47 | 48 | The dialplan location to execute as a callback (context,exten,priority). 49 | 50 | 51 | 52 | Example: 53 | 54 | exten => 1,1,Set(MESSAGE_INTERCEPT_SUB()=on-message) 55 | 56 | [on-message] 57 | exten => _X!,1,NoOp(${MESSAGE_INTERCEPT_SUB())}) 58 | same => n,Return() 59 | 60 | 61 | 62 | ***/ 63 | 64 | struct message_intercept_data { 65 | struct ast_frame *frame; 66 | char context[AST_MAX_CONTEXT]; 67 | }; 68 | 69 | static void datastore_destroy_cb(void *data) { 70 | ast_free(data); 71 | } 72 | 73 | static const struct ast_datastore_info message_intercept_datastore = { 74 | .type = "message_intercept_sub", 75 | .destroy = datastore_destroy_cb 76 | }; 77 | 78 | static void hook_destroy_cb(void *framedata) 79 | { 80 | ast_free(framedata); 81 | } 82 | 83 | static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data) 84 | { 85 | int res; 86 | struct ast_msg_data *msg; 87 | struct message_intercept_data *framedata = data; 88 | 89 | if (!frame) { 90 | return frame; 91 | } 92 | 93 | if (event != AST_FRAMEHOOK_EVENT_WRITE && event != AST_FRAMEHOOK_EVENT_READ) { 94 | return frame; 95 | } 96 | 97 | if (frame->frametype != AST_FRAME_TEXT_DATA) { 98 | return frame; 99 | } 100 | 101 | msg = frame->data.ptr; 102 | ast_debug(3, "Received message: '%s'\n", ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_BODY)); 103 | 104 | /* All this logic would be more elegant if it were in the core, in channel.c. 105 | * Overall idea is not dissimilar to CONNECTED_LINE_SEND_SUB, REDIRECTING_SEND_SUB in channel.c: */ 106 | 107 | /* ast_channel_get_intercept_mode is public API, but channel_set_intercept_mode is not, 108 | * so we can't actually set it. 109 | * However, this only seems to get used by res_agi, so unlikely to make much difference outside of that. */ 110 | 111 | /* channel_set_intercept_mode(1); */ 112 | framedata->frame = frame; 113 | res = ast_app_run_sub(NULL, chan, framedata->context, "", 1); 114 | framedata->frame = NULL; 115 | /* channel_set_intercept_mode(0); */ 116 | 117 | ast_debug(3, "Message intercept sub res: %d\n", res); 118 | return frame; 119 | } 120 | 121 | struct message_intercept_datastore_data { 122 | int framehook_id; 123 | struct message_intercept_data *data; 124 | }; 125 | 126 | static int read_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) 127 | { 128 | struct ast_msg_data *msg; 129 | struct ast_datastore *datastore; 130 | struct message_intercept_datastore_data *mdata; 131 | struct message_intercept_data *framedata; 132 | 133 | if (!(datastore = ast_channel_datastore_find(chan, &message_intercept_datastore, NULL))) { 134 | ast_log(LOG_WARNING, "No message intercept hook is currently active\n"); 135 | return -1; 136 | } 137 | 138 | mdata = datastore->data; 139 | framedata = mdata->data; 140 | 141 | ast_channel_lock(chan); 142 | if (!framedata->frame) { 143 | ast_channel_unlock(chan); 144 | ast_log(LOG_WARNING, "No message data currently active\n"); 145 | return -1; 146 | } 147 | 148 | /* Caller can't actually use the MESSAGE and MESSAGE_DATA functions, 149 | * since there's no message on the channel, 150 | * so we need to provide the data through this callback: */ 151 | 152 | msg = framedata->frame->data.ptr; 153 | ast_debug(3, "Received message: '%s'\n", ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_BODY)); 154 | 155 | ast_copy_string(buf, ast_msg_data_get_attribute(msg, AST_MSG_DATA_ATTR_BODY), len); 156 | ast_channel_unlock(chan); 157 | return 0; 158 | } 159 | 160 | static int write_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value) 161 | { 162 | struct message_intercept_data *framedata; 163 | struct ast_datastore *datastore = NULL; 164 | struct ast_framehook_interface interface = { 165 | .version = AST_FRAMEHOOK_INTERFACE_VERSION, 166 | .event_cb = hook_event_cb, 167 | .destroy_cb = hook_destroy_cb, 168 | }; 169 | int i = 0; 170 | 171 | if (!chan) { 172 | ast_log(LOG_ERROR, "No channel was provided to %s function.\n", cmd); 173 | return -1; 174 | } 175 | if (ast_strlen_zero(value)) { 176 | ast_log(LOG_ERROR, "Missing context argument\n"); 177 | return -1; 178 | } 179 | 180 | if (!(framedata = ast_calloc(1, sizeof(*framedata)))) { 181 | return 0; 182 | } 183 | 184 | interface.data = framedata; 185 | ast_copy_string(framedata->context, value, sizeof(framedata->context)); 186 | 187 | ast_channel_lock(chan); 188 | i = ast_framehook_attach(chan, &interface); 189 | if (i >= 0) { 190 | int id; 191 | struct message_intercept_datastore_data *mdata; 192 | if ((datastore = ast_channel_datastore_find(chan, &message_intercept_datastore, NULL))) { 193 | mdata = datastore->data; 194 | id = mdata->framehook_id; 195 | ast_framehook_detach(chan, id); 196 | ast_channel_datastore_remove(chan, datastore); 197 | ast_datastore_free(datastore); 198 | } 199 | 200 | if (!(datastore = ast_datastore_alloc(&message_intercept_datastore, NULL))) { 201 | ast_framehook_detach(chan, i); 202 | ast_channel_unlock(chan); 203 | return 0; 204 | } 205 | 206 | if (!(mdata = ast_calloc(1, sizeof(*mdata)))) { 207 | ast_datastore_free(datastore); 208 | ast_framehook_detach(chan, i); 209 | ast_channel_unlock(chan); 210 | return 0; 211 | } 212 | 213 | mdata->framehook_id = i; /* Store off the id. The channel is still locked so it is safe to access this ptr. */ 214 | mdata->data = framedata; 215 | datastore->data = mdata; 216 | ast_channel_datastore_add(chan, datastore); 217 | } 218 | ast_channel_unlock(chan); 219 | 220 | return 0; 221 | } 222 | 223 | static struct ast_custom_function message_intercept_function = { 224 | .name = "MESSAGE_INTERCEPT_SUB", 225 | .read = read_helper, 226 | .write = write_helper, 227 | }; 228 | 229 | static int unload_module(void) 230 | { 231 | return ast_custom_function_unregister(&message_intercept_function); 232 | } 233 | 234 | static int load_module(void) 235 | { 236 | return ast_custom_function_register(&message_intercept_function); 237 | } 238 | 239 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Message data intercept"); 240 | -------------------------------------------------------------------------------- /funcs/func_query.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2022, Naveen Albert 5 | * 6 | * See http://www.asterisk.org for more information about 7 | * the Asterisk project. Please do not directly contact 8 | * any of the maintainers of this project for assistance; 9 | * the project provides a web site, mailing lists and IRC 10 | * channels for your use. 11 | * 12 | * This program is free software, distributed under the terms of 13 | * the GNU General Public License Version 2. See the LICENSE file 14 | * at the top of the source tree. 15 | */ 16 | 17 | /*! \file 18 | * 19 | * \brief Remote text querying 20 | * 21 | * \author Naveen Albert 22 | * 23 | * \ingroup functions 24 | */ 25 | 26 | /*** MODULEINFO 27 | extended 28 | ***/ 29 | 30 | #include "asterisk.h" 31 | 32 | #include "asterisk/module.h" 33 | #include "asterisk/pbx.h" /* function register/unregister */ 34 | #include "asterisk/utils.h" 35 | #include "asterisk/app.h" 36 | #include "asterisk/format_cache.h" 37 | #include "asterisk/frame.h" 38 | #include "asterisk/strings.h" 39 | #include "asterisk/conversions.h" 40 | 41 | /*** DOCUMENTATION 42 | 43 | 44 | Remote string querying 45 | 46 | 47 | 48 | Dial string, such as provided to the Dial application 49 | 50 | 51 | Timeout to wait, in seconds. Default is 5 seconds. 52 | 53 | 54 | 55 | Initiate a call and receive a text data transfer. 56 | This function can be used to implement simple remote procedure 57 | calls between endpoints that support text frames. For example, you 58 | can use this to retrieve the results of certain dialplan functions 59 | on a different node as easily as if they were local. 60 | The other end should use SendText to send the data transfer. 61 | 62 | [rx-node] ; Node A 63 | exten => rx,1,Set(remotestate=${TEXT_QUERY(IAX2/mainbranch/2368@device-state-context)}) 64 | same => n,ExecIf($[ "${remotestate}" = "NOT_INUSE" ]?Dial(IAX2/mainbranch/2368@extensions)) 65 | same => n,Hangup() 66 | [extensions] ; Node B: allow other Asterisk systems to query local device states. 67 | exten => _2XXX,1,Dial(${HINT(${EXTEN})}) 68 | same => n,Hangup() 69 | [device-state-context] ; Node B 70 | exten => _2XXX,1,SendText(${DEVICE_STATE(${HINT(${EXTEN})})}) 71 | same => n,Hangup() 72 | 73 | 74 | 75 | ***/ 76 | 77 | static int acf_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) 78 | { 79 | struct ast_format_cap *cap; 80 | struct ast_channel *c; 81 | char *parse, *rbuf; 82 | char *tech, *destination; 83 | int timeout_sec = 0, timeout_ms = 5000; 84 | AST_DECLARE_APP_ARGS(args, 85 | AST_APP_ARG(dialstr); 86 | AST_APP_ARG(timeout); 87 | ); 88 | struct ast_custom_function *cdr_prop_func = ast_custom_function_find("CDR_PROP"); 89 | 90 | if (ast_strlen_zero(data)) { 91 | ast_log(LOG_WARNING, "Missing arguments: dialstring\n"); 92 | return -1; 93 | } 94 | 95 | parse = ast_strdupa(data); 96 | AST_STANDARD_APP_ARGS(args, parse); 97 | 98 | if (ast_strlen_zero(args.dialstr)) { 99 | ast_log(LOG_WARNING, "Missing arguments: dialstring\n"); 100 | return -1; 101 | } 102 | if (!ast_strlen_zero(args.timeout)) { 103 | if ((ast_str_to_int(args.timeout, &timeout_sec) || timeout_sec < 0)) { 104 | ast_log(LOG_WARNING, "Invalid timeout: %s\n", args.timeout); 105 | } else { 106 | timeout_ms *= 1000; 107 | } 108 | } 109 | 110 | tech = args.dialstr; 111 | destination = strchr(tech, '/'); 112 | if (destination) { 113 | *destination = '\0'; 114 | destination++; 115 | } else { 116 | ast_log(LOG_WARNING, "Dial string must have technology/resource\n"); 117 | return -1; 118 | } 119 | 120 | cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); 121 | if (!cap) { 122 | return -1; 123 | } 124 | ast_format_cap_append(cap, ast_format_slin, 0); 125 | 126 | c = ast_request(tech, cap, NULL, chan, destination, NULL); /* null chan OK */ 127 | ao2_cleanup(cap); 128 | 129 | if (!c) { 130 | return -1; 131 | } 132 | 133 | /* Disable CDR for this temporary channel. */ 134 | if (cdr_prop_func) { 135 | ast_func_write(c, "CDR_PROP(disable)", "1"); 136 | } 137 | 138 | /* Copy Caller ID, if we have it. */ 139 | if (chan && ast_channel_caller(chan)->id.number.valid) { 140 | ast_channel_caller(c)->id.number.valid = 1; 141 | ast_channel_caller(c)->id.number.str = ast_strdup(ast_channel_caller(chan)->id.number.str); 142 | /* It's really the connected line that matters here, not the caller id, because that's what'll be the Caller ID on the channel we call. */ 143 | ast_channel_connected(c)->id.number.valid = 1; 144 | ast_channel_connected(c)->id.number.str = ast_strdup(ast_channel_caller(chan)->id.number.str); 145 | } 146 | 147 | if (ast_call(c, destination, 0)) { 148 | ast_log(LOG_ERROR, "Unable to place outbound call to %s/%s\n", tech, destination); 149 | ast_hangup(c); 150 | return -1; 151 | } 152 | 153 | /* Wait for data transfer */ 154 | if (chan) { 155 | ast_autoservice_start(chan); 156 | } 157 | ast_channel_ref(c); 158 | rbuf = ast_recvtext(c, timeout_ms); 159 | ast_channel_unref(c); 160 | ast_hangup(c); 161 | if (chan) { 162 | ast_autoservice_stop(chan); 163 | } 164 | 165 | if (!rbuf) { 166 | ast_log(LOG_WARNING, "No data received before channel hung up\n"); 167 | return -1; 168 | } 169 | 170 | ast_copy_string(buf, rbuf, len); 171 | 172 | return 0; 173 | } 174 | 175 | static struct ast_custom_function query_function = { 176 | .name = "TEXT_QUERY", 177 | .read = acf_query_read, 178 | }; 179 | 180 | static int unload_module(void) 181 | { 182 | return ast_custom_function_unregister(&query_function); 183 | } 184 | 185 | static int load_module(void) 186 | { 187 | return ast_custom_function_register(&query_function); 188 | } 189 | 190 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Remote text querying"); 191 | -------------------------------------------------------------------------------- /funcs/func_tech.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2022, Naveen Albert 5 | * 6 | * See http://www.asterisk.org for more information about 7 | * the Asterisk project. Please do not directly contact 8 | * any of the maintainers of this project for assistance; 9 | * the project provides a web site, mailing lists and IRC 10 | * channels for your use. 11 | * 12 | * This program is free software, distributed under the terms of 13 | * the GNU General Public License Version 2. See the LICENSE file 14 | * at the top of the source tree. 15 | */ 16 | 17 | /*! \file 18 | * 19 | * \brief Channel technology function 20 | * 21 | * \author Naveen Albert 22 | * \ingroup functions 23 | */ 24 | 25 | /*** MODULEINFO 26 | extended 27 | ***/ 28 | 29 | #include "asterisk.h" 30 | 31 | #include "asterisk/module.h" 32 | #include "asterisk/pbx.h" 33 | #include "asterisk/channel.h" 34 | #include "asterisk/utils.h" 35 | 36 | /*** DOCUMENTATION 37 | 38 | 39 | Checks if the specified channel technology exists and is usable. 40 | 41 | 42 | 43 | The name of the channel technology (e.g. DAHDI, PJSIP, IAX2, etc.). 44 | 45 | 46 | 47 | Returns 1 if the channel technology tech exists, 0 if not. 48 | 49 | 50 | ***/ 51 | 52 | static int func_tech_exists_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t maxlen) 53 | { 54 | if (ast_strlen_zero(data)) { 55 | ast_log(LOG_WARNING, "%s: Channel tech required\n", function); 56 | return -1; 57 | } 58 | snprintf(buf, maxlen, "%d", (ast_get_channel_tech(data) ? 1 : 0)); 59 | return 0; 60 | } 61 | 62 | static struct ast_custom_function tech_exists_function = { 63 | .name = "TECH_EXISTS", 64 | .read = func_tech_exists_read, 65 | }; 66 | 67 | static int unload_module(void) 68 | { 69 | return ast_custom_function_unregister(&tech_exists_function); 70 | } 71 | 72 | static int load_module(void) 73 | { 74 | return ast_custom_function_register(&tech_exists_function); 75 | } 76 | 77 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Channel technology function"); 78 | -------------------------------------------------------------------------------- /patches/af_wanpipe.diff: -------------------------------------------------------------------------------- 1 | diff --git a/patches/kdrivers/include/wanpipe_kernel.h b/patches/kdrivers/include/wanpipe_kernel.h 2 | index fffef26..215d8d0 100644 3 | --- a/patches/kdrivers/include/wanpipe_kernel.h 4 | +++ b/patches/kdrivers/include/wanpipe_kernel.h 5 | @@ -87,6 +87,66 @@ 6 | #define WAN_DEV_NAME(device) device->dev.bus_id 7 | #endif 8 | 9 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 10 | +#define virt_to_bus virt_to_phys 11 | +#define bus_to_virt phys_to_virt 12 | +#endif 13 | + 14 | +static inline struct sk_buff * 15 | +wp_skb_recv_datagram_noblock(struct sock *sk, unsigned int flags, int *err) 16 | +{ 17 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) 18 | + return skb_recv_datagram(sk, flags | MSG_DONTWAIT, err); 19 | +#else 20 | + return skb_recv_datagram(sk, flags, 1, err); 21 | +#endif 22 | +} 23 | + 24 | + 25 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) 26 | +#include 27 | + 28 | +#define PCI_DMA_TODEVICE DMA_TO_DEVICE 29 | +#define PCI_DMA_FROMDEVICE DMA_FROM_DEVICE 30 | + 31 | +static inline void * 32 | +pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) 33 | +{ 34 | + return dma_alloc_coherent(hwdev == NULL ? NULL : &hwdev->dev, size, dma_handle, GFP_ATOMIC); 35 | +} 36 | + 37 | +static inline void 38 | +pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) 39 | +{ 40 | + dma_free_coherent(hwdev == NULL ? NULL : &hwdev->dev, size, vaddr, dma_handle); 41 | +} 42 | + 43 | +static inline dma_addr_t 44 | +pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) 45 | +{ 46 | + return dma_map_single(&hwdev->dev, ptr, size, (enum dma_data_direction)direction); 47 | +} 48 | + 49 | +static inline void 50 | +pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, int direction) 51 | +{ 52 | + dma_unmap_single(&hwdev->dev, dma_addr, size, (enum dma_data_direction)direction); 53 | +} 54 | + 55 | +static inline void 56 | +pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction) 57 | +{ 58 | + dma_sync_single_for_cpu(&hwdev->dev, dma_handle, size, (enum dma_data_direction)direction); 59 | +} 60 | + 61 | +static inline void 62 | +pci_dma_sync_single_for_device(struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction) 63 | +{ 64 | + dma_sync_single_for_device(&hwdev->dev, dma_handle, size, (enum dma_data_direction)direction); 65 | +} 66 | + 67 | +#endif 68 | + 69 | /////////////2.6.36///////////////////////////// 70 | /* enable UNLOCKED_IOCTL for all kernel version > 5.9.0 */ 71 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)) 72 | @@ -188,8 +248,7 @@ void *PDE_DATA(const struct inode *inode) 73 | *==========================================================================*/ 74 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) 75 | #define LINUX_3_0 76 | - /* Not sure exactly when they removed write_proc_t, but in 3.11.8 is not there anymore */ 77 | - #if defined(KERN_PROC_PDE_FEATURE) && KERN_PROC_PDE_FEATURE > 0 78 | + #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) 79 | typedef int (write_proc_t)(struct file *, const char *, unsigned long, void *); 80 | #endif 81 | #ifndef pci_dev_b 82 | @@ -286,7 +345,9 @@ void *PDE_DATA(const struct inode *inode) 83 | 84 | #define dev_init_buffers(a) 85 | 86 | - #if defined(KERN_PROC_PDE_FEATURE) && KERN_PROC_PDE_FEATURE > 0 87 | + #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) 88 | + # define WP_PDE_DATA pde_data 89 | + #elif defined(KERN_PROC_PDE_FEATURE) && KERN_PROC_PDE_FEATURE > 0 90 | # define WP_PDE_DATA PDE_DATA 91 | #else 92 | #include 93 | diff --git a/patches/kdrivers/src/net/wanpipe_cdev_linux.c b/patches/kdrivers/src/net/wanpipe_cdev_linux.c 94 | index 998992b..4b19416 100644 95 | --- a/patches/kdrivers/src/net/wanpipe_cdev_linux.c 96 | +++ b/patches/kdrivers/src/net/wanpipe_cdev_linux.c 97 | @@ -1014,9 +1014,11 @@ EXPORT_SYMBOL(wanpipe_global_cdev_free); 98 | 99 | EXPORT_SYMBOL(wanpipe_cdev_free); 100 | 101 | +#if defined (KERN_MODPOST_STATIC_ERR) && KERN_MODPOST_STATIC_ERR > 0 102 | EXPORT_SYMBOL(wanpipe_cdev_tx_wake); 103 | EXPORT_SYMBOL(wanpipe_cdev_rx_wake); 104 | EXPORT_SYMBOL(wanpipe_cdev_event_wake); 105 | +#endif 106 | EXPORT_SYMBOL(wanpipe_cdev_tdm_create); 107 | EXPORT_SYMBOL(wanpipe_cdev_tdm_ctrl_create); 108 | EXPORT_SYMBOL(wanpipe_cdev_cfg_ctrl_create); 109 | diff --git a/patches/kdrivers/src/wanrouter/af_wanpipe.c b/patches/kdrivers/src/wanrouter/af_wanpipe.c 110 | index cee7da2..7378acb 100644 111 | --- a/patches/kdrivers/src/wanrouter/af_wanpipe.c 112 | +++ b/patches/kdrivers/src/wanrouter/af_wanpipe.c 113 | @@ -238,7 +238,7 @@ static int wanpipe_ioctl(struct socket *sock, unsigned int cmd, unsigned long ar 114 | AF_SKB_DEC(skb->truesize); 115 | skb_free_datagram(sk, skb); 116 | } 117 | - while ((skb=skb_recv_datagram(sk,0,1,&err)) != NULL){ 118 | + while ((skb=wp_skb_recv_datagram_noblock(sk,0,&err)) != NULL){ 119 | AF_SKB_DEC(skb->truesize); 120 | skb_free_datagram(sk, skb); 121 | } 122 | @@ -600,7 +600,11 @@ static int wanpipe_accept(struct socket *sock, struct socket *newsock, int flags 123 | return -EPROTOTYPE; 124 | 125 | add_wait_queue(WAN_SK_SLEEP(sk),&wait); 126 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) 127 | + WRITE_ONCE(current->__state, TASK_INTERRUPTIBLE); 128 | +#else 129 | current->state = TASK_INTERRUPTIBLE; 130 | +#endif 131 | for (;;){ 132 | skb = skb_dequeue(&sk->sk_receive_queue); 133 | if (skb){ 134 | @@ -619,7 +623,11 @@ static int wanpipe_accept(struct socket *sock, struct socket *newsock, int flags 135 | } 136 | schedule(); 137 | } 138 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) 139 | + WRITE_ONCE(current->__state, TASK_RUNNING); 140 | +#else 141 | current->state = TASK_RUNNING; 142 | +#endif 143 | remove_wait_queue(WAN_SK_SLEEP(sk),&wait); 144 | 145 | if (err != 0) 146 | @@ -1688,7 +1696,7 @@ static int wanpipe_recvmsg(struct socket *sock, struct msghdr *msg, int len, 147 | if (flags & MSG_OOB){ 148 | skb=skb_dequeue(&sk->sk_error_queue); 149 | }else{ 150 | - skb=skb_recv_datagram(sk,flags,1,&err); 151 | + skb=wp_skb_recv_datagram_noblock(sk,flags,&err); 152 | } 153 | /* 154 | * An error occurred so return it. Because skb_recv_datagram() 155 | diff --git a/patches/kdrivers/src/wanrouter/af_wanpipe_annexg_api.c b/patches/kdrivers/src/wanrouter/af_wanpipe_annexg_api.c 156 | index bbec8bd..02220ca 100644 157 | --- a/patches/kdrivers/src/wanrouter/af_wanpipe_annexg_api.c 158 | +++ b/patches/kdrivers/src/wanrouter/af_wanpipe_annexg_api.c 159 | @@ -1350,7 +1350,7 @@ int wanpipe_annexg_recvmsg(struct socket *sock, struct msghdr *msg, int len, 160 | if (flags & MSG_OOB){ 161 | skb=skb_dequeue(&sk->error_queue); 162 | }else{ 163 | - skb=skb_recv_datagram(sk,flags,1,&err); 164 | + skb=wp_skb_recv_datagram_noblock(sk,flags,&err); 165 | } 166 | /* 167 | * An error occurred so return it. Because skb_recv_datagram() 168 | -------------------------------------------------------------------------------- /patches/agi_record_noisefirst.diff: -------------------------------------------------------------------------------- 1 | diff --git a/res/res_agi.c b/res/res_agi.c 2 | index cafe13ba2d..10fd9cb0ac 100644 3 | --- a/res/res_agi.c 4 | +++ b/res/res_agi.c 5 | @@ -487,6 +487,13 @@ 6 | arguments. If specified, this parameter must be preceded by 7 | s=. 8 | 9 | + 10 | + The number of milliseconds of noise that are required before the 11 | + silence detection is enabled, regardless of the 12 | + escape_digits or timeout 13 | + arguments. If specified, this parameter must be preceded by 14 | + n=. 15 | + 16 | 17 | 18 | Record to a file until a given dtmf digit in the sequence is received. 19 | @@ -2902,10 +2909,13 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const 20 | 21 | struct ast_dsp *sildet=NULL; /* silence detector dsp */ 22 | int totalsilence = 0; 23 | + int totalnoise = 0; 24 | int dspsilence = 0; 25 | int silence = 0; /* amount of silence to allow */ 26 | + int noise = 0; /* amount of noise to require first */ 27 | int gotsilence = 0; /* did we timeout for silence? */ 28 | char *silencestr = NULL; 29 | + char *noisestr = NULL; 30 | RAII_VAR(struct ast_format *, rfmt, NULL, ao2_cleanup); 31 | struct ast_silence_generator *silgen = NULL; 32 | 33 | @@ -2923,6 +2933,16 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const 34 | if ((argc > 8) && (!silencestr)) 35 | silencestr = strchr(argv[8],'s'); 36 | 37 | + if (argc > 7) { 38 | + noisestr = strchr(argv[7], 'n'); 39 | + } 40 | + if ((argc > 8) && (!noisestr)) { 41 | + noisestr = strchr(argv[8], 'n'); 42 | + } 43 | + if ((argc > 9) && (!noisestr)) { 44 | + noisestr = strchr(argv[9], 'n'); 45 | + } 46 | + 47 | if (silencestr) { 48 | if (strlen(silencestr) > 2) { 49 | if ((silencestr[0] == 's') && (silencestr[1] == '=')) { 50 | @@ -2936,6 +2956,14 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const 51 | } 52 | } 53 | 54 | + if (noisestr && strlen(noisestr) > 2 && (noisestr[0] == 'n') && (noisestr[1] == '=')) { 55 | + noisestr += 2; 56 | + if (!ast_strlen_zero(noisestr)) { 57 | + noise = atoi(noisestr); 58 | + } 59 | + /* already in ms, don't *= 1000 */ 60 | + } 61 | + 62 | if (silence > 0) { 63 | rfmt = ao2_bump(ast_channel_readformat(chan)); 64 | res = ast_set_read_format(chan, ast_format_slin); 65 | @@ -2967,6 +2995,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const 66 | if (res) { 67 | ast_agi_send(agi->fd, chan, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset); 68 | } else { 69 | + int gotnoise = noise > 0 ? 0 : 1; /* If not requiring noise, assume we already got it to waive requirement */ 70 | + ast_debug(6, "Will require %d s of silence, and before that, %d ms of noise\n", silence, noise); 71 | fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY | (sample_offset ? O_APPEND : 0), 0, AST_FILE_MODE); 72 | if (!fs) { 73 | res = -1; 74 | @@ -2990,7 +3020,7 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const 75 | } 76 | 77 | start = ast_tvnow(); 78 | - while ((ms < 0) || ast_tvdiff_ms(ast_tvnow(), start) < ms) { 79 | + while ((ms < 0) || (ast_tvdiff_ms(ast_tvnow(), start) < ms)) { 80 | res = ast_waitfor(chan, ms - ast_tvdiff_ms(ast_tvnow(), start)); 81 | if (res < 0) { 82 | ast_closestream(fs); 83 | @@ -3037,17 +3067,36 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const 84 | * location */ 85 | sample_offset = ast_tellstream(fs); 86 | if (silence > 0) { 87 | - dspsilence = 0; 88 | - ast_dsp_silence(sildet, f, &dspsilence); 89 | - if (dspsilence) { 90 | - totalsilence = dspsilence; 91 | + if (gotnoise) { 92 | + dspsilence = 0; 93 | + ast_dsp_silence(sildet, f, &dspsilence); 94 | + if (dspsilence) { 95 | + totalsilence = dspsilence; 96 | + } else { 97 | + totalsilence = 0; 98 | + } 99 | + ast_debug(3, "total silence: %d (offset %lu)\n", totalsilence, sample_offset); 100 | + if (totalsilence > silence) { 101 | + /* Ended happily with silence */ 102 | + gotsilence = 1; 103 | + ast_debug(3, "Got enough silence (total silence = %d, silence = %d), ending recording\n", totalsilence, silence); 104 | + break; 105 | + } 106 | } else { 107 | - totalsilence = 0; 108 | - } 109 | - if (totalsilence > silence) { 110 | - /* Ended happily with silence */ 111 | - gotsilence = 1; 112 | - break; 113 | + /* Need to get some audio first before silence detection kicks in */ 114 | + int dspnoise = 0; 115 | + ast_dsp_noise(sildet, f, &dspnoise); 116 | + if (dspnoise) { 117 | + totalnoise = dspnoise; 118 | + } else { 119 | + totalnoise = 0; 120 | + } 121 | + ast_debug(3, "total noise: %d (offset %lu)\n", totalnoise, sample_offset); 122 | + if (totalnoise > noise) { 123 | + ast_debug(3, "Got enough noise (%d) for silence detection to be enabled\n", totalnoise); 124 | + gotnoise = 1; 125 | + start = ast_tvnow(); 126 | + } 127 | } 128 | } 129 | break; 130 | @@ -3058,8 +3107,9 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const 131 | break; 132 | } 133 | ast_frfree(f); 134 | - if (gotsilence) 135 | + if (gotsilence) { 136 | break; 137 | + } 138 | } 139 | 140 | if (gotsilence) { 141 | @@ -3069,6 +3119,7 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const 142 | } 143 | ast_closestream(fs); 144 | ast_agi_send(agi->fd, chan, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset); 145 | + ast_debug(3, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset); 146 | } 147 | 148 | if (silence > 0) { 149 | -------------------------------------------------------------------------------- /patches/alsa.diff: -------------------------------------------------------------------------------- 1 | diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in 2 | index 6594a922eb..21a8a2ac49 100644 3 | --- a/build_tools/menuselect-deps.in 4 | +++ b/build_tools/menuselect-deps.in 5 | @@ -1,3 +1,4 @@ 6 | +ALSA=@PBX_ALSA@ 7 | BLUETOOTH=@PBX_BLUETOOTH@ 8 | BEANSTALK=@PBX_BEANSTALK@ 9 | COROSYNC=@PBX_COROSYNC@ 10 | diff --git a/configure.ac b/configure.ac 11 | index a3563e97a9..1d6a6143af 100644 12 | --- a/configure.ac 13 | +++ b/configure.ac 14 | @@ -530,6 +530,7 @@ THIRD_PARTY_CONFIGURE() 15 | # by the --with option name (the third field), 16 | # to make things easier for the users. 17 | 18 | +AST_EXT_LIB_SETUP([ALSA], [Advanced Linux Sound Architecture], [asound]) 19 | AST_EXT_LIB_SETUP([BFD], [Debug symbol decoding], [bfd]) 20 | 21 | # BKTR is used for backtrace support on platforms that do not 22 | @@ -1633,6 +1634,8 @@ fi 23 | 24 | # do the package library checks now 25 | 26 | +AST_EXT_LIB_CHECK([ALSA], [asound], [snd_pcm_open], [alsa/asoundlib.h]) 27 | + 28 | AST_EXT_LIB_CHECK([BFD], [bfd], [bfd_openr], [bfd.h]) 29 | # Fedora/RedHat/CentOS require extra libraries 30 | AST_EXT_LIB_CHECK([BFD], [bfd], [bfd_openr], [bfd.h], [-ldl -liberty]) 31 | diff --git a/makeopts.in b/makeopts.in 32 | index f7824f3476..bddf77ac1a 100644 33 | --- a/makeopts.in 34 | +++ b/makeopts.in 35 | @@ -130,6 +130,9 @@ C_COMPILER_FAMILY=@AST_C_COMPILER_FAMILY@ 36 | AST_RPATH=@AST_RPATH@ 37 | AST_FORTIFY_SOURCE=@AST_FORTIFY_SOURCE@ 38 | 39 | +ALSA_INCLUDE=@ALSA_INCLUDE@ 40 | +ALSA_LIB=@ALSA_LIB@ 41 | + 42 | BFD_INCLUDE=@BFD_INCLUDE@ 43 | BFD_LIB=@BFD_LIB@ 44 | 45 | -------------------------------------------------------------------------------- /patches/app_confbridge_Fix_bridge_shutdown_race_condition.patch: -------------------------------------------------------------------------------- 1 | From ee084255826034bb84af0bfe6f8b5f75d9cc58af Mon Sep 17 00:00:00 2001 2 | From: Naveen Albert 3 | Date: Thu, 26 May 2022 01:37:53 +0000 4 | Subject: [PATCH] app_confbridge: Fix bridge shutdown race condition. 5 | 6 | A race condition exists where if a bridge is left vacant 7 | for a split instant, the bridge will shut down and go 8 | away at the same instant that somebody else could be trying 9 | to join it. The newcomer will find the conference still 10 | in conference_bridges and thus take it and run with it. 11 | At the same time, shutdown of the bridge completes and it 12 | is removed from conference_bridges. This would generally 13 | be rare since the timing must line up exactly right, but 14 | if the timing is right, it can happen very frequently. 15 | 16 | As a result, the newcomer will end up joining the bridge 17 | corresponding to the original conference that is now 18 | defunct. When the next party joins the same named bridge, 19 | it won't be found in conference_bridges and thus a new 20 | bridge gets created. This can happen right afterwards, 21 | but in theory it could happen at any point after the first. 22 | 23 | As a result, the newcomer that joined the bridge during 24 | the shutdown ends up stranded in a bridge that isn't 25 | known to app_confbridge anymore and will never get conferenced 26 | with anything, since it's been removed from the list of 27 | conferences already. 28 | 29 | To prevent this, we now check after we join the bridge 30 | if the same conference object is returned when searching 31 | for it. If it isn't, then we try to join the bridge again, 32 | which will succeed the second time since we won't find 33 | the shutting down conference anymore. 34 | 35 | ASTERISK-30081 #close 36 | 37 | Change-Id: I08a440eafbf83ec4b502d1e44c3f4d44c4a522f9 38 | --- 39 | 40 | diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c 41 | index ea206bf..410df78 100644 42 | --- a/apps/app_confbridge.c 43 | +++ b/apps/app_confbridge.c 44 | @@ -1765,11 +1765,13 @@ 45 | */ 46 | static struct confbridge_conference *join_conference_bridge(const char *conference_name, struct confbridge_user *user) 47 | { 48 | - struct confbridge_conference *conference; 49 | + struct confbridge_conference *conference, *conference2; 50 | struct post_join_action *action; 51 | int max_members_reached = 0; 52 | 53 | /* We explicitly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same time */ 54 | +attempt: 55 | + /* We explicitly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */ 56 | ao2_lock(conference_bridges); 57 | 58 | ast_debug(1, "Trying to find conference bridge '%s'\n", conference_name); 59 | @@ -1941,6 +1943,21 @@ 60 | return NULL; 61 | } 62 | 63 | + /* Rare, but if threads interleave exactly right, the bridge could disappear 64 | + * just AFTER we found it. At this point, it's not empty anymore, so if it's 65 | + * still intact, it's safe to use. If not, then start over, or we'll end 66 | + * up joining a ghost bridge that isn't registered anymore in the conf list. */ 67 | + conference2 = ao2_find(conference_bridges, conference_name, OBJ_KEY); 68 | + if (conference != conference2) { 69 | + ao2_unlock(conference); 70 | + leave_conference(user); 71 | + ast_debug(1, "Conference (bridge %p) %s before we could join it\n", 72 | + conference->bridge, conference2 ? "changed" : "disappeared"); 73 | + goto attempt; 74 | + } 75 | + 76 | + ao2_ref(conference2, -1); 77 | + 78 | ao2_unlock(conference); 79 | 80 | /* If an announcement is to be played play it */ 81 | @@ -2884,6 +2901,8 @@ 82 | async_play_sound_ready(user.chan); 83 | } 84 | 85 | + ast_debug(2, "Joining conference in bridge %p\n", conference->bridge); 86 | + 87 | ast_bridge_join(conference->bridge, 88 | chan, 89 | NULL, 90 | -------------------------------------------------------------------------------- /patches/ast_rtoutpulsing2.diff: -------------------------------------------------------------------------------- 1 | diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c 2 | index 3baabb0872..81feb3ad0b 100644 3 | --- a/channels/chan_dahdi.c 4 | +++ b/channels/chan_dahdi.c 5 | @@ -1050,6 +1050,7 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void) 6 | .mohsuggest = "", 7 | .parkinglot = "", 8 | .transfertobusy = 1, 9 | + .realtimepulsing = 0, 10 | .dialmode = 0, 11 | 12 | .ani_info_digits = 2, 13 | @@ -12958,6 +12959,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, 14 | tmp->immediate = conf->chan.immediate; 15 | tmp->immediatering = conf->chan.immediatering; 16 | tmp->transfertobusy = conf->chan.transfertobusy; 17 | + tmp->realtimepulsing = conf->chan.realtimepulsing; 18 | tmp->dialmode = conf->chan.dialmode; 19 | if (chan_sig & __DAHDI_SIG_FXS) { 20 | tmp->mwimonitor_fsk = conf->chan.mwimonitor_fsk; 21 | @@ -13298,6 +13300,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, 22 | analog_p->threewaycalling = conf->chan.threewaycalling; 23 | analog_p->transfer = conf->chan.transfer; 24 | analog_p->transfertobusy = conf->chan.transfertobusy; 25 | + analog_p->realtimepulsing = conf->chan.realtimepulsing; 26 | analog_p->dialmode = conf->chan.dialmode; 27 | analog_p->use_callerid = tmp->use_callerid; 28 | analog_p->usedistinctiveringdetection = tmp->usedistinctiveringdetection; 29 | @@ -18588,6 +18591,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct 30 | confp->chan.immediatering = ast_true(v->value); 31 | } else if (!strcasecmp(v->name, "transfertobusy")) { 32 | confp->chan.transfertobusy = ast_true(v->value); 33 | + } else if (!strcasecmp(v->name, "realtimepulsing")) { 34 | + confp->chan.realtimepulsing = ast_true(v->value); 35 | } else if (!strcasecmp(v->name, "dialmode")) { 36 | if (!strcasecmp(v->value, "pulse")) { 37 | confp->chan.dialmode = ANALOG_DIALMODE_PULSE; 38 | -------------------------------------------------------------------------------- /patches/asterisk-prevent-duplicate-processes.diff: -------------------------------------------------------------------------------- 1 | diff --git a/main/asterisk.c b/main/asterisk.c 2 | index b0f8a14311..ee89c45ad9 100644 3 | --- a/main/asterisk.c 4 | +++ b/main/asterisk.c 5 | @@ -1709,6 +1709,37 @@ static int ast_tryconnect(void) 6 | return 1; 7 | } 8 | 9 | +static int ast_is_starting(void) 10 | +{ 11 | + /* Don't use kill since that only works if Asterisk was started as the same user. */ 12 | + struct stat st; 13 | + FILE *f; 14 | + long file_pid; 15 | + char procpath[PATH_MAX]; 16 | + 17 | + /* Get current value from PID file */ 18 | + f = fopen(ast_config_AST_PID, "r"); 19 | + if (!f) { 20 | + return 0; /* PID file doesn't exist? No way to tell. */ 21 | + } 22 | + if (fscanf(f, "%ld", &file_pid) < 1) { 23 | + file_pid = 0; /* Failure */ 24 | + } 25 | + fclose(f); 26 | + if (!file_pid) { 27 | + return 0; 28 | + } 29 | + 30 | + /* Check if such a process is running */ 31 | + snprintf(procpath, sizeof(procpath), "/proc/%ld", file_pid); 32 | + if (stat(procpath, &st) == -1 && errno == ENOENT) { 33 | + /* Process doesn't exist */ 34 | + return 0; 35 | + } 36 | + return 1; 37 | +} 38 | + 39 | + 40 | /*! \brief Urgent handler 41 | * 42 | * Called by soft_hangup to interrupt the poll, read, or other 43 | @@ -4080,6 +4111,12 @@ int main(int argc, char *argv[]) 44 | exit(1); 45 | } 46 | 47 | + if (ast_is_starting()) { 48 | + fprintf(stderr, "Asterisk is currently starting. Use 'asterisk -r' to connect momentarily.\n"); 49 | + printf("%s", term_quit()); 50 | + exit(1); 51 | + } 52 | + 53 | #ifdef HAVE_CAP 54 | child_cap = cap_from_text("cap_net_admin-eip"); 55 | #endif 56 | -------------------------------------------------------------------------------- /patches/blueboxing.diff: -------------------------------------------------------------------------------- 1 | diff --git a/main/dsp.c b/main/dsp.c 2 | index 8b4e3ee60f..9ddaf68bec 100644 3 | --- a/main/dsp.c 4 | +++ b/main/dsp.c 5 | @@ -199,7 +199,7 @@ enum gsamp_thresh { 6 | 7 | #define BELL_MF_THRESHOLD 1.6e9 8 | #define BELL_MF_TWIST 4.0 /* 6dB */ 9 | -#define BELL_MF_RELATIVE_PEAK 12.6 /* 11dB */ 10 | +#define BELL_MF_RELATIVE_PEAK 6.3 /* 6dB */ 11 | 12 | #if defined(BUSYDETECT_TONEONLY) && defined(BUSYDETECT_COMPARE_TONE_AND_SILENCE) 13 | #error You cant use BUSYDETECT_TONEONLY together with BUSYDETECT_COMPARE_TONE_AND_SILENCE 14 | @@ -622,22 +622,23 @@ static int tone_detect(struct ast_dsp *dsp, tone_detect_state_t *s, int16_t *amp 15 | hit = 0; 16 | if (TONE_THRESHOLD <= tone_energy 17 | && tone_energy > s->energy * s->threshold) { 18 | - ast_debug(10, "%d Hz tone Hit! %2d Ew=%.4E, Et=%.4E, s/n=%10.2f\n", s->freq, s->hit_count, tone_energy, s->energy, tone_energy / (s->energy - tone_energy)); 19 | + ast_debug(7, "%d Hz tone Hit! %2d Ew=%.4E, Et=%.4E, s/n=%10.2f\n", s->freq, s->hit_count, tone_energy, s->energy, tone_energy / (s->energy - tone_energy)); 20 | hit = 1; 21 | } 22 | 23 | - if (s->hit_count) { 24 | + /* Sometimes we may get a lot of hits but not get 2 in succession, leading to a false negative, so always increment XXX Never mind. */ 25 | + if (s->hit_count) { /* XXX Was 'hit', but reverted because this broke the SF receiver, so reverting this part may make blue boxing slightly harder again */ 26 | s->hit_count++; 27 | } 28 | 29 | if (hit == s->last_hit) { 30 | if (!hit) { 31 | /* Two successive misses. Tone ended */ 32 | + if (s->hit_count) { 33 | + ast_debug(9, "Tone ended (had %d/%d hits)\n", s->hit_count, s->hits_required); 34 | + } 35 | s->hit_count = 0; 36 | - } else if (!s->hit_count) { 37 | - s->hit_count++; 38 | } 39 | - 40 | } 41 | 42 | if (s->hit_count == s->hits_required) { 43 | -------------------------------------------------------------------------------- /patches/coindsp.patch: -------------------------------------------------------------------------------- 1 | diff --git a/main/dsp.c b/main/dsp.c 2 | index 8b4e3ee60f..f0a754dc18 100644 3 | --- a/main/dsp.c 4 | +++ b/main/dsp.c 5 | @@ -165,7 +165,7 @@ enum gsamp_thresh { 6 | 7 | #define MAX_DTMF_DIGITS 128 8 | 9 | -#define DTMF_MATRIX_SIZE 4 10 | +#define DTMF_MATRIX_SIZE 5 11 | 12 | /* Basic DTMF (AT&T) specs: 13 | * 14 | @@ -317,15 +317,15 @@ typedef struct 15 | } digit_detect_state_t; 16 | 17 | static const float dtmf_row[] = { 18 | - 697.0, 770.0, 852.0, 941.0 19 | + 697.0, 770.0, 852.0, 941.0, 1700 20 | }; 21 | static const float dtmf_col[] = { 22 | - 1209.0, 1336.0, 1477.0, 1633.0 23 | + 1209.0, 1336.0, 1477.0, 1633.0, 2200 24 | }; 25 | static const float mf_tones[] = { 26 | 700.0, 900.0, 1100.0, 1300.0, 1500.0, 1700.0 27 | }; 28 | -static const char dtmf_positions[] = "123A" "456B" "789C" "*0#D"; 29 | +static const char dtmf_positions[] = "123A-" "456B-" "789C-" "*0#D-" "----$"; 30 | static const char bell_mf_positions[] = "1247C-358A--69*---0B----#"; 31 | static int thresholds[THRESHOLD_MAX]; 32 | static float dtmf_normal_twist; /* AT&T = 8dB */ 33 | @@ -734,6 +734,8 @@ static int dtmf_detect(struct ast_dsp *dsp, digit_detect_state_t *s, int16_t amp 34 | goertzel_sample(s->td.dtmf.col_out + 2, samp); 35 | goertzel_sample(s->td.dtmf.row_out + 3, samp); 36 | goertzel_sample(s->td.dtmf.col_out + 3, samp); 37 | + goertzel_sample(s->td.dtmf.row_out + 4, samp); 38 | + goertzel_sample(s->td.dtmf.col_out + 4, samp); 39 | /* go up to DTMF_MATRIX_SIZE - 1 */ 40 | } 41 | s->td.dtmf.current_sample += (limit - sample); 42 | @@ -776,9 +778,9 @@ static int dtmf_detect(struct ast_dsp *dsp, digit_detect_state_t *s, int16_t amp 43 | } 44 | /* ... and fraction of total energy test */ 45 | if (i >= DTMF_MATRIX_SIZE && 46 | - (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY * s->td.dtmf.energy) { 47 | + (row_energy[best_row] + col_energy[best_col]) > (DTMF_TO_TOTAL_ENERGY / (relax ? 2 : 1)) * s->td.dtmf.energy) { 48 | /* Got a hit */ 49 | - hit = dtmf_positions[(best_row << 2) + best_col]; 50 | + hit = dtmf_positions[(best_row * DTMF_MATRIX_SIZE) + best_col]; 51 | ast_debug(10, "DTMF hit '%c'\n", hit); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /patches/core_local_bug_fix_for_dial_string_parsing.patch: -------------------------------------------------------------------------------- 1 | From 0a4e149dd20e0ab2941a8cc3d42b2686197a4255 Mon Sep 17 00:00:00 2001 2 | From: Naveen Albert 3 | Date: Fri, 08 Apr 2022 23:17:22 +0000 4 | Subject: [PATCH] core_local: Fix local channel parsing with slashes 5 | 6 | Currently, trying to call a Local channel with a slash 7 | in the extension will fail due to the parsing of characters 8 | after such a slash as being dial modifiers. Additionally, 9 | core_local is inconsistent and incomplete with 10 | its parsing of Local dial strings in that sometimes it 11 | uses the first slash and at other times it uses the last. 12 | 13 | This creates inconsistent behavior for users, since using 14 | a slash in an extension is perfectly acceptable, and using 15 | a Goto to accomplish this works fine, but if specified 16 | through a Local channel, the parsing prevents this. 17 | 18 | This fixes this and improves the consistency of the dial 19 | string parsing to always check for options after the last 20 | slash in the dial string and only treat them as options if, 21 | in fact, they are actually options; otherwise, it will leave 22 | them alone. 23 | 24 | ASTERISK-30013 #close 25 | 26 | Change-Id: I8e85bc14072e452d0537a801aba17779569d0360 27 | --- 28 | 29 | diff --git a/main/core_local.c b/main/core_local.c 30 | index 26b1ba1..0ada7dc 100644 31 | --- a/main/core_local.c 32 | +++ b/main/core_local.c 33 | @@ -304,6 +304,25 @@ 34 | return peer; 35 | } 36 | 37 | +/*! \brief Determine if text after a '/' is really options or just part of the exten */ 38 | +static int really_local_options(char *data) 39 | +{ 40 | + /* The stuff after the slash might actually be part of the exten, not options. If so, don't lop them off. */ 41 | + char *ptr = data; 42 | + if (strlen(ptr) > 4) { 43 | + return 0; 44 | + } 45 | + while (*ptr) { 46 | + /* b option no longer exists, but accept it for this purpose for compatibility */ 47 | + if (*ptr != '/' && *ptr != 'n' && *ptr != 'j' && *ptr != 'm' && *ptr != 'b') { 48 | + return 0; 49 | + } 50 | + ptr++; 51 | + } 52 | + /* Okay, fine, these are definitely options... */ 53 | + return 1; 54 | +} 55 | + 56 | /*! \brief Adds devicestate to local channels */ 57 | static int local_devicestate(const char *data) 58 | { 59 | @@ -316,8 +335,8 @@ 60 | struct ao2_iterator it; 61 | 62 | /* Strip options if they exist */ 63 | - opts = strchr(exten, '/'); 64 | - if (opts) { 65 | + opts = strrchr(exten, '/'); 66 | + if (opts && really_local_options(opts)) { 67 | *opts = '\0'; 68 | } 69 | 70 | @@ -713,7 +732,7 @@ 71 | * that off for our argument to setting up the CC_INTERFACES 72 | * variable. 73 | */ 74 | - if ((slash = strrchr(reduced_dest, '/'))) { 75 | + if ((slash = strrchr(reduced_dest, '/')) && really_local_options(slash)) { 76 | *slash = '\0'; 77 | } 78 | ast_set_cc_interfaces_chanvar(chan, reduced_dest); 79 | @@ -885,7 +904,7 @@ 80 | ast_set_flag(&pvt->base, AST_UNREAL_MOH_INTERCEPT); 81 | 82 | /* Look for options */ 83 | - if ((opts = strchr(parse, '/'))) { 84 | + if ((opts = strrchr(parse, '/')) && really_local_options(opts)) { 85 | *opts++ = '\0'; 86 | if (strchr(opts, 'n')) { 87 | ast_set_flag(&pvt->base, AST_UNREAL_NO_OPTIMIZATION); 88 | @@ -900,6 +919,9 @@ 89 | if (strchr(opts, 'm')) { 90 | ast_clear_flag(&pvt->base, AST_UNREAL_MOH_INTERCEPT); 91 | } 92 | + } else if (opts) { 93 | + /* This isn't any kind of problem. Slashes are okay in the extension. */ 94 | + ast_debug(3, "Local dial string '%s' contains slash, but no options detected\n", parse); 95 | } 96 | 97 | /* Look for a context */ 98 | -------------------------------------------------------------------------------- /patches/dahdi_irq_handler.diff: -------------------------------------------------------------------------------- 1 | From cdd6ddd0fd08cb8b7313b16074882439fbb58045 Mon Sep 17 00:00:00 2001 2 | From: Shaun Ruffell 3 | Date: Fri, 11 Jan 2019 04:43:26 +0000 4 | Subject: [PATCH] Remove unnecessary DAHDI_IRQ_HANDLER macro. 5 | 6 | All supported kernels now use the same signature for the IRQ handler. 7 | 8 | Signed-off-by: Shaun Ruffell 9 | --- 10 | drivers/dahdi/wcb4xxp/base.c | 2 +- 11 | drivers/dahdi/wct4xxp/base.c | 4 ++-- 12 | drivers/dahdi/wctc4xxp/base.c | 2 +- 13 | drivers/dahdi/wcxb.c | 2 +- 14 | include/dahdi/kernel.h | 2 -- 15 | 5 files changed, 5 insertions(+), 7 deletions(-) 16 | 17 | diff --git a/drivers/dahdi/wcb4xxp/base.c b/drivers/dahdi/wcb4xxp/base.c 18 | index 5fe6fb2..1f72f14 100644 19 | --- a/drivers/dahdi/wcb4xxp/base.c 20 | +++ b/drivers/dahdi/wcb4xxp/base.c 21 | @@ -2963,7 +2963,7 @@ static void init_spans(struct b4xxp *b4) 22 | static void b4xxp_bottom_half(unsigned long data); 23 | 24 | /* top-half interrupt handler */ 25 | -DAHDI_IRQ_HANDLER(b4xxp_interrupt) 26 | +static irqreturn_t b4xxp_interrupt(int irq, void *dev_id) 27 | { 28 | struct b4xxp *b4 = dev_id; 29 | unsigned char status; 30 | diff --git a/drivers/dahdi/wct4xxp/base.c b/drivers/dahdi/wct4xxp/base.c 31 | index 1b37c43..c1d9612 100644 32 | --- a/drivers/dahdi/wct4xxp/base.c 33 | +++ b/drivers/dahdi/wct4xxp/base.c 34 | @@ -3941,7 +3941,7 @@ static irqreturn_t _t4_interrupt(int irq, void *dev_id) 35 | return IRQ_RETVAL(1); 36 | } 37 | 38 | -DAHDI_IRQ_HANDLER(t4_interrupt) 39 | +static irqreturn_t t4_interrupt(int irq, void *dev_id) 40 | { 41 | irqreturn_t ret; 42 | unsigned long flags; 43 | @@ -4263,7 +4263,7 @@ out: 44 | return IRQ_RETVAL(1); 45 | } 46 | 47 | -DAHDI_IRQ_HANDLER(t4_interrupt_gen2) 48 | +static irqreturn_t t4_interrupt_gen2(int irq, void *dev_id) 49 | { 50 | irqreturn_t ret; 51 | unsigned long flags; 52 | diff --git a/drivers/dahdi/wctc4xxp/base.c b/drivers/dahdi/wctc4xxp/base.c 53 | index 55c6026..54e28d3 100644 54 | --- a/drivers/dahdi/wctc4xxp/base.c 55 | +++ b/drivers/dahdi/wctc4xxp/base.c 56 | @@ -2731,7 +2731,7 @@ static void deferred_work_func(struct work_struct *work) 57 | service_rx_ring(wc); 58 | } 59 | 60 | -DAHDI_IRQ_HANDLER(wctc4xxp_interrupt) 61 | +static irqreturn_t wctc4xxp_interrupt(int irq, void *dev_id) 62 | { 63 | struct wcdte *wc = dev_id; 64 | bool packets_to_process = false; 65 | diff --git a/drivers/dahdi/wcxb.c b/drivers/dahdi/wcxb.c 66 | index 4eaba8f..122f9d3 100644 67 | --- a/drivers/dahdi/wcxb.c 68 | +++ b/drivers/dahdi/wcxb.c 69 | @@ -478,7 +478,7 @@ static irqreturn_t _wcxb_isr(int irq, void *dev_id) 70 | return IRQ_HANDLED; 71 | } 72 | 73 | -DAHDI_IRQ_HANDLER(wcxb_isr) 74 | +static irqreturn_t wcxb_isr(int irq, void *dev_id) 75 | { 76 | irqreturn_t ret; 77 | unsigned long flags; 78 | diff --git a/include/dahdi/kernel.h b/include/dahdi/kernel.h 79 | index 363b613..eb3f691 100644 80 | --- a/include/dahdi/kernel.h 81 | +++ b/include/dahdi/kernel.h 82 | @@ -60,8 +60,6 @@ 83 | 84 | #define dahdi_pci_module pci_register_driver 85 | 86 | -#define DAHDI_IRQ_HANDLER(a) static irqreturn_t a(int irq, void *dev_id) 87 | - 88 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) 89 | #define HAVE_NET_DEVICE_OPS 90 | #endif 91 | -- 92 | 1.7.9.5 93 | 94 | -------------------------------------------------------------------------------- /patches/dahdi_pci_module.diff: -------------------------------------------------------------------------------- 1 | From 4af6f69fff19ea3cfb9ab0ff86a681be86045215 Mon Sep 17 00:00:00 2001 2 | From: Shaun Ruffell 3 | Date: Fri, 11 Jan 2019 04:46:35 +0000 4 | Subject: [PATCH] Remove unnecessary dahdi_pci_module macro. 5 | 6 | All supported kernel variations support the same signature for 7 | registering a PCI module, so we can eliminate the macro. 8 | 9 | Signed-off-by: Shaun Ruffell 10 | --- 11 | drivers/dahdi/wcaxx-base.c | 2 +- 12 | drivers/dahdi/wcb4xxp/base.c | 2 +- 13 | drivers/dahdi/wct4xxp/base.c | 2 +- 14 | drivers/dahdi/wctc4xxp/base.c | 2 +- 15 | drivers/dahdi/wctdm24xxp/base.c | 2 +- 16 | drivers/dahdi/wcte13xp-base.c | 2 +- 17 | drivers/dahdi/wcte43x-base.c | 2 +- 18 | include/dahdi/kernel.h | 2 -- 19 | 8 files changed, 7 insertions(+), 9 deletions(-) 20 | 21 | diff --git a/drivers/dahdi/wcaxx-base.c b/drivers/dahdi/wcaxx-base.c 22 | index ed7207a..b934960 100644 23 | --- a/drivers/dahdi/wcaxx-base.c 24 | +++ b/drivers/dahdi/wcaxx-base.c 25 | @@ -4474,7 +4474,7 @@ static int __init wcaxx_init(void) 26 | if (!battthresh) 27 | battthresh = fxo_modes[_opermode].battthresh; 28 | 29 | - res = dahdi_pci_module(&wcaxx_driver); 30 | + res = pci_register_driver(&wcaxx_driver); 31 | if (res) 32 | return -ENODEV; 33 | 34 | diff --git a/drivers/dahdi/wcb4xxp/base.c b/drivers/dahdi/wcb4xxp/base.c 35 | index 1f72f14..784929f 100644 36 | --- a/drivers/dahdi/wcb4xxp/base.c 37 | +++ b/drivers/dahdi/wcb4xxp/base.c 38 | @@ -3672,7 +3672,7 @@ static int __init b4xx_init(void) 39 | printk(KERN_ERR "%s: ERROR: Could not initialize /proc/%s\n",THIS_MODULE->name, PROCFS_NAME); 40 | } 41 | #endif 42 | - if (dahdi_pci_module(&b4xx_driver)) 43 | + if (pci_register_driver(&b4xx_driver)) 44 | return -ENODEV; 45 | 46 | return 0; 47 | diff --git a/drivers/dahdi/wct4xxp/base.c b/drivers/dahdi/wct4xxp/base.c 48 | index c1d9612..4d0c769 100644 49 | --- a/drivers/dahdi/wct4xxp/base.c 50 | +++ b/drivers/dahdi/wct4xxp/base.c 51 | @@ -5543,7 +5543,7 @@ static int __init t4_init(void) 52 | "Please use 'default_linemode' instead.\n"); 53 | } 54 | 55 | - res = dahdi_pci_module(&t4_driver); 56 | + res = pci_register_driver(&t4_driver); 57 | if (res) 58 | return -ENODEV; 59 | 60 | diff --git a/drivers/dahdi/wctc4xxp/base.c b/drivers/dahdi/wctc4xxp/base.c 61 | index 54e28d3..3bd56d1 100644 62 | --- a/drivers/dahdi/wctc4xxp/base.c 63 | +++ b/drivers/dahdi/wctc4xxp/base.c 64 | @@ -4236,7 +4236,7 @@ static int __init wctc4xxp_init(void) 65 | return -ENOMEM; 66 | spin_lock_init(&wctc4xxp_list_lock); 67 | INIT_LIST_HEAD(&wctc4xxp_list); 68 | - res = dahdi_pci_module(&wctc4xxp_driver); 69 | + res = pci_register_driver(&wctc4xxp_driver); 70 | if (res) { 71 | kmem_cache_destroy(cmd_cache); 72 | return -ENODEV; 73 | diff --git a/drivers/dahdi/wctdm24xxp/base.c b/drivers/dahdi/wctdm24xxp/base.c 74 | index d2e481a..16bf384 100644 75 | --- a/drivers/dahdi/wctdm24xxp/base.c 76 | +++ b/drivers/dahdi/wctdm24xxp/base.c 77 | @@ -6102,7 +6102,7 @@ static int __init wctdm_init(void) 78 | 79 | b400m_module_init(); 80 | 81 | - res = dahdi_pci_module(&wctdm_driver); 82 | + res = pci_register_driver(&wctdm_driver); 83 | if (res) 84 | return -ENODEV; 85 | 86 | diff --git a/drivers/dahdi/wcte13xp-base.c b/drivers/dahdi/wcte13xp-base.c 87 | index 25d4e31..b5f8043 100644 88 | --- a/drivers/dahdi/wcte13xp-base.c 89 | +++ b/drivers/dahdi/wcte13xp-base.c 90 | @@ -2788,7 +2788,7 @@ static int __init te13xp_init(void) 91 | return -EINVAL; 92 | } 93 | 94 | - res = dahdi_pci_module(&te13xp_driver); 95 | + res = pci_register_driver(&te13xp_driver); 96 | if (res) 97 | return -ENODEV; 98 | 99 | diff --git a/drivers/dahdi/wcte43x-base.c b/drivers/dahdi/wcte43x-base.c 100 | index 722d18d..45b0f6c 100644 101 | --- a/drivers/dahdi/wcte43x-base.c 102 | +++ b/drivers/dahdi/wcte43x-base.c 103 | @@ -3612,7 +3612,7 @@ static int __init t43x_init(void) 104 | return -EINVAL; 105 | } 106 | 107 | - res = dahdi_pci_module(&t43x_driver); 108 | + res = pci_register_driver(&t43x_driver); 109 | if (res) 110 | return -ENODEV; 111 | 112 | diff --git a/include/dahdi/kernel.h b/include/dahdi/kernel.h 113 | index eb3f691..1b4ba10 100644 114 | --- a/include/dahdi/kernel.h 115 | +++ b/include/dahdi/kernel.h 116 | @@ -58,8 +58,6 @@ 117 | 118 | #include 119 | 120 | -#define dahdi_pci_module pci_register_driver 121 | - 122 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) 123 | #define HAVE_NET_DEVICE_OPS 124 | #endif 125 | -- 126 | 1.7.9.5 127 | 128 | -------------------------------------------------------------------------------- /patches/dahdi_rtoutpulse.diff: -------------------------------------------------------------------------------- 1 | diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c 2 | index bcd6147..6e181e6 100644 3 | --- a/drivers/dahdi/dahdi-base.c 4 | +++ b/drivers/dahdi/dahdi-base.c 5 | @@ -8449,9 +8449,10 @@ static void __dahdi_hooksig_pvt(struct dahdi_chan *chan, enum dahdi_rxsig rxsig) 6 | chan->pulsecount++; 7 | 8 | chan->pulsetimer = DAHDI_PULSETIMEOUT; 9 | - chan->itimerset = chan->itimer = 0; 10 | + chan->itimerset = chan->itimer = 0; 11 | if (chan->pulsecount == 1) 12 | __qevent(chan,DAHDI_EVENT_PULSE_START); 13 | + __qevent(chan,DAHDI_EVENT_PULSE); 14 | } 15 | } 16 | break; 17 | @@ -8463,6 +8464,7 @@ static void __dahdi_hooksig_pvt(struct dahdi_chan *chan, enum dahdi_rxsig rxsig) 18 | case DAHDI_RXSIG_ONHOOK: /* went on hook */ 19 | /* This interface is now going on hook. 20 | Check for WINK, etc */ 21 | + __qevent(chan,DAHDI_EVENT_PULSE_BREAK); 22 | if (chan->itimer) 23 | __qevent(chan,DAHDI_EVENT_WINKFLASH); 24 | #if defined(EMFLASH) || defined(EMPULSE) 25 | @@ -8550,6 +8552,7 @@ static void __dahdi_hooksig_pvt(struct dahdi_chan *chan, enum dahdi_rxsig rxsig) 26 | chan->itimer = chan->itimerset; 27 | if (chan->pulsecount == 1) 28 | __qevent(chan,DAHDI_EVENT_PULSE_START); 29 | + __qevent(chan,DAHDI_EVENT_PULSE); 30 | } 31 | } else 32 | __qevent(chan,DAHDI_EVENT_WINKFLASH); 33 | @@ -8565,6 +8568,7 @@ static void __dahdi_hooksig_pvt(struct dahdi_chan *chan, enum dahdi_rxsig rxsig) 34 | break; 35 | case DAHDI_RXSIG_ONHOOK: /* went on hook */ 36 | /* if not during offhook debounce time */ 37 | + __qevent(chan,DAHDI_EVENT_PULSE_BREAK); 38 | if ((chan->txstate != DAHDI_TXSTATE_DEBOUNCE) && 39 | (chan->txstate != DAHDI_TXSTATE_KEWL) && 40 | (chan->txstate != DAHDI_TXSTATE_AFTERKEWL)) { 41 | diff --git a/drivers/dahdi/xpp/dahdi_debug.h b/drivers/dahdi/xpp/dahdi_debug.h 42 | index 0488451..f4e625b 100644 43 | --- a/drivers/dahdi/xpp/dahdi_debug.h 44 | +++ b/drivers/dahdi/xpp/dahdi_debug.h 45 | @@ -180,6 +180,10 @@ static inline char *event2str(int event) 46 | return "BITSCHANGED"; 47 | case DAHDI_EVENT_PULSE_START: 48 | return "PULSE_START"; 49 | + case DAHDI_EVENT_PULSE: 50 | + return "PULSE"; 51 | + case DAHDI_EVENT_PULSE_BREAK: 52 | + return "PULSE_BREAK"; 53 | case DAHDI_EVENT_TIMER_EXPIRED: 54 | return "TIMER_EXPIRED"; 55 | case DAHDI_EVENT_TIMER_PING: 56 | diff --git a/include/dahdi/user.h b/include/dahdi/user.h 57 | index 68e72e0..db05257 100644 58 | --- a/include/dahdi/user.h 59 | +++ b/include/dahdi/user.h 60 | @@ -453,6 +453,12 @@ enum { 61 | /* The channel's write buffer encountered an underrun condition */ 62 | #define DAHDI_EVENT_WRITE_UNDERRUN 30 63 | 64 | +/* A pulse was detected on a channel */ 65 | +#define DAHDI_EVENT_PULSE 31 66 | + 67 | +/* Momentary on-hook (pulse break) */ 68 | +#define DAHDI_EVENT_PULSE_BREAK 32 69 | + 70 | #define DAHDI_EVENT_PULSEDIGIT (1 << 16) /* This is OR'd with the digit received */ 71 | #define DAHDI_EVENT_DTMFDOWN (1 << 17) /* Ditto for DTMF key down event */ 72 | #define DAHDI_EVENT_DTMFUP (1 << 18) /* Ditto for DTMF key up event */ 73 | -------------------------------------------------------------------------------- /patches/disablenewexten.diff: -------------------------------------------------------------------------------- 1 | --- main/manager_channels.c.orig 2022-01-06 22:09:28.738866540 +0000 2 | +++ main/manager_channels.c 2022-01-09 13:41:42.440213491 +0000 3 | @@ -611,6 +611,9 @@ 4 | struct ast_channel_snapshot *old_snapshot, 5 | struct ast_channel_snapshot *new_snapshot) 6 | { 7 | + /* This event is useless, and is so frequent that it 8 | + can add a significant performance overhead. */ 9 | + return NULL; 10 | /* Empty application is not valid for a Newexten event */ 11 | if (ast_strlen_zero(new_snapshot->dialplan->appl)) { 12 | return NULL; 13 | -------------------------------------------------------------------------------- /patches/freepbx_installvercheck.diff: -------------------------------------------------------------------------------- 1 | --- installlib/installcommand.class.php.orig 2022-02-11 12:02:47.859242419 -0500 2 | +++ installlib/installcommand.class.php 2022-02-11 12:05:53.038004749 -0500 3 | @@ -286,6 +286,14 @@ 4 | $output->writeln("Try starting Asterisk with the './start_asterisk start' command in this directory"); 5 | exit(1); 6 | } 7 | + 8 | + // If running a command ends in "Asterisk ending (0).", which will appear at the end, cut that out first. 9 | + if (count($tmpout) > 2 && $tmpout[2] === 'Asterisk ending (0).') { 10 | + unset($tmpout[2]); 11 | + } else if (count($tmpout) > 1 && $tmpout[1] === 'Asterisk ending (0).') { 12 | + unset($tmpout[1]); 13 | + } 14 | + 15 | // If this machine doesn't have an ethernet interface (which opens a WHOLE NEW can of worms), 16 | // asterisk will say "No ethernet interface detected". There may, also, be other errors about 17 | // other modules or configuration issues. The last line, however, is always the version. 18 | -------------------------------------------------------------------------------- /patches/hearpulsing-ast.diff: -------------------------------------------------------------------------------- 1 | diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c 2 | index e6c41e51e9..09b0a3f398 100644 3 | --- a/channels/chan_dahdi.c 4 | +++ b/channels/chan_dahdi.c 5 | @@ -1062,6 +1062,7 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void) 6 | .use_callerid = 1, 7 | .sig = -1, 8 | .outsigmod = -1, 9 | + .hearpulsing = 0, 10 | 11 | .cid_rxgain = +5.0, 12 | 13 | @@ -9014,7 +9015,7 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast) 14 | #if 0 15 | ast_debug(1, "Read %d of voice on %s\n", p->subs[idx].f.datalen, ast->name); 16 | #endif 17 | - if ((p->dialing && !p->waitingfordt.tv_sec) || p->radio || /* Transmitting something */ 18 | + if ((!p->hearpulsing && ( (p->dialing && !p->waitingfordt.tv_sec) || p->radio )) || /* Transmitting something */ 19 | (idx && (ast_channel_state(ast) != AST_STATE_UP)) || /* Three-way or callwait that isn't up */ 20 | ((idx == SUB_CALLWAIT) && !p->subs[SUB_CALLWAIT].inthreeway) /* Inactive and non-confed call-wait */ 21 | ) { 22 | @@ -13014,6 +13015,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, 23 | tmp->dahditrcallerid = conf->chan.dahditrcallerid; 24 | tmp->restrictcid = conf->chan.restrictcid; 25 | tmp->use_callingpres = conf->chan.use_callingpres; 26 | + tmp->hearpulsing = conf->chan.hearpulsing; 27 | if (tmp->usedistinctiveringdetection) { 28 | if (!tmp->use_callerid) { 29 | ast_log(LOG_NOTICE, "Distinctive Ring detect requires 'usecallerid' be on\n"); 30 | @@ -13165,6 +13167,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, 31 | tmp->ani_wink_time = conf->chan.ani_wink_time; 32 | tmp->ani_timeout = conf->chan.ani_timeout; 33 | tmp->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch; 34 | + tmp->hearpulsing = conf->chan.hearpulsing; 35 | tmp->reoriginate = conf->chan.reoriginate; 36 | tmp->sendcalleridafter = conf->chan.sendcalleridafter; 37 | ast_cc_copy_config_params(tmp->cc_params, conf->chan.cc_params); 38 | @@ -13274,6 +13277,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, 39 | analog_p->ani_timeout = conf->chan.ani_timeout; 40 | analog_p->ani_wink_time = conf->chan.ani_wink_time; 41 | analog_p->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch; 42 | + analog_p->hearpulsing = conf->chan.hearpulsing; 43 | analog_p->permcallwaiting = conf->chan.callwaiting; /* permcallwaiting possibly modified in analog_config_complete */ 44 | analog_p->calledsubscriberheld = conf->chan.calledsubscriberheld; /* Only actually used in analog pvt, not DAHDI pvt */ 45 | analog_p->callreturn = conf->chan.callreturn; 46 | @@ -18690,6 +18694,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct 47 | confp->chan.reoriginate = ast_true(v->value); 48 | } else if (!strcasecmp(v->name, "sendcalleridafter")) { 49 | confp->chan.sendcalleridafter = atoi(v->value); 50 | + } else if(!strcasecmp(v->name, "hearpulsing")) { 51 | + confp->chan.hearpulsing = ast_true(v->value); 52 | } else if (!strcasecmp(v->name, "mwimonitornotify")) { 53 | ast_copy_string(mwimonitornotify, v->value, sizeof(mwimonitornotify)); 54 | } else if (ast_cc_is_config_param(v->name)) { 55 | diff --git a/channels/chan_dahdi.h b/channels/chan_dahdi.h 56 | index b7955cdddf..53a3d46d9f 100644 57 | --- a/channels/chan_dahdi.h 58 | +++ b/channels/chan_dahdi.h 59 | @@ -472,6 +472,11 @@ struct dahdi_pvt { 60 | struct sig_pri_span *pri; 61 | int logicalspan; 62 | #endif /* defined(HAVE_PRI) */ 63 | + 64 | + /*!\brief TRUE if the caller should hear signaling on analog 65 | + * \analog channels 66 | + */ 67 | + unsigned int hearpulsing:1; 68 | /*! 69 | * \brief TRUE if SMDI (Simplified Message Desk Interface) is enabled 70 | * \note Set from the "usesmdi" value read in from chan_dahdi.conf 71 | diff --git a/channels/sig_analog.c b/channels/sig_analog.c 72 | index 7ebb06239f..1dd1667e4d 100644 73 | --- a/channels/sig_analog.c 74 | +++ b/channels/sig_analog.c 75 | @@ -1271,6 +1271,13 @@ int analog_call(struct analog_pvt *p, struct ast_channel *ast, const char *rdest 76 | ast_debug(1, "not yet implemented\n"); 77 | return -1; 78 | } 79 | + 80 | + /* Allows callers to hear pulsing on analog channels */ 81 | + if (p->hearpulsing) { 82 | + ast_debug(1, "Enqueueing progress frame when dialling has begun in chan %d\n", p->channel); 83 | + ast_queue_control(p->owner, AST_CONTROL_PROGRESS); 84 | + } 85 | + 86 | return 0; 87 | } 88 | 89 | diff --git a/channels/sig_analog.h b/channels/sig_analog.h 90 | index 81043f39a5..51b859fac1 100644 91 | --- a/channels/sig_analog.h 92 | +++ b/channels/sig_analog.h 93 | @@ -314,6 +314,11 @@ struct analog_pvt { 94 | /*! \brief The SMDI interface to get SMDI messages from. */ 95 | struct ast_smdi_interface *smdi_iface; 96 | 97 | + /*!\brief TRUE if the caller should hear signaling on analog 98 | + * \analog channels 99 | + */ 100 | + unsigned int hearpulsing:1; 101 | + 102 | /* Not used for anything but log messages. Could be just the TCID */ 103 | int channel; /*!< Channel Number */ 104 | 105 | -------------------------------------------------------------------------------- /patches/hearpulsing-dahlin.diff: -------------------------------------------------------------------------------- 1 | diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c 2 | index bcd6147..487ba09 100644 3 | --- a/drivers/dahdi/dahdi-base.c 4 | +++ b/drivers/dahdi/dahdi-base.c 5 | @@ -4987,6 +4987,8 @@ static int dahdi_ioctl_chanconfig(struct file *file, unsigned long data) 6 | !dahdi_have_netdev(chan)) 7 | module_printk(KERN_NOTICE, "Unable to register HDLC device for channel %s\n", chan->name); 8 | if (!res) { 9 | + /* Make pulsing audible */ 10 | + chan->hearpulsing = ch.hearpulsing; 11 | /* Setup default law */ 12 | chan->deflaw = ch.deflaw; 13 | /* Copy back any modified settings */ 14 | @@ -7831,7 +7833,8 @@ static inline void __dahdi_process_getaudio_chunk(struct dahdi_chan *ss, unsigne 15 | } 16 | #endif 17 | 18 | - if ((!ms->confmute && !ms->dialing) || (is_pseudo_chan(ms))) { 19 | + /* heapulsing is configured on a per-channel basis in system.conf */ 20 | + if ((!ms->confmute && ((!ms->dialing) || (ms->hearpulsing))) || (is_pseudo_chan(ms))) { 21 | struct dahdi_chan *const conf_chan = ms->conf_chan; 22 | /* Handle conferencing on non-clear channel and non-HDLC channels */ 23 | switch(ms->confmode & DAHDI_CONF_MODE_MASK) { 24 | @@ -8883,7 +8886,8 @@ static inline void __dahdi_process_putaudio_chunk(struct dahdi_chan *ss, unsigne 25 | 26 | if (ms->dialing) ms->afterdialingtimer = 50; 27 | else if (ms->afterdialingtimer) ms->afterdialingtimer--; 28 | - if (ms->afterdialingtimer && !is_pseudo_chan(ms)) { 29 | + /* heapulsing is configured on a per-channel basis in system.conf */ 30 | + if (ms->afterdialingtimer && !is_pseudo_chan(ms) && (!ms->hearpulsing)) { 31 | /* Be careful since memset is likely a macro */ 32 | rxb[0] = DAHDI_LIN2X(0, ms); 33 | memset(&rxb[1], rxb[0], DAHDI_CHUNKSIZE - 1); /* receive as silence if dialing */ 34 | diff --git a/include/dahdi/kernel.h b/include/dahdi/kernel.h 35 | index 22b9b66..c84650c 100644 36 | --- a/include/dahdi/kernel.h 37 | +++ b/include/dahdi/kernel.h 38 | @@ -570,6 +570,9 @@ struct dahdi_chan { 39 | /*! Idle signalling if CAS signalling */ 40 | int idlebits; 41 | 42 | + /* Cut audio through early to hear pulsing */ 43 | + short hearpulsing; 44 | + 45 | int deflaw; /*! 1 = mulaw, 2=alaw, 0=undefined */ 46 | short *xlaw; 47 | #ifdef OPTIMIZE_CHANMUTE 48 | diff --git a/include/dahdi/user.h b/include/dahdi/user.h 49 | index 68e72e0..ef3478b 100644 50 | --- a/include/dahdi/user.h 51 | +++ b/include/dahdi/user.h 52 | @@ -685,6 +685,7 @@ struct dahdi_chanconfig { 53 | int idlebits; /* Idle bits (if this is a CAS channel) or 54 | channel to monitor (if this is DACS channel) */ 55 | char netdev_name[16];/* name for the hdlc network device*/ 56 | + int hearpulsing; /* make outpulsing audible to caller */ 57 | }; 58 | 59 | #define DAHDI_CHANCONFIG _IOW(DAHDI_CODE, 19, struct dahdi_chanconfig) 60 | -------------------------------------------------------------------------------- /patches/hearpulsing-dahtool.patch: -------------------------------------------------------------------------------- 1 | From 8a9040c89cf6ff515874feb30865a3afc7e3cdfd Mon Sep 17 00:00:00 2001 2 | From: Sarah Autumn 3 | Date: Mon, 9 Aug 2021 12:50:36 -0700 4 | Subject: [PATCH] configure hearpulsing per channel 5 | 6 | --- 7 | dahdi_cfg.c | 29 +++++++++++++++++++++++++++++ 8 | 1 file changed, 29 insertions(+) 9 | 10 | diff --git a/dahdi_cfg.c b/dahdi_cfg.c 11 | index ecd4477..566c604 100644 12 | --- a/dahdi_cfg.c 13 | +++ b/dahdi_cfg.c 14 | @@ -785,6 +785,33 @@ static int setfiftysixkhdlc(char *keyword, char *args) 15 | return 0; 16 | } 17 | 18 | +/* Allows hearpulsing to be a configurable option */ 19 | +static int sethearpulsing(char *keyword, char *args) 20 | +{ 21 | + int res; 22 | + short setting; 23 | + int x; 24 | + int chans[DAHDI_MAX_CHANNELS]; 25 | + 26 | + bzero(chans, sizeof(chans)); 27 | + res = apply_channels(chans, args); 28 | + if (res <= 0) 29 | + return -1; 30 | + if (!strcasecmp(keyword, "hearpulsing")) { 31 | + setting = 1; 32 | + } else { 33 | + fprintf(stderr, "Huh??? Don't know about '%s' hearpulsing setting\n", keyword); 34 | + return -1; 35 | + } 36 | + for (x=0;xtxstate == DAHDI_TXSTATE_KEWL) || 20 | - (chan->txstate == DAHDI_TXSTATE_AFTERKEWL)) { 21 | + } else if ((chan->txstate == DAHDI_TXSTATE_KEWL || chan->txstate == DAHDI_TXSTATE_KEWLUSER) || 22 | + (chan->txstate == DAHDI_TXSTATE_AFTERKEWL || chan->txstate == DAHDI_TXSTATE_AFTERKEWLUSER)) { 23 | param.rxisoffhook = 1; 24 | } else { 25 | param.rxisoffhook = 0; 26 | @@ -6812,14 +6814,27 @@ static int dahdi_chan_ioctl(struct file *file, unsigned int cmd, unsigned long d 27 | break; 28 | case DAHDI_OFFHOOK: 29 | spin_lock_irqsave(&chan->lock, flags); 30 | - if ((chan->txstate == DAHDI_TXSTATE_KEWL) || 31 | - (chan->txstate == DAHDI_TXSTATE_AFTERKEWL)) { 32 | + if ((chan->txstate == DAHDI_TXSTATE_KEWL || chan->txstate == DAHDI_TXSTATE_KEWLUSER) || 33 | + (chan->txstate == DAHDI_TXSTATE_AFTERKEWL || chan->txstate == DAHDI_TXSTATE_AFTERKEWLUSER)) { 34 | spin_unlock_irqrestore(&chan->lock, flags); 35 | return -EBUSY; 36 | } 37 | dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_DEBOUNCE, chan->debouncetime); 38 | spin_unlock_irqrestore(&chan->lock, flags); 39 | break; 40 | + case DAHDI_KEWL: 41 | + spin_lock_irqsave(&chan->lock, flags); 42 | + if ((chan->sig == DAHDI_SIG_FXOKS) && (chan->txstate != DAHDI_TXSTATE_ONHOOK) 43 | + /* if other party is already on-hook we shouldn't do any battery drop */ 44 | + && !((chan->rxhooksig == DAHDI_RXSIG_ONHOOK) && (chan->itimer <= 0))) { 45 | + /* Do RBS signalling on the channel's behalf */ 46 | + dahdi_rbs_sethook(chan, DAHDI_TXSIG_KEWL, DAHDI_TXSTATE_KEWLUSER, DAHDI_KEWLTIME); 47 | + } else { 48 | + spin_unlock_irqrestore(&chan->lock, flags); 49 | + return -EBUSY; 50 | + } 51 | + spin_unlock_irqrestore(&chan->lock, flags); 52 | + break; 53 | case DAHDI_RING: 54 | case DAHDI_START: 55 | spin_lock_irqsave(&chan->lock, flags); 56 | @@ -8352,6 +8367,13 @@ static inline void __rbs_otimer_expire(struct dahdi_chan *chan) 57 | wake_up_interruptible(&chan->waitq); 58 | break; 59 | 60 | + case DAHDI_TXSTATE_KEWLUSER: 61 | + dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_AFTERKEWLUSER, DAHDI_AFTERKEWLTIME); 62 | + if (chan->file && (chan->file->f_flags & O_NONBLOCK)) 63 | + __qevent(chan, DAHDI_EVENT_HOOKCOMPLETE); 64 | + wake_up_interruptible(&chan->waitq); 65 | + break; 66 | + 67 | case DAHDI_TXSTATE_AFTERKEWL: 68 | if (chan->kewlonhook) { 69 | __qevent(chan,DAHDI_EVENT_ONHOOK); 70 | @@ -8360,6 +8382,10 @@ static inline void __rbs_otimer_expire(struct dahdi_chan *chan) 71 | chan->gotgs = 0; 72 | break; 73 | 74 | + case DAHDI_TXSTATE_AFTERKEWLUSER: 75 | + chan->txstate = DAHDI_TXSTATE_OFFHOOK; 76 | + break; 77 | + 78 | case DAHDI_TXSTATE_PULSEBREAK: 79 | dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_PULSEMAKE, 80 | chan->pulsemaketime); 81 | diff --git a/include/dahdi/user.h b/include/dahdi/user.h 82 | index 68e72e0..63e997e 100644 83 | --- a/include/dahdi/user.h 84 | +++ b/include/dahdi/user.h 85 | @@ -274,6 +274,9 @@ enum { 86 | /* Value for DAHDI_HOOK, turn ringer off */ 87 | #define DAHDI_RINGOFF 6 88 | 89 | +/* Value for DAHDI_HOOK, loop disconnect on demand */ 90 | +#define DAHDI_KEWL 8 91 | + 92 | /* Flush and stop the read (input) process */ 93 | #define DAHDI_FLUSH_READ 1 94 | 95 | -------------------------------------------------------------------------------- /patches/kewl2.diff: -------------------------------------------------------------------------------- 1 | diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c 2 | index 53ae4ea..a810f0f 100644 3 | --- a/drivers/dahdi/dahdi-base.c 4 | +++ b/drivers/dahdi/dahdi-base.c 5 | @@ -8549,11 +8549,11 @@ static void __dahdi_hooksig_pvt(struct dahdi_chan *chan, enum dahdi_rxsig rxsig) 6 | case DAHDI_RXSIG_ONHOOK: /* went on hook */ 7 | /* if not during offhook debounce time */ 8 | if ((chan->txstate != DAHDI_TXSTATE_DEBOUNCE) && 9 | - (chan->txstate != DAHDI_TXSTATE_KEWL) && 10 | - (chan->txstate != DAHDI_TXSTATE_AFTERKEWL)) { 11 | + (chan->txstate != DAHDI_TXSTATE_KEWL && chan->txstate != DAHDI_TXSTATE_KEWLUSER) && 12 | + (chan->txstate != DAHDI_TXSTATE_AFTERKEWL && chan->txstate != DAHDI_TXSTATE_AFTERKEWLUSER)) { 13 | chan->itimerset = chan->itimer = chan->rxflashtime * DAHDI_CHUNKSIZE; 14 | } 15 | - if (chan->txstate == DAHDI_TXSTATE_KEWL) 16 | + if (chan->txstate == DAHDI_TXSTATE_KEWL && chan->txstate == DAHDI_TXSTATE_KEWLUSER) 17 | chan->kewlonhook = 1; 18 | break; 19 | default: 20 | -------------------------------------------------------------------------------- /patches/loader_deprecated.patch: -------------------------------------------------------------------------------- 1 | --- main/loader.c.orig 2021-12-12 02:31:08.947096630 +0000 2 | +++ main/loader.c 2021-12-12 02:32:17.263866627 +0000 3 | @@ -2484,7 +2484,12 @@ 4 | } 5 | 6 | if (ast_str_strlen(warning_msg)) { 7 | - ast_log(LOG_WARNING, "%s\n", ast_str_buffer(warning_msg)); 8 | + int notserious = (!strcmp(mod_name, "res_adsi") || !strcmp(mod_name, "app_adsiprog") || !strcmp(mod_name, "app_getcpeid")); /* these are being maintained and there are being kept in the tree indefinitely, so don't alarm people unnecessarily */ 9 | + if (notserious) { 10 | + ast_log(LOG_NOTICE, "%s\n", ast_str_buffer(warning_msg)); 11 | + } else { 12 | + ast_log(LOG_WARNING, "%s\n", ast_str_buffer(warning_msg)); 13 | + } 14 | } 15 | 16 | ast_free(mod_name); 17 | -------------------------------------------------------------------------------- /patches/modfinal.diff: -------------------------------------------------------------------------------- 1 | --- /usr/lib/linux-kbuild-6.1/scripts/Makefile.modfinal.orig 2024-11-09 17:26:54.648766021 -0500 2 | +++ /usr/lib/linux-kbuild-6.1/scripts/Makefile.modfinal 2024-11-09 17:27:00.900879469 -0500 3 | @@ -58,9 +58,9 @@ 4 | # Re-generate module BTFs if either module's .ko or vmlinux changed 5 | $(modules): %.ko: %.o %.mod.o $(ARCH_MODULE_LDS) $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),vmlinux) FORCE 6 | +$(call if_changed_except,ld_ko_o,vmlinux) 7 | -ifdef CONFIG_DEBUG_INFO_BTF_MODULES 8 | - +$(if $(newer-prereqs),$(call cmd,btf_ko)) 9 | -endif 10 | +#ifdef CONFIG_DEBUG_INFO_BTF_MODULES 11 | +# +$(if $(newer-prereqs),$(call cmd,btf_ko)) 12 | +#endif 13 | 14 | targets += $(modules) $(modules:.ko=.mod.o) 15 | 16 | -------------------------------------------------------------------------------- /patches/pciradio_Kbuild.diff: -------------------------------------------------------------------------------- 1 | --- drivers/dahdi/Kbuild.orig 2023-09-27 20:20:17.025040055 +0000 2 | +++ drivers/dahdi/Kbuild 2023-09-27 20:21:44.829846216 +0000 3 | @@ -167,4 +167,7 @@ 4 | $(obj)/tor2fw.h: $(src)/tormenta2.rbt $(obj)/makefw 5 | $(obj)/makefw $< tor2fw > $@ 6 | 7 | -clean-files := radfw.h 8 | +$(obj)/radfw.h: $(src)/pciradio.rbt $(obj)/makefw 9 | + $(obj)/makefw $< radfw > $@ 10 | + 11 | +clean-files := tor2fw.h radfw.h 12 | -------------------------------------------------------------------------------- /patches/prefixinclude.diff: -------------------------------------------------------------------------------- 1 | diff --git a/main/pbx.c b/main/pbx.c 2 | index c655f1bcfc..20acce4aeb 100644 3 | --- a/main/pbx.c 4 | +++ b/main/pbx.c 5 | @@ -2768,10 +2768,34 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan, 6 | const struct ast_include *i = ast_context_includes_get(tmp, idx); 7 | 8 | if (include_valid(i)) { 9 | - if ((e = pbx_find_extension(chan, bypass, q, include_rname(i), exten, priority, label, callerid, action))) { 10 | + const char *include_exten = exten; 11 | + const char *prefix = include_prefix(i); 12 | + if (!ast_strlen_zero(prefix)) { 13 | + /* Determine if we should remove a prefix before accessing the 14 | + * extensions in an include. For example, remove '9' from 15 | + * from-internal before going to included context pstn. */ 16 | + int pfxlen = strlen(prefix); 17 | + if (!strncmp(include_exten, prefix, pfxlen)) { 18 | + include_exten += pfxlen; 19 | + if (ast_strlen_zero(include_exten)) { 20 | + ast_debug(1, "Removed prefix '%s' for included context '%s', but the prefix was the entire extension, reverting\n", 21 | + prefix, include_rname(i)); 22 | + include_exten -= pfxlen; /* Tack the prefix back on, since this wasn't really a prefix. */ 23 | + } else { 24 | + ast_debug(2, "Removing prefix '%s' (=> %s) before searching included context '%s'\n", prefix, include_exten, include_rname(i)); 25 | + } 26 | + } else { 27 | + continue; 28 | + } 29 | + } 30 | + if ((e = pbx_find_extension(chan, bypass, q, include_rname(i), include_exten, priority, label, callerid, action))) { 31 | #ifdef NEED_DEBUG_HERE 32 | ast_log(LOG_NOTICE,"Returning recursive match of %s\n", e->exten); 33 | #endif 34 | + if (0 && action == E_SPAWN && chan && include_exten != exten) { /* XXX Do not do this or it will mess up execution after priority 1 */ 35 | + ast_debug(1, "Updating EXTEN from %s to %s\n", exten, include_exten); /* And don't set the context explicitly, that's wrong. EXTEN cannot change. */ 36 | + ast_channel_exten_set(chan, include_exten); /* So that ${EXTEN} matches the EXTEN in the included context */ 37 | + } 38 | return e; 39 | } 40 | if (q->swo) 41 | diff --git a/main/pbx_include.c b/main/pbx_include.c 42 | index 188bce1ad1..e660dd9ea4 100644 43 | --- a/main/pbx_include.c 44 | +++ b/main/pbx_include.c 45 | @@ -38,6 +38,8 @@ struct ast_include { 46 | const char *name; 47 | /*! Context to include */ 48 | const char *rname; 49 | + /*! Prefix to strip for include traversal */ 50 | + const char *prefix; 51 | /*! Registrar */ 52 | const char *registrar; 53 | /*! If time construct exists */ 54 | @@ -52,6 +54,11 @@ const char *ast_get_include_name(const struct ast_include *inc) 55 | return inc ? inc->name : NULL; 56 | } 57 | 58 | +const char *include_prefix(const struct ast_include *inc) 59 | +{ 60 | + return inc ? inc->prefix : NULL; 61 | +} 62 | + 63 | const char *include_rname(const struct ast_include *inc) 64 | { 65 | return inc ? inc->rname : NULL; 66 | @@ -74,7 +81,7 @@ int include_valid(const struct ast_include *inc) 67 | struct ast_include *include_alloc(const char *value, const char *registrar) 68 | { 69 | struct ast_include *new_include; 70 | - char *c; 71 | + char *c, *prefix = NULL; 72 | int valuebufsz = strlen(value) + 1; 73 | char *p; 74 | 75 | @@ -96,15 +103,32 @@ struct ast_include *include_alloc(const char *value, const char *registrar) 76 | /* Strip off timing info, and process if it is there */ 77 | if ( (c = strchr(p, '|')) || (c = strchr(p, ',')) ) { 78 | *c++ = '\0'; 79 | + if (c) { 80 | + prefix = strchr(c, '|'); 81 | + if (prefix) { 82 | + *prefix++ = '\0'; 83 | + } 84 | + } 85 | new_include->hastime = ast_build_timing(&(new_include->timing), c); 86 | } 87 | new_include->registrar = registrar; 88 | 89 | + if (!ast_strlen_zero(prefix)) { 90 | + ast_debug(2, "include is prefixed: '%s'\n", prefix); 91 | + new_include->prefix = ast_strdup(prefix); 92 | + if (!new_include->prefix) { 93 | + ast_log(LOG_ERROR, "Failed to strdup prefix\n"); 94 | + } 95 | + } 96 | + 97 | return new_include; 98 | } 99 | 100 | void include_free(struct ast_include *inc) 101 | { 102 | + if (inc->prefix) { 103 | + ast_free((char*) inc->prefix); 104 | + } 105 | ast_destroy_timing(&(inc->timing)); 106 | ast_free(inc); 107 | } 108 | diff --git a/main/pbx_private.h b/main/pbx_private.h 109 | index da1060e0e9..cb409d61fe 100644 110 | --- a/main/pbx_private.h 111 | +++ b/main/pbx_private.h 112 | @@ -48,6 +48,7 @@ struct ast_include *include_alloc(const char *value, const char *registrar); 113 | void include_free(struct ast_include *inc); 114 | int include_valid(const struct ast_include *inc); 115 | const char *include_rname(const struct ast_include *inc); 116 | +const char *include_prefix(const struct ast_include *inc); 117 | 118 | /*! pbx_sw.c */ 119 | struct ast_sw; 120 | -------------------------------------------------------------------------------- /patches/pulsar.patch: -------------------------------------------------------------------------------- 1 | --- /var/lib/asterisk/agi-bin/pulsar.agi 2020-11-25 01:47:54.373182874 +0000 2 | +++ /var/lib/asterisk/agi-bin/pulsar.agi 2020-11-25 01:47:54.093186626 +0000 3 | @@ -86,20 +86,20 @@ 4 | $type = $_SERVER["argv"][3]; 5 | if(!type) $type = "panel"; 6 | 7 | -execute_agi("EXEC Playback pulsar/$type/begin"); 8 | +execute_agi("EXEC Playback pulsar/$type/begin,noanswer"); 9 | 10 | $rp = translate($dialcode, $b_side); 11 | for($i = 0; $i < 5; $i ++) { 12 | - execute_agi("EXEC Playback pulsar/$type/start$i"); 13 | - if($rp[$i] > 1) execute_agi("EXEC Playback pulsar/$type/first$i"); 14 | + execute_agi("EXEC Playback pulsar/$type/start$i,noanswer"); 15 | + if($rp[$i] > 1) execute_agi("EXEC Playback pulsar/$type/first$i,noanswer"); 16 | $k = $rp[$i] - 2; 17 | for($j = 0; $j < $k; $j++) { 18 | - execute_agi("EXEC Playback pulsar/$type/middle$i"); 19 | + execute_agi("EXEC Playback pulsar/$type/middle$i,noanswer"); 20 | } 21 | - execute_agi("EXEC Playback pulsar/$type/last$i"); 22 | + execute_agi("EXEC Playback pulsar/$type/last$i,noanswer"); 23 | } 24 | 25 | -execute_agi("EXEC Playback pulsar/$type/end"); 26 | +execute_agi("EXEC Playback pulsar/$type/end,noanswer"); 27 | 28 | fclose($stdin); 29 | fclose($stdout); 30 | -------------------------------------------------------------------------------- /patches/returnif.patch: -------------------------------------------------------------------------------- 1 | --- orig/app_stack.c 2021-09-23 23:45:32.748561851 +0000 2 | +++ app_stack.c 2021-09-23 23:44:00.103518708 +0000 3 | @@ -113,6 +113,36 @@ 4 | any, is saved in the channel variable GOSUB_RETVAL. 5 | 6 | 7 | + ReturnIf 8 | + Gosub 9 | + StackPop 10 | + 11 | + 12 | + 13 | + 14 | + Conditionally return from gosub routine. 15 | + 16 | + 17 | + 18 | + 19 | + 20 | + 21 | + 22 | + 23 | + 24 | + 25 | + 26 | + 27 | + 28 | + If expression is true, jumps to the last label on the stack, 29 | + removing it. The return valueiftrue, if any, is saved in the channel 30 | + variable GOSUB_RETVAL. If expression is 31 | + false, and valueiffalse is specified, jumps to the last 32 | + label on the stack, removing it, and saving valueiffalse 33 | + in the the channel variable GOSUB_RETVAL. 34 | + 35 | + 36 | + Return 37 | Gosub 38 | StackPop 39 | 40 | @@ -232,6 +262,7 @@ 41 | static const char app_gosub[] = "Gosub"; 42 | static const char app_gosubif[] = "GosubIf"; 43 | static const char app_return[] = "Return"; 44 | +static const char app_returnif[] = "ReturnIf"; 45 | static const char app_pop[] = "StackPop"; 46 | 47 | static void gosub_free(void *data); 48 | @@ -428,6 +459,42 @@ 49 | return res; 50 | } 51 | 52 | +static int returnif_exec(struct ast_channel *chan, const char *data) 53 | +{ 54 | + char *args; 55 | + int res=0; 56 | + AST_DECLARE_APP_ARGS(cond, 57 | + AST_APP_ARG(ition); 58 | + AST_APP_ARG(values); 59 | + ); 60 | + AST_DECLARE_APP_ARGS(value, 61 | + AST_APP_ARG(iftrue); 62 | + AST_APP_ARG(iffalse); 63 | + ); 64 | + 65 | + if (ast_strlen_zero(data)) { 66 | + ast_log(LOG_WARNING, "ReturnIf requires an argument: ReturnIf(cond?value1:value2\n"); 67 | + return 0; 68 | + } 69 | + 70 | + args = ast_strdupa(data); 71 | + AST_NONSTANDARD_RAW_ARGS(cond, args, '?'); 72 | + if (cond.argc != 2) { 73 | + ast_log(LOG_WARNING, "ReturnIf requires an argument: ReturnIf(cond?value1:value2\n"); 74 | + return 0; 75 | + } 76 | + 77 | + AST_NONSTANDARD_RAW_ARGS(value, cond.values, ':'); 78 | + 79 | + if (pbx_checkcondition(cond.ition)) { 80 | + res = return_exec(chan, value.iftrue); 81 | + } else if (!ast_strlen_zero(value.iffalse)) { 82 | + res = return_exec(chan, value.iffalse); 83 | + } 84 | + 85 | + return res; 86 | +} 87 | + 88 | /*! 89 | * \internal 90 | * \brief Add missing context and/or exten to Gosub application argument string. 91 | @@ -1282,6 +1349,7 @@ 92 | ast_agi_unregister(&gosub_agi_command); 93 | 94 | ast_unregister_application(app_return); 95 | + ast_unregister_application(app_returnif); 96 | ast_unregister_application(app_pop); 97 | ast_unregister_application(app_gosubif); 98 | ast_unregister_application(app_gosub); 99 | @@ -1304,6 +1372,7 @@ 100 | 101 | ast_register_application_xml(app_pop, pop_exec); 102 | ast_register_application_xml(app_return, return_exec); 103 | + ast_register_application_xml(app_returnif, returnif_exec); 104 | ast_register_application_xml(app_gosubif, gosubif_exec); 105 | ast_register_application_xml(app_gosub, gosub_exec); 106 | ast_custom_function_register(&local_function); 107 | -------------------------------------------------------------------------------- /patches/sipfaxcontrol.diff: -------------------------------------------------------------------------------- 1 | --- channels/chan_sip.c.orig 2021-11-14 13:06:50.883178874 +0000 2 | +++ channels/chan_sip.c 2021-11-17 18:39:54.766985851 +0000 3 | @@ -8721,6 +8721,46 @@ 4 | } 5 | 6 | if (f && p->dsp) { 7 | + int features; 8 | + /* the channel is already locked here */ 9 | + features = ast_dsp_get_features(p->dsp); 10 | + if (features & DSP_FEATURE_FAX_DETECT) { /* if no fax detect, then skip all this */ 11 | + /* the variables are set in the dialplan after the channel is created, 12 | + so we're going to need to continually check these things or we 13 | + might miss something. */ 14 | + const char *sseconds, *senabled; 15 | + int disablefax = 0; 16 | + sseconds = pbx_builtin_getvar_helper(ast, "FAX_DETECT_SECONDS"); 17 | + senabled = pbx_builtin_getvar_helper(ast, "FAX_DETECT_OFF"); 18 | + if (senabled) { 19 | + disablefax = atoi(senabled); 20 | + } 21 | + if (disablefax) { /* per-call disable fax detection. So bail out now! */ 22 | + ast_debug(1, "Disabled fax detection for this call\n"); 23 | + /* If we only needed this DSP for fax detection purposes we can just drop it now */ 24 | + if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) { 25 | + ast_dsp_set_features(p->dsp, DSP_FEATURE_DIGIT_DETECT); 26 | + } else { 27 | + disable_dsp_detect(p); 28 | + } 29 | + return f; 30 | + } else if (sseconds) { /* see if time allotted for fax detect has been exceeded */ 31 | + int age, maxseconds; 32 | + age = ast_channel_get_duration(ast); 33 | + maxseconds = atoi(sseconds); 34 | + if (age > maxseconds) { 35 | + ast_debug(1, "Disabled fax detection for remainder of this call\n"); 36 | + /* If we only needed this DSP for fax detection purposes we can just drop it now */ 37 | + if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) { 38 | + ast_dsp_set_features(p->dsp, DSP_FEATURE_DIGIT_DETECT); 39 | + } else { 40 | + disable_dsp_detect(p); 41 | + } 42 | + return f; 43 | + } 44 | + } 45 | + } 46 | + 47 | f = ast_dsp_process(p->owner, p->dsp, f); 48 | if (f && f->frametype == AST_FRAME_DTMF) { 49 | if (f->subclass.integer == 'f') { 50 | @@ -11174,22 +11214,41 @@ 51 | change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */ 52 | /* If fax detection is enabled then send us off to the fax extension */ 53 | if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_T38)) { 54 | - ast_channel_lock(p->owner); 55 | - if (strcmp(ast_channel_exten(p->owner), "fax")) { 56 | - const char *target_context = S_OR(ast_channel_macrocontext(p->owner), ast_channel_context(p->owner)); 57 | - ast_channel_unlock(p->owner); 58 | - if (ast_exists_extension(p->owner, target_context, "fax", 1, 59 | - S_COR(ast_channel_caller(p->owner)->id.number.valid, ast_channel_caller(p->owner)->id.number.str, NULL))) { 60 | - ast_verb(2, "Redirecting '%s' to fax extension due to peer T.38 re-INVITE\n", ast_channel_name(p->owner)); 61 | - pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", ast_channel_exten(p->owner)); 62 | - if (ast_async_goto(p->owner, target_context, "fax", 1)) { 63 | - ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", ast_channel_name(p->owner), target_context); 64 | + const char *sseconds, *senabled; 65 | + int disablefax = 0; 66 | + sseconds = pbx_builtin_getvar_helper(p->owner, "FAX_DETECT_SECONDS"); 67 | + senabled = pbx_builtin_getvar_helper(p->owner, "FAX_DETECT_OFF"); 68 | + if (senabled) { 69 | + disablefax = atoi(senabled); 70 | + } 71 | + if (!disablefax && sseconds) { /* see if time allotted for fax detect has been exceeded */ 72 | + int age, maxseconds; 73 | + age = ast_channel_get_duration(p->owner); 74 | + maxseconds = atoi(sseconds); 75 | + if (age > maxseconds) { 76 | + disablefax = 1; 77 | + } 78 | + } 79 | + if (disablefax) { 80 | + ast_debug(1, "Detected T.38 re-invite, but ignoring\n"); 81 | + } else { 82 | + ast_channel_lock(p->owner); 83 | + if (strcmp(ast_channel_exten(p->owner), "fax")) { 84 | + const char *target_context = S_OR(ast_channel_macrocontext(p->owner), ast_channel_context(p->owner)); 85 | + ast_channel_unlock(p->owner); 86 | + if (ast_exists_extension(p->owner, target_context, "fax", 1, 87 | + S_COR(ast_channel_caller(p->owner)->id.number.valid, ast_channel_caller(p->owner)->id.number.str, NULL))) { 88 | + ast_verb(2, "Redirecting '%s' to fax extension due to peer T.38 re-INVITE\n", ast_channel_name(p->owner)); 89 | + pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", ast_channel_exten(p->owner)); 90 | + if (ast_async_goto(p->owner, target_context, "fax", 1)) { 91 | + ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", ast_channel_name(p->owner), target_context); 92 | + } 93 | + } else { 94 | + ast_log(LOG_NOTICE, "T.38 re-INVITE detected but no fax extension\n"); 95 | } 96 | } else { 97 | - ast_log(LOG_NOTICE, "T.38 re-INVITE detected but no fax extension\n"); 98 | + ast_channel_unlock(p->owner); 99 | } 100 | - } else { 101 | - ast_channel_unlock(p->owner); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /patches/srtp.diff: -------------------------------------------------------------------------------- 1 | --- res/res_srtp.c.orig 2022-01-18 20:23:52.937607322 +0000 2 | +++ res/res_srtp.c 2022-01-18 21:09:06.480830488 +0000 3 | @@ -467,8 +467,18 @@ 4 | * whether they use a custom library or an outdated version of libSRTP. 5 | */ 6 | if (rtcp) { 7 | - ast_verb(2, "SRTCP unprotect failed on SSRC %u because of %s\n", 8 | - ast_rtp_instance_get_ssrc(srtp->rtp), srtp_errstr(res)); 9 | + /* This patch makes it slightly less annoying when SRTP is used 10 | + with ATAs like the Grandstream HT 7xx. Otherwise, we 11 | + get a message in the CLI every second. */ 12 | + if (srtp->warned % 30) { 13 | + srtp->warned++; 14 | + } else if (srtp->warned >= 30) { 15 | + srtp->warned = 6; 16 | + } 17 | + if (srtp->warned < 5 || !(srtp->warned % 30)) { 18 | + ast_verb(2, "SRTCP unprotect failed on SSRC %u because of %s\n", 19 | + ast_rtp_instance_get_ssrc(srtp->rtp), srtp_errstr(res)); 20 | + } 21 | } else { 22 | if ((srtp->warned >= 10) && !((srtp->warned - 10) % 150)) { 23 | ast_verb(2, "SRTP unprotect failed on SSRC %u because of %s %d\n", 24 | -------------------------------------------------------------------------------- /patches/tor2_Kbuild.diff: -------------------------------------------------------------------------------- 1 | --- drivers/dahdi/Kbuild.bak 2023-09-27 13:45:46.096351443 +0000 2 | +++ drivers/dahdi/Kbuild 2023-09-27 13:47:29.817323574 +0000 3 | @@ -158,9 +158,11 @@ 4 | obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_HPEC) += dahdi_echocan_hpec.o 5 | endif 6 | 7 | +$(obj)/tor2.o: $(obj)/tor2fw.h 8 | 9 | hostprogs := makefw 10 | 11 | - 12 | +$(obj)/tor2fw.h: $(src)/tormenta2.rbt $(obj)/makefw 13 | + $(obj)/makefw $< tor2fw > $@ 14 | 15 | clean-files := radfw.h 16 | -------------------------------------------------------------------------------- /phreakscript_autocomplete.sh: -------------------------------------------------------------------------------- 1 | _script() 2 | { 3 | _script_commands=$(/usr/local/sbin/phreaknet commandlist) 4 | 5 | local cur 6 | COMPREPLY=() 7 | cur="${COMP_WORDS[COMP_CWORD]}" 8 | COMPREPLY=( $(compgen -W "${_script_commands}" -- ${cur}) ) 9 | 10 | return 0 11 | } 12 | complete -F _script phreaknet 13 | -------------------------------------------------------------------------------- /phreakscript_update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | g() { printf "\e[32;1m%s\e[0m\n" "$*" >&2; } 3 | err() { printf "\e[31;1m%s\e[0m\n" "$*" >&2; } # https://stackoverflow.com/questions/2990414/echo-that-outputs-to-stderr 4 | v1=`grep "# v" $2 | head -1 | cut -d'v' -f2` 5 | tmp=/tmp/phreaknet.sh 6 | src=$1 7 | if [ "$src" = "https://docs.phreaknet.org/script" ]; then 8 | src="https://docs.phreaknet.org/script/phreaknet.sh" # needed for compatability with < 0.0.38, can be removed eventually 9 | fi 10 | printf "Upstream: %s\n" "$src" 11 | wget -q $src -O $tmp --no-cache # always replace 12 | chmod +x $tmp 13 | test=`$tmp help 2>&- | grep "PhreakScript" | grep "(" | wc -l` 14 | if [ "$test" = "1" ]; then 15 | mv $tmp $2 16 | v2=`grep "# v" $2 | head -1 | cut -d'v' -f2` 17 | g "Successfully updated PhreakScript from $v1 to $v2" 18 | exec rm -f "$3" 19 | else 20 | err "PhreakScript failed to update - upstream contains errors ($test)." 21 | $tmp -o # run the flag test, if the script is invalid, it should dump the error 22 | rm "$tmp" 23 | exit 1 24 | fi 25 | -------------------------------------------------------------------------------- /res/res_deadlock.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2022, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! 20 | * \file 21 | * \author Naveen Albert 22 | * 23 | * \brief Simulated Deadlock Testing from the CLI 24 | * 25 | */ 26 | 27 | /*** MODULEINFO 28 | no 29 | extended 30 | ***/ 31 | 32 | #include "asterisk.h" 33 | 34 | #include 35 | 36 | #include "asterisk/module.h" 37 | #include "asterisk/lock.h" 38 | #include "asterisk/cli.h" 39 | #include "asterisk/utils.h" 40 | 41 | static ast_mutex_t test_lock; 42 | static pthread_t thread_id = AST_PTHREADT_NULL, thread_id2 = AST_PTHREADT_NULL; 43 | static int flag = 0; 44 | 45 | static void *lock_thread(void *arg) 46 | { 47 | ast_mutex_lock(&test_lock); 48 | flag = 1; 49 | while (flag) { 50 | usleep(100000); /* Sleep for 100 ms */ 51 | } 52 | ast_mutex_unlock(&test_lock); 53 | thread_id = AST_PTHREADT_NULL; 54 | return NULL; 55 | } 56 | 57 | static void *lockwait_thread(void *arg) 58 | { 59 | while (!flag) { 60 | usleep(1000); /* Wait for other thread to grab lock first. */ 61 | } 62 | ast_mutex_lock(&test_lock); 63 | ast_mutex_unlock(&test_lock); 64 | thread_id2 = AST_PTHREADT_NULL; 65 | return NULL; 66 | } 67 | 68 | static char *handle_start(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 69 | { 70 | switch (cmd) { 71 | case CLI_INIT: 72 | e->command = "simulation start deadlock"; 73 | e->usage = 74 | "Usage: simulation start deadlock\n" 75 | " Start a simulated deadlock.\n"; 76 | return NULL; 77 | case CLI_GENERATE: 78 | return NULL; 79 | } 80 | 81 | if (a->argc != e->args) { 82 | return CLI_SHOWUSAGE; 83 | } 84 | 85 | if (thread_id != AST_PTHREADT_NULL || thread_id2 != AST_PTHREADT_NULL) { 86 | ast_cli(a->fd, "Deadlock test already in progress\n"); 87 | return CLI_FAILURE; 88 | } else if (ast_pthread_create(&thread_id, NULL, lock_thread, NULL)) { 89 | return CLI_FAILURE; 90 | } else if (ast_pthread_create(&thread_id2, NULL, lockwait_thread, NULL)) { 91 | return CLI_FAILURE; 92 | } 93 | 94 | ast_cli(a->fd, "Simulated deadlock started\n"); 95 | return CLI_SUCCESS; 96 | } 97 | 98 | static char *handle_stop(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 99 | { 100 | switch (cmd) { 101 | case CLI_INIT: 102 | e->command = "simulation stop deadlock"; 103 | e->usage = 104 | "Usage: simulation stop deadlock\n" 105 | " Stop a simulated deadlock.\n"; 106 | return NULL; 107 | case CLI_GENERATE: 108 | return NULL; 109 | } 110 | 111 | if (a->argc != e->args) { 112 | return CLI_SHOWUSAGE; 113 | } 114 | 115 | if (!flag) { 116 | ast_cli(a->fd, "No deadlock test currently in progress\n"); 117 | return CLI_FAILURE; 118 | } 119 | 120 | flag = 0; 121 | ast_cli(a->fd, "Simulated deadlock stopped\n"); 122 | return CLI_SUCCESS; 123 | } 124 | 125 | static struct ast_cli_entry cli_testdeadlock[] = { 126 | AST_CLI_DEFINE(handle_start, "Start a simulated deadlock"), 127 | AST_CLI_DEFINE(handle_stop, "Stop a simulated deadlock"), 128 | }; 129 | 130 | static int unload_module(void) 131 | { 132 | int res = ast_cli_unregister_multiple(cli_testdeadlock, ARRAY_LEN(cli_testdeadlock)); 133 | flag = 0; /* If lock thread still running, signal it to end now. */ 134 | return res; 135 | } 136 | 137 | static int load_module(void) 138 | { 139 | int res; 140 | ast_mutex_init(&test_lock); 141 | res = ast_cli_register_multiple(cli_testdeadlock, ARRAY_LEN(cli_testdeadlock)); 142 | return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS; 143 | } 144 | 145 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Simulated Deadlock Testing from the CLI"); 146 | -------------------------------------------------------------------------------- /res/res_msp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2023, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*! \file 20 | * 21 | * \brief Send a message via the RFC1312 Message Send Protocol (version 2) 22 | * 23 | * \author Naveen Albert 24 | * 25 | * \ingroup applications 26 | */ 27 | 28 | /*** MODULEINFO 29 | extended 30 | ***/ 31 | 32 | #include "asterisk.h" 33 | 34 | #include "asterisk/logger.h" 35 | #include "asterisk/module.h" 36 | #include "asterisk/message.h" 37 | #include "asterisk/netsock2.h" 38 | #include "asterisk/config.h" 39 | #include "asterisk/utils.h" 40 | 41 | /*** DOCUMENTATION 42 | 43 | Specifying a prefix of msp: will send the 44 | message to the specified hostname using the Message Send Protocol version 2. 45 | For example: 46 | same => n,Set(MESSAGE(body)=This is my message) will set the message 47 | and same => n,MessageSend(10.1.2.3,Asterisk,Alice) will 48 | send a message to host 10.1.2.3, with a sender of Asterisk 49 | and a recipient of Alice.. 50 | Other Message Send Protocol parameters are not currently supported. 51 | 52 | 53 | Specifying a prefix of msp: will specify the 54 | name of the sender of the message, as defined by RFC 1312. 55 | 56 | 57 | The name of the recipient, as defined in the Message Send Protocol. 58 | Specifying a prefix of msp: will specify the 59 | name of the recipient, as defined by RFC 1312. 60 | This parameter is optional, but may be required by the MSP server. 61 | 62 | ***/ 63 | 64 | static int msp_send(const struct ast_msg *msg, const char *destination, const char *from) 65 | { 66 | ssize_t res; 67 | int sfd; 68 | struct ast_sockaddr addr; 69 | char destbuf[128]; 70 | char *tmp; 71 | const char *hostname, *recip; 72 | char buf[512]; /* Max size of a MSP message */ 73 | int len; 74 | const char *body; 75 | 76 | if (ast_strlen_zero(destination)) { 77 | ast_log(LOG_ERROR, "Missing destination for MSP message\n"); 78 | return -1; 79 | } else if (ast_strlen_zero(from)) { 80 | ast_log(LOG_ERROR, "Missing sender for MSP message\n"); 81 | } 82 | 83 | ast_copy_string(destbuf, destination, sizeof(destbuf)); 84 | tmp = destbuf; 85 | strsep(&tmp, ":"); /* Skip msp: */ 86 | hostname = tmp; 87 | recip = ast_msg_get_to(msg); 88 | 89 | if (ast_strlen_zero(hostname)) { 90 | ast_log(LOG_ERROR, "Missing hostname\n"); 91 | return -1; 92 | } 93 | 94 | body = ast_msg_get_body(msg); 95 | if (ast_strlen_zero(body)) { 96 | ast_log(LOG_ERROR, "No message body to send\n"); 97 | return -1; 98 | } 99 | 100 | /* Construct MSP message */ 101 | len = snprintf(buf, sizeof(buf), "%c%s%c%c%s%c%s%c%c%c%c", /* To satisfy -Werror=format-contains-nul */ 102 | 'B', /* Version 2 of the MSP protocol */ 103 | S_OR(recip, ""), 104 | '\0', 105 | '\0', 106 | body, 107 | '\0', 108 | from, 109 | '\0', 110 | '\0', '\0', '\0'); 111 | 112 | ast_debug(3, "Sending %d-byte MSP message from %s -> %s@%s: %s\n", len, S_OR(from, ""), S_OR(recip, ""), hostname, body); 113 | ast_debug(3, "TO: %s\n", ast_msg_get_to(msg)); 114 | 115 | memset(&addr, 0, sizeof(addr)); 116 | ast_parse_arg(hostname, PARSE_ADDR, &addr); 117 | ast_sockaddr_set_port(&addr, 18); 118 | 119 | /* Message Send Protocol supports delivery via both TCP and UDP. 120 | * Send via UDP, since there's less overhead, and we don't care about the acknowledgment */ 121 | sfd = socket(AF_INET, SOCK_DGRAM, 0); 122 | if (sfd < 0) { 123 | ast_log(LOG_ERROR, "ast_socket failed: %s\n", strerror(errno)); 124 | return -1; 125 | } 126 | 127 | res = ast_sendto(sfd, buf, len, 0, &addr); 128 | if (res <= 0) { 129 | ast_log(LOG_ERROR, "ast_sendto(%s) failed: %s\n", hostname, strerror(errno)); 130 | } 131 | 132 | close(sfd); 133 | return res <= 0 ? -1 : 0; 134 | } 135 | 136 | static const struct ast_msg_tech msg_tech = { 137 | .name = "msp", 138 | .msg_send = msp_send, 139 | }; 140 | 141 | static int unload_module(void) 142 | { 143 | return ast_msg_tech_unregister(&msg_tech); 144 | } 145 | 146 | static int load_module(void) 147 | { 148 | return ast_msg_tech_register(&msg_tech); 149 | } 150 | 151 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Message Send Protocol"); 152 | -------------------------------------------------------------------------------- /res/res_pjsip_presence.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * Copyright (C) 2022, Naveen Albert 5 | * 6 | * Naveen Albert 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 | 19 | /*** MODULEINFO 20 | pjproject 21 | res_pjsip 22 | res_pjsip_pubsub 23 | extended 24 | ***/ 25 | 26 | #include "asterisk.h" 27 | 28 | #include 29 | #include 30 | 31 | #include "asterisk/res_pjsip.h" 32 | #include "asterisk/res_pjsip_pubsub.h" 33 | #include "asterisk/module.h" 34 | #include "asterisk/logger.h" 35 | #include "asterisk/presencestate.h" 36 | #include "asterisk/xml.h" 37 | #include "asterisk/astdb.h" 38 | 39 | static const char astdb_family[] = "CustomPresence"; 40 | 41 | static int parse_incoming_xml(char *xmlbody, int xmllen, const char *endpoint) 42 | { 43 | int res = -1; 44 | const char *nodename; 45 | const char *tmpstr; 46 | struct ast_xml_node *root, *tuple = NULL, *tmpnode = NULL; 47 | struct ast_xml_doc *xmldoc = ast_xml_read_memory(xmlbody, xmllen); 48 | enum ast_presence_state state = AST_PRESENCE_NOT_SET; 49 | int open; 50 | char note[32] = ""; 51 | char full_dev[256]; 52 | char *dev = full_dev; 53 | char *subtype = ""; 54 | 55 | if (!xmldoc) { 56 | ast_log(LOG_WARNING, "Failed to parse as XML: %.*s\n", xmllen, xmlbody); 57 | return -1; 58 | } 59 | 60 | /* 61 | * RFC 3863 62 | * 63 | * 64 | * 66 | * 67 | * 68 | * open 69 | * 70 | * 2022-10-23T11:48:04.538Z 71 | * Idle 72 | * 73 | * 74 | * 75 | * 76 | * 77 | * Idle 78 | * 79 | * 80 | */ 81 | 82 | if (DEBUG_ATLEAST(1)) { 83 | char *doc_str = NULL; 84 | int doc_len = 0; 85 | ast_xml_doc_dump_memory(xmldoc, &doc_str, &doc_len); 86 | ast_debug(4, "Incoming doc len: %d\n%s\n", doc_len, doc_len ? doc_str : ""); 87 | ast_xml_free_text(doc_str); 88 | doc_str = NULL; 89 | doc_len = 0; 90 | } 91 | 92 | root = ast_xml_get_root(xmldoc); 93 | if (!root) { 94 | ast_log(LOG_WARNING, "No root?\n"); 95 | goto cleanup; 96 | } 97 | nodename = ast_xml_node_get_name(root); 98 | 99 | if (strcmp(nodename, "presence")) { 100 | ast_log(LOG_WARNING, "Unexpected root node name: %s\n", nodename); 101 | goto cleanup; 102 | } 103 | 104 | /* The device is not used by Broadworks, and the phone can set this to any value. See 11-BD5196-00, 3.1.2 */ 105 | tuple = ast_xml_find_child_element(root, "tuple", NULL, NULL); 106 | if (!tuple) { 107 | ast_log(LOG_WARNING, "Presence update contains no tuple?\n"); 108 | goto cleanup; 109 | } 110 | 111 | /* Note is optional, but note it down (no pun intended) if we get one. */ 112 | tmpnode = ast_xml_find_child_element(tuple, "note", NULL, NULL); 113 | if (tmpnode) { 114 | tmpstr = ast_xml_get_text(tmpnode); 115 | ast_copy_string(note, tmpstr, sizeof(note)); 116 | ast_xml_free_text(tmpstr); 117 | } 118 | 119 | tmpnode = ast_xml_find_child_element(tuple, "status", NULL, NULL); 120 | if (!tmpnode) { 121 | ast_log(LOG_WARNING, "Tuple missing status node?\n"); 122 | goto cleanup; 123 | } 124 | 125 | tmpnode = ast_xml_find_child_element(tmpnode, "basic", NULL, NULL); 126 | if (!tmpnode) { 127 | ast_log(LOG_WARNING, "status missing basic node?\n"); 128 | goto cleanup; 129 | } 130 | 131 | tmpstr = ast_xml_get_text(tmpnode); 132 | 133 | /* must contain either open or closed. */ 134 | /*! \todo We should support more than just available/unavailable, we can parse using ast_presence_state_val */ 135 | if (!strcmp(tmpstr, "open")) { 136 | open = 1; 137 | } else if (!strcmp(tmpstr, "closed")) { 138 | open = 0; 139 | } else { 140 | ast_xml_free_text(tmpstr); 141 | ast_log(LOG_WARNING, "Unexpected status: %s\n", tmpstr); 142 | goto cleanup; 143 | } 144 | 145 | ast_xml_free_text(tmpstr); 146 | res = 0; 147 | 148 | state = open ? AST_PRESENCE_AVAILABLE : AST_PRESENCE_UNAVAILABLE; 149 | 150 | ast_verb(4, "Presence for %s changed to %s%s%s%s\n", endpoint, ast_presence_state2str(state), 151 | !ast_strlen_zero(note) ? " (" : "", S_OR(note, ""), !ast_strlen_zero(note) ? ")" : ""); 152 | 153 | /* Publish a presence update, same as func_presencestate. */ 154 | snprintf(full_dev, sizeof(full_dev), "CustomPresence:PJSIP/%s", endpoint); 155 | dev += strlen("CustomPresence:"); 156 | 157 | ast_db_put(astdb_family, dev, ast_presence_state2str(state)); 158 | ast_presence_state_changed_literal(state, subtype, S_OR(note, ""), full_dev); 159 | 160 | cleanup: 161 | ast_xml_close(xmldoc); 162 | return res; 163 | } 164 | 165 | static int presence_publication_new(struct ast_sip_endpoint *endpoint, const char *resource, const char *event_configuration) 166 | { 167 | return 200; /* Accept anything as long as the inbound publication is accordingly configured to allow it. */ 168 | } 169 | 170 | static int presence_publication_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body, enum ast_sip_publish_state state) 171 | { 172 | struct ast_sip_endpoint *endpoint; 173 | 174 | /* If no body exists this is a refresh and can be ignored */ 175 | if (!body) { 176 | return 0; 177 | } 178 | 179 | if (!ast_sip_is_content_type(&body->content_type, "application", "pidf+xml")) { 180 | ast_debug(2, "Received unsupported content type for presence event\n"); 181 | return -1; 182 | } 183 | 184 | endpoint = ast_sip_publication_get_endpoint(pub); 185 | if (!endpoint) { 186 | return -1; 187 | } 188 | 189 | parse_incoming_xml(body->data, body->len, ast_sorcery_object_get_id(endpoint)); 190 | /* No need to cleanup the endpoint. ast_sip_publication_get_endpoint doesn't bump the ref count. */ 191 | return 0; 192 | } 193 | 194 | struct ast_sip_publish_handler presence_publication_handler = { 195 | .event_name = "presence", 196 | .new_publication = presence_publication_new, 197 | .publication_state_change = presence_publication_state_change, 198 | }; 199 | 200 | static int load_module(void) 201 | { 202 | if (ast_sip_register_publish_handler(&presence_publication_handler)) { 203 | ast_log(LOG_WARNING, "Unable to register event publication handler %s\n", presence_publication_handler.event_name); 204 | return AST_MODULE_LOAD_DECLINE; 205 | } 206 | 207 | return AST_MODULE_LOAD_SUCCESS; 208 | } 209 | 210 | static int unload_module(void) 211 | { 212 | ast_sip_unregister_publish_handler(&presence_publication_handler); 213 | return 0; 214 | } 215 | 216 | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Presence PUBLISH Support", 217 | .support_level = AST_MODULE_SUPPORT_EXTENDED, 218 | .load = load_module, 219 | .unload = unload_module, 220 | .load_pri = AST_MODPRI_CHANNEL_DEPEND + 5, 221 | .requires = "res_pjsip,res_pjsip_pubsub", 222 | ); 223 | -------------------------------------------------------------------------------- /testsuite/tests/apps/assert/configs/ast1/extensions.conf: -------------------------------------------------------------------------------- 1 | 2 | [default] 3 | exten => s,1,Answer() 4 | same => n,Assert($[${EPOCH}!=$[${EPOCH}-2]]) 5 | same => n,UserEvent(AssertSuccess,Result: Pass) 6 | same => n,Assert($[${EPOCH}=$[${EPOCH}-2]],d) 7 | same => n,UserEvent(AssertSuccess,Result: Pass) ; assert fails, but doesn't crash the call 8 | same => n,Assert($[${EPOCH}=$[${EPOCH}-2]]) ; goodbye 9 | same => n,UserEvent(AssertFail,Result: Fail) 10 | same => n,Hangup() 11 | 12 | [nothing] 13 | exten => 0,1,Answer() 14 | same => n,Wait(10) 15 | same => n,Hangup() 16 | -------------------------------------------------------------------------------- /testsuite/tests/apps/assert/test-config.yaml: -------------------------------------------------------------------------------- 1 | testinfo: 2 | summary: 'Ensure that app_assert functions correctly.' 3 | description: | 4 | 'This tests the Assert application to make sure 5 | that it functions correctly.' 6 | 7 | test-modules: 8 | test-object: 9 | config-section: test-object-config 10 | typename: 'test_case.TestCaseModule' 11 | modules: 12 | - 13 | config-section: caller-originator 14 | typename: 'pluggable_modules.Originator' 15 | - 16 | config-section: hangup-monitor 17 | typename: 'pluggable_modules.HangupMonitor' 18 | - 19 | config-section: ami-config 20 | typename: 'pluggable_modules.EventActionModule' 21 | 22 | test-object-config: 23 | connect-ami: True 24 | 25 | caller-originator: 26 | channel: 'Local/s@default' 27 | context: 'nothing' 28 | exten: '0' 29 | priority: '1' 30 | trigger: 'ami_connect' 31 | 32 | hangup-monitor: 33 | ids: '0' 34 | 35 | ami-config: 36 | - 37 | ami-events: 38 | conditions: 39 | match: 40 | Event: 'UserEvent' 41 | UserEvent: 'AssertSuccess' 42 | requirements: 43 | match: 44 | Result: 'Pass' 45 | count: 2 46 | stop_test: 47 | 48 | properties: 49 | tags: 50 | - dial 51 | - funcs 52 | dependencies: 53 | - python: 'twisted' 54 | - python: 'starpy' 55 | - asterisk: 'app_dial' 56 | - asterisk: 'app_userevent' 57 | - asterisk: 'app_originate' 58 | - asterisk: 'app_assert' 59 | - asterisk: 'pbx_config' 60 | -------------------------------------------------------------------------------- /testsuite/tests/apps/coin/configs/ast1/10c.ulaw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InterLinked1/phreakscript/bb6322cdec31188003bdfef80ccafbd4b54d84f8/testsuite/tests/apps/coin/configs/ast1/10c.ulaw -------------------------------------------------------------------------------- /testsuite/tests/apps/coin/configs/ast1/25c.ulaw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InterLinked1/phreakscript/bb6322cdec31188003bdfef80ccafbd4b54d84f8/testsuite/tests/apps/coin/configs/ast1/25c.ulaw -------------------------------------------------------------------------------- /testsuite/tests/apps/coin/configs/ast1/5c.ulaw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InterLinked1/phreakscript/bb6322cdec31188003bdfef80ccafbd4b54d84f8/testsuite/tests/apps/coin/configs/ast1/5c.ulaw -------------------------------------------------------------------------------- /testsuite/tests/apps/coin/configs/ast1/extensions.conf: -------------------------------------------------------------------------------- 1 | 2 | [default] 3 | exten => s,1,Answer() 4 | same => n,Set(i=0) 5 | same => n,While($[${INC(i)}<=5]) 6 | same => n,Originate(Local/${i}@red-boxer,exten,red-boxed,${i},1,,a) 7 | same => n,EndWhile() 8 | same => n,Hangup() 9 | 10 | [nothing] 11 | exten => 0,1,Answer() 12 | same => n,Wait(8) 13 | same => n,Hangup() 14 | 15 | [red-boxer] 16 | exten => 1,1,Answer(0.5) 17 | same => n,Playback(silence/1) ; Wait doesn't work (makes sense, no audio frames...) but playing silence makes the detection perfect in this test. 18 | same => n,Playback(<>/5c) 19 | same => n,Playback(silence/2) ; we need audio flowing for detection to finish 20 | same => n,Wait(99) 21 | same => n,Hangup() 22 | exten => _[245],1,Answer(0.5) 23 | same => n,Playback(silence/1) 24 | same => n,Playback(<>/10c) 25 | same => n,Playback(silence/2) 26 | same => n,Wait(99) 27 | same => n,Hangup() 28 | exten => 3,1,Answer(0.5) 29 | same => n,Playback(silence/2) 30 | same => n,Playback(<>/25c) 31 | same => n,Playback(silence/2) 32 | same => n,Wait(99) 33 | same => n,Hangup() 34 | 35 | [red-boxed] 36 | exten => 1,1,Answer() 37 | same => n,WaitForDeposit(5,4,1) 38 | same => n,GotoIf($[$["${WAITFORDEPOSITSTATUS}"="SUCCESS"]&$["${WAITFORDEPOSITAMOUNT}"="5"]]?success,1:fail,1) 39 | exten => 2,1,Answer() 40 | same => n,WaitForDeposit(10,4,1) 41 | same => n,GotoIf($[$["${WAITFORDEPOSITSTATUS}"="SUCCESS"]&$["${WAITFORDEPOSITAMOUNT}"="10"]]?success,1:fail,1) 42 | exten => 3,1,Answer() 43 | same => n,WaitForDeposit(25,4,1) ; yes, believe it or not, in perfect DSP conditions, we can accurately detect all 25 cents of a quarter! 44 | same => n,GotoIf($[$["${WAITFORDEPOSITSTATUS}"="SUCCESS"]&$["${WAITFORDEPOSITAMOUNT}"="25"]]?success,1:fail,1) 45 | exten => 4,1,Answer() 46 | same => n,Set(COIN_DETECT(a(10)d(1)rg(successrx,1))=) 47 | same => n,Wait(6) ; no audio is sent in the TX direction. Same characteristic as the first 3 tests. 48 | same => n,UserEvent(CoinSuccess,Result: Fail ${COIN_DETECT(rx)},Reason: ${COIN_DETECT(rx)}) 49 | same => n,Hangup() 50 | exten => 5,1,Answer() 51 | same => n,Set(COIN_DETECT(a(10)d(1)lrg(successrx,1))=) ; relax detection. Needed for correct operation with audio TX on the channel during detection. To ensure we don't overdetect, we delay completion by 1s. 52 | same => n,Playback(silence/6) ; send audio in the TX direction. This taxes the DSP a LOT more! It won't behave the same as with no audio. 53 | same => n,UserEvent(CoinSuccess,Result: Fail ${COIN_DETECT(rx)},Reason: ${COIN_DETECT(rx)}) 54 | same => n,Hangup() 55 | exten => successrx,1,GotoIf($["${COIN_DETECT(rx)}"="10"]?success,1) 56 | same => n,UserEvent(CoinSuccess,Result: Fail ${COIN_DETECT(rx)},Reason: ${COIN_DETECT(rx)}) 57 | same => n,Hangup() 58 | exten => success,1,Answer(1) 59 | same => n,UserEvent(CoinSuccess,Result: Pass) 60 | same => n,Hangup() 61 | exten => fail,1,Answer(1) 62 | same => n,UserEvent(CoinSuccess,Result: Fail ${WAITFORDEPOSITSTATUS} ${WAITFORDEPOSITAMOUNT},Reason: ${WAITFORDEPOSITAMOUNT}) 63 | same => n,Hangup() 64 | -------------------------------------------------------------------------------- /testsuite/tests/apps/coin/test-config.yaml: -------------------------------------------------------------------------------- 1 | testinfo: 2 | summary: 'Ensure that app_coindetect module functions correctly.' 3 | description: | 4 | 'This tests the app_coindetect module for proper 5 | functioning.' 6 | 7 | test-modules: 8 | test-object: 9 | config-section: test-object-config 10 | typename: 'test_case.TestCaseModule' 11 | modules: 12 | - 13 | config-section: caller-originator 14 | typename: 'pluggable_modules.Originator' 15 | - 16 | config-section: hangup-monitor 17 | typename: 'pluggable_modules.HangupMonitor' 18 | - 19 | config-section: ami-config 20 | typename: 'pluggable_modules.EventActionModule' 21 | 22 | test-object-config: 23 | connect-ami: True 24 | 25 | caller-originator: 26 | channel: 'Local/s@default' 27 | context: 'nothing' 28 | exten: '0' 29 | priority: '1' 30 | trigger: 'ami_connect' 31 | 32 | hangup-monitor: 33 | ids: '0' 34 | 35 | ami-config: 36 | - 37 | ami-events: 38 | conditions: 39 | match: 40 | Event: 'UserEvent' 41 | UserEvent: 'CoinSuccess' 42 | requirements: 43 | match: 44 | Result: 'Pass' 45 | count: 5 46 | stop_test: 47 | 48 | properties: 49 | tags: 50 | - dial 51 | - apps 52 | dependencies: 53 | - python: 'twisted' 54 | - python: 'starpy' 55 | - asterisk: 'app_dial' 56 | - asterisk: 'app_userevent' 57 | - asterisk: 'app_originate' 58 | - asterisk: 'res_coindetect' 59 | - asterisk: 'pbx_config' 60 | -------------------------------------------------------------------------------- /testsuite/tests/apps/dialtone/configs/ast1/extensions.conf: -------------------------------------------------------------------------------- 1 | 2 | [default] 3 | exten => s,1,Answer() 4 | same => n,Set(GLOBAL(senddtmf1)=*123#DB) 5 | same => n,Set(GLOBAL(senddtmf2)=*123A#2#DB54545) 6 | same => n,Set(GLOBAL(senddtmf3)=**123#DC9594562) 7 | same => n,Set(GLOBAL(senddtmf4)=55512121212) 8 | same => n,Set(GLOBAL(senddtmf5)=20094357264) 9 | same => n,Set(GLOBAL(senddtmf6)=*009####) 10 | same => n,Set(i=0) 11 | same => n,While($[${INC(i)}<=6]) 12 | same => n,Originate(Local/${i}@send-dtmf,exten,read-dtmf,${i},1,,a) 13 | same => n,EndWhile() 14 | same => n,Hangup() 15 | 16 | [nothing] 17 | exten => 0,1,Answer() 18 | same => n,Wait(8) 19 | same => n,Hangup() 20 | 21 | [send-dtmf] 22 | exten => _X!,1,Answer(0.5) 23 | same => n,SendDTMF(${senddtmf${EXTEN}}) 24 | same => n,Wait(15) ; needed so DialTone has time to finish. 25 | same => n,Hangup() 26 | 27 | [match-nothing] 28 | 29 | [match-context] 30 | exten => _NNXXXXX,1,Return(1) 31 | 32 | [match-fancy] 33 | exten => _[A-D0-9*#]!,1,Return($[${LEN(${EXTEN})}=7]) 34 | 35 | [read-dtmf] 36 | exten => 1,1,Answer() 37 | same => n,DialTone(digits,match-nothing,dial,dial,10,,,ipt) 38 | same => n,GotoIf($["${digits}"="${CUT(senddtmf${EXTEN},#,1)}"]?success,1:fail,1) 39 | exten => 2,1,Answer() 40 | same => n,DialTone(digits,match-nothing,silence/5,silence/5,10,,,pr) ; silence/5 should exist on every system, and it's a few seconds long, just what we need... 41 | same => n,GotoIf($["${digits}"="${senddtmf${EXTEN}:0:10}"]?success,1:fail,1) 42 | exten => 3,1,Answer() 43 | same => n,DialTone(digits,match-context,silence/5,silence/5,10,,,p) 44 | same => n,GotoIf($["${digits}"="${senddtmf${EXTEN}:0:10}"]?success,1:fail,1) 45 | exten => 4,1,Answer() 46 | same => n,DialTone(digits,match-context,silence/5,silence/5,10,,,pr) 47 | same => n,GotoIf($["${digits}"="${senddtmf${EXTEN}:0:7}"]?success,1:fail,1) 48 | exten => 5,1,Answer() 49 | same => n,DialTone(digits,match-context,silence/5,silence/5,10,,,pr) 50 | same => n,GotoIf($["${digits}"="${senddtmf${EXTEN}:0:10}"]?success,1:fail,1) 51 | exten => 6,1,Answer() 52 | same => n,DialTone(digits,match-fancy,silence/5,silence/5,10,,,pr) 53 | same => n,GotoIf($["${digits}"="${senddtmf${EXTEN}:0:7}"]?success,1:fail,1) 54 | exten => success,1,Answer(1) 55 | same => n,UserEvent(DialToneSuccess,Result: Pass) 56 | same => n,Hangup() 57 | exten => fail,1,Answer(1) 58 | same => n,UserEvent(DialToneFailure,Result: Fail ${digits},Reason: ${digits}) 59 | same => n,Hangup() 60 | -------------------------------------------------------------------------------- /testsuite/tests/apps/dialtone/test-config.yaml: -------------------------------------------------------------------------------- 1 | testinfo: 2 | summary: 'Ensure that app_dialtone works correctly.' 3 | description: | 4 | 'This tests the DialTone application to 5 | ensure that it functions correctly.' 6 | 7 | test-modules: 8 | test-object: 9 | config-section: test-object-config 10 | typename: 'test_case.TestCaseModule' 11 | modules: 12 | - 13 | config-section: caller-originator 14 | typename: 'pluggable_modules.Originator' 15 | - 16 | config-section: hangup-monitor 17 | typename: 'pluggable_modules.HangupMonitor' 18 | - 19 | config-section: ami-config 20 | typename: 'pluggable_modules.EventActionModule' 21 | 22 | test-object-config: 23 | connect-ami: True 24 | 25 | caller-originator: 26 | channel: 'Local/s@default' 27 | context: 'nothing' 28 | exten: '0' 29 | priority: '1' 30 | trigger: 'ami_connect' 31 | 32 | hangup-monitor: 33 | ids: '0' 34 | 35 | ami-config: 36 | - 37 | ami-events: 38 | conditions: 39 | match: 40 | Event: 'UserEvent' 41 | UserEvent: 'DialToneSuccess' 42 | requirements: 43 | match: 44 | Result: 'Pass' 45 | count: 6 46 | stop_test: 47 | 48 | properties: 49 | tags: 50 | - dial 51 | - apps 52 | dependencies: 53 | - python: 'twisted' 54 | - python: 'starpy' 55 | - asterisk: 'app_dial' 56 | - asterisk: 'app_userevent' 57 | - asterisk: 'app_originate' 58 | - asterisk: 'app_dialtone' 59 | - asterisk: 'pbx_config' 60 | -------------------------------------------------------------------------------- /testsuite/tests/apps/verify/configs/ast1/extensions.conf: -------------------------------------------------------------------------------- 1 | 2 | [nothing] 3 | exten => 0,1,Answer() 4 | same => n,Wait(10) 5 | same => n,Hangup() 6 | 7 | [default] 8 | exten => s,1,Answer() 9 | same => n,Set(dial=IAX2/localhost:mysecret@127.0.0.1) 10 | same => n,Originate(${dial}/5551212,app,Wait,20,,,c(18004444444)) ; valid Caller ID 11 | same => n,Originate(${dial}/5551213,app,Wait,20,,,a) ; no Caller ID 12 | same => n,Originate(${dial}/5551214,app,Wait,20,,,ac(15551212)) ; invalid NANPA Caller ID 13 | same => n,Originate(${dial}/5551215,app,Wait,20,,,ac(anonymous)) ; anonymous 14 | same => n,Originate(${dial}/5551216,app,Wait,20,,,ac(5551000)) 15 | same => n,Originate(${dial}/5551217,app,Wait,20,,,ac(5551212)) 16 | same => n,Originate(${dial}/5551218,app,Wait,20,,,ac(5551111)) 17 | same => n,Originate(${dial}/5551219,app,Wait,20,,,ac(555)) ; there will be no UserEvent for this if successful. 18 | same => n,Originate(${dial}/5551220,app,Wait,20,,,ac(555)) 19 | same => n,Hangup() 20 | 21 | [from-localhost] 22 | exten => success,1,UserEvent(VerifySuccess,Result: Pass) 23 | same => n,Hangup() 24 | exten => failure,1,UserEvent(VerifyFail,Result: Fail) 25 | same => n,Hangup() 26 | exten => 5551212,1,Answer() 27 | same => n,Verify(pstn-us) 28 | same => n,GotoIf($["${clidverif}"="30"]?success,1:failure,1) 29 | exten => 5551213,1,Answer() 30 | same => n,Verify(pstn-us) 31 | same => n,GotoIf($[${GROUP_COUNT(pstn-us@spam)}=0]?failure,1) 32 | same => n,GotoIf($["${clidverif}"="32"]?success,1:failure,1) ; should be anonymous 33 | exten => 5551214,1,Answer() 34 | same => n,Verify(pstn-us) 35 | same => n,GotoIf($[${GROUP_COUNT(pstn-us@spam)}=0]?failure,1) 36 | same => n,GotoIf($["${clidverif}"="31"]?success,1:failure,1) 37 | exten => 5551215,1,Answer() 38 | same => n,Verify(pstn-us) 39 | same => n,GotoIf($[${GROUP_COUNT(pstn-us@spam)}=0]?failure,1) 40 | same => n,GotoIf($["${clidverif}"="32"]?success,1:failure,1) 41 | exten => 5551216,1,Answer() 42 | same => n,Verify(regex) 43 | same => n,GotoIf($[${GROUP_COUNT(regex@spam)}=0]?failure,1) 44 | same => n,GotoIf($["${clidverif}"="31"]?success,1:failure,1) 45 | exten => 5551217,1,Answer() 46 | same => n,Verify(regex) 47 | same => n,GotoIf($["${clidverif}"="30"]?success,1:failure,1) 48 | exten => 5551218,1,Answer() 49 | same => n,Verify(regex) 50 | same => n,GotoIf($["${clidverif}"="30"]?success,1:failure,1) 51 | exten => 5551219,1,Answer() 52 | same => n,Verify(hangup) 53 | same => n,UserEvent(VerifyFail,Result: Fail) ; should never get here 54 | exten => 5551220,1,Answer() 55 | same => n,Verify(redirect) 56 | same => n,UserEvent(VerifyFail,Result: Fail) ; should never get here 57 | 58 | [redirect] 59 | exten => s,1,Goto(from-localhost,success,1) 60 | 61 | [pstn-us-verify-patterns] 62 | exten => _[A-Za-z]!,1,Return(32) 63 | exten => _X!,1,Return(31) 64 | exten => _NXXNXXXXXX,1,Return(30) 65 | exten => _1NXXNXXXXXX,1,Return(30) 66 | exten => _N00NXXXXXX,1,Return(31) 67 | exten => _1N00NXXXXXX,1,Return(31) 68 | exten => _800NXXXXXX,1,Return(30) 69 | exten => _1800NXXXXXX,1,Return(30) 70 | exten => _[01]XXXXXXXXX,1,Return(31) 71 | exten => _XXX[01]XXXXXX,1,Return(31) 72 | exten => _[2-79]00NXXXXXX,1,Return(31) 73 | exten => _[2-79]22NXXXXXX,1,Return(31) 74 | exten => _[2-79]33NXXXXXX,1,Return(31) 75 | exten => _[2-79]44NXXXXXX,1,Return(31) 76 | exten => _[2-79]55NXXXXXX,1,Return(31) 77 | exten => _[2-79]66NXXXXXX,1,Return(31) 78 | exten => _[2-79]77NXXXXXX,1,Return(31) 79 | exten => _[2-79]88NXXXXXX,1,Return(31) 80 | exten => _[2-79]99NXXXXXX,1,Return(31) 81 | exten => _X11XXXXXXX,1,Return(31) 82 | -------------------------------------------------------------------------------- /testsuite/tests/apps/verify/configs/ast1/iax.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | bindport=4569 3 | bindaddr=127.0.0.1 4 | disallow=all 5 | allow=ulaw 6 | jitterbuffer=no 7 | forcejitterbuffer=no 8 | encryption=yes 9 | 10 | [localhost] 11 | type = friend 12 | context = from-localhost 13 | username = localhost 14 | auth = md5 15 | secret = mysecret 16 | -------------------------------------------------------------------------------- /testsuite/tests/apps/verify/configs/ast1/verify.conf: -------------------------------------------------------------------------------- 1 | [pstn](!) 2 | failgroup = spam 3 | local_var = __clidverif 4 | threshold = 10 5 | code_fail = 32 ; should catch emtpy caller IDs 6 | 7 | [pstn-us](pstn) 8 | verifymethod = pattern 9 | verifycontext = pstn-us-verify-patterns 10 | successregex = [0-9]0 11 | 12 | [regex](pstn) 13 | verifymethod = regex 14 | successregex = 555[12][12][12][12] 15 | code_good = 30 16 | code_fail = 31 17 | 18 | [hangup](pstn) 19 | verifymethod = regex 20 | successregex = 555[12][12][12][12] 21 | failureaction = hangup 22 | code_good = 30 23 | code_fail = 31 24 | 25 | [redirect](pstn) 26 | verifymethod = regex 27 | successregex = 555[12][12][12][12] 28 | failureaction = redirect 29 | failurelocation = redirect,s,1 30 | code_good = 30 31 | code_fail = 31 32 | -------------------------------------------------------------------------------- /testsuite/tests/apps/verify/test-config.yaml: -------------------------------------------------------------------------------- 1 | testinfo: 2 | summary: 'Ensure that app_verify functions correctly.' 3 | description: | 4 | 'This tests the Verify application to make sure 5 | that it functions correctly.' 6 | 7 | test-modules: 8 | test-object: 9 | config-section: test-object-config 10 | typename: 'test_case.TestCaseModule' 11 | modules: 12 | - 13 | config-section: caller-originator 14 | typename: 'pluggable_modules.Originator' 15 | - 16 | config-section: hangup-monitor 17 | typename: 'pluggable_modules.HangupMonitor' 18 | - 19 | config-section: ami-config 20 | typename: 'pluggable_modules.EventActionModule' 21 | 22 | test-object-config: 23 | connect-ami: True 24 | 25 | caller-originator: 26 | channel: 'Local/s@default' 27 | context: 'nothing' 28 | exten: '0' 29 | priority: '1' 30 | trigger: 'ami_connect' 31 | 32 | hangup-monitor: 33 | ids: '0' 34 | 35 | ami-config: 36 | - 37 | ami-events: 38 | conditions: 39 | match: 40 | Event: 'UserEvent' 41 | UserEvent: 'VerifySuccess' 42 | requirements: 43 | match: 44 | Result: 'Pass' 45 | count: 8 46 | stop_test: 47 | 48 | properties: 49 | tags: 50 | - dial 51 | - funcs 52 | dependencies: 53 | - python: 'twisted' 54 | - python: 'starpy' 55 | - asterisk: 'app_dial' 56 | - asterisk: 'app_userevent' 57 | - asterisk: 'app_originate' 58 | - asterisk: 'app_verify' 59 | - asterisk: 'pbx_config' 60 | -------------------------------------------------------------------------------- /testsuite/tests/funcs/func_dbchan/configs/ast1/extensions.conf: -------------------------------------------------------------------------------- 1 | 2 | [default] 3 | exten => failure,1,UserEvent(DBChanSuccess,Result: Fail) 4 | same => n,DBdeltree(dbchantest) ; be nice and clean up 5 | same => n,Hangup() 6 | exten => s,1,Answer() 7 | same => n,Originate(Local/1@hold,app,Wait,5,,,a) 8 | same => n,Wait(2) ; ensure hold adds something to the DB first 9 | same => n,Set(DB(dbchantests/test1/${EPOCH})=${CHANNEL}) 10 | same => n,GotoIf($["${DB(${DB_MAXKEY(dbchantests/test1)})}"="${CHANNEL}"]?:failure,1) 11 | same => n,GotoIf($["${DB(${DB_MINKEY(dbchantests/test1)})}"="${minkey}"]?:failure,1) 12 | same => n,Set(dbchan=${DB_CHANNEL(dbchantests/test1)}) 13 | same => n,GotoIf(${ISNULL(${dbchan})}?failure,1) 14 | same => n,Set(dbchan=${DB(dbchantests/test1/${dbchan})}) 15 | same => n,GotoIf(${ISNULL(${dbchan})}?failure,1) 16 | same => n,GotoIf($["${dbchan}"="${minkey}"]?:failure,1) 17 | 18 | same => n,Set(i=0) 19 | same => n,While($[${INC(i)}<=11]) ; this should really stress test it 20 | same => n,Originate(Local/s@set-and-drop,exten,nothing,0,1,,a) 21 | same => n,EndWhile() 22 | 23 | same => n,Wait(1.5) 24 | same => n,Set(keys=${DB_KEYS(dbchantests/test2)}) 25 | same => n,Set(fieldcount=${FIELDQTY(keys,\,)}) 26 | same => n,GotoIf($["${fieldcount}"="11"]?:failure,1) 27 | same => n,Set(pruned=${DB_CHANNEL_PRUNE(dbchantests/test2)}) 28 | same => n,Set(keys=${DB_KEYS(dbchantests/test2)}) 29 | same => n,GotoIf($["${pruned}"="11"]?:failure,1) 30 | same => n,GotoIf($["${keys}"=""]?:failure,1) 31 | 32 | same => n,Set(i=0) 33 | same => n,While($[${INC(i)}<=11]) ; this should really stress test it 34 | same => n,Originate(Local/s@set-and-wait,exten,nothing,0,1,,a) 35 | same => n,Wait(0.5) 36 | same => n,EndWhile() 37 | 38 | same => n,Wait(0.5) 39 | same => n,Set(keys=${DB_KEYS(dbchantests/test3)}) 40 | same => n,Set(fieldcount=${FIELDQTY(keys,\,)}) 41 | same => n,GotoIf($["${fieldcount}"="11"]?:failure,1) 42 | same => n,Set(pruned=${DB_CHANNEL_PRUNE(dbchantests/test3)}) 43 | same => n,GotoIf($[${pruned}<3|${pruned}>9]?failure,1) ; they shouldn't all be pruned 44 | same => n,Set(dbchan=${DB_CHANNEL(dbchantests/test3)}) 45 | same => n,GotoIf(${CHANNEL_EXISTS(${DB(dbchantests/test3/${dbchan})})}?:failure,1) 46 | 47 | same => n,Set(i=0) 48 | same => n,While($[${INC(i)}<=3]) 49 | same => n,Originate(Local/s@set-and-wait-2,exten,nothing,0,1,,a) 50 | same => n,Wait(1) 51 | same => n,EndWhile() 52 | 53 | same => n,Set(pruned=${DB_CHANNEL_PRUNE_TIME($[${EPOCH}-4],dbchantests/test4)}) 54 | same => n,GotoIf($[${pruned}=0]?:failure,1) 55 | same => n,Wait(5) 56 | same => n,Set(pruned=${DB_CHANNEL_PRUNE_TIME($[${EPOCH}-4],dbchantests/test4)}) 57 | same => n,GotoIf($[${pruned}=3]?:failure,1) 58 | 59 | same => n,DBdeltree(dbchantest) ; be nice and clean up 60 | same => n,UserEvent(DBChanSuccess,Result: Pass) ; this is weird, but emitting UserEvents throughout causes the test suite to start cleaning up, we're doing this all in one channel so one at the end is good enough anyways... 61 | same => n,Hangup() 62 | 63 | [set-and-drop] 64 | exten => s,1,Answer() 65 | same => n,NoOp(${DB_UNIQUE(dbchantests/test2/${EPOCH})}) 66 | same => n,Set(DB_UNIQUE(dbchantests/test2/${EPOCH})=${CHANNEL}) 67 | same => n,Hangup() 68 | 69 | [set-and-wait] 70 | exten => s,1,Answer() 71 | same => n,NoOp(${DB_UNIQUE(dbchantests/test3/${EPOCH})}) 72 | same => n,Set(DB_UNIQUE(dbchantests/test3/${EPOCH})=${CHANNEL}) 73 | same => n,Wait(3) 74 | same => n,Hangup() 75 | 76 | [set-and-wait-2] 77 | exten => s,1,Answer() 78 | same => n,Set(DB(dbchantests/test4/${EPOCH})=${CHANNEL}) 79 | same => n,Wait(3) 80 | same => n,Hangup() 81 | 82 | [hold] 83 | exten => 1,1,Answer() 84 | same => n,Set(GLOBAL(minkey)=${CHANNEL}) 85 | same => n,Set(DB(dbchantests/test1/${EPOCH})=${CHANNEL}) 86 | same => n,Wait(5) 87 | same => n,Hangup() 88 | 89 | [nothing] 90 | exten => 0,1,Answer() 91 | same => n,Wait(20) 92 | same => n,Hangup() 93 | -------------------------------------------------------------------------------- /testsuite/tests/funcs/func_dbchan/test-config.yaml: -------------------------------------------------------------------------------- 1 | testinfo: 2 | summary: 'Ensure that func_dbchan functions correctly.' 3 | description: | 4 | 'This tests func_dbchan for correct functionality.' 5 | 6 | test-modules: 7 | test-object: 8 | config-section: test-object-config 9 | typename: 'test_case.TestCaseModule' 10 | modules: 11 | - 12 | config-section: caller-originator 13 | typename: 'pluggable_modules.Originator' 14 | - 15 | config-section: hangup-monitor 16 | typename: 'pluggable_modules.HangupMonitor' 17 | - 18 | config-section: ami-config 19 | typename: 'pluggable_modules.EventActionModule' 20 | 21 | test-object-config: 22 | connect-ami: True 23 | 24 | caller-originator: 25 | channel: 'Local/s@default' 26 | context: 'nothing' 27 | exten: '0' 28 | priority: '1' 29 | trigger: 'ami_connect' 30 | 31 | hangup-monitor: 32 | ids: '0' 33 | 34 | ami-config: 35 | - 36 | ami-events: 37 | conditions: 38 | match: 39 | Event: 'UserEvent' 40 | UserEvent: 'DBChanSuccess' 41 | requirements: 42 | match: 43 | Result: 'Pass' 44 | count: 1 45 | stop_test: 46 | 47 | properties: 48 | tags: 49 | - dial 50 | - funcs 51 | dependencies: 52 | - python: 'twisted' 53 | - python: 'starpy' 54 | - asterisk: 'app_dial' 55 | - asterisk: 'app_userevent' 56 | - asterisk: 'app_originate' 57 | - asterisk: 'func_dbchan' 58 | - asterisk: 'pbx_config' 59 | --------------------------------------------------------------------------------