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