00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #include "asterisk.h"
00032
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 275105 $")
00034
00035 #include <math.h>
00036
00037 #include "asterisk/module.h"
00038 #include "asterisk/channel.h"
00039 #include "asterisk/pbx.h"
00040 #include "asterisk/utils.h"
00041 #include "asterisk/app.h"
00042 #include "asterisk/config.h"
00043 #include "asterisk/test.h"
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108 enum TypeOfFunctions {
00109 ADDFUNCTION,
00110 DIVIDEFUNCTION,
00111 MULTIPLYFUNCTION,
00112 SUBTRACTFUNCTION,
00113 MODULUSFUNCTION,
00114 POWFUNCTION,
00115 SHLEFTFUNCTION,
00116 SHRIGHTFUNCTION,
00117 BITWISEANDFUNCTION,
00118 BITWISEXORFUNCTION,
00119 BITWISEORFUNCTION,
00120 GTFUNCTION,
00121 LTFUNCTION,
00122 GTEFUNCTION,
00123 LTEFUNCTION,
00124 EQFUNCTION
00125 };
00126
00127 enum TypeOfResult {
00128 FLOAT_RESULT,
00129 INT_RESULT,
00130 HEX_RESULT,
00131 CHAR_RESULT
00132 };
00133
00134 static int math(struct ast_channel *chan, const char *cmd, char *parse,
00135 char *buf, size_t len)
00136 {
00137 double fnum1;
00138 double fnum2;
00139 double ftmp = 0;
00140 char *op;
00141 int iaction = -1;
00142 int type_of_result = FLOAT_RESULT;
00143 char *mvalue1, *mvalue2 = NULL, *mtype_of_result;
00144 int negvalue1 = 0;
00145 AST_DECLARE_APP_ARGS(args,
00146 AST_APP_ARG(argv0);
00147 AST_APP_ARG(argv1);
00148 );
00149
00150 if (ast_strlen_zero(parse)) {
00151 ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
00152 return -1;
00153 }
00154
00155 AST_STANDARD_APP_ARGS(args, parse);
00156
00157 if (args.argc < 1) {
00158 ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
00159 return -1;
00160 }
00161
00162 mvalue1 = args.argv0;
00163
00164 if (mvalue1[0] == '-') {
00165 negvalue1 = 1;
00166 mvalue1++;
00167 }
00168
00169 if ((op = strchr(mvalue1, '*'))) {
00170 iaction = MULTIPLYFUNCTION;
00171 *op = '\0';
00172 } else if ((op = strchr(mvalue1, '/'))) {
00173 iaction = DIVIDEFUNCTION;
00174 *op = '\0';
00175 } else if ((op = strchr(mvalue1, '%'))) {
00176 iaction = MODULUSFUNCTION;
00177 *op = '\0';
00178 } else if ((op = strchr(mvalue1, '^'))) {
00179 iaction = POWFUNCTION;
00180 *op = '\0';
00181 } else if ((op = strstr(mvalue1, "AND"))) {
00182 iaction = BITWISEANDFUNCTION;
00183 *op = '\0';
00184 op += 2;
00185 } else if ((op = strstr(mvalue1, "XOR"))) {
00186 iaction = BITWISEXORFUNCTION;
00187 *op = '\0';
00188 op += 2;
00189 } else if ((op = strstr(mvalue1, "OR"))) {
00190 iaction = BITWISEORFUNCTION;
00191 *op = '\0';
00192 ++op;
00193 } else if ((op = strchr(mvalue1, '>'))) {
00194 iaction = GTFUNCTION;
00195 *op = '\0';
00196 if (*(op + 1) == '=') {
00197 iaction = GTEFUNCTION;
00198 ++op;
00199 } else if (*(op + 1) == '>') {
00200 iaction = SHRIGHTFUNCTION;
00201 ++op;
00202 }
00203 } else if ((op = strchr(mvalue1, '<'))) {
00204 iaction = LTFUNCTION;
00205 *op = '\0';
00206 if (*(op + 1) == '=') {
00207 iaction = LTEFUNCTION;
00208 ++op;
00209 } else if (*(op + 1) == '<') {
00210 iaction = SHLEFTFUNCTION;
00211 ++op;
00212 }
00213 } else if ((op = strchr(mvalue1, '='))) {
00214 *op = '\0';
00215 if (*(op + 1) == '=') {
00216 iaction = EQFUNCTION;
00217 ++op;
00218 } else
00219 op = NULL;
00220 } else if ((op = strchr(mvalue1, '+'))) {
00221 iaction = ADDFUNCTION;
00222 *op = '\0';
00223 } else if ((op = strchr(mvalue1, '-'))) {
00224 iaction = SUBTRACTFUNCTION;
00225 *op = '\0';
00226 }
00227
00228 if (op)
00229 mvalue2 = op + 1;
00230
00231
00232 mtype_of_result = args.argv1;
00233 if (mtype_of_result) {
00234 if (!strcasecmp(mtype_of_result, "float")
00235 || !strcasecmp(mtype_of_result, "f"))
00236 type_of_result = FLOAT_RESULT;
00237 else if (!strcasecmp(mtype_of_result, "int")
00238 || !strcasecmp(mtype_of_result, "i"))
00239 type_of_result = INT_RESULT;
00240 else if (!strcasecmp(mtype_of_result, "hex")
00241 || !strcasecmp(mtype_of_result, "h"))
00242 type_of_result = HEX_RESULT;
00243 else if (!strcasecmp(mtype_of_result, "char")
00244 || !strcasecmp(mtype_of_result, "c"))
00245 type_of_result = CHAR_RESULT;
00246 else {
00247 ast_log(LOG_WARNING, "Unknown type of result requested '%s'.\n",
00248 mtype_of_result);
00249 return -1;
00250 }
00251 }
00252
00253 if (!mvalue1 || !mvalue2) {
00254 ast_log(LOG_WARNING,
00255 "Supply all the parameters - just this once, please\n");
00256 return -1;
00257 }
00258
00259 if (sscanf(mvalue1, "%30lf", &fnum1) != 1) {
00260 ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue1);
00261 return -1;
00262 }
00263
00264 if (sscanf(mvalue2, "%30lf", &fnum2) != 1) {
00265 ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue2);
00266 return -1;
00267 }
00268
00269 if (negvalue1)
00270 fnum1 = 0 - fnum1;
00271
00272 switch (iaction) {
00273 case ADDFUNCTION:
00274 ftmp = fnum1 + fnum2;
00275 break;
00276 case DIVIDEFUNCTION:
00277 if (fnum2 <= 0)
00278 ftmp = 0;
00279 else
00280 ftmp = (fnum1 / fnum2);
00281 break;
00282 case MULTIPLYFUNCTION:
00283 ftmp = (fnum1 * fnum2);
00284 break;
00285 case SUBTRACTFUNCTION:
00286 ftmp = (fnum1 - fnum2);
00287 break;
00288 case MODULUSFUNCTION:
00289 {
00290 int inum1 = fnum1;
00291 int inum2 = fnum2;
00292
00293 if (inum2 == 0) {
00294 ftmp = 0;
00295 } else {
00296 ftmp = (inum1 % inum2);
00297 }
00298
00299 break;
00300 }
00301 case POWFUNCTION:
00302 ftmp = pow(fnum1, fnum2);
00303 break;
00304 case SHLEFTFUNCTION:
00305 {
00306 int inum1 = fnum1;
00307 int inum2 = fnum2;
00308
00309 ftmp = (inum1 << inum2);
00310 break;
00311 }
00312 case SHRIGHTFUNCTION:
00313 {
00314 int inum1 = fnum1;
00315 int inum2 = fnum2;
00316
00317 ftmp = (inum1 >> inum2);
00318 break;
00319 }
00320 case BITWISEANDFUNCTION:
00321 {
00322 int inum1 = fnum1;
00323 int inum2 = fnum2;
00324 ftmp = (inum1 & inum2);
00325 break;
00326 }
00327 case BITWISEXORFUNCTION:
00328 {
00329 int inum1 = fnum1;
00330 int inum2 = fnum2;
00331 ftmp = (inum1 ^ inum2);
00332 break;
00333 }
00334 case BITWISEORFUNCTION:
00335 {
00336 int inum1 = fnum1;
00337 int inum2 = fnum2;
00338 ftmp = (inum1 | inum2);
00339 break;
00340 }
00341 case GTFUNCTION:
00342 ast_copy_string(buf, (fnum1 > fnum2) ? "TRUE" : "FALSE", len);
00343 break;
00344 case LTFUNCTION:
00345 ast_copy_string(buf, (fnum1 < fnum2) ? "TRUE" : "FALSE", len);
00346 break;
00347 case GTEFUNCTION:
00348 ast_copy_string(buf, (fnum1 >= fnum2) ? "TRUE" : "FALSE", len);
00349 break;
00350 case LTEFUNCTION:
00351 ast_copy_string(buf, (fnum1 <= fnum2) ? "TRUE" : "FALSE", len);
00352 break;
00353 case EQFUNCTION:
00354 ast_copy_string(buf, (fnum1 == fnum2) ? "TRUE" : "FALSE", len);
00355 break;
00356 default:
00357 ast_log(LOG_WARNING,
00358 "Something happened that neither of us should be proud of %d\n",
00359 iaction);
00360 return -1;
00361 }
00362
00363 if (iaction < GTFUNCTION || iaction > EQFUNCTION) {
00364 if (type_of_result == FLOAT_RESULT)
00365 snprintf(buf, len, "%f", ftmp);
00366 else if (type_of_result == INT_RESULT)
00367 snprintf(buf, len, "%i", (int) ftmp);
00368 else if (type_of_result == HEX_RESULT)
00369 snprintf(buf, len, "%x", (unsigned int) ftmp);
00370 else if (type_of_result == CHAR_RESULT)
00371 snprintf(buf, len, "%c", (unsigned char) ftmp);
00372 }
00373
00374 return 0;
00375 }
00376
00377 static int crement_function_read(struct ast_channel *chan, const char *cmd,
00378 char *data, char *buf, size_t len)
00379 {
00380 int ret = -1;
00381 int int_value = 0;
00382 int modify_orig = 0;
00383 const char *var;
00384 char endchar = 0, returnvar[12];
00385
00386 if (ast_strlen_zero(data)) {
00387 ast_log(LOG_WARNING, "Syntax: %s(<data>) - missing argument!\n", cmd);
00388 return -1;
00389 }
00390
00391 ast_channel_lock(chan);
00392
00393 if (!(var = pbx_builtin_getvar_helper(chan, data))) {
00394 ast_log(LOG_NOTICE, "Failed to obtain variable %s, bailing out\n", data);
00395 ast_channel_unlock(chan);
00396 return -1;
00397 }
00398
00399 if (ast_strlen_zero(var)) {
00400 ast_log(LOG_NOTICE, "Variable %s doesn't exist - are you sure you wrote it correctly?\n", data);
00401 ast_channel_unlock(chan);
00402 return -1;
00403 }
00404
00405 if (sscanf(var, "%30d%1c", &int_value, &endchar) == 0 || endchar != 0) {
00406 ast_log(LOG_NOTICE, "The content of ${%s} is not a numeric value - bailing out!\n", data);
00407 ast_channel_unlock(chan);
00408 return -1;
00409 }
00410
00411
00412 if (!strcasecmp(cmd, "INC")) {
00413 int_value++;
00414 modify_orig = 1;
00415 } else if (!strcasecmp(cmd, "DEC")) {
00416 int_value--;
00417 modify_orig = 1;
00418 }
00419
00420 ast_log(LOG_NOTICE, "The value is now: %d\n", int_value);
00421
00422 if (snprintf(returnvar, sizeof(returnvar), "%d", int_value) > 0) {
00423 pbx_builtin_setvar_helper(chan, data, returnvar);
00424 if (modify_orig) {
00425 ast_copy_string(buf, returnvar, len);
00426 }
00427 ret = 0;
00428 } else {
00429 pbx_builtin_setvar_helper(chan, data, "0");
00430 if (modify_orig) {
00431 ast_copy_string(buf, "0", len);
00432 }
00433 ast_log(LOG_NOTICE, "Variable %s refused to be %sREMENTED, setting value to 0", data, cmd);
00434 ret = 0;
00435 }
00436
00437 ast_channel_unlock(chan);
00438
00439 return ret;
00440 }
00441
00442
00443 static struct ast_custom_function math_function = {
00444 .name = "MATH",
00445 .read = math
00446 };
00447
00448 static struct ast_custom_function increment_function = {
00449 .name = "INC",
00450 .read = crement_function_read,
00451 };
00452
00453 static struct ast_custom_function decrement_function = {
00454 .name = "DEC",
00455 .read = crement_function_read,
00456 };
00457
00458 #ifdef TEST_FRAMEWORK
00459 AST_TEST_DEFINE(test_MATH_function)
00460 {
00461 enum ast_test_result_state res = AST_TEST_PASS;
00462 struct ast_str *expr, *result;
00463
00464 switch (cmd) {
00465 case TEST_INIT:
00466 info->name = "test_MATH_function";
00467 info->category = "/main/pbx/";
00468 info->summary = "Test MATH function substitution";
00469 info->description =
00470 "Executes a series of variable substitutions using the MATH function and ensures that the expected results are received.";
00471 return AST_TEST_NOT_RUN;
00472 case TEST_EXECUTE:
00473 break;
00474 }
00475
00476 ast_test_status_update(test, "Testing MATH() substitution ...\n");
00477
00478 if (!(expr = ast_str_create(16)) || !(result = ast_str_create(16))) {
00479 if (expr) {
00480 ast_free(expr);
00481 }
00482 if (result) {
00483 ast_free(result);
00484 }
00485 return AST_TEST_FAIL;
00486 }
00487
00488 ast_str_set(&expr, 0, "${MATH(170 AND 63,i)}");
00489 ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
00490 if (strcmp(ast_str_buffer(result), "42") != 0) {
00491 ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
00492 ast_str_buffer(result));
00493 res = AST_TEST_FAIL;
00494 }
00495
00496 ast_str_set(&expr, 0, "${MATH(170AND63,i)}");
00497 ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
00498 if (strcmp(ast_str_buffer(result), "42") != 0) {
00499 ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
00500 ast_str_buffer(result));
00501 res = AST_TEST_FAIL;
00502 }
00503
00504 return res;
00505 }
00506 #endif
00507
00508 static int unload_module(void)
00509 {
00510 int res = 0;
00511
00512 res |= ast_custom_function_unregister(&math_function);
00513 res |= ast_custom_function_unregister(&increment_function);
00514 res |= ast_custom_function_unregister(&decrement_function);
00515 AST_TEST_UNREGISTER(test_MATH_function);
00516
00517 return res;
00518 }
00519
00520 static int load_module(void)
00521 {
00522 int res = 0;
00523
00524 res |= ast_custom_function_register(&math_function);
00525 res |= ast_custom_function_register(&increment_function);
00526 res |= ast_custom_function_register(&decrement_function);
00527 AST_TEST_REGISTER(test_MATH_function);
00528
00529 return res;
00530 }
00531
00532 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");