Asterisk - The Open Source Telephony Project  18.5.0
app_dtmfstore.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2021, Naveen Albert
5  *
6  * Naveen Albert <[email protected]>
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 Technology independent asynchronous DTMF collection
22  *
23  * \author Naveen Albert <[email protected]>
24  *
25  * \ingroup functions
26  *
27  */
28 
29 /*** MODULEINFO
30  <support_level>extended</support_level>
31  ***/
32 
33 #include "asterisk.h"
34 
35 #include "asterisk/module.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/framehook.h"
39 #include "asterisk/app.h"
40 #include "asterisk/conversions.h"
41 
42 /*** DOCUMENTATION
43  <application name="StoreDTMF" language="en_US">
44  <synopsis>
45  Stores DTMF digits transmitted or received on a channel.
46  </synopsis>
47  <syntax>
48  <parameter name="direction" required="true">
49  <para>Must be <literal>TX</literal> or <literal>RX</literal>.</para>
50  </parameter>
51  </syntax>
52  <description>
53  <para>The StoreDTMF function can be used to obtain digits sent in the
54  <literal>TX</literal> or <literal>RX</literal> direction of any channel.</para>
55  <para>The arguments are:</para>
56  <para><replaceable>var_name</replaceable>: Name of variable to which to append
57  digits.</para>
58  <para><replaceable>max_digits</replaceable>: The maximum number of digits to
59  store in the variable. Defaults to 0 (no maximum). After reading <literal>
60  maximum</literal> digits, no more digits will be stored.</para>
61  <para>For example:</para>
62  <para>StoreDTMF(TX,CDR(digits))</para>
63  <para>StoreDTMF(RX,testvar,24)</para>
64  <para>StoreDTMF(remove)</para>
65  </description>
66  </application>
67  ***/
68 
69 static char *app = "StoreDTMF";
70 
71 /*! \brief Private data structure used with the function's datastore */
74  char *rx_var;
75  char *tx_var;
76  int maxdigits;
77 };
78 
79 static void datastore_destroy_cb(void *data) {
80  struct dtmf_store_data *d;
81  d = data;
82  if (d) {
83  if (d->rx_var) {
84  ast_free(d->rx_var);
85  }
86  if (d->tx_var) {
87  ast_free(d->tx_var);
88  }
89  ast_free(data);
90  }
91 }
92 
93 /*! \brief The channel datastore the function uses to store state */
95  .type = "dtmf_store",
96  .destroy = datastore_destroy_cb
97 };
98 
99 /*! \internal \brief Store digits tx/rx on the channel */
100 static int remove_dtmf_store(struct ast_channel *chan)
101 {
102  struct ast_datastore *datastore = NULL;
103  struct dtmf_store_data *data;
104  SCOPED_CHANNELLOCK(chan_lock, chan);
105 
106  datastore = ast_channel_datastore_find(chan, &dtmf_store_datastore, NULL);
107  if (!datastore) {
108  ast_log(AST_LOG_WARNING, "Cannot remove StoreDTMF from %s: StoreDTMF not currently enabled\n",
109  ast_channel_name(chan));
110  return -1;
111  }
112  data = datastore->data;
113 
114  if (ast_framehook_detach(chan, data->framehook_id)) {
115  ast_log(AST_LOG_WARNING, "Failed to remove StoreDTMF framehook from channel %s\n",
116  ast_channel_name(chan));
117  return -1;
118  }
119 
120  if (ast_channel_datastore_remove(chan, datastore)) {
121  ast_log(AST_LOG_WARNING, "Failed to remove StoreDTMF datastore from channel %s\n",
122  ast_channel_name(chan));
123  return -1;
124  }
125  ast_datastore_free(datastore);
126 
127  return 0;
128 }
129 
130 /*! \brief Frame hook that is called to intercept digit/undigit */
131 static struct ast_frame *dtmf_store_framehook(struct ast_channel *chan,
132  struct ast_frame *f, enum ast_framehook_event event, void *data)
133 {
134  char currentdata[512];
135  char varnamesub[64];
136  char *varname = NULL;
137  struct dtmf_store_data *framedata = data;
138  int len;
139 
140  if (!f || !framedata) {
141  return f;
142  }
143 
144  if ((event != AST_FRAMEHOOK_EVENT_WRITE) && (event != AST_FRAMEHOOK_EVENT_READ)) {
145  return f;
146  }
147 
148  if (f->frametype != AST_FRAME_DTMF_END) {
149  return f;
150  }
151 
152  /* If this is DTMF then store the digits */
153  if (event == AST_FRAMEHOOK_EVENT_READ && framedata->rx_var) { /* coming from source */
154  varname = framedata->rx_var;
155  } else if (event == AST_FRAMEHOOK_EVENT_WRITE && framedata->tx_var) { /* going to source */
156  varname = framedata->tx_var;
157  }
158 
159  if (!varname) {
160  return f;
161  }
162 
163  sprintf(varnamesub, "${%s}", varname);
164  pbx_substitute_variables_helper(chan, varnamesub, currentdata, 511);
165  /* pbx_builtin_getvar_helper works for regular vars but not CDR vars */
166  if (ast_strlen_zero(currentdata)) { /* var doesn't exist yet */
167  ast_debug(3, "Creating new digit store: %s\n", varname);
168  }
169  len = strlen(currentdata);
170  if (framedata->maxdigits > 0 && len >= framedata->maxdigits) {
171  ast_debug(3, "Reached digit limit: %d\n", framedata->maxdigits);
172  remove_dtmf_store(chan); /* reached max digit count, stop now */
173  return f;
174  } else {
175  char newdata[len + 2]; /* one more char + terminator */
176  if (len > 0) {
177  ast_copy_string(newdata, currentdata, len + 2);
178  }
179  newdata[len] = (unsigned) f->subclass.integer;
180  newdata[len + 1] = '\0';
181  ast_debug(3, "Appending to digit store: now %s\n", newdata);
182  pbx_builtin_setvar_helper(chan, varname, newdata);
183  }
184  return f;
185 }
186 
187 /*! \internal \brief Enable digit interception on the channel */
188 static int dtmfstore_exec(struct ast_channel *chan, const char *appdata)
189 {
190  struct ast_datastore *datastore;
191  struct dtmf_store_data *data;
192  static struct ast_framehook_interface digit_framehook_interface = {
194  .event_cb = dtmf_store_framehook,
195  .disable_inheritance = 1,
196  };
197  char *parse = ast_strdupa(appdata);
200  AST_APP_ARG(varname);
202  );
203  SCOPED_CHANNELLOCK(chan_lock, chan);
204  AST_STANDARD_APP_ARGS(args, parse);
205 
206  if (ast_strlen_zero(appdata)) {
207  ast_log(AST_LOG_WARNING, "StoreDTMF requires an argument\n");
208  return -1;
209  }
210 
211  if (!strcasecmp(args.direction, "remove")) {
212  return remove_dtmf_store(chan);
213  }
214 
215  datastore = ast_channel_datastore_find(chan, &dtmf_store_datastore, NULL);
216  if (datastore) {
217  ast_log(AST_LOG_WARNING, "StoreDTMF already set on '%s'\n",
218  ast_channel_name(chan));
219  return 0;
220  }
221 
222  datastore = ast_datastore_alloc(&dtmf_store_datastore, NULL);
223  if (!datastore) {
224  return -1;
225  }
226 
227  data = ast_calloc(1, sizeof(*data));
228  if (!data) {
229  ast_datastore_free(datastore);
230  return -1;
231  }
232 
233  digit_framehook_interface.data = data;
234 
235  data->rx_var = NULL;
236  data->tx_var = NULL;
237  data->maxdigits = 0;
238 
239  if (!strcasecmp(args.direction, "tx")) {
240  data->tx_var = ast_strdup(args.varname);
241  } else if (!strcasecmp(args.direction, "rx")) {
242  data->rx_var = ast_strdup(args.varname);
243  } else {
244  ast_log(LOG_ERROR, "Direction must be either RX or TX\n");
245  return -1;
246  }
247 
248  if (!ast_strlen_zero(args.maxdigits)) {
249  if (ast_str_to_int(args.maxdigits,&(data->maxdigits))) {
250  ast_log(LOG_ERROR, "Invalid integer: %s\n", args.maxdigits);
251  return -1;
252  }
253  if (data->maxdigits < 0) {
254  ast_log(LOG_ERROR, "Invalid natural number: %d\n", data->maxdigits);
255  return -1;
256  } else if (data->maxdigits == 0) {
257  ast_log(LOG_WARNING, "No maximum digit count set\n");
258  }
259  }
260 
261  data->framehook_id = ast_framehook_attach(chan, &digit_framehook_interface);
262  if (data->framehook_id < 0) {
263  ast_log(AST_LOG_WARNING, "Failed to attach StoreDTMF framehook to '%s'\n",
264  ast_channel_name(chan));
265  ast_datastore_free(datastore);
266  ast_free(data);
267  return -1;
268  }
269  datastore->data = data;
270 
271  ast_channel_datastore_add(chan, datastore);
272 
273  return 0;
274 }
275 
276 static int unload_module(void)
277 {
279 }
280 
281 static int load_module(void)
282 {
284 }
285 
286 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Technology independent async DTMF storage");
const char * type
Definition: datastore.h:32
static int dtmfstore_exec(struct ast_channel *chan, const char *appdata)
Main Channel structure associated with a channel.
static struct ast_frame * dtmf_store_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
Frame hook that is called to intercept digit/undigit.
Asterisk main include file. File version handling, generic pbx functions.
static int load_module(void)
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define LOG_WARNING
Definition: logger.h:274
ast_framehook_event
These are the types of events that the framehook&#39;s event callback can receive.
Definition: framehook.h:151
static struct test_val d
static int remove_dtmf_store(struct ast_channel *chan)
#define AST_LOG_WARNING
Definition: logger.h:279
int ast_framehook_detach(struct ast_channel *chan, int framehook_id)
Detach an framehook from a channel.
Definition: framehook.c:177
Structure for a data store type.
Definition: datastore.h:31
Definition: astman.c:222
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
Structure for a data store object.
Definition: datastore.h:68
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2404
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Technology independent async DTMF storage")
const char * args
#define NULL
Definition: resample.c:96
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
struct ast_frame_subclass subclass
#define ast_strlen_zero(foo)
Definition: strings.h:52
int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interface *i)
Attach an framehook onto a channel for frame interception.
Definition: framehook.c:132
static const struct ast_datastore_info dtmf_store_datastore
The channel datastore the function uses to store state.
Definition: app_dtmfstore.c:94
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define SCOPED_CHANNELLOCK(varname, chan)
scoped lock specialization for channels.
Definition: lock.h:617
General Asterisk PBX channel definitions.
Conversion utility functions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
Core PBX routines and definitions.
static char * app
Definition: app_dtmfstore.c:69
int ast_str_to_int(const char *str, int *res)
Convert the given string to a signed integer.
Definition: conversions.c:44
#define LOG_ERROR
Definition: logger.h:285
#define AST_FRAMEHOOK_INTERFACE_VERSION
Definition: framehook.h:227
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Private data structure used with the function&#39;s datastore.
Definition: app_dtmfstore.c:72
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static void datastore_destroy_cb(void *data)
Definition: app_dtmfstore.c:79
static int unload_module(void)
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
FrameHook Architecture.
void * data
Definition: datastore.h:70
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
const char * ast_channel_name(const struct ast_channel *chan)
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
Definition: ael_main.c:211
Data structure associated with a single frame of data.
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:89
union ast_frame::@263 data
enum ast_frame_type frametype
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
direction
Asterisk module definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2390
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2399
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
#define AST_APP_ARG(name)
Define an application argument.