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 #include "asterisk.h"
00033
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 368738 $")
00035
00036 #include <regex.h>
00037 #include <ctype.h>
00038
00039 #include "asterisk/module.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/utils.h"
00043 #include "asterisk/app.h"
00044 #include "asterisk/localtime.h"
00045 #include "asterisk/test.h"
00046
00047 AST_THREADSTORAGE(result_buf);
00048 AST_THREADSTORAGE(tmp_buf);
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
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420 static int function_fieldqty_helper(struct ast_channel *chan, const char *cmd,
00421 char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
00422 {
00423 char *varsubst;
00424 struct ast_str *str = ast_str_thread_get(&result_buf, 16);
00425 int fieldcount = 0;
00426 AST_DECLARE_APP_ARGS(args,
00427 AST_APP_ARG(varname);
00428 AST_APP_ARG(delim);
00429 );
00430 char delim[2] = "";
00431 size_t delim_used;
00432
00433 if (!str) {
00434 return -1;
00435 }
00436
00437 AST_STANDARD_APP_ARGS(args, parse);
00438 if (args.delim) {
00439 ast_get_encoded_char(args.delim, delim, &delim_used);
00440
00441 varsubst = alloca(strlen(args.varname) + 4);
00442
00443 sprintf(varsubst, "${%s}", args.varname);
00444 ast_str_substitute_variables(&str, 0, chan, varsubst);
00445 if (ast_str_strlen(str) == 0) {
00446 fieldcount = 0;
00447 } else {
00448 char *varval = ast_str_buffer(str);
00449 while (strsep(&varval, delim)) {
00450 fieldcount++;
00451 }
00452 }
00453 } else {
00454 fieldcount = 1;
00455 }
00456 if (sbuf) {
00457 ast_str_set(sbuf, len, "%d", fieldcount);
00458 } else {
00459 snprintf(buf, len, "%d", fieldcount);
00460 }
00461
00462 return 0;
00463 }
00464
00465 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
00466 char *parse, char *buf, size_t len)
00467 {
00468 return function_fieldqty_helper(chan, cmd, parse, buf, NULL, len);
00469 }
00470
00471 static int function_fieldqty_str(struct ast_channel *chan, const char *cmd,
00472 char *parse, struct ast_str **buf, ssize_t len)
00473 {
00474 return function_fieldqty_helper(chan, cmd, parse, NULL, buf, len);
00475 }
00476
00477 static struct ast_custom_function fieldqty_function = {
00478 .name = "FIELDQTY",
00479 .read = function_fieldqty,
00480 .read2 = function_fieldqty_str,
00481 };
00482
00483 static int function_fieldnum_helper(struct ast_channel *chan, const char *cmd,
00484 char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
00485 {
00486 char *varsubst, *field;
00487 struct ast_str *str = ast_str_thread_get(&result_buf, 16);
00488 int fieldindex = 0, res = 0;
00489 AST_DECLARE_APP_ARGS(args,
00490 AST_APP_ARG(varname);
00491 AST_APP_ARG(delim);
00492 AST_APP_ARG(field);
00493 );
00494 char delim[2] = "";
00495 size_t delim_used;
00496
00497 if (!str) {
00498 return -1;
00499 }
00500
00501 AST_STANDARD_APP_ARGS(args, parse);
00502
00503 if (args.argc < 3) {
00504 ast_log(LOG_ERROR, "Usage: FIELDNUM(<listname>,<delimiter>,<fieldvalue>)\n");
00505 res = -1;
00506 } else {
00507 varsubst = alloca(strlen(args.varname) + 4);
00508 sprintf(varsubst, "${%s}", args.varname);
00509
00510 ast_str_substitute_variables(&str, 0, chan, varsubst);
00511
00512 if (ast_str_strlen(str) == 0 || ast_strlen_zero(args.delim)) {
00513 fieldindex = 0;
00514 } else if (ast_get_encoded_char(args.delim, delim, &delim_used) == -1) {
00515 res = -1;
00516 } else {
00517 char *varval = ast_str_buffer(str);
00518
00519 while ((field = strsep(&varval, delim)) != NULL) {
00520 fieldindex++;
00521
00522 if (!strcasecmp(field, args.field)) {
00523 break;
00524 }
00525 }
00526
00527 if (!field) {
00528 fieldindex = 0;
00529 }
00530
00531 res = 0;
00532 }
00533 }
00534
00535 if (sbuf) {
00536 ast_str_set(sbuf, len, "%d", fieldindex);
00537 } else {
00538 snprintf(buf, len, "%d", fieldindex);
00539 }
00540
00541 return res;
00542 }
00543
00544 static int function_fieldnum(struct ast_channel *chan, const char *cmd,
00545 char *parse, char *buf, size_t len)
00546 {
00547 return function_fieldnum_helper(chan, cmd, parse, buf, NULL, len);
00548 }
00549
00550 static int function_fieldnum_str(struct ast_channel *chan, const char *cmd,
00551 char *parse, struct ast_str **buf, ssize_t len)
00552 {
00553 return function_fieldnum_helper(chan, cmd, parse, NULL, buf, len);
00554 }
00555
00556 static struct ast_custom_function fieldnum_function = {
00557 .name = "FIELDNUM",
00558 .read = function_fieldnum,
00559 .read2 = function_fieldnum_str,
00560 };
00561
00562 static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, struct ast_str **bufstr, ssize_t len)
00563 {
00564 AST_DECLARE_APP_ARGS(args,
00565 AST_APP_ARG(listname);
00566 AST_APP_ARG(delimiter);
00567 AST_APP_ARG(fieldvalue);
00568 );
00569 struct ast_str *orig_list = ast_str_thread_get(&tmp_buf, 16);
00570 const char *begin, *cur, *next;
00571 int dlen, flen, first = 1;
00572 struct ast_str *result, **result_ptr = &result;
00573 char *delim, *varsubst;
00574
00575 AST_STANDARD_APP_ARGS(args, parse);
00576
00577 if (buf) {
00578 if (!(result = ast_str_thread_get(&result_buf, 16))) {
00579 return -1;
00580 }
00581 } else {
00582
00583 result_ptr = bufstr;
00584 }
00585
00586 if (args.argc < 3) {
00587 ast_log(LOG_ERROR, "Usage: LISTFILTER(<listname>,<delimiter>,<fieldvalue>)\n");
00588 return -1;
00589 }
00590
00591 varsubst = alloca(strlen(args.listname) + 4);
00592 sprintf(varsubst, "${%s}", args.listname);
00593
00594
00595 if (chan) {
00596 ast_channel_lock(chan);
00597 }
00598 ast_str_substitute_variables(&orig_list, 0, chan, varsubst);
00599 if (!ast_str_strlen(orig_list)) {
00600 ast_log(LOG_ERROR, "List variable '%s' not found\n", args.listname);
00601 if (chan) {
00602 ast_channel_unlock(chan);
00603 }
00604 return -1;
00605 }
00606
00607
00608 if (!strstr(ast_str_buffer(orig_list), args.fieldvalue)) {
00609 if (buf) {
00610 ast_copy_string(buf, ast_str_buffer(orig_list), len);
00611 } else {
00612 ast_str_set(result_ptr, len, "%s", ast_str_buffer(orig_list));
00613 }
00614 if (chan) {
00615 ast_channel_unlock(chan);
00616 }
00617 return 0;
00618 }
00619
00620 dlen = strlen(args.delimiter);
00621 delim = alloca(dlen + 1);
00622 ast_get_encoded_str(args.delimiter, delim, dlen + 1);
00623
00624 if ((dlen = strlen(delim)) == 0) {
00625 delim = ",";
00626 dlen = 1;
00627 }
00628
00629 flen = strlen(args.fieldvalue);
00630
00631 ast_str_reset(*result_ptr);
00632
00633 if (len > -1) {
00634 ast_str_make_space(result_ptr, len ? len : ast_str_strlen(orig_list) + 1);
00635 }
00636
00637 begin = ast_str_buffer(orig_list);
00638 next = strstr(begin, delim);
00639
00640 do {
00641
00642 if (next) {
00643 cur = next;
00644 next = strstr(cur + dlen, delim);
00645 } else {
00646 cur = strchr(begin + dlen, '\0');
00647 }
00648
00649 if (flen == cur - begin && !strncmp(begin, args.fieldvalue, flen)) {
00650
00651 begin += flen + dlen;
00652 } else {
00653
00654 if (!first) {
00655 ast_str_append(result_ptr, len, "%s", delim);
00656 }
00657
00658 ast_str_append_substr(result_ptr, len, begin, cur - begin);
00659 first = 0;
00660 begin = cur + dlen;
00661 }
00662 } while (*cur != '\0');
00663 if (chan) {
00664 ast_channel_unlock(chan);
00665 }
00666
00667 if (buf) {
00668 ast_copy_string(buf, ast_str_buffer(result), len);
00669 }
00670
00671 return 0;
00672 }
00673
00674 static int listfilter_read(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
00675 {
00676 return listfilter(chan, cmd, parse, buf, NULL, len);
00677 }
00678
00679 static int listfilter_read2(struct ast_channel *chan, const char *cmd, char *parse, struct ast_str **buf, ssize_t len)
00680 {
00681 return listfilter(chan, cmd, parse, NULL, buf, len);
00682 }
00683
00684 static struct ast_custom_function listfilter_function = {
00685 .name = "LISTFILTER",
00686 .read = listfilter_read,
00687 .read2 = listfilter_read2,
00688 };
00689
00690 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
00691 size_t len)
00692 {
00693 AST_DECLARE_APP_ARGS(args,
00694 AST_APP_ARG(allowed);
00695 AST_APP_ARG(string);
00696 );
00697 char *outbuf = buf;
00698 unsigned char ac;
00699 char allowed[256] = "";
00700 size_t allowedlen = 0;
00701 int32_t bitfield[8] = { 0, };
00702
00703 AST_STANDARD_RAW_ARGS(args, parse);
00704
00705 if (!args.string) {
00706 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
00707 return -1;
00708 }
00709
00710 if (args.allowed[0] == '"' && !ast_opt_dont_warn) {
00711 ast_log(LOG_WARNING, "FILTER allowed characters includes the quote (\") character. This may not be what you want.\n");
00712 }
00713
00714
00715 for (; *(args.allowed);) {
00716 char c1 = 0, c2 = 0;
00717 size_t consumed = 0;
00718
00719 if (ast_get_encoded_char(args.allowed, &c1, &consumed))
00720 return -1;
00721 args.allowed += consumed;
00722
00723 if (*(args.allowed) == '-') {
00724 if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
00725 c2 = c1;
00726 args.allowed += consumed + 1;
00727
00728 if ((unsigned char) c2 < (unsigned char) c1 && !ast_opt_dont_warn) {
00729 ast_log(LOG_WARNING, "Range wrapping in FILTER(%s,%s). This may not be what you want.\n", parse, args.string);
00730 }
00731
00732
00733
00734
00735
00736 for (ac = (unsigned char) c1; ac != (unsigned char) c2; ac++) {
00737 bitfield[ac / 32] |= 1 << (ac % 32);
00738 }
00739 bitfield[ac / 32] |= 1 << (ac % 32);
00740
00741 ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
00742 } else {
00743 ac = (unsigned char) c1;
00744 ast_debug(4, "c1=%d, consumed=%d, args.allowed=%s\n", c1, (int) consumed, args.allowed - consumed);
00745 bitfield[ac / 32] |= 1 << (ac % 32);
00746 }
00747 }
00748
00749 for (ac = 1; ac != 0; ac++) {
00750 if (bitfield[ac / 32] & (1 << (ac % 32))) {
00751 allowed[allowedlen++] = ac;
00752 }
00753 }
00754
00755 ast_debug(1, "Allowed: %s\n", allowed);
00756
00757 for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
00758 if (strchr(allowed, *(args.string)))
00759 *outbuf++ = *(args.string);
00760 }
00761 *outbuf = '\0';
00762
00763 return 0;
00764 }
00765
00766 static struct ast_custom_function filter_function = {
00767 .name = "FILTER",
00768 .read = filter,
00769 };
00770
00771 static int replace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00772 {
00773 AST_DECLARE_APP_ARGS(args,
00774 AST_APP_ARG(varname);
00775 AST_APP_ARG(find);
00776 AST_APP_ARG(replace);
00777 );
00778 char *strptr, *varsubst;
00779 struct ast_str *str = ast_str_thread_get(&result_buf, 16);
00780 char find[256];
00781 char replace[2] = "";
00782 size_t unused;
00783
00784 AST_STANDARD_APP_ARGS(args, data);
00785
00786 if (!str) {
00787 return -1;
00788 }
00789
00790 if (args.argc < 2) {
00791 ast_log(LOG_ERROR, "Usage: %s(<varname>,<search-chars>[,<replace-char>])\n", cmd);
00792 return -1;
00793 }
00794
00795
00796 ast_get_encoded_str(args.find, find, sizeof(find));
00797 ast_get_encoded_char(args.replace, replace, &unused);
00798
00799 if (ast_strlen_zero(find) || ast_strlen_zero(args.varname)) {
00800 ast_log(LOG_ERROR, "The characters to search for and the variable name must not be empty.\n");
00801 return -1;
00802 }
00803
00804 varsubst = alloca(strlen(args.varname) + 4);
00805 sprintf(varsubst, "${%s}", args.varname);
00806 ast_str_substitute_variables(&str, 0, chan, varsubst);
00807
00808 if (!ast_str_strlen(str)) {
00809
00810 return -1;
00811 }
00812
00813 ast_debug(3, "String to search: (%s)\n", ast_str_buffer(str));
00814 ast_debug(3, "Characters to find: (%s)\n", find);
00815 ast_debug(3, "Character to replace with: (%s)\n", replace);
00816
00817 for (strptr = ast_str_buffer(str); *strptr; strptr++) {
00818
00819
00820 if (strchr(find, *strptr)) {
00821 if (ast_strlen_zero(replace)) {
00822
00823 strcpy(strptr, strptr + 1);
00824 strptr--;
00825 } else {
00826
00827 *strptr = *replace;
00828 }
00829 }
00830 }
00831
00832 ast_str_set(buf, len, "%s", ast_str_buffer(str));
00833 return 0;
00834 }
00835
00836 static struct ast_custom_function replace_function = {
00837 .name = "REPLACE",
00838 .read2 = replace,
00839 };
00840
00841 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
00842 size_t len)
00843 {
00844 AST_DECLARE_APP_ARGS(args,
00845 AST_APP_ARG(null);
00846 AST_APP_ARG(reg);
00847 AST_APP_ARG(str);
00848 );
00849 int errcode;
00850 regex_t regexbuf;
00851
00852 buf[0] = '\0';
00853
00854 AST_NONSTANDARD_APP_ARGS(args, parse, '"');
00855
00856 if (args.argc != 3) {
00857 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
00858 return -1;
00859 }
00860 if ((*args.str == ' ') || (*args.str == '\t'))
00861 args.str++;
00862
00863 ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
00864
00865 if ((errcode = regcomp(®exbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
00866 regerror(errcode, ®exbuf, buf, len);
00867 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
00868 return -1;
00869 }
00870
00871 strcpy(buf, regexec(®exbuf, args.str, 0, NULL, 0) ? "0" : "1");
00872
00873 regfree(®exbuf);
00874
00875 return 0;
00876 }
00877
00878 static struct ast_custom_function regex_function = {
00879 .name = "REGEX",
00880 .read = regex,
00881 };
00882
00883 #define HASH_PREFIX "~HASH~%s~"
00884 #define HASH_FORMAT HASH_PREFIX "%s~"
00885
00886 static char *app_clearhash = "ClearHash";
00887
00888
00889 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
00890 {
00891 struct ast_var_t *var;
00892 int len = strlen(prefix);
00893 AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
00894 if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
00895 AST_LIST_REMOVE_CURRENT(entries);
00896 ast_free(var);
00897 }
00898 }
00899 AST_LIST_TRAVERSE_SAFE_END
00900 }
00901
00902 static int exec_clearhash(struct ast_channel *chan, const char *data)
00903 {
00904 char prefix[80];
00905 snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
00906 clearvar_prefix(chan, prefix);
00907 return 0;
00908 }
00909
00910 static int array(struct ast_channel *chan, const char *cmd, char *var,
00911 const char *value)
00912 {
00913 AST_DECLARE_APP_ARGS(arg1,
00914 AST_APP_ARG(var)[100];
00915 );
00916 AST_DECLARE_APP_ARGS(arg2,
00917 AST_APP_ARG(val)[100];
00918 );
00919 char *origvar = "", *value2, varname[256];
00920 int i, ishash = 0;
00921
00922 value2 = ast_strdupa(value);
00923 if (!var || !value2)
00924 return -1;
00925
00926 if (!strcmp(cmd, "HASH")) {
00927 const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
00928 origvar = var;
00929 if (var2)
00930 var = ast_strdupa(var2);
00931 else {
00932 if (chan)
00933 ast_autoservice_stop(chan);
00934 return -1;
00935 }
00936 ishash = 1;
00937 }
00938
00939
00940
00941
00942
00943
00944
00945 ast_debug(1, "array (%s=%s)\n", var, S_OR(value2, ""));
00946 AST_STANDARD_APP_ARGS(arg1, var);
00947
00948 AST_STANDARD_APP_ARGS(arg2, value2);
00949
00950 for (i = 0; i < arg1.argc; i++) {
00951 ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
00952 S_OR(arg2.val[i], ""));
00953 if (i < arg2.argc) {
00954 if (ishash) {
00955 if (origvar[0] == '_') {
00956 if (origvar[1] == '_') {
00957 snprintf(varname, sizeof(varname), "__" HASH_FORMAT, origvar + 2, arg1.var[i]);
00958 } else {
00959 snprintf(varname, sizeof(varname), "_" HASH_FORMAT, origvar + 1, arg1.var[i]);
00960 }
00961 } else {
00962 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
00963 }
00964
00965 pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
00966 } else {
00967 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
00968 }
00969 } else {
00970
00971
00972 if (ishash) {
00973 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
00974 pbx_builtin_setvar_helper(chan, varname, "");
00975 } else {
00976 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
00977 }
00978 }
00979 }
00980
00981 return 0;
00982 }
00983
00984 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00985 {
00986 struct ast_var_t *newvar;
00987 struct ast_str *prefix = ast_str_alloca(80);
00988
00989 ast_str_set(&prefix, -1, HASH_PREFIX, data);
00990 memset(buf, 0, len);
00991
00992 AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
00993 if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
00994
00995 strncat(buf, ast_var_name(newvar) + ast_str_strlen(prefix), len - strlen(buf) - 1);
00996
00997 buf[strlen(buf) - 1] = ',';
00998 }
00999 }
01000
01001 buf[strlen(buf) - 1] = '\0';
01002 return 0;
01003 }
01004
01005 static int hashkeys_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01006 {
01007 struct ast_var_t *newvar;
01008 struct ast_str *prefix = ast_str_alloca(80);
01009 char *tmp;
01010
01011 ast_str_set(&prefix, -1, HASH_PREFIX, data);
01012
01013 AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
01014 if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
01015
01016 ast_str_append(buf, len, "%s", ast_var_name(newvar) + ast_str_strlen(prefix));
01017
01018 tmp = ast_str_buffer(*buf);
01019 tmp[ast_str_strlen(*buf) - 1] = ',';
01020 }
01021 }
01022
01023 tmp = ast_str_buffer(*buf);
01024 tmp[ast_str_strlen(*buf) - 1] = '\0';
01025 return 0;
01026 }
01027
01028 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
01029 {
01030 char varname[256];
01031 AST_DECLARE_APP_ARGS(arg,
01032 AST_APP_ARG(hashname);
01033 AST_APP_ARG(hashkey);
01034 );
01035
01036 if (!strchr(var, ',')) {
01037
01038 return array(chan, "HASH", var, value);
01039 }
01040
01041 AST_STANDARD_APP_ARGS(arg, var);
01042 if (arg.hashname[0] == '_') {
01043 if (arg.hashname[1] == '_') {
01044 snprintf(varname, sizeof(varname), "__" HASH_FORMAT, arg.hashname + 2, arg.hashkey);
01045 } else {
01046 snprintf(varname, sizeof(varname), "_" HASH_FORMAT, arg.hashname + 1, arg.hashkey);
01047 }
01048 } else {
01049 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
01050 }
01051 pbx_builtin_setvar_helper(chan, varname, value);
01052
01053 return 0;
01054 }
01055
01056 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01057 {
01058 char varname[256];
01059 const char *varvalue;
01060 AST_DECLARE_APP_ARGS(arg,
01061 AST_APP_ARG(hashname);
01062 AST_APP_ARG(hashkey);
01063 );
01064
01065 AST_STANDARD_APP_ARGS(arg, data);
01066 if (arg.argc == 2) {
01067 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
01068 varvalue = pbx_builtin_getvar_helper(chan, varname);
01069 if (varvalue)
01070 ast_copy_string(buf, varvalue, len);
01071 else
01072 *buf = '\0';
01073 } else if (arg.argc == 1) {
01074 char colnames[4096];
01075 int i;
01076 AST_DECLARE_APP_ARGS(arg2,
01077 AST_APP_ARG(col)[100];
01078 );
01079
01080
01081 hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
01082 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
01083
01084 AST_STANDARD_APP_ARGS(arg2, colnames);
01085 *buf = '\0';
01086
01087
01088 for (i = 0; i < arg2.argc; i++) {
01089 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
01090 varvalue = pbx_builtin_getvar_helper(chan, varname);
01091 strncat(buf, varvalue, len - strlen(buf) - 1);
01092 strncat(buf, ",", len - strlen(buf) - 1);
01093 }
01094
01095
01096 buf[strlen(buf) - 1] = '\0';
01097 }
01098
01099 return 0;
01100 }
01101
01102 static struct ast_custom_function hash_function = {
01103 .name = "HASH",
01104 .write = hash_write,
01105 .read = hash_read,
01106 };
01107
01108 static struct ast_custom_function hashkeys_function = {
01109 .name = "HASHKEYS",
01110 .read = hashkeys_read,
01111 .read2 = hashkeys_read2,
01112 };
01113
01114 static struct ast_custom_function array_function = {
01115 .name = "ARRAY",
01116 .write = array,
01117 };
01118
01119 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01120 {
01121 char *bufptr = buf, *dataptr = data;
01122
01123 if (len < 3){
01124 ast_log(LOG_ERROR, "Not enough buffer\n");
01125 return -1;
01126 }
01127
01128 if (ast_strlen_zero(data)) {
01129 ast_log(LOG_WARNING, "No argument specified!\n");
01130 ast_copy_string(buf, "\"\"", len);
01131 return 0;
01132 }
01133
01134 *bufptr++ = '"';
01135 for (; bufptr < buf + len - 3; dataptr++) {
01136 if (*dataptr == '\\') {
01137 *bufptr++ = '\\';
01138 *bufptr++ = '\\';
01139 } else if (*dataptr == '"') {
01140 *bufptr++ = '\\';
01141 *bufptr++ = '"';
01142 } else if (*dataptr == '\0') {
01143 break;
01144 } else {
01145 *bufptr++ = *dataptr;
01146 }
01147 }
01148 *bufptr++ = '"';
01149 *bufptr = '\0';
01150 return 0;
01151 }
01152
01153 static struct ast_custom_function quote_function = {
01154 .name = "QUOTE",
01155 .read = quote,
01156 };
01157
01158 static int csv_quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01159 {
01160 char *bufptr = buf, *dataptr = data;
01161
01162 if (len < 3) {
01163 ast_log(LOG_ERROR, "Not enough buffer\n");
01164 return -1;
01165 }
01166
01167 if (ast_strlen_zero(data)) {
01168 ast_copy_string(buf, "\"\"", len);
01169 return 0;
01170 }
01171
01172 *bufptr++ = '"';
01173 for (; bufptr < buf + len - 3; dataptr++){
01174 if (*dataptr == '"') {
01175 *bufptr++ = '"';
01176 *bufptr++ = '"';
01177 } else if (*dataptr == '\0') {
01178 break;
01179 } else {
01180 *bufptr++ = *dataptr;
01181 }
01182 }
01183 *bufptr++ = '"';
01184 *bufptr='\0';
01185 return 0;
01186 }
01187
01188 static struct ast_custom_function csv_quote_function = {
01189 .name = "CSV_QUOTE",
01190 .read = csv_quote,
01191 };
01192
01193 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
01194 {
01195 int length = 0;
01196
01197 if (data)
01198 length = strlen(data);
01199
01200 snprintf(buf, buflen, "%d", length);
01201
01202 return 0;
01203 }
01204
01205 static struct ast_custom_function len_function = {
01206 .name = "LEN",
01207 .read = len,
01208 .read_max = 12,
01209 };
01210
01211 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
01212 char *buf, size_t buflen)
01213 {
01214 AST_DECLARE_APP_ARGS(args,
01215 AST_APP_ARG(epoch);
01216 AST_APP_ARG(timezone);
01217 AST_APP_ARG(format);
01218 );
01219 struct timeval when;
01220 struct ast_tm tm;
01221
01222 buf[0] = '\0';
01223
01224 AST_STANDARD_APP_ARGS(args, parse);
01225
01226 ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
01227 ast_localtime(&when, &tm, args.timezone);
01228
01229 if (!args.format)
01230 args.format = "%c";
01231
01232 if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
01233 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
01234
01235 buf[buflen - 1] = '\0';
01236
01237 return 0;
01238 }
01239
01240 static struct ast_custom_function strftime_function = {
01241 .name = "STRFTIME",
01242 .read = acf_strftime,
01243 };
01244
01245 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
01246 char *buf, size_t buflen)
01247 {
01248 AST_DECLARE_APP_ARGS(args,
01249 AST_APP_ARG(timestring);
01250 AST_APP_ARG(timezone);
01251 AST_APP_ARG(format);
01252 );
01253 struct ast_tm tm;
01254
01255 buf[0] = '\0';
01256
01257 if (!data) {
01258 ast_log(LOG_ERROR,
01259 "Asterisk function STRPTIME() requires an argument.\n");
01260 return -1;
01261 }
01262
01263 AST_STANDARD_APP_ARGS(args, data);
01264
01265 if (ast_strlen_zero(args.format)) {
01266 ast_log(LOG_ERROR,
01267 "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
01268 return -1;
01269 }
01270
01271 if (!ast_strptime(args.timestring, args.format, &tm)) {
01272 ast_log(LOG_WARNING, "STRPTIME() found no time specified within the string\n");
01273 } else {
01274 struct timeval when;
01275 when = ast_mktime(&tm, args.timezone);
01276 snprintf(buf, buflen, "%d", (int) when.tv_sec);
01277 }
01278
01279 return 0;
01280 }
01281
01282 static struct ast_custom_function strptime_function = {
01283 .name = "STRPTIME",
01284 .read = acf_strptime,
01285 };
01286
01287 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
01288 char *buf, size_t buflen)
01289 {
01290 if (ast_strlen_zero(data)) {
01291 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
01292 return -1;
01293 }
01294
01295 pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
01296
01297 return 0;
01298 }
01299
01300 static int function_eval2(struct ast_channel *chan, const char *cmd, char *data,
01301 struct ast_str **buf, ssize_t buflen)
01302 {
01303 if (ast_strlen_zero(data)) {
01304 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
01305 return -1;
01306 }
01307
01308 ast_str_substitute_variables(buf, buflen, chan, data);
01309
01310 return 0;
01311 }
01312
01313 static struct ast_custom_function eval_function = {
01314 .name = "EVAL",
01315 .read = function_eval,
01316 .read2 = function_eval2,
01317 };
01318
01319 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
01320 {
01321 char *bufptr, *dataptr;
01322
01323 for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
01324 if (*dataptr == '\0') {
01325 *bufptr++ = '\0';
01326 break;
01327 } else if (*dataptr == '1') {
01328 *bufptr++ = '1';
01329 } else if (strchr("AaBbCc2", *dataptr)) {
01330 *bufptr++ = '2';
01331 } else if (strchr("DdEeFf3", *dataptr)) {
01332 *bufptr++ = '3';
01333 } else if (strchr("GgHhIi4", *dataptr)) {
01334 *bufptr++ = '4';
01335 } else if (strchr("JjKkLl5", *dataptr)) {
01336 *bufptr++ = '5';
01337 } else if (strchr("MmNnOo6", *dataptr)) {
01338 *bufptr++ = '6';
01339 } else if (strchr("PpQqRrSs7", *dataptr)) {
01340 *bufptr++ = '7';
01341 } else if (strchr("TtUuVv8", *dataptr)) {
01342 *bufptr++ = '8';
01343 } else if (strchr("WwXxYyZz9", *dataptr)) {
01344 *bufptr++ = '9';
01345 } else if (*dataptr == '0') {
01346 *bufptr++ = '0';
01347 }
01348 }
01349 buf[buflen - 1] = '\0';
01350
01351 return 0;
01352 }
01353
01354 static struct ast_custom_function keypadhash_function = {
01355 .name = "KEYPADHASH",
01356 .read = keypadhash,
01357 };
01358
01359 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
01360 {
01361 char *bufptr = buf, *dataptr = data;
01362
01363 while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
01364
01365 return 0;
01366 }
01367
01368 static int string_toupper2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
01369 {
01370 char *bufptr, *dataptr = data;
01371
01372 if (buflen > -1) {
01373 ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
01374 }
01375 bufptr = ast_str_buffer(*buf);
01376 while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = toupper(*dataptr++)));
01377 ast_str_update(*buf);
01378
01379 return 0;
01380 }
01381
01382 static struct ast_custom_function toupper_function = {
01383 .name = "TOUPPER",
01384 .read = string_toupper,
01385 .read2 = string_toupper2,
01386 };
01387
01388 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
01389 {
01390 char *bufptr = buf, *dataptr = data;
01391
01392 while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
01393
01394 return 0;
01395 }
01396
01397 static int string_tolower2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
01398 {
01399 char *bufptr, *dataptr = data;
01400
01401 if (buflen > -1) {
01402 ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
01403 }
01404 bufptr = ast_str_buffer(*buf);
01405 while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = tolower(*dataptr++)));
01406 ast_str_update(*buf);
01407
01408 return 0;
01409 }
01410
01411 static struct ast_custom_function tolower_function = {
01412 .name = "TOLOWER",
01413 .read = string_tolower,
01414 .read2 = string_tolower2,
01415 };
01416
01417 static int shift_pop(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01418 {
01419 #define beginning (cmd[0] == 'S')
01420 char *after, delimiter[2] = ",", *varsubst;
01421 size_t unused;
01422 struct ast_str *before = ast_str_thread_get(&result_buf, 16);
01423 char *(*search_func)(const char *s, int c) = (beginning ? strchr : strrchr);
01424 AST_DECLARE_APP_ARGS(args,
01425 AST_APP_ARG(var);
01426 AST_APP_ARG(delimiter);
01427 );
01428
01429 if (!before) {
01430 return -1;
01431 }
01432
01433 AST_STANDARD_APP_ARGS(args, data);
01434
01435 if (ast_strlen_zero(args.var)) {
01436 ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
01437 return -1;
01438 }
01439
01440 varsubst = alloca(strlen(args.var) + 4);
01441 sprintf(varsubst, "${%s}", args.var);
01442 ast_str_substitute_variables(&before, 0, chan, varsubst);
01443
01444 if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
01445 ast_get_encoded_char(args.delimiter, delimiter, &unused);
01446 }
01447
01448 if (!ast_str_strlen(before)) {
01449
01450 return -1;
01451 }
01452
01453 if (!(after = search_func(ast_str_buffer(before), delimiter[0]))) {
01454
01455 ast_str_set(buf, len, "%s", ast_str_buffer(before));
01456 pbx_builtin_setvar_helper(chan, args.var, "");
01457 } else {
01458 *after++ = '\0';
01459 ast_str_set(buf, len, "%s", beginning ? ast_str_buffer(before) : after);
01460 pbx_builtin_setvar_helper(chan, args.var, beginning ? after : ast_str_buffer(before));
01461 }
01462
01463 return 0;
01464 #undef beginning
01465 }
01466
01467 static struct ast_custom_function shift_function = {
01468 .name = "SHIFT",
01469 .read2 = shift_pop,
01470 };
01471
01472 static struct ast_custom_function pop_function = {
01473 .name = "POP",
01474 .read2 = shift_pop,
01475 };
01476
01477 static int unshift_push(struct ast_channel *chan, const char *cmd, char *data, const char *new_value)
01478 {
01479 #define beginning (cmd[0] == 'U')
01480 char delimiter[2] = ",", *varsubst;
01481 size_t unused;
01482 struct ast_str *buf, *previous_value;
01483 AST_DECLARE_APP_ARGS(args,
01484 AST_APP_ARG(var);
01485 AST_APP_ARG(delimiter);
01486 );
01487
01488 if (!(buf = ast_str_thread_get(&result_buf, 16)) ||
01489 !(previous_value = ast_str_thread_get(&tmp_buf, 16))) {
01490 return -1;
01491 }
01492
01493 AST_STANDARD_APP_ARGS(args, data);
01494
01495 if (ast_strlen_zero(args.var)) {
01496 ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
01497 return -1;
01498 }
01499
01500 if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
01501 ast_get_encoded_char(args.delimiter, delimiter, &unused);
01502 }
01503
01504 varsubst = alloca(strlen(args.var) + 4);
01505 sprintf(varsubst, "${%s}", args.var);
01506 ast_str_substitute_variables(&previous_value, 0, chan, varsubst);
01507
01508 if (!ast_str_strlen(previous_value)) {
01509 ast_str_set(&buf, 0, "%s", new_value);
01510 } else {
01511 ast_str_set(&buf, 0, "%s%c%s",
01512 beginning ? new_value : ast_str_buffer(previous_value),
01513 delimiter[0],
01514 beginning ? ast_str_buffer(previous_value) : new_value);
01515 }
01516
01517 pbx_builtin_setvar_helper(chan, args.var, ast_str_buffer(buf));
01518
01519 return 0;
01520 #undef beginning
01521 }
01522
01523 static struct ast_custom_function push_function = {
01524 .name = "PUSH",
01525 .write = unshift_push,
01526 };
01527
01528 static struct ast_custom_function unshift_function = {
01529 .name = "UNSHIFT",
01530 .write = unshift_push,
01531 };
01532
01533 static int passthru(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01534 {
01535 ast_str_set(buf, len, "%s", data);
01536 return 0;
01537 }
01538
01539 static struct ast_custom_function passthru_function = {
01540 .name = "PASSTHRU",
01541 .read2 = passthru,
01542 };
01543
01544 #ifdef TEST_FRAMEWORK
01545 AST_TEST_DEFINE(test_FIELDNUM)
01546 {
01547 int i, res = AST_TEST_PASS;
01548 struct ast_channel *chan;
01549 struct ast_str *str;
01550 char expression[256];
01551 struct {
01552 const char *fields;
01553 const char *delim;
01554 const char *field;
01555 const char *expected;
01556 } test_args[] = {
01557 {"abc,def,ghi,jkl", "\\,", "ghi", "3"},
01558 {"abc def ghi jkl", " ", "abc", "1"},
01559 {"abc/def/ghi/jkl", "\\\\x2f", "def", "2"},
01560 {"abc$def$ghi$jkl", "", "ghi", "0"},
01561 {"abc,def,ghi,jkl", "-", "", "0"},
01562 {"abc-def-ghi-jkl", "-", "mno", "0"}
01563 };
01564
01565 switch (cmd) {
01566 case TEST_INIT:
01567 info->name = "func_FIELDNUM_test";
01568 info->category = "/funcs/func_strings/";
01569 info->summary = "Test FIELDNUM function";
01570 info->description = "Verify FIELDNUM behavior";
01571 return AST_TEST_NOT_RUN;
01572 case TEST_EXECUTE:
01573 break;
01574 }
01575
01576 if (!(chan = ast_dummy_channel_alloc())) {
01577 ast_test_status_update(test, "Unable to allocate dummy channel\n");
01578 return AST_TEST_FAIL;
01579 }
01580
01581 if (!(str = ast_str_create(16))) {
01582 ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
01583 ast_channel_release(chan);
01584 return AST_TEST_FAIL;
01585 }
01586
01587 for (i = 0; i < ARRAY_LEN(test_args); i++) {
01588 struct ast_var_t *var = ast_var_assign("FIELDS", test_args[i].fields);
01589 AST_LIST_INSERT_HEAD(&chan->varshead, var, entries);
01590
01591 snprintf(expression, sizeof(expression), "${FIELDNUM(%s,%s,%s)}", var->name, test_args[i].delim, test_args[i].field);
01592 ast_str_substitute_variables(&str, 0, chan, expression);
01593
01594 AST_LIST_REMOVE(&chan->varshead, var, entries);
01595 ast_var_delete(var);
01596
01597 if (strcasecmp(ast_str_buffer(str), test_args[i].expected)) {
01598 ast_test_status_update(test, "Evaluation of '%s' returned '%s' instead of the expected value '%s'\n",
01599 expression, ast_str_buffer(str), test_args[i].expected);
01600 res = AST_TEST_FAIL;
01601 break;
01602 }
01603 }
01604
01605 ast_free(str);
01606 ast_channel_release(chan);
01607
01608 return res;
01609 }
01610
01611 AST_TEST_DEFINE(test_FILTER)
01612 {
01613 int i, res = AST_TEST_PASS;
01614 const char *test_strings[][2] = {
01615 {"A-R", "DAHDI"},
01616 {"A\\-R", "A"},
01617 {"\\x41-R", "DAHDI"},
01618 {"0-9A-Ca-c", "0042133333A12212"},
01619 {"0-9a-cA-C_+\\-", "0042133333A12212"},
01620 {NULL, NULL},
01621 };
01622
01623 switch (cmd) {
01624 case TEST_INIT:
01625 info->name = "func_FILTER_test";
01626 info->category = "/funcs/func_strings/";
01627 info->summary = "Test FILTER function";
01628 info->description = "Verify FILTER behavior";
01629 return AST_TEST_NOT_RUN;
01630 case TEST_EXECUTE:
01631 break;
01632 }
01633
01634 for (i = 0; test_strings[i][0]; i++) {
01635 char tmp[256], tmp2[256] = "";
01636 snprintf(tmp, sizeof(tmp), "${FILTER(%s,0042133333&DAHDI/g1/2212)}", test_strings[i][0]);
01637 pbx_substitute_variables_helper(NULL, tmp, tmp2, sizeof(tmp2) - 1);
01638 if (strcmp(test_strings[i][1], tmp2)) {
01639 ast_test_status_update(test, "Format string '%s' substituted to '%s'. Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][1]);
01640 res = AST_TEST_FAIL;
01641 }
01642 }
01643 return res;
01644 }
01645 #endif
01646
01647 static int unload_module(void)
01648 {
01649 int res = 0;
01650
01651 AST_TEST_UNREGISTER(test_FIELDNUM);
01652 AST_TEST_UNREGISTER(test_FILTER);
01653 res |= ast_custom_function_unregister(&fieldqty_function);
01654 res |= ast_custom_function_unregister(&fieldnum_function);
01655 res |= ast_custom_function_unregister(&filter_function);
01656 res |= ast_custom_function_unregister(&replace_function);
01657 res |= ast_custom_function_unregister(&listfilter_function);
01658 res |= ast_custom_function_unregister(®ex_function);
01659 res |= ast_custom_function_unregister(&array_function);
01660 res |= ast_custom_function_unregister("e_function);
01661 res |= ast_custom_function_unregister(&csv_quote_function);
01662 res |= ast_custom_function_unregister(&len_function);
01663 res |= ast_custom_function_unregister(&strftime_function);
01664 res |= ast_custom_function_unregister(&strptime_function);
01665 res |= ast_custom_function_unregister(&eval_function);
01666 res |= ast_custom_function_unregister(&keypadhash_function);
01667 res |= ast_custom_function_unregister(&hashkeys_function);
01668 res |= ast_custom_function_unregister(&hash_function);
01669 res |= ast_unregister_application(app_clearhash);
01670 res |= ast_custom_function_unregister(&toupper_function);
01671 res |= ast_custom_function_unregister(&tolower_function);
01672 res |= ast_custom_function_unregister(&shift_function);
01673 res |= ast_custom_function_unregister(&pop_function);
01674 res |= ast_custom_function_unregister(&push_function);
01675 res |= ast_custom_function_unregister(&unshift_function);
01676 res |= ast_custom_function_unregister(&passthru_function);
01677
01678 return res;
01679 }
01680
01681 static int load_module(void)
01682 {
01683 int res = 0;
01684
01685 AST_TEST_REGISTER(test_FIELDNUM);
01686 AST_TEST_REGISTER(test_FILTER);
01687 res |= ast_custom_function_register(&fieldqty_function);
01688 res |= ast_custom_function_register(&fieldnum_function);
01689 res |= ast_custom_function_register(&filter_function);
01690 res |= ast_custom_function_register(&replace_function);
01691 res |= ast_custom_function_register(&listfilter_function);
01692 res |= ast_custom_function_register(®ex_function);
01693 res |= ast_custom_function_register(&array_function);
01694 res |= ast_custom_function_register("e_function);
01695 res |= ast_custom_function_register(&csv_quote_function);
01696 res |= ast_custom_function_register(&len_function);
01697 res |= ast_custom_function_register(&strftime_function);
01698 res |= ast_custom_function_register(&strptime_function);
01699 res |= ast_custom_function_register(&eval_function);
01700 res |= ast_custom_function_register(&keypadhash_function);
01701 res |= ast_custom_function_register(&hashkeys_function);
01702 res |= ast_custom_function_register(&hash_function);
01703 res |= ast_register_application_xml(app_clearhash, exec_clearhash);
01704 res |= ast_custom_function_register(&toupper_function);
01705 res |= ast_custom_function_register(&tolower_function);
01706 res |= ast_custom_function_register(&shift_function);
01707 res |= ast_custom_function_register(&pop_function);
01708 res |= ast_custom_function_register(&push_function);
01709 res |= ast_custom_function_register(&unshift_function);
01710 res |= ast_custom_function_register(&passthru_function);
01711
01712 return res;
01713 }
01714
01715 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");