Skip to content

Commit 72ab3fe

Browse files
committed
func_message_sub: Add function to run intercept sub for messages.
Add a trivial function that will run a subroutine when a text frame containing a message is received on a channel. This can be used to do something when a SIP MESSAGE is received from an endpoint during a call.
1 parent 6867689 commit 72ab3fe

File tree

3 files changed

+248
-0
lines changed

3 files changed

+248
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ PhreakScript installs:
8989
- ``DB_UNIQUE``
9090
- ``NANPA``
9191
- ``SIP_PARAMETER``
92+
- ``MESSAGE_INTERCEPT_SUB``
9293
- ``GROUP_VAR`` (third-party)
9394
- ``GROUP_MATCH_LIST_START`` (third-party)
9495
- Miscellaneous improvements

funcs/func_message_sub.c

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/*
2+
* Asterisk -- An open source telephony toolkit.
3+
*
4+
* Copyright (C) 2024, Naveen Albert
5+
*
6+
* Naveen Albert <asterisk@phreaknet.org>
7+
*
8+
* See http://www.asterisk.org for more information about
9+
* the Asterisk project. Please do not directly contact
10+
* any of the maintainers of this project for assistance;
11+
* the project provides a web site, mailing lists and IRC
12+
* channels for your use.
13+
*
14+
* This program is free software, distributed under the terms of
15+
* the GNU General Public License Version 2. See the LICENSE file
16+
* at the top of the source tree.
17+
*/
18+
19+
/*! \file
20+
*
21+
* \brief Message intercept hook
22+
*
23+
* \author Naveen Albert <asterisk@phreaknet.org>
24+
*
25+
* \ingroup functions
26+
*/
27+
28+
/*** MODULEINFO
29+
<support_level>extended</support_level>
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+
<function name="MESSAGE_INTERCEPT_SUB" language="en_US">
43+
<synopsis>
44+
Add an intercept subroutine to run when text messages are received on a channel
45+
</synopsis>
46+
<syntax>
47+
<parameter name="context" required="true">
48+
<para>The dialplan location to execute as a callback (context,exten,priority).</para>
49+
</parameter>
50+
</syntax>
51+
<description>
52+
<para>Example:</para>
53+
<example title="Add message intercept">
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+
</example>
60+
</description>
61+
</function>
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");

phreaknet.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,13 @@ install_dahdi() {
13731373
linux_headers_install_apt
13741374
elif [ "$PAC_MAN" = "yum" ]; then
13751375
dnf install -y m4 libtool automake autoconf kernel-devel kernel-headers
1376+
dnf list installed 'kernel*'
1377+
if [ -f /etc/redhat-release ]; then
1378+
# https://access.redhat.com/discussions/4656371?tour=8
1379+
# Red Hat may install newer headers than the current system
1380+
dnf downgrade kernel-headers-$(uname -r)
1381+
ls -la /usr/src/kernels
1382+
fi
13761383
#dnf install -y kernel-headers-$(uname -r)
13771384
dnf list installed | grep kernel-headers
13781385
rpm -qa | grep kernel
@@ -1855,6 +1862,7 @@ phreak_patches() { # $1 = $PATCH_DIR, $2 = $AST_SRC_DIR
18551862
phreak_tree_module "funcs/func_dbchan.c"
18561863
phreak_tree_module "funcs/func_dtmf_flash.c"
18571864
phreak_tree_module "funcs/func_dtmf_trace.c"
1865+
phreak_tree_module "funcs/func_message_sub.c"
18581866
phreak_tree_module "funcs/func_nanpa.c"
18591867
phreak_tree_module "funcs/func_notchfilter.c"
18601868
phreak_tree_module "funcs/func_numpeer.c"

0 commit comments

Comments
 (0)