Asterisk - The Open Source Telephony Project  18.5.0
func_sayfiles.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2021, Naveen Albert <[email protected]>
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 Returns files played by Say applications
20  *
21  * \author Naveen Albert <[email protected]>
22  * \ingroup functions
23  */
24 
25 /*** MODULEINFO
26  <support_level>extended</support_level>
27  ***/
28 
29 #include "asterisk.h"
30 
31 #include "asterisk/pbx.h"
32 #include "asterisk/file.h"
33 #include "asterisk/channel.h"
34 #include "asterisk/say.h"
35 #include "asterisk/lock.h"
36 #include "asterisk/localtime.h"
37 #include "asterisk/utils.h"
38 #include "asterisk/app.h"
39 #include "asterisk/test.h"
40 #include "asterisk/module.h"
41 
42 /*** DOCUMENTATION
43  <function name="SAYFILES" language="en_US">
44  <synopsis>
45  Returns the file names that would be played by the Say applications (e.g. SayAlpha, SayDigits).
46  </synopsis>
47  <syntax>
48  <parameter name="value" required="true">
49  <para>The value to be translated to filenames.</para>
50  </parameter>
51  <parameter name="value">
52  <para>Say application type.</para>
53  <enumlist>
54  <enum name="alpha">
55  <para>Files played by SayAlpha(). Default if none is specified.</para>
56  </enum>
57  <enum name="phonetic">
58  <para>Files played by SayPhonetic().</para>
59  </enum>
60  <enum name="digits">
61  <para>Files played by SayDigits().</para>
62  </enum>
63  <enum name="number">
64  <para>Files played by SayNumber(). Currently supported for English only.</para>
65  </enum>
66  <enum name="money">
67  <para>Files played by SayMoney(). Currently supported for English and US dollars only.</para>
68  </enum>
69  </enumlist>
70  </parameter>
71  </syntax>
72  <description>
73  <para>Returns the files that would be played by a Say application. These filenames could then be
74  passed directly into Playback, BackGround, Read, Queue, or any application which supports
75  playback of multiple ampersand-delimited files.</para>
76  <example title="Read using the number 123">
77  same => n,Read(response,${SAYFILES(123,number)})
78  </example>
79  </description>
80  <see-also>
81  <ref type="application">SayAlpha</ref>
82  <ref type="application">SayPhonetic</ref>
83  <ref type="application">SayDigits</ref>
84  <ref type="application">SayNumber</ref>
85  <ref type="application">SayMoney</ref>
86  </see-also>
87  </function>
88  ***/
89 static int sayfile_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
90 {
91  char *value, *type, *files;
92  const char *lang;
93  struct ast_str *filenames = NULL;
95  AST_APP_ARG(value);
96  AST_APP_ARG(type);
97  );
98 
99  if (ast_strlen_zero(data)) {
100  ast_log(LOG_WARNING, "SAYFILES requires an argument\n");
101  return 0;
102  }
103 
105 
106  value = args.value;
107  if (ast_strlen_zero(args.type))
108  type = "alpha";
109  else
110  type = args.type;
111 
112  if (chan)
113  lang = ast_channel_language(chan);
114  else
115  lang = "en"; /* No chan for unit tests */
116 
117  if (!strcmp(type, "alpha")) {
118  filenames = ast_get_character_str(value, lang, AST_SAY_CASE_NONE);
119  } else if (!strcmp(type, "phonetic")) {
120  filenames = ast_get_phonetic_str(value, lang);
121  } else if (!strcmp(type, "digits")) {
122  filenames = ast_get_digit_str(value, lang);
123  } else if (!strcmp(type, "number")) {
124  int num;
125  if (sscanf(value, "%d", &num) == 1)
126  filenames = ast_get_number_str(num, lang);
127  else
128  ast_log(LOG_WARNING, "Invalid numeric argument: %s\n", value);
129  } else if (!strcmp(type, "money")) {
130  filenames = ast_get_money_str(value, lang);
131  } else {
132  ast_log(LOG_WARNING, "Invalid say type specified: %s\n", type);
133  }
134 
135  if (!filenames) {
136  return -1;
137  }
138 
139  files = ast_str_buffer(filenames);
140  snprintf(buf, len, "%s", files);
141  ast_free(filenames);
142 
143  return 0;
144 }
145 
146 static struct ast_custom_function sayfiles = {
147  .name = "SAYFILES",
148  .read = sayfile_exec,
149 };
150 
151 #ifdef TEST_FRAMEWORK
152 AST_TEST_DEFINE(test_SAYFILES_function)
153 {
155  struct ast_str *expr, *result;
156 
157  switch (cmd) {
158  case TEST_INIT:
159  info->name = "test_SAYFILES_function";
160  info->category = "/funcs/sayfiles/";
161  info->summary = "Test SAYFILES function substitution";
162  info->description =
163  "Executes a series of variable substitutions using the SAYFILES function and ensures that the expected results are received.";
164  return AST_TEST_NOT_RUN;
165  case TEST_EXECUTE:
166  break;
167  }
168 
169  ast_test_status_update(test, "Testing SAYFILES() substitution ...\n");
170 
171  if (!(expr = ast_str_create(16))) {
172  return AST_TEST_FAIL;
173  }
174  if (!(result = ast_str_create(16))) {
175  ast_free(expr);
176  return AST_TEST_FAIL;
177  }
178 
179  ast_str_set(&expr, 0, "${SAYFILES(hi Th3re,alpha)}");
181  if (strcmp(ast_str_buffer(result), "letters/h&letters/i&letters/space&letters/t&letters/h&digits/3&letters/r&letters/e") != 0) {
182  ast_test_status_update(test, "SAYFILES(hi Th3re,alpha) test failed ('%s')\n",
183  ast_str_buffer(result));
184  res = AST_TEST_FAIL;
185  }
186 
187  ast_str_set(&expr, 0, "${SAYFILES(phreak,phonetic)}");
189  if (strcmp(ast_str_buffer(result), "phonetic/p_p&phonetic/h_p&phonetic/r_p&phonetic/e_p&phonetic/a_p&phonetic/k_p") != 0) {
190  ast_test_status_update(test, "SAYFILES(phreak,phonetic) test failed ('%s')\n",
191  ast_str_buffer(result));
192  res = AST_TEST_FAIL;
193  }
194 
195  ast_str_set(&expr, 0, "${SAYFILES(35,digits)}");
197  if (strcmp(ast_str_buffer(result), "digits/3&digits/5") != 0) {
198  ast_test_status_update(test, "SAYFILES(35,digits) test failed ('%s')\n",
199  ast_str_buffer(result));
200  res = AST_TEST_FAIL;
201  }
202 
203  ast_str_set(&expr, 0, "${SAYFILES(35,number)}");
205  if (strcmp(ast_str_buffer(result), "digits/30&digits/5") != 0) {
206  ast_test_status_update(test, "SAYFILES(35,number) test failed ('%s')\n",
207  ast_str_buffer(result));
208  res = AST_TEST_FAIL;
209  }
210 
211  ast_str_set(&expr, 0, "${SAYFILES(1042,number)}");
213  if (strcmp(ast_str_buffer(result), "digits/1&digits/thousand&digits/40&digits/2") != 0) {
214  ast_test_status_update(test, "SAYFILES(1042,number) test failed ('%s')\n",
215  ast_str_buffer(result));
216  res = AST_TEST_FAIL;
217  }
218 
219  ast_str_set(&expr, 0, "${SAYFILES(0,number)}");
221  if (strcmp(ast_str_buffer(result), "digits/0") != 0) {
222  ast_test_status_update(test, "SAYFILES(0,digits) test failed ('%s')\n",
223  ast_str_buffer(result));
224  res = AST_TEST_FAIL;
225  }
226 
227  ast_str_set(&expr, 0, "${SAYFILES(0,money)}");
229  if (strcmp(ast_str_buffer(result), "digits/0&cents") != 0) {
230  ast_test_status_update(test, "SAYFILES(0,money) test failed ('%s')\n",
231  ast_str_buffer(result));
232  res = AST_TEST_FAIL;
233  }
234 
235  ast_str_set(&expr, 0, "${SAYFILES(0.01,money)}");
237  if (strcmp(ast_str_buffer(result), "digits/1&cent") != 0) {
238  ast_test_status_update(test, "SAYFILES(0.01,money) test failed ('%s')\n",
239  ast_str_buffer(result));
240  res = AST_TEST_FAIL;
241  }
242 
243  ast_str_set(&expr, 0, "${SAYFILES(0.42,money)}");
245  if (strcmp(ast_str_buffer(result), "digits/40&digits/2&cents") != 0) {
246  ast_test_status_update(test, "SAYFILES(0.42,money) test failed ('%s')\n",
247  ast_str_buffer(result));
248  res = AST_TEST_FAIL;
249  }
250 
251  ast_str_set(&expr, 0, "${SAYFILES(1.00,money)}");
253  if (strcmp(ast_str_buffer(result), "digits/1&letters/dollar") != 0) {
254  ast_test_status_update(test, "SAYFILES(1.00,money) test failed ('%s')\n",
255  ast_str_buffer(result));
256  res = AST_TEST_FAIL;
257  }
258 
259  ast_str_set(&expr, 0, "${SAYFILES(1.42,money)}");
261  if (strcmp(ast_str_buffer(result), "digits/1&letters/dollar_&and&digits/40&digits/2&cents") != 0) {
262  ast_test_status_update(test, "SAYFILES(1.42,money) test failed ('%s')\n",
263  ast_str_buffer(result));
264  res = AST_TEST_FAIL;
265  }
266 
267  ast_str_set(&expr, 0, "${SAYFILES(2.00,money)}");
269  if (strcmp(ast_str_buffer(result), "digits/2&dollars") != 0) {
270  ast_test_status_update(test, "SAYFILES(2.00,money) test failed ('%s')\n",
271  ast_str_buffer(result));
272  res = AST_TEST_FAIL;
273  }
274 
275  ast_str_set(&expr, 0, "${SAYFILES(2.42,money)}");
277  if (strcmp(ast_str_buffer(result), "digits/2&dollars&and&digits/40&digits/2&cents") != 0) {
278  ast_test_status_update(test, "SAYFILES(2.42,money) test failed ('%s')\n",
279  ast_str_buffer(result));
280  res = AST_TEST_FAIL;
281  }
282 
283  ast_free(expr);
284  ast_free(result);
285 
286  return res;
287 }
288 #endif
289 
290 static int unload_module(void)
291 {
292  AST_TEST_UNREGISTER(test_SAYFILES_function);
293  return ast_custom_function_unregister(&sayfiles);
294 }
295 
296 static int load_module(void)
297 {
298  AST_TEST_REGISTER(test_SAYFILES_function);
299  return ast_custom_function_register(&sayfiles);
300 }
301 
302 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Say application files");
const char * name
Definition: pbx.h:119
static const char type[]
Definition: chan_ooh323.c:109
Main Channel structure associated with a channel.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
static struct ast_custom_function sayfiles
static int unload_module(void)
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#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
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
struct ast_str * ast_get_number_str(int num, const char *lang)
Returns an ast_str of files for SayNumber playback.
Definition: say.c:525
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
Test Framework API.
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
struct ast_str * ast_get_phonetic_str(const char *str, const char *lang)
Returns an ast_str of files for SayPhonetic playback.
Definition: say.c:195
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
const char * args
#define NULL
Definition: resample.c:96
int value
Definition: syslog.c:37
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Utility functions.
struct ast_str * ast_get_money_str(const char *str, const char *lang)
Returns an ast_str of files for SayMoney playback.
Definition: say.c:419
AST_TEST_DEFINE(test_SAYFILES_function)
#define ast_strlen_zero(foo)
Definition: strings.h:52
Custom localtime functions for multiple timezones.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
#define ast_log
Definition: astobj2.c:42
General Asterisk PBX channel definitions.
struct ast_str * ast_get_digit_str(const char *str, const char *lang)
Returns an ast_str of files for SayDigits playback.
Definition: say.c:294
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
static int sayfile_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_sayfiles.c:89
Core PBX routines and definitions.
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
def info(msg)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define ast_free(a)
Definition: astmm.h:182
struct ast_str * ast_get_character_str(const char *str, const char *lang, enum ast_say_case_sensitivity sensitivity)
Returns an ast_str of files for SayAlpha playback.
Definition: say.c:63
static PGresult * result
Definition: cel_pgsql.c:88
const char * ast_channel_language(const struct ast_channel *chan)
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#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...
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508
ast_test_result_state
Definition: test.h:200
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
#define AST_APP_ARG(name)
Define an application argument.
static int load_module(void)