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