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