Merge "add cmd connection creation on creation ooh323 call data structure"
[asterisk/asterisk.git] / funcs / func_math.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2004 - 2006, Andy Powell
5  *
6  * Updated by Mark Spencer <markster@digium.com>
7  * Updated by Nir Simionovich <nirs@greenfieldtech.net>
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 /*! \file
21  *
22  * \brief Math related dialplan function
23  *
24  * \author Andy Powell
25  * \author Mark Spencer <markster@digium.com>
26  * \author Nir Simionovich <nirs@greenfieldtech.net>
27  *
28  * \ingroup functions
29  */
30
31 /*** MODULEINFO
32         <support_level>core</support_level>
33  ***/
34
35 #include "asterisk.h"
36
37 #include <math.h>
38
39 #include "asterisk/module.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/utils.h"
43 #include "asterisk/app.h"
44 #include "asterisk/config.h"
45 #include "asterisk/test.h"
46
47 /*** DOCUMENTATION
48         <function name="MATH" language="en_US">
49                 <synopsis>
50                         Performs Mathematical Functions.
51                 </synopsis>
52                 <syntax>
53                         <parameter name="expression" required="true">
54                                 <para>Is of the form:
55                                 <replaceable>number1</replaceable><replaceable>op</replaceable><replaceable>number2</replaceable>
56                                 where the possible values for <replaceable>op</replaceable>
57                                 are:</para>
58                                 <para>+,-,/,*,%,&lt;&lt;,&gt;&gt;,^,AND,OR,XOR,&lt;,&gt;,&lt;=,&gt;=,== (and behave as their C equivalents)</para>
59                         </parameter>
60                         <parameter name="type">
61                                 <para>Wanted type of result:</para>
62                                 <para>f, float - float(default)</para>
63                                 <para>i, int - integer</para>
64                                 <para>h, hex - hex</para>
65                                 <para>c, char - char</para>
66                         </parameter>
67                 </syntax>
68                 <description>
69                         <para>Performs mathematical functions based on two parameters and an operator.  The returned
70                         value type is <replaceable>type</replaceable></para>
71                         <para>Example: Set(i=${MATH(123%16,int)}) - sets var i=11</para>
72                 </description>
73         </function>
74         <function name="INC" language="en_US">
75                 <synopsis>
76                         Increments the value of a variable, while returning the updated value to the dialplan
77                 </synopsis>
78                 <syntax>
79                         <parameter name="variable" required="true">
80                                 <para>
81                                 The variable name to be manipulated, without the braces.
82                                 </para>
83                         </parameter>
84                 </syntax>
85                 <description>
86                         <para>Increments the value of a variable, while returning the updated value to the dialplan</para>
87                         <para>Example: INC(MyVAR) - Increments MyVar</para>
88                         <para>Note: INC(${MyVAR}) - Is wrong, as INC expects the variable name, not its value</para>
89                 </description>
90         </function>
91         <function name="DEC" language="en_US">
92                 <synopsis>
93                         Decrements the value of a variable, while returning the updated value to the dialplan
94                 </synopsis>
95                 <syntax>
96                         <parameter name="variable" required="true">
97                                 <para>
98                                 The variable name to be manipulated, without the braces.
99                                 </para>
100                         </parameter>
101                 </syntax>
102                 <description>
103                         <para>Decrements the value of a variable, while returning the updated value to the dialplan</para>
104                         <para>Example: DEC(MyVAR) - Decrements MyVar</para>
105                         <para>Note: DEC(${MyVAR}) - Is wrong, as DEC expects the variable name, not its value</para>
106                 </description>
107         </function>
108  ***/
109
110 enum TypeOfFunctions {
111         ADDFUNCTION,
112         DIVIDEFUNCTION,
113         MULTIPLYFUNCTION,
114         SUBTRACTFUNCTION,
115         MODULUSFUNCTION,
116         POWFUNCTION,
117         SHLEFTFUNCTION,
118         SHRIGHTFUNCTION,
119         BITWISEANDFUNCTION,
120         BITWISEXORFUNCTION,
121         BITWISEORFUNCTION,
122         GTFUNCTION,
123         LTFUNCTION,
124         GTEFUNCTION,
125         LTEFUNCTION,
126         EQFUNCTION
127 };
128
129 enum TypeOfResult {
130         FLOAT_RESULT,
131         INT_RESULT,
132         HEX_RESULT,
133         CHAR_RESULT
134 };
135
136 static int math(struct ast_channel *chan, const char *cmd, char *parse,
137                 char *buf, size_t len)
138 {
139         double fnum1;
140         double fnum2;
141         double ftmp = 0;
142         char *op;
143         int iaction = -1;
144         int type_of_result = FLOAT_RESULT;
145         char *mvalue1, *mvalue2 = NULL, *mtype_of_result;
146         int negvalue1 = 0;
147         AST_DECLARE_APP_ARGS(args,
148                              AST_APP_ARG(argv0);
149                              AST_APP_ARG(argv1);
150         );
151
152         if (ast_strlen_zero(parse)) {
153                 ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
154                 return -1;
155         }
156
157         AST_STANDARD_APP_ARGS(args, parse);
158
159         if (args.argc < 1) {
160                 ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
161                 return -1;
162         }
163
164         mvalue1 = args.argv0;
165
166         if (mvalue1[0] == '-') {
167                 negvalue1 = 1;
168                 mvalue1++;
169         }
170
171         if ((op = strchr(mvalue1, '*'))) {
172                 iaction = MULTIPLYFUNCTION;
173                 *op = '\0';
174         } else if ((op = strchr(mvalue1, '/'))) {
175                 iaction = DIVIDEFUNCTION;
176                 *op = '\0';
177         } else if ((op = strchr(mvalue1, '%'))) {
178                 iaction = MODULUSFUNCTION;
179                 *op = '\0';
180         } else if ((op = strchr(mvalue1, '^'))) {
181                 iaction = POWFUNCTION;
182                 *op = '\0';
183         } else if ((op = strstr(mvalue1, "AND"))) {
184                 iaction = BITWISEANDFUNCTION;
185                 *op = '\0';
186                 op += 2;
187         } else if ((op = strstr(mvalue1, "XOR"))) {
188                 iaction = BITWISEXORFUNCTION;
189                 *op = '\0';
190                 op += 2;
191         } else if ((op = strstr(mvalue1, "OR"))) {
192                 iaction = BITWISEORFUNCTION;
193                 *op = '\0';
194                 ++op;
195         } else if ((op = strchr(mvalue1, '>'))) {
196                 iaction = GTFUNCTION;
197                 *op = '\0';
198                 if (*(op + 1) == '=') {
199                         iaction = GTEFUNCTION;
200                         ++op;
201                 } else if (*(op + 1) == '>') {
202                         iaction = SHRIGHTFUNCTION;
203                         ++op;
204                 }
205         } else if ((op = strchr(mvalue1, '<'))) {
206                 iaction = LTFUNCTION;
207                 *op = '\0';
208                 if (*(op + 1) == '=') {
209                         iaction = LTEFUNCTION;
210                         ++op;
211                 } else if (*(op + 1) == '<') {
212                         iaction = SHLEFTFUNCTION;
213                         ++op;
214                 }
215         } else if ((op = strchr(mvalue1, '='))) {
216                 *op = '\0';
217                 if (*(op + 1) == '=') {
218                         iaction = EQFUNCTION;
219                         ++op;
220                 } else
221                         op = NULL;
222         } else if ((op = strchr(mvalue1, '+'))) {
223                 iaction = ADDFUNCTION;
224                 *op = '\0';
225         } else if ((op = strchr(mvalue1, '-'))) { /* subtraction MUST always be last, in case we have a negative second number */
226                 iaction = SUBTRACTFUNCTION;
227                 *op = '\0';
228         }
229
230         if (op)
231                 mvalue2 = op + 1;
232
233         /* detect wanted type of result */
234         mtype_of_result = args.argv1;
235         if (mtype_of_result) {
236                 if (!strcasecmp(mtype_of_result, "float")
237                     || !strcasecmp(mtype_of_result, "f"))
238                         type_of_result = FLOAT_RESULT;
239                 else if (!strcasecmp(mtype_of_result, "int")
240                          || !strcasecmp(mtype_of_result, "i"))
241                         type_of_result = INT_RESULT;
242                 else if (!strcasecmp(mtype_of_result, "hex")
243                          || !strcasecmp(mtype_of_result, "h"))
244                         type_of_result = HEX_RESULT;
245                 else if (!strcasecmp(mtype_of_result, "char")
246                          || !strcasecmp(mtype_of_result, "c"))
247                         type_of_result = CHAR_RESULT;
248                 else {
249                         ast_log(LOG_WARNING, "Unknown type of result requested '%s'.\n",
250                                         mtype_of_result);
251                         return -1;
252                 }
253         }
254
255         if (!mvalue2) {
256                 ast_log(LOG_WARNING,
257                                 "Supply all the parameters - just this once, please\n");
258                 return -1;
259         }
260
261         if (sscanf(mvalue1, "%30lf", &fnum1) != 1) {
262                 ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue1);
263                 return -1;
264         }
265
266         if (sscanf(mvalue2, "%30lf", &fnum2) != 1) {
267                 ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue2);
268                 return -1;
269         }
270
271         if (negvalue1)
272                 fnum1 = 0 - fnum1;
273
274         switch (iaction) {
275         case ADDFUNCTION:
276                 ftmp = fnum1 + fnum2;
277                 break;
278         case DIVIDEFUNCTION:
279                 if (fnum2 <= 0)
280                         ftmp = 0;                       /* can't do a divide by 0 */
281                 else
282                         ftmp = (fnum1 / fnum2);
283                 break;
284         case MULTIPLYFUNCTION:
285                 ftmp = (fnum1 * fnum2);
286                 break;
287         case SUBTRACTFUNCTION:
288                 ftmp = (fnum1 - fnum2);
289                 break;
290         case MODULUSFUNCTION:
291                 {
292                         int inum1 = fnum1;
293                         int inum2 = fnum2;
294
295                         if (inum2 == 0) {
296                                 ftmp = 0;
297                         } else {
298                                 ftmp = (inum1 % inum2);
299                         }
300
301                         break;
302                 }
303         case POWFUNCTION:
304                 ftmp = pow(fnum1, fnum2);
305                 break;
306         case SHLEFTFUNCTION:
307                 {
308                         int inum1 = fnum1;
309                         int inum2 = fnum2;
310
311                         ftmp = (inum1 << inum2);
312                         break;
313                 }
314         case SHRIGHTFUNCTION:
315                 {
316                         int inum1 = fnum1;
317                         int inum2 = fnum2;
318
319                         ftmp = (inum1 >> inum2);
320                         break;
321                 }
322         case BITWISEANDFUNCTION:
323                 {
324                         int inum1 = fnum1;
325                         int inum2 = fnum2;
326                         ftmp = (inum1 & inum2);
327                         break;
328                 }
329         case BITWISEXORFUNCTION:
330                 {
331                         int inum1 = fnum1;
332                         int inum2 = fnum2;
333                         ftmp = (inum1 ^ inum2);
334                         break;
335                 }
336         case BITWISEORFUNCTION:
337                 {
338                         int inum1 = fnum1;
339                         int inum2 = fnum2;
340                         ftmp = (inum1 | inum2);
341                         break;
342                 }
343         case GTFUNCTION:
344                 ast_copy_string(buf, (fnum1 > fnum2) ? "TRUE" : "FALSE", len);
345                 break;
346         case LTFUNCTION:
347                 ast_copy_string(buf, (fnum1 < fnum2) ? "TRUE" : "FALSE", len);
348                 break;
349         case GTEFUNCTION:
350                 ast_copy_string(buf, (fnum1 >= fnum2) ? "TRUE" : "FALSE", len);
351                 break;
352         case LTEFUNCTION:
353                 ast_copy_string(buf, (fnum1 <= fnum2) ? "TRUE" : "FALSE", len);
354                 break;
355         case EQFUNCTION:
356                 ast_copy_string(buf, (fnum1 == fnum2) ? "TRUE" : "FALSE", len);
357                 break;
358         default:
359                 ast_log(LOG_WARNING,
360                                 "Something happened that neither of us should be proud of %d\n",
361                                 iaction);
362                 return -1;
363         }
364
365         if (iaction < GTFUNCTION || iaction > EQFUNCTION) {
366                 if (type_of_result == FLOAT_RESULT)
367                         snprintf(buf, len, "%f", ftmp);
368                 else if (type_of_result == INT_RESULT)
369                         snprintf(buf, len, "%i", (int) ftmp);
370                 else if (type_of_result == HEX_RESULT)
371                         snprintf(buf, len, "%x", (unsigned int) ftmp);
372                 else if (type_of_result == CHAR_RESULT)
373                         snprintf(buf, len, "%c", (unsigned char) ftmp);
374         }
375
376         return 0;
377 }
378
379 static int crement_function_read(struct ast_channel *chan, const char *cmd,
380                      char *data, char *buf, size_t len)
381 {
382         int ret = -1;
383         int int_value = 0;
384         int modify_orig = 0;
385         const char *var;
386         char endchar = 0, returnvar[12]; /* If you need a variable longer than 11 digits - something is way wrong */
387
388         if (ast_strlen_zero(data)) {
389                 ast_log(LOG_WARNING, "Syntax: %s(<data>) - missing argument!\n", cmd);
390                 return -1;
391         }
392
393         if (!chan) {
394                 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
395                 return -1;
396         }
397
398         ast_channel_lock(chan);
399
400         if (!(var = pbx_builtin_getvar_helper(chan, data))) {
401                 ast_log(LOG_NOTICE, "Failed to obtain variable %s, bailing out\n", data);
402                 ast_channel_unlock(chan);
403                 return -1;
404         }
405
406         if (ast_strlen_zero(var)) {
407                 ast_log(LOG_NOTICE, "Variable %s doesn't exist - are you sure you wrote it correctly?\n", data);
408                 ast_channel_unlock(chan);
409                 return -1;
410         }
411
412         if (sscanf(var, "%30d%1c", &int_value, &endchar) == 0 || endchar != 0) {
413                 ast_log(LOG_NOTICE, "The content of ${%s} is not a numeric value - bailing out!\n", data);
414                 ast_channel_unlock(chan);
415                 return -1;
416         }
417
418         /* now we'll actually do something useful */
419         if (!strcasecmp(cmd, "INC")) {              /* Increment variable */
420                 int_value++;
421                 modify_orig = 1;
422         } else if (!strcasecmp(cmd, "DEC")) {       /* Decrement variable */
423                 int_value--;
424                 modify_orig = 1;
425         }
426
427         if (snprintf(returnvar, sizeof(returnvar), "%d", int_value) > 0) {
428                 pbx_builtin_setvar_helper(chan, data, returnvar);
429                 if (modify_orig) {
430                         ast_copy_string(buf, returnvar, len);
431                 }
432                 ret = 0;
433         } else {
434                 pbx_builtin_setvar_helper(chan, data, "0");
435                 if (modify_orig) {
436                         ast_copy_string(buf, "0", len);
437                 }
438                 ast_log(LOG_NOTICE, "Variable %s refused to be %sREMENTED, setting value to 0", data, cmd);
439                 ret = 0;
440         }
441
442         ast_channel_unlock(chan);
443
444         return ret;
445 }
446
447
448 static struct ast_custom_function math_function = {
449         .name = "MATH",
450         .read = math
451 };
452
453 static struct ast_custom_function increment_function = {
454         .name = "INC",
455         .read = crement_function_read,
456 };
457
458 static struct ast_custom_function decrement_function = {
459         .name = "DEC",
460         .read = crement_function_read,
461 };
462
463 #ifdef TEST_FRAMEWORK
464 AST_TEST_DEFINE(test_MATH_function)
465 {
466         enum ast_test_result_state res = AST_TEST_PASS;
467         struct ast_str *expr, *result;
468
469         switch (cmd) {
470         case TEST_INIT:
471                 info->name = "test_MATH_function";
472                 info->category = "/main/pbx/";
473                 info->summary = "Test MATH function substitution";
474                 info->description =
475                         "Executes a series of variable substitutions using the MATH function and ensures that the expected results are received.";
476                 return AST_TEST_NOT_RUN;
477         case TEST_EXECUTE:
478                 break;
479         }
480
481         ast_test_status_update(test, "Testing MATH() substitution ...\n");
482
483         if (!(expr = ast_str_create(16))) {
484                 return AST_TEST_FAIL;
485         }
486         if (!(result = ast_str_create(16))) {
487                 ast_free(expr);
488                 return AST_TEST_FAIL;
489         }
490
491         ast_str_set(&expr, 0, "${MATH(170 AND 63,i)}");
492         ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
493         if (strcmp(ast_str_buffer(result), "42") != 0) {
494                 ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
495                                 ast_str_buffer(result));
496                 res = AST_TEST_FAIL;
497         }
498
499         ast_str_set(&expr, 0, "${MATH(170AND63,i)}");
500         ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
501         if (strcmp(ast_str_buffer(result), "42") != 0) {
502                 ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
503                                 ast_str_buffer(result));
504                 res = AST_TEST_FAIL;
505         }
506
507         ast_free(expr);
508         ast_free(result);
509
510         return res;
511 }
512 #endif
513
514 static int unload_module(void)
515 {
516         int res = 0;
517
518         res |= ast_custom_function_unregister(&math_function);
519         res |= ast_custom_function_unregister(&increment_function);
520         res |= ast_custom_function_unregister(&decrement_function);
521         AST_TEST_UNREGISTER(test_MATH_function);
522
523         return res;
524 }
525
526 static int load_module(void)
527 {
528         int res = 0;
529
530         res |= ast_custom_function_register(&math_function);
531         res |= ast_custom_function_register(&increment_function);
532         res |= ast_custom_function_register(&decrement_function);
533         AST_TEST_REGISTER(test_MATH_function);
534
535         return res;
536 }
537
538 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");