diff options
Diffstat (limited to 'trunk/utils')
-rw-r--r-- | trunk/utils/Makefile | 177 | ||||
-rw-r--r-- | trunk/utils/ael_main.c | 584 | ||||
-rw-r--r-- | trunk/utils/astcanary.c | 84 | ||||
-rw-r--r-- | trunk/utils/astman.1 | 102 | ||||
-rw-r--r-- | trunk/utils/astman.c | 752 | ||||
-rwxr-xr-x | trunk/utils/build-extensions-conf.lua | 81 | ||||
-rw-r--r-- | trunk/utils/check_expr.c | 459 | ||||
-rw-r--r-- | trunk/utils/clicompat.c | 16 | ||||
-rw-r--r-- | trunk/utils/conf2ael.c | 692 | ||||
-rw-r--r-- | trunk/utils/expr2.testinput | 126 | ||||
-rw-r--r-- | trunk/utils/extconf.c | 6211 | ||||
-rw-r--r-- | trunk/utils/frame.c | 1034 | ||||
-rw-r--r-- | trunk/utils/frame.h | 300 | ||||
-rw-r--r-- | trunk/utils/hashtest.c | 364 | ||||
-rw-r--r-- | trunk/utils/hashtest2.c | 376 | ||||
-rw-r--r-- | trunk/utils/muted.c | 698 | ||||
-rw-r--r-- | trunk/utils/smsq.c | 771 | ||||
-rw-r--r-- | trunk/utils/stereorize.c | 159 | ||||
-rw-r--r-- | trunk/utils/streamplayer.c | 121 |
19 files changed, 13107 insertions, 0 deletions
diff --git a/trunk/utils/Makefile b/trunk/utils/Makefile new file mode 100644 index 000000000..4c6bb1114 --- /dev/null +++ b/trunk/utils/Makefile @@ -0,0 +1,177 @@ +# +# Asterisk -- A telephony toolkit for Linux. +# +# Various utilities +# +# Copyright (C) 1999-2006, Digium +# +# Mark Spencer <markster@digium.com> +# +# This program is free software, distributed under the terms of +# the GNU General Public License +# + +ASTTOPDIR?=.. +-include $(ASTTOPDIR)/menuselect.makeopts + +.PHONY: clean all uninstall + +# to get check_expr, add it to the ALL_UTILS list +ALL_UTILS:=astman smsq stereorize streamplayer aelparse muted check_expr conf2ael hashtest2 hashtest astcanary +UTILS:=$(ALL_UTILS) + +LIBS += $(BKTR_LIB) # astobj2 with devmode uses backtrace + +include $(ASTTOPDIR)/Makefile.rules + +ifeq ($(OSARCH),SunOS) + LIBS+=-lsocket -lnsl + UTILS:=$(filter-out muted,$(UTILS)) +endif + +ifeq ($(OSARCH),OpenBSD) + UTILS:=$(filter-out muted,$(UTILS)) +endif + +ifeq ($(OSARCH),cygwin) + UTILS:=$(filter-out muted,$(UTILS)) +endif + +ifeq ($(OSARCH),mingw32) + UTILS:= +endif + +ifneq ($(findstring darwin,$(OSARCH)),) + AUDIO_LIBS=-framework CoreAudio +endif + +ifeq ($(POPT_LIB),) + UTILS:=$(filter-out smsq,$(UTILS)) +endif + +ifeq ($(NEWT_LIB),) + UTILS:=$(filter-out astman,$(UTILS)) +endif + +ifneq ($(filter pbx_ael,$(MENUSELECT_PBX)),) + UTILS:=$(filter-out aelparse,$(UTILS)) + UTILS:=$(filter-out conf2ael,$(UTILS)) +endif + +all: $(UTILS) + +install: + for x in $(UTILS); do \ + if [ "$$x" != "none" ]; then \ + $(INSTALL) -m 755 $$x $(DESTDIR)$(ASTSBINDIR)/$$x; \ + fi; \ + done + +uninstall: + for x in $(ALL_UTILS); do rm -f $$x $(DESTDIR)$(ASTSBINDIR)/$$x; done + +clean: + rm -f *.o $(ALL_UTILS) check_expr + rm -f .*.o.d .*.oo.d + rm -f *.s *.i + rm -f md5.c strcompat.c ast_expr2.c ast_expr2f.c pbx_ael.c pval.c hashtab.c + rm -f aelparse.c aelbison.c conf2ael + rm -f utils.c threadstorage.c sha1.c astobj2.c hashtest2 hashtest + +md5.c: $(ASTTOPDIR)/main/md5.c + @cp $< $@ + +astman: astman.o md5.o +astman: LIBS+=$(NEWT_LIB) + +stereorize: stereorize.o frame.o +stereorize: LIBS+=-lm + +hashtab.c: $(ASTTOPDIR)/main/hashtab.c + @cp $< $@ + +strcompat.c: $(ASTTOPDIR)/main/strcompat.c + @cp $< $@ + +$(ASTTOPDIR)/main/ast_expr2.c: + @echo " [BISON] $(ASTTOPDIR)/main/ast_expr2.y -> $@" + @bison -o $@ -d --name-prefix=ast_yy $(ASTTOPDIR)/main/ast_expr2.y + +$(ASTTOPDIR)/main/ast_expr2f.c: + @echo " [FLEX] $(ASTTOPDIR)/main/ast_expr2.fl -> $@" + @flex -o $@ --full $(ASTTOPDIR)/main/ast_expr2.fl + +pval.c: $(ASTTOPDIR)/res/ael/pval.c + @cp $< $@ + +ast_expr2.c: $(ASTTOPDIR)/main/ast_expr2.c + @cp $< $@ + +ast_expr2f.c: $(ASTTOPDIR)/main/ast_expr2f.c + @cp $< $@ + +ast_expr2f.o: ASTCFLAGS+=-DSTANDALONE_AEL -I$(ASTTOPDIR)/main + +pval.o : ASTCFLAGS+=-DSTANDALONE + +check_expr: check_expr.o ast_expr2.o ast_expr2f.o strcompat.o threadstorage.o clicompat.o + +aelbison.c: $(ASTTOPDIR)/res/ael/ael.tab.c + @cp $< $@ + +aelbison.o: ASTCFLAGS+=-I$(ASTTOPDIR)/res/ael -DYYENABLE_NLS=0 + +pbx_ael.c: $(ASTTOPDIR)/pbx/pbx_ael.c + @cp $< $@ + +pbx_ael.o: ASTCFLAGS+=-DSTANDALONE_AEL + +aelparse.c: $(ASTTOPDIR)/res/ael/ael_lex.c + @cp $< $@ + +aelparse.o: ASTCFLAGS+=-I$(ASTTOPDIR)/res -DSTANDALONE_AEL + +aelparse: aelparse.o aelbison.o pbx_ael.o ael_main.o ast_expr2f.o ast_expr2.o strcompat.o pval.o extconf.o + +astobj2.c: $(ASTTOPDIR)/main/astobj2.c + @cp $< $@ + +utils.c: $(ASTTOPDIR)/main/utils.c + @cp $< $@ + +sha1.c: $(ASTTOPDIR)/main/sha1.c + @cp $< $@ + +threadstorage.c: $(ASTTOPDIR)/main/threadstorage.c + @cp $< $@ + +hashtest2.o: ASTCFLAGS+=-O0 + +hashtest2: hashtest2.o md5.o utils.o astobj2.o sha1.o strcompat.o threadstorage.o clicompat.o + +hashtest: hashtest.o md5.o hashtab.o utils.o sha1.o strcompat.o threadstorage.o clicompat.o + +hashtest.o: ASTCFLAGS+=-O0 + +extconf.o: extconf.c + +conf2ael: conf2ael.o ast_expr2f.o ast_expr2.o aelbison.o aelparse.o pbx_ael.o pval.o extconf.o strcompat.o + +testexpr2s: $(ASTTOPDIR)/main/ast_expr2f.c $(ASTTOPDIR)/main/ast_expr2.c $(ASTTOPDIR)/main/ast_expr2.h + $(CC) -g -c -I$(ASTTOPDIR)/include -DSTANDALONE_AEL $(ASTTOPDIR)/main/ast_expr2f.c -o ast_expr2f.o + $(CC) -g -c -I$(ASTTOPDIR)/include -DSTANDALONE_AEL $(ASTTOPDIR)/main/ast_expr2.c -o ast_expr2.o + $(CC) -g -o testexpr2s ast_expr2f.o ast_expr2.o + rm ast_expr2.o ast_expr2f.o + ./testexpr2s expr2.testinput + +smsq: smsq.o strcompat.o +smsq: LIBS+=$(POPT_LIB) + +streamplayer: streamplayer.o + +muted: muted.o +muted: LIBS+=$(AUDIO_LIBS) + +ifneq ($(wildcard .*.d),) + include .*.d +endif diff --git a/trunk/utils/ael_main.c b/trunk/utils/ael_main.c new file mode 100644 index 000000000..7f20365f3 --- /dev/null +++ b/trunk/utils/ael_main.c @@ -0,0 +1,584 @@ +/* + * XXX this file probably need a fair amount of cleanup, at the very least: + * + * - documenting its purpose; + * - removing all unnecessary headers and other stuff from the sources + * it was copied from; + * - fixing the formatting + */ +#include "asterisk.h" + +#include <locale.h> +#include <ctype.h> +#include <regex.h> +#include <limits.h> + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/channel.h" +#include "asterisk/ast_expr.h" +#include "asterisk/module.h" +#include "asterisk/app.h" +#include "asterisk/ael_structs.h" +#include "asterisk/extconf.h" + +/*** MODULEINFO + <depend>res_ael_share</depend> + ***/ + +struct namelist +{ + char name[100]; + char name2[100]; + struct namelist *next; +}; + +struct ast_context +{ + int extension_count; + char name[100]; + char registrar[100]; + struct namelist *includes; + struct namelist *ignorepats; + struct namelist *switches; + struct namelist *eswitches; + + struct namelist *includes_last; + struct namelist *ignorepats_last; + struct namelist *switches_last; + struct namelist *eswitches_last; + + struct ast_context *next; +}; + +#define ADD_LAST(headptr,memptr) if(!headptr){ headptr=(memptr); (headptr##_last)=(memptr);} else {(headptr##_last)->next = (memptr); (headptr##_last) = (memptr);} + +void destroy_namelist(struct namelist *x); +void destroy_namelist(struct namelist *x) +{ + struct namelist *z,*z2; + for(z=x; z; z = z2) + { + z2 = z->next; + z->next = 0; + free(z); + } +} + +struct namelist *create_name(const char *name); +struct namelist *create_name(const char *name) +{ + struct namelist *x = calloc(1, sizeof(*x)); + if (!x) + return NULL; + strncpy(x->name, name, sizeof(x->name) - 1); + return x; +} + +struct ast_context *context_list; +struct ast_context *last_context; +struct namelist *globalvars; +struct namelist *globalvars_last; + +int conts=0, extens=0, priors=0; +char last_exten[18000]; + +static char config_dir[PATH_MAX]; +static char var_dir[PATH_MAX]; +const char *ast_config_AST_CONFIG_DIR = config_dir; +const char *ast_config_AST_VAR_DIR = var_dir; + +void ast_cli_register_multiple(void); +int ast_add_extension2(struct ast_context *con, + int replace, const char *extension, int priority, const char *label, const char *callerid, + const char *application, void *data, void (*datad)(void *), + const char *registrar); +void pbx_builtin_setvar(void *chan, void *data); +struct ast_context * ast_context_create(void **extcontexts, const char *name, const char *registrar); +struct ast_context * ast_context_find_or_create(void **extcontexts, const char *name, const char *registrar); +void ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar); +void ast_context_add_include2(struct ast_context *con, const char *value, const char *registrar); +void ast_context_add_switch2(struct ast_context *con, const char *value, const char *data, int eval, const char *registrar); +void ast_merge_contexts_and_delete(void); +void ast_context_verify_includes(void); +struct ast_context * ast_walk_contexts(void); +void ast_cli_unregister_multiple(void); +void ast_context_destroy(void); +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...); +char *ast_process_quotes_and_slashes(char *start, char find, char replace_with); +void ast_verbose(const char *fmt, ...); +struct ast_app *pbx_findapp(const char *app); +void filter_leading_space_from_exprs(char *str); +void filter_newlines(char *str); +static int quiet = 0; +static int no_comp = 0; +static int use_curr_dir = 0; +static int dump_extensions = 0; +static int FIRST_TIME = 0; +static FILE *dumpfile; + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + + printf("LOG: lev:%d file:%s line:%d func: %s ", + level, file, line, function); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +struct ast_exten *pbx_find_extension(struct ast_channel *chan, + struct ast_context *bypass, + struct pbx_find_info *q, + const char *context, + const char *exten, + int priority, + const char *label, + const char *callerid, + enum ext_match_t action); + +struct ast_exten *pbx_find_extension(struct ast_channel *chan, + struct ast_context *bypass, + struct pbx_find_info *q, + const char *context, + const char *exten, + int priority, + const char *label, + const char *callerid, + enum ext_match_t action) +{ + return localized_find_extension(bypass, q, context, exten, priority, label, callerid, action); +} + +struct ast_app *pbx_findapp(const char *app) +{ + return (struct ast_app*)1; /* so as not to trigger an error */ +} + +struct ast_custom_function *ast_custom_function_find(const char *name); + + +struct ast_custom_function *ast_custom_function_find(const char *name) +{ + return 0; /* in "standalone" mode, functions are just not avail */ +} + +void ast_register_file_version(const char *file, const char *version) +{ +} + +void ast_unregister_file_version(const char *file) +{ +} + +int ast_add_profile(const char *x, uint64_t scale) +{ + if (!no_comp) + printf("Executed ast_add_profile();\n"); + + return 0; +} + +int ast_loader_register(int (*updater)(void)) +{ + return 1; +} + +int ast_loader_unregister(int (*updater)(void)) +{ + return 1; +} +void ast_module_register(const struct ast_module_info *x) +{ +} + +void ast_module_unregister(const struct ast_module_info *x) +{ +} + + +void ast_cli_register_multiple(void) +{ + if(!no_comp) + printf("Executed ast_cli_register_multiple();\n"); +} + +void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count); +void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count) +{ + if (cp1 && *cp1) + strncpy(cp2,cp1,AST_MAX_EXTENSION); /* Right now, this routine is ONLY being called for + a possible var substitution on extension names, + so....! */ + else + *cp2 = 0; +} + +int ast_add_extension2(struct ast_context *con, + int replace, const char *extension, int priority, const char *label, const char *callerid, + const char *application, void *data, void (*datad)(void *), + const char *registrar) +{ + priors++; + con->extension_count++; + if (strcmp(extension,last_exten) != 0) { + extens++; + strcpy(last_exten, extension); + } + if (!label) { + label = "(null)"; + } + if (!callerid) { + callerid = "(null)"; + } + if (!application) { + application = "(null)"; + } + + if(!no_comp) + printf("Executed ast_add_extension2(context=%s, rep=%d, exten=%s, priority=%d, label=%s, callerid=%s, appl=%s, data=%s, FREE, registrar=%s);\n", + con->name, replace, extension, priority, label, callerid, application, (data?(char*)data:"(null)"), registrar); + + if( dump_extensions && dumpfile ) { + struct namelist *n; + char *data2,*data3=0; + int commacount = 0; + + if( FIRST_TIME ) { + FIRST_TIME = 0; + + if( globalvars ) + fprintf(dumpfile,"[globals]\n"); + + for(n=globalvars;n;n=n->next) { + fprintf(dumpfile, "%s\n", n->name); + } + } + + /* print out each extension , possibly the context header also */ + if( con != last_context ) { + fprintf(dumpfile,"\n\n[%s]\n", con->name); + last_context = con; + for(n=con->ignorepats;n;n=n->next) { + fprintf(dumpfile, "ignorepat => %s\n", n->name); + } + for(n=con->includes;n;n=n->next) { + fprintf(dumpfile, "include => %s\n", n->name); + } + for(n=con->switches;n;n=n->next) { + fprintf(dumpfile, "switch => %s/%s\n", n->name, n->name2); + } + for(n=con->eswitches;n;n=n->next) { + fprintf(dumpfile, "eswitch => %s/%s\n", n->name, n->name2); + } + + } + if( data ) { + filter_newlines((char*)data); + filter_leading_space_from_exprs((char*)data); + + /* compiling turns commas into vertical bars in the app data, and also removes the backslash from before escaped commas; + we have to restore the escaping backslash in front of any commas; the vertical bars are OK to leave as-is */ + for (data2 = data; *data2; data2++) { + if (*data2 == ',') + commacount++; /* we need to know how much bigger the string will grow-- one backslash for each comma */ + } + if (commacount) + { + char *d3,*d4; + + data2 = (char*)malloc(strlen(data)+commacount+1); + data3 = data; + d3 = data; + d4 = data2; + while (*d3) { + if (*d3 == ',') { + *d4++ = '\\'; /* put a backslash in front of each comma */ + *d4++ = *d3++; + } else + *d4++ = *d3++; /* or just copy the char */ + } + *d4++ = 0; /* cap off the new string */ + data = data2; + } else + data2 = 0; + + if( strcmp(label,"(null)") != 0 ) + fprintf(dumpfile,"exten => %s,%d(%s),%s(%s)\n", extension, priority, label, application, (char*)data); + else + fprintf(dumpfile,"exten => %s,%d,%s(%s)\n", extension, priority, application, (char*)data); + + if (data2) { + free(data2); + data2 = 0; + data = data3; /* restore data to pre-messedup state */ + } + + } else { + + if( strcmp(label,"(null)") != 0 ) + fprintf(dumpfile,"exten => %s,%d(%s),%s\n", extension, priority, label, application); + else + fprintf(dumpfile,"exten => %s,%d,%s\n", extension, priority, application); + } + } + + /* since add_extension2 is responsible for the malloc'd data stuff */ + if( data ) + free(data); + return 0; +} + +void pbx_builtin_setvar(void *chan, void *data) +{ + struct namelist *x = create_name(data); + if(!no_comp) + printf("Executed pbx_builtin_setvar(chan, data=%s);\n", (char*)data); + + if( dump_extensions ) { + x = create_name(data); + ADD_LAST(globalvars,x); + } +} + + +struct ast_context * ast_context_create(void **extcontexts, const char *name, const char *registrar) +{ + struct ast_context *x = calloc(1, sizeof(*x)); + if (!x) + return NULL; + x->next = context_list; + context_list = x; + if (!no_comp) + printf("Executed ast_context_create(conts, name=%s, registrar=%s);\n", name, registrar); + conts++; + strncpy(x->name, name, sizeof(x->name) - 1); + strncpy(x->registrar, registrar, sizeof(x->registrar) - 1); + return x; +} + +struct ast_context * ast_context_find_or_create(void **extcontexts, const char *name, const char *registrar) +{ + struct ast_context *x = calloc(1, sizeof(*x)); + if (!x) + return NULL; + x->next = context_list; + context_list = x; + if (!no_comp) + printf("Executed ast_context_find_or_create(conts, name=%s, registrar=%s);\n", name, registrar); + conts++; + strncpy(x->name, name, sizeof(x->name) - 1); + strncpy(x->registrar, registrar, sizeof(x->registrar) - 1); + return x; +} + +void ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar) +{ + if(!no_comp) + printf("Executed ast_context_add_ignorepat2(con, value=%s, registrar=%s);\n", value, registrar); + if( dump_extensions ) { + struct namelist *x; + x = create_name(value); + ADD_LAST(con->ignorepats,x); + } +} + +void ast_context_add_include2(struct ast_context *con, const char *value, const char *registrar) +{ + if(!no_comp) + printf("Executed ast_context_add_include2(con, value=%s, registrar=%s);\n", value, registrar); + if( dump_extensions ) { + struct namelist *x; + x = create_name((char*)value); + ADD_LAST(con->includes,x); + } +} + +void ast_context_add_switch2(struct ast_context *con, const char *value, const char *data, int eval, const char *registrar) +{ + if(!no_comp) + printf("Executed ast_context_add_switch2(con, value=%s, data=%s, eval=%d, registrar=%s);\n", value, data, eval, registrar); + if( dump_extensions ) { + struct namelist *x; + x = create_name((char*)value); + strncpy(x->name2,data,100); + if( eval ) { + + ADD_LAST(con->switches,x); + + } else { + + ADD_LAST(con->eswitches,x); + } + } +} + +void ast_merge_contexts_and_delete(void) +{ + if(!no_comp) + printf("Executed ast_merge_contexts_and_delete();\n"); +} + +void ast_context_verify_includes(void) +{ + if(!no_comp) + printf("Executed ast_context_verify_includes();\n"); +} + +struct ast_context * ast_walk_contexts(void) +{ + if(!no_comp) + printf("Executed ast_walk_contexts();\n"); + return 0; +} + +void ast_cli_unregister_multiple(void) +{ + if(!no_comp) + printf("Executed ast_cli_unregister_multiple();\n"); +} + +void ast_context_destroy(void) +{ + if( !no_comp) + printf("Executed ast_context_destroy();\n"); +} + +void filter_leading_space_from_exprs(char *str) +{ + /* Mainly for aesthetics */ + char *t, *v, *u = str; + + while ( u && *u ) { + + if( *u == '$' && *(u+1) == '[' ) { + t = u+2; + while( *t == '\n' || *t == '\r' || *t == '\t' || *t == ' ' ) { + v = t; + while ( *v ) { + *v = *(v+1); + v++; + } + } + } + + u++; + } +} + +void filter_newlines(char *str) +{ + /* remove all newlines, returns */ + char *t=str; + while( t && *t ) { + if( *t == '\n' || *t == '\r' ) { + *t = ' '; /* just replace newlines and returns with spaces; they act as + token separators, and just blindly removing them could be + harmful. */ + } + t++; + } +} + + +extern struct module_symbols mod_data; +int ael_external_load_module(void); + + +int main(int argc, char **argv) +{ + int i; + struct namelist *n; + struct ast_context *lp,*lp2; + + for(i=1;i<argc;i++) { + if( argv[i][0] == '-' && argv[i][1] == 'n' ) + no_comp =1; + if( argv[i][0] == '-' && argv[i][1] == 'q' ) { + quiet = 1; + no_comp =1; + } + if( argv[i][0] == '-' && argv[i][1] == 'd' ) + use_curr_dir =1; + if( argv[i][0] == '-' && argv[i][1] == 'w' ) + dump_extensions =1; + } + + if( !quiet ) { + printf("\n(If you find progress and other non-error messages irritating, you can use -q to suppress them)\n"); + if( !no_comp ) + printf("\n(You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler)\n\n"); + if( !use_curr_dir ) + printf("\n(You can use the -d option if you want to use the current working directory as the CONFIG_DIR. I will look in this dir for extensions.ael* and its included files)\n\n"); + if( !dump_extensions ) + printf("\n(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)\n"); + } + + if( use_curr_dir ) { + strcpy(config_dir, "."); + localized_use_local_dir(); + } + else { + strcpy(config_dir, "/etc/asterisk"); + localized_use_conf_dir(); + } + strcpy(var_dir, "/var/lib/asterisk"); + + if( dump_extensions ) { + dumpfile = fopen("extensions.conf.aeldump","w"); + if( !dumpfile ) { + printf("\n\nSorry, cannot open extensions.conf.aeldump for writing! Correct the situation and try again!\n\n"); + exit(10); + } + + } + + FIRST_TIME = 1; + + ael_external_load_module(); + + ast_log(4, "ael2_parse", __LINE__, "main", "%d contexts, %d extensions, %d priorities\n", conts, extens, priors); + + if( dump_extensions && dumpfile ) { + + for( lp = context_list; lp; lp = lp->next ) { /* print out any contexts that didn't have any + extensions in them */ + if( lp->extension_count == 0 ) { + + fprintf(dumpfile,"\n\n[%s]\n", lp->name); + + for(n=lp->ignorepats;n;n=n->next) { + fprintf(dumpfile, "ignorepat => %s\n", n->name); + } + for(n=lp->includes;n;n=n->next) { + fprintf(dumpfile, "include => %s\n", n->name); + } + for(n=lp->switches;n;n=n->next) { + fprintf(dumpfile, "switch => %s/%s\n", n->name, n->name2); + } + for(n=lp->eswitches;n;n=n->next) { + fprintf(dumpfile, "eswitch => %s/%s\n", n->name, n->name2); + } + } + } + } + + if( dump_extensions && dumpfile ) + fclose(dumpfile); + + for( lp = context_list; lp; lp = lp2 ) { /* free the ast_context structs */ + lp2 = lp->next; + lp->next = 0; + + destroy_namelist(lp->includes); + destroy_namelist(lp->ignorepats); + destroy_namelist(lp->switches); + destroy_namelist(lp->eswitches); + + free(lp); + } + + return 0; +} diff --git a/trunk/utils/astcanary.c b/trunk/utils/astcanary.c new file mode 100644 index 000000000..eb9f17208 --- /dev/null +++ b/trunk/utils/astcanary.c @@ -0,0 +1,84 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2007, Digium, Inc. + * + * Tilghman Lesher <tlesher AT digium DOT com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <utime.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +/*!\brief + * At one time, canaries were carried along with coal miners down + * into a mine. Their purpose was to alert the miners when they + * had drilled into a pocket of methane gas or another noxious + * substance. The canary, being the most sensitive animal would + * immediately fall over. Seeing this, the miners could take + * action to escape the mine, seeing an imminent danger. + * + * This process serves a similar purpose, though with the realtime + * priority being the reason. When a thread starts running away + * with the processor, it is typically difficult to tell what + * thread caused the problem, as the machine acts as if it is + * locked up (in fact, what has happened is that Asterisk runs at + * a higher priority than even the login shell, so the runaway + * thread hogs all available CPU time. + * + * If that happens, this canary process will cease to get any + * process time, which we can monitor with a realtime thread in + * Asterisk. Should that happen, that monitoring thread may take + * immediate action to slow down Asterisk to regular priority, + * thus allowing an administrator to login to the system and + * restart Asterisk or perhaps take another course of action + * (such as retrieving a backtrace to let the developers know + * what precisely went wrong). + * + * Note that according to POSIX.1, all threads inside a single + * process must share the same priority, so when the monitoring + * thread deprioritizes itself, it deprioritizes all threads at + * the same time. This is also why this canary must exist as a + * completely separate process and not simply as a thread within + * Asterisk itself. + */ + +int main(int argc, char *argv[]) +{ + int fd; + /* Run at normal priority */ + setpriority(PRIO_PROCESS, 0, 0); + for (;;) { + /* Update the modification times (checked from Asterisk) */ + if (utime(argv[1], NULL)) { + /* Recreate the file if it doesn't exist */ + if ((fd = open(argv[1], O_RDWR | O_TRUNC | O_CREAT)) > -1) + close(fd); + else + exit(1); + continue; + } + + /* Run occasionally */ + sleep(5); + } + + /* Never reached */ + return 0; +} + diff --git a/trunk/utils/astman.1 b/trunk/utils/astman.1 new file mode 100644 index 000000000..6a36ca4da --- /dev/null +++ b/trunk/utils/astman.1 @@ -0,0 +1,102 @@ +.\" $Header$ +.\" +.\" transcript compatibility for postscript use. +.\" +.\" synopsis: .P! <file.ps> +.\" +.de P! +.fl +\!!1 setgray +.fl +\\&.\" +.fl +\!!0 setgray +.fl \" force out current output buffer +\!!save /psv exch def currentpoint translate 0 0 moveto +\!!/showpage{}def +.fl \" prolog +.sy sed \-e 's/^/!/' \\$1\" bring in postscript file +\!!psv restore +. +.de pF +.ie \\*(f1 .ds f1 \\n(.f +.el .ie \\*(f2 .ds f2 \\n(.f +.el .ie \\*(f3 .ds f3 \\n(.f +.el .ie \\*(f4 .ds f4 \\n(.f +.el .tm ? font overflow +.ft \\$1 +.. +.de fP +.ie !\\*(f4 \{\ +. ft \\*(f4 +. ds f4\" +' br \} +.el .ie !\\*(f3 \{\ +. ft \\*(f3 +. ds f3\" +' br \} +.el .ie !\\*(f2 \{\ +. ft \\*(f2 +. ds f2\" +' br \} +.el .ie !\\*(f1 \{\ +. ft \\*(f1 +. ds f1\" +' br \} +.el .tm ? font underflow +.. +.ds f1\" +.ds f2\" +.ds f3\" +.ds f4\" +'\" t +.ta 8n 16n 24n 32n 40n 48n 56n 64n 72n +.TH ASTMAN 1 "Jun 12th, 2005" "astman" "Linux Programmer's Manual" +.SH NAME +.B astman +-- a client to asterisk's manager interface +.SH SYNOPSIS +.PP +.B astman +.I hostname + +.SH DESCRIPTION +.B astman +This program is a full-screen (terminal) client for Asterisk's manager +interface. + +.SH OPTIONS +.B hostname + +The host name or IP address to connect to (TCP port 5038). If astman +fails to connect it will exit immidiately. + +.SH USAGE +If \fBastman\fR has successfully cunnected to the manager port it will +prompt the user for a username and a secret (password) for the manager +interface on the remote Asterisk manager interface. It will then be able +to report existing channels (calls). You will then be able to redirect +calls to or terminate them. + +.SH "SEE ALSO" +asterisk(8) + +http://www.voip-info.org/wiki-Asterisk+astman + +.SH BUGS +The hostname does not default to localhost. + +Impossible to use a port other than 5038. + +The username and password cannot be defined from the command-line. + +I mean, what's the point in a man page if the syntax is so simple? + +.SH "AUTHOR" +This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com> +Permission is granted to copy, distribute and/or modify this document under +the terms of the GNU General Public License, Version 2 any +later version published by the Free Software Foundation. + +On Debian systems, the complete text of the GNU General Public +License can be found in /usr/share/common-licenses/GPL. diff --git a/trunk/utils/astman.c b/trunk/utils/astman.c new file mode 100644 index 000000000..900e6f759 --- /dev/null +++ b/trunk/utils/astman.c @@ -0,0 +1,752 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* + * + * ASTerisk MANager + * + */ + +#include "asterisk.h" +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") +#include <newt.h> +#include <stdio.h> +#include <sys/time.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +#include "asterisk/md5.h" +#include "asterisk/linkedlists.h" + +#undef gethostbyname + +#define MAX_HEADERS 80 +#define MAX_LEN 256 + +/* + * 2005.05.27 - different versions of newt define the type of the buffer + * for the 5th argument to newtEntry() as char ** or const char ** . To + * let the code compile cleanly with -Werror, we cast it to void * through + * _NEWT_CAST. + */ +#define _NEWT_CAST (void *) + +#define DEFAULT_MANAGER_PORT 5038 + +struct message { + unsigned int hdrcount; + char headers[MAX_HEADERS][MAX_LEN]; +}; + +static struct ast_mansession { + struct sockaddr_in sin; + int fd; + char inbuf[MAX_LEN]; + int inlen; +} session; + +struct ast_chan { + char name[80]; + char exten[20]; + char context[20]; + char priority[20]; + char callerid[40]; + char state[10]; + AST_LIST_ENTRY(ast_chan) list; +}; + +static AST_LIST_HEAD_NOLOCK_STATIC(chans, ast_chan); + +/* dummy functions to be compatible with the Asterisk core for md5.c */ +void ast_register_file_version(const char *file, const char *version); +void ast_register_file_version(const char *file, const char *version) +{ +} + +void ast_unregister_file_version(const char *file); +void ast_unregister_file_version(const char *file) +{ +} + +int ast_add_profile(const char *, uint64_t scale); +int ast_add_profile(const char *s, uint64_t scale) +{ + return -1; +} + +int64_t ast_profile(int, int64_t); +int64_t ast_profile(int key, int64_t val) +{ + return 0; +} +int64_t ast_mark(int, int start1_stop0); +int64_t ast_mark(int key, int start1_stop0) +{ + return 0; +} + +/* end of dummy functions */ + +static struct ast_chan *find_chan(char *name) +{ + struct ast_chan *chan; + AST_LIST_TRAVERSE(&chans, chan, list) { + if (!strcmp(name, chan->name)) + return chan; + } + chan = malloc(sizeof(struct ast_chan)); + if (chan) { + memset(chan, 0, sizeof(struct ast_chan)); + strncpy(chan->name, name, sizeof(chan->name) - 1); + AST_LIST_INSERT_TAIL(&chans, chan, list); + } + return chan; +} + +static void del_chan(char *name) +{ + struct ast_chan *chan; + + AST_LIST_TRAVERSE_SAFE_BEGIN(&chans, chan, list) { + if (!strcmp(name, chan->name)) { + AST_LIST_REMOVE_CURRENT(list); + free(chan); + return; + } + } + AST_LIST_TRAVERSE_SAFE_END; +} + +static void fdprintf(int fd, char *fmt, ...) +{ + char stuff[4096]; + va_list ap; + va_start(ap, fmt); + vsnprintf(stuff, sizeof(stuff), fmt, ap); + va_end(ap); + write(fd, stuff, strlen(stuff)); +} + +static char *get_header(struct message *m, char *var) +{ + char cmp[80]; + int x; + snprintf(cmp, sizeof(cmp), "%s: ", var); + for (x=0;x<m->hdrcount;x++) + if (!strncasecmp(cmp, m->headers[x], strlen(cmp))) + return m->headers[x] + strlen(cmp); + return ""; +} + +static int event_newstate(struct ast_mansession *s, struct message *m) +{ + struct ast_chan *chan; + chan = find_chan(get_header(m, "Channel")); + strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1); + return 0; +} + +static int event_newexten(struct ast_mansession *s, struct message *m) +{ + struct ast_chan *chan; + chan = find_chan(get_header(m, "Channel")); + strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1); + strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1); + strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1); + return 0; +} + +static int event_newchannel(struct ast_mansession *s, struct message *m) +{ + struct ast_chan *chan; + chan = find_chan(get_header(m, "Channel")); + strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1); + strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1); + return 0; +} + +static int event_status(struct ast_mansession *s, struct message *m) +{ + struct ast_chan *chan; + chan = find_chan(get_header(m, "Channel")); + strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1); + strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1); + strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1); + strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1); + strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1); + return 0; +} + +static int event_hangup(struct ast_mansession *s, struct message *m) +{ + del_chan(get_header(m, "Channel")); + return 0; +} + +static int event_ignore(struct ast_mansession *s, struct message *m) +{ + return 0; +} + +static int event_rename(struct ast_mansession *s, struct message *m) +{ + struct ast_chan *chan; + chan = find_chan(get_header(m, "Oldname")); + strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1); + return 0; +} +static struct event { + char *event; + int (*func)(struct ast_mansession *s, struct message *m); +} events[] = { + { "Newstate", event_newstate }, + { "Newchannel", event_newchannel }, + { "Newexten", event_newexten }, + { "Hangup", event_hangup }, + { "Rename", event_rename }, + { "Status", event_status }, + { "Link", event_ignore }, + { "Unlink", event_ignore }, + { "StatusComplete", event_ignore }, + { "Dial", event_ignore }, + { "PeerStatus", event_ignore }, + { "MessageWaiting", event_ignore }, + { "Newcallerid", event_ignore } +}; + +static int process_message(struct ast_mansession *s, struct message *m) +{ + int x; + char event[80] = ""; + strncpy(event, get_header(m, "Event"), sizeof(event) - 1); + if (!strlen(event)) { + fprintf(stderr, "Missing event in request"); + return 0; + } + for (x=0;x<sizeof(events) / sizeof(events[0]);x++) { + if (!strcasecmp(event, events[x].event)) { + if (events[x].func(s, m)) + return -1; + break; + } + } + if (x >= sizeof(events) / sizeof(events[0])) + fprintf(stderr, "Ignoring unknown event '%s'", event); +#if 0 + for (x=0;x<m->hdrcount;x++) { + printf("Header: %s\n", m->headers[x]); + } +#endif + return 0; +} + +static void rebuild_channels(newtComponent c) +{ + void *prev = NULL; + struct ast_chan *chan; + char tmpn[42]; + char tmp[256]; + int x=0; + prev = newtListboxGetCurrent(c); + newtListboxClear(c); + AST_LIST_TRAVERSE(&chans, chan, list) { + snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid); + if (strlen(chan->exten)) + snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s", + tmpn, chan->state, + chan->exten, chan->context, chan->priority); + else + snprintf(tmp, sizeof(tmp), "%-30s %8s", + tmpn, chan->state); + newtListboxAppendEntry(c, tmp, chan); + x++; + } + if (!x) + newtListboxAppendEntry(c, " << No Active Channels >> ", NULL); + newtListboxSetCurrentByKey(c, prev); +} + +static int has_input(struct ast_mansession *s) +{ + int x; + for (x=1;x<s->inlen;x++) + if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) + return 1; + return 0; +} + +static int get_input(struct ast_mansession *s, char *output) +{ + /* output must have at least sizeof(s->inbuf) space */ + int res; + int x; + struct timeval tv = {0, 0}; + fd_set fds; + for (x=1;x<s->inlen;x++) { + if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) { + /* Copy output data up to and including \r\n */ + memcpy(output, s->inbuf, x + 1); + /* Add trailing \0 */ + output[x+1] = '\0'; + /* Move remaining data back to the front */ + memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x); + s->inlen -= (x + 1); + return 1; + } + } + if (s->inlen >= sizeof(s->inbuf) - 1) { + fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf); + s->inlen = 0; + } + FD_ZERO(&fds); + FD_SET(s->fd, &fds); + res = select(s->fd + 1, &fds, NULL, NULL, &tv); + if (res < 0) { + fprintf(stderr, "Select returned error: %s\n", strerror(errno)); + } else if (res > 0) { + res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen); + if (res < 1) + return -1; + s->inlen += res; + s->inbuf[s->inlen] = '\0'; + } else { + return 2; + } + return 0; +} + +static int input_check(struct ast_mansession *s, struct message **mout) +{ + static struct message m; + int res; + + if (mout) + *mout = NULL; + + for(;;) { + res = get_input(s, m.headers[m.hdrcount]); + if (res == 1) { +#if 0 + fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]); + fgetc(stdin); +#endif + /* Strip trailing \r\n */ + if (strlen(m.headers[m.hdrcount]) < 2) + continue; + m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0'; + if (!strlen(m.headers[m.hdrcount])) { + if (mout && strlen(get_header(&m, "Response"))) { + *mout = &m; + return 0; + } + if (process_message(s, &m)) + break; + memset(&m, 0, sizeof(&m)); + } else if (m.hdrcount < MAX_HEADERS - 1) + m.hdrcount++; + } else if (res < 0) { + return -1; + } else if (res == 2) + return 0; + } + return -1; +} + +static struct message *wait_for_response(int timeout) +{ + struct message *m; + struct timeval tv; + int res; + fd_set fds; + for (;;) { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + FD_SET(session.fd, &fds); + res = select(session.fd + 1, &fds, NULL, NULL, &tv); + if (res < 1) + break; + if (input_check(&session, &m) < 0) { + return NULL; + } + if (m) + return m; + } + return NULL; +} + +static int manager_action(char *action, char *fmt, ...) +{ + struct ast_mansession *s; + char tmp[4096]; + va_list ap; + + s = &session; + fdprintf(s->fd, "Action: %s\r\n", action); + va_start(ap, fmt); + vsnprintf(tmp, sizeof(tmp), fmt, ap); + va_end(ap); + write(s->fd, tmp, strlen(tmp)); + fdprintf(s->fd, "\r\n"); + return 0; +} + +static int show_message(char *title, char *msg) +{ + newtComponent form; + newtComponent label; + newtComponent ok; + struct newtExitStruct es; + + newtCenteredWindow(60,7, title); + + label = newtLabel(4,1,msg); + ok = newtButton(27, 3, "OK"); + form = newtForm(NULL, NULL, 0); + newtFormAddComponents(form, label, ok, NULL); + newtFormRun(form, &es); + newtPopWindow(); + newtFormDestroy(form); + return 0; +} + +static newtComponent showform; +static int show_doing(char *title, char *tmp) +{ + struct newtExitStruct es; + newtComponent label; + showform = newtForm(NULL, NULL, 0); + newtCenteredWindow(70,4, title); + label = newtLabel(3,1,tmp); + newtFormAddComponents(showform,label, NULL); + newtFormSetTimer(showform, 200); + newtFormRun(showform, &es); + return 0; +} + +static int hide_doing(void) +{ + newtPopWindow(); + newtFormDestroy(showform); + return 0; +} + +static void try_status(void) +{ + struct message *m; + manager_action("Status", ""); + m = wait_for_response(10000); + if (!m) { + show_message("Status Failed", "Timeout waiting for response"); + } else if (strcasecmp(get_header(m, "Response"), "Success")) { + show_message("Status Failed Failed", get_header(m, "Message")); + } +} + + +static void try_hangup(newtComponent c) +{ + struct ast_chan *chan; + struct message *m; + + chan = newtListboxGetCurrent(c); + if (chan) { + manager_action("Hangup", "Channel: %s\r\n", chan->name); + m = wait_for_response(10000); + if (!m) { + show_message("Hangup Failed", "Timeout waiting for response"); + } else if (strcasecmp(get_header(m, "Response"), "Success")) { + show_message("Hangup Failed", get_header(m, "Message")); + } + } + +} + +static int get_user_input(char *msg, char *buf, int buflen) +{ + newtComponent form; + newtComponent ok; + newtComponent cancel; + newtComponent inpfield; + const char *input; + int res = -1; + struct newtExitStruct es; + + newtCenteredWindow(60,7, msg); + + inpfield = newtEntry(5, 2, "", 50, _NEWT_CAST &input, 0); + ok = newtButton(22, 3, "OK"); + cancel = newtButton(32, 3, "Cancel"); + form = newtForm(NULL, NULL, 0); + newtFormAddComponents(form, inpfield, ok, cancel, NULL); + newtFormRun(form, &es); + strncpy(buf, input, buflen - 1); + if (es.u.co == ok) + res = 0; + else + res = -1; + newtPopWindow(); + newtFormDestroy(form); + return res; +} + +static void try_redirect(newtComponent c) +{ + struct ast_chan *chan; + char dest[256]; + struct message *m; + char channame[256]; + char tmp[80]; + char *context; + + chan = newtListboxGetCurrent(c); + if (chan) { + strncpy(channame, chan->name, sizeof(channame) - 1); + snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame); + if (get_user_input(tmp, dest, sizeof(dest))) + return; + if ((context = strchr(dest, '@'))) { + *context = '\0'; + context++; + manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest); + } else { + manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest); + } + m = wait_for_response(10000); + if (!m) { + show_message("Hangup Failed", "Timeout waiting for response"); + } else if (strcasecmp(get_header(m, "Response"), "Success")) { + show_message("Hangup Failed", get_header(m, "Message")); + } + } + +} + +static int manage_calls(char *host) +{ + newtComponent form; + newtComponent quit; + newtComponent hangup; + newtComponent redirect; + newtComponent channels; + struct newtExitStruct es; + char tmp[80]; + + /* Mark: If there's one thing you learn from this code, it is this... + Never, ever fly Air France. Their customer service is absolutely + the worst. I've never heard the words "That's not my problem" as + many times as I have from their staff -- It should, without doubt + be their corporate motto if it isn't already. Don't bother giving + them business because you're just a pain in their side and they + will be sure to let you know the first time you speak to them. + + If you ever want to make me happy just tell me that you, too, will + never fly Air France again either (in spite of their excellent + cuisine). + + Update by oej: The merger with KLM has transferred this + behaviour to KLM as well. + Don't bother giving them business either... + + Only if you want to travel randomly without luggage, you + might pick either of them. + + */ + snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host); + newtCenteredWindow(74, 20, tmp); + form = newtForm(NULL, NULL, 0); + newtFormWatchFd(form, session.fd, NEWT_FD_READ); + newtFormSetTimer(form, 100); + quit = newtButton(62, 16, "Quit"); + redirect = newtButton(35, 16, "Redirect"); + hangup = newtButton(50, 16, "Hangup"); + channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL); + newtFormAddComponents(form, channels, redirect, hangup, quit, NULL); + newtListboxSetWidth(channels, 72); + + show_doing("Getting Status", "Retrieving system status..."); + try_status(); + hide_doing(); + + for(;;) { + newtFormRun(form, &es); + if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) { + if (input_check(&session, NULL)) { + show_message("Disconnected", "Disconnected from remote host"); + break; + } + } else if (es.reason == NEWT_EXIT_COMPONENT) { + if (es.u.co == quit) + break; + if (es.u.co == hangup) { + try_hangup(channels); + } else if (es.u.co == redirect) { + try_redirect(channels); + } + } + rebuild_channels(channels); + } + newtFormDestroy(form); + return 0; +} + +static int login(char *hostname) +{ + newtComponent form; + newtComponent cancel; + newtComponent login; + newtComponent username; + newtComponent password; + newtComponent label; + newtComponent ulabel; + newtComponent plabel; + const char *user; + const char *pass; + struct message *m; + struct newtExitStruct es; + char tmp[55]; + struct hostent *hp; + int res = -1; + + session.fd = socket(AF_INET, SOCK_STREAM, 0); + if (session.fd < 0) { + snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno)); + show_message("Socket failed", tmp); + return -1; + } + + snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname); + show_doing("Connecting....", tmp); + + + hp = gethostbyname(hostname); + if (!hp) { + snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname); + show_message("Host lookup failed", tmp); + return -1; + } + hide_doing(); + snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname); + show_doing("Connecting...", tmp); + + session.sin.sin_family = AF_INET; + session.sin.sin_port = htons(DEFAULT_MANAGER_PORT); + memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr)); + + if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) { + snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno)); + show_message("Connect Failed", tmp); + return -1; + } + + hide_doing(); + + login = newtButton(5, 6, "Login"); + cancel = newtButton(25, 6, "Cancel"); + newtCenteredWindow(40, 10, "Asterisk Manager Login"); + snprintf(tmp, sizeof(tmp), "Host: %s", hostname); + label = newtLabel(4,1, tmp); + + ulabel = newtLabel(4,2,"Username:"); + plabel = newtLabel(4,3,"Password:"); + + username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0); + password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN); + + form = newtForm(NULL, NULL, 0); + newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL); + newtFormRun(form, &es); + if (es.reason == NEWT_EXIT_COMPONENT) { + if (es.u.co == login) { + snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user); + show_doing("Logging in", tmp); + /* Check to see if the remote host supports MD5 Authentication */ + manager_action("Challenge", "AuthType: MD5\r\n"); + m = wait_for_response(10000); + if (m && !strcasecmp(get_header(m, "Response"), "Success")) { + char *challenge = get_header(m, "Challenge"); + int x; + int len = 0; + char md5key[256] = ""; + struct MD5Context md5; + unsigned char digest[16]; + MD5Init(&md5); + MD5Update(&md5, (unsigned char *)challenge, strlen(challenge)); + MD5Update(&md5, (unsigned char *)pass, strlen(pass)); + MD5Final(digest, &md5); + for (x=0; x<16; x++) + len += sprintf(md5key + len, "%2.2x", digest[x]); + manager_action("Login", + "AuthType: MD5\r\n" + "Username: %s\r\n" + "Key: %s\r\n", + user, md5key); + m = wait_for_response(10000); + hide_doing(); + if (!strcasecmp(get_header(m, "Response"), "Success")) { + res = 0; + } else { + show_message("Login Failed", get_header(m, "Message")); + } + } else { + memset(m, 0, sizeof(m)); + manager_action("Login", + "Username: %s\r\n" + "Secret: %s\r\n", + user, pass); + m = wait_for_response(10000); + hide_doing(); + if (m) { + if (!strcasecmp(get_header(m, "Response"), "Success")) { + res = 0; + } else { + show_message("Login Failed", get_header(m, "Message")); + } + } + } + } + } + newtFormDestroy(form); + return res; +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + fprintf(stderr, "Usage: astman <host>\n"); + exit(1); + } + newtInit(); + newtCls(); + newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc."); + newtPushHelpLine("Welcome to the Asterisk Manager!"); + if (login(argv[1])) { + newtFinished(); + exit(1); + } + manage_calls(argv[1]); + newtFinished(); + return 0; +} diff --git a/trunk/utils/build-extensions-conf.lua b/trunk/utils/build-extensions-conf.lua new file mode 100755 index 000000000..a3f159def --- /dev/null +++ b/trunk/utils/build-extensions-conf.lua @@ -0,0 +1,81 @@ +#!/usr/bin/env lua +--[[ + +This utility can be used to generate an extensions.conf file to match an +existing extensions.lua file. As an argument it takes the patch of the +extensions.lua file to read from, otherwise it uses +/etc/asterisk/extensions.lua. + +This script can also be used to automatically include extensions.lua in +extensions.conf via a #exec as well. + +#exec /usr/bin/build-extensions-conf.lua -c + +--]] + +usage = [[ + +Usage: + ]] .. arg[0] .. [[ [options] [extensions.lua path] + +This utility can generate an extensions.conf file with all of the contexts in +your extensions.lua file defined as including the Lua switch. This is useful +if you want to use your extensions.lua file exclusively. By using this utility +you dont't have to create each extension in extensions.conf manually. + +The resulting extensions.conf file is printed to standard output. + + --contexts-only, -c Don't print the [global] or [general] sections. This + is useful for including the generated file into an + existing extensions.conf via #include or #exec. + + --help, -h Print this message. + +]] + +extensions_file = "/etc/asterisk/extensions.lua" + +options = {} + +for k, v in ipairs(arg) do + if v:sub(1, 1) == "-" then + if v == "-h" or v == "--help" then + print("match") + options["help"] = true + elseif v == "-c" or v == "--contexts-only" then + options["contexts-only"] = true + end + else + options["extensions-file"] = v + end +end + +if options["help"] then + io.stderr:write(usage) + os.exit(0) +end + +if options["extensions-file"] then + extensions_file = options["extensions-file"] +end + +result, error_message = pcall(dofile, extensions_file) + +if not result then + io.stderr:write(error_message .. "\n") + os.exit(1) +end + +if not extensions then + io.stderr:write("Error: extensions table not found in '" .. extensions_file .. "'\n") + os.exit(1) +end + +if not options["contexts-only"] then + io.stdout:write("[general]\n\n[globals]\n\n") +end + +for context, extens in pairs(extensions) do + io.stdout:write("[" .. tostring(context) .. "]\nswitch => Lua\n\n") +end + diff --git a/trunk/utils/check_expr.c b/trunk/utils/check_expr.c new file mode 100644 index 000000000..00d00d802 --- /dev/null +++ b/trunk/utils/check_expr.c @@ -0,0 +1,459 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/ast_expr.h" + +#define AST_API_MODULE 1 +#include "asterisk/inline_api.h" + +#define AST_API_MODULE 1 +#include "asterisk/lock.h" + +#include "asterisk/strings.h" + +/* I included this from utils.c, so as not to have everything in that .c + file included */ +/*! + * core handler for dynamic strings. + * This is not meant to be called directly, but rather through the + * various wrapper macros + * ast_str_set(...) + * ast_str_append(...) + * ast_str_set_va(...) + * ast_str_append_va(...) + */ +int __ast_str_helper(struct ast_str **buf, size_t max_len, + int append, const char *fmt, va_list ap) +{ + int res, need; + int offset = (append && (*buf)->len) ? (*buf)->used : 0; + + if (max_len < 0) + max_len = (*buf)->len; /* don't exceed the allocated space */ + /* + * Ask vsnprintf how much space we need. Remember that vsnprintf + * does not count the final '\0' so we must add 1. + */ + res = vsnprintf((*buf)->str + offset, (*buf)->len - offset, fmt, ap); + + need = res + offset + 1; + /* + * If there is not enough space and we are below the max length, + * reallocate the buffer and return a message telling to retry. + */ + if (need > (*buf)->len && (max_len == 0 || (*buf)->len < max_len) ) { + if (max_len && max_len < need) /* truncate as needed */ + need = max_len; + else if (max_len == 0) /* if unbounded, give more room for next time */ + need += 16 + need/4; + if (ast_str_make_space(buf, need)) { + return AST_DYNSTR_BUILD_FAILED; + } + (*buf)->str[offset] = '\0'; /* Truncate the partial write. */ + + /* va_end() and va_start() must be done before calling + * vsnprintf() again. */ + return AST_DYNSTR_BUILD_RETRY; + } + /* update space used, keep in mind the truncation */ + (*buf)->used = (res + offset > (*buf)->len) ? (*buf)->len : res + offset; + + return res; +} +#ifndef DEBUG_THREADS +enum ast_lock_type { + AST_MUTEX, + AST_RDLOCK, + AST_WRLOCK, +}; +#endif +void ast_store_lock_info(enum ast_lock_type type, const char *filename, + int line_num, const char *func, const char *lock_name, void *lock_addr); +void ast_store_lock_info(enum ast_lock_type type, const char *filename, + int line_num, const char *func, const char *lock_name, void *lock_addr) +{ + /* not a lot to do in a standalone w/o threading! */ +} + +void ast_mark_lock_acquired(void); +void ast_mark_lock_acquired(void) +{ + /* not a lot to do in a standalone w/o threading! */ +} + +void ast_remove_lock_info(void *lock_addr); +void ast_remove_lock_info(void *lock_addr) +{ + /* not a lot to do in a standalone w/o threading! */ +} + +static int global_lineno = 1; +static int global_expr_count=0; +static int global_expr_max_size=0; +static int global_expr_tot_size=0; +static int global_warn_count=0; +static int global_OK_count=0; + +struct varz +{ + char varname[100]; /* a really ultra-simple, space-wasting linked list of var=val data */ + char varval[1000]; /* if any varname is bigger than 100 chars, or val greater than 1000, then **CRASH** */ + struct varz *next; +}; + +struct varz *global_varlist; + +/* Our own version of ast_log, since the expr parser uses it. */ + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6))); + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + + printf("LOG: lev:%d file:%s line:%d func: %s ", + level, file, line, function); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} +//void ast_register_file_version(const char *file, const char *version); +//void ast_unregister_file_version(const char *file); + +char *find_var(const char *varname); +void set_var(const char *varname, const char *varval); +unsigned int check_expr(char* buffer, char* error_report); +int check_eval(char *buffer, char *error_report); +void parse_file(const char *fname); + +void ast_register_file_version(const char *file, const char *version) { } +int ast_add_profile(const char *x, uint64_t scale) { return 0;} + +int ast_atomic_fetchadd_int_slow(volatile int *p, int v) +{ + int ret; + ret = *p; + *p += v; + return ret; +} + +void ast_unregister_file_version(const char *file) +{ +} + +char *find_var(const char *varname) /* the list should be pretty short, if there's any list at all */ +{ + struct varz *t; + for (t= global_varlist; t; t = t->next) { + if (!strcmp(t->varname, varname)) { + return t->varval; + } + } + return 0; +} + +void set_var(const char *varname, const char *varval); + +void set_var(const char *varname, const char *varval) +{ + struct varz *t = (struct varz*)calloc(1,sizeof(struct varz)); + if (!t) + return; + strcpy(t->varname, varname); + strcpy(t->varval, varval); + t->next = global_varlist; + global_varlist = t; +} + +unsigned int check_expr(char* buffer, char* error_report) +{ + char* cp; + unsigned int warn_found = 0; + + error_report[0] = 0; + + for (cp = buffer; *cp; ++cp) + { + switch (*cp) + { + case '"': + /* skip to the other end */ + while (*(++cp) && *cp != '"') ; + + if (*cp == 0) + { + fprintf(stderr, + "Trouble? Unterminated double quote found at line %d\n", + global_lineno); + } + break; + + case '>': + case '<': + case '!': + if ( (*(cp + 1) == '=') + && ( ( (cp > buffer) && (*(cp - 1) != ' ') ) || (*(cp + 2) != ' ') ) ) + { + char msg[200]; + snprintf(msg, + sizeof(msg), + "WARNING: line %d: '%c%c' operator not separated by spaces. This may lead to confusion. You may wish to use double quotes to quote the grouping it is in. Please check!\n", + global_lineno, *cp, *(cp + 1)); + strcat(error_report, msg); + ++global_warn_count; + ++warn_found; + } + break; + + case '|': + case '&': + case '=': + case '+': + case '-': + case '*': + case '/': + case '%': + case '?': + case ':': + if ( ( (cp > buffer) && (*(cp - 1) != ' ') ) || (*(cp + 1) != ' ') ) + { + char msg[200]; + snprintf(msg, + sizeof(msg), + "WARNING: line %d: '%c' operator not separated by spaces. This may lead to confusion. You may wish to use double quotes to quote the grouping it is in. Please check!\n", + global_lineno, *cp ); + strcat(error_report, msg); + ++global_warn_count; + ++warn_found; + } + break; + } + } + + return warn_found; +} + +int check_eval(char *buffer, char *error_report); + +struct ast_custom_function *ast_custom_function_find(const char *name); + +struct ast_custom_function *ast_custom_function_find(const char *name) +{ + return 0; +} + +int check_eval(char *buffer, char *error_report) +{ + char *cp, *ep; + char s[4096]; + char evalbuf[80000]; + int result; + + error_report[0] = 0; + ep = evalbuf; + + for (cp=buffer;*cp;cp++) { + if (*cp == '$' && *(cp+1) == '{') { + int brack_lev = 1; + char *xp= cp+2; + + while (*xp) { + if (*xp == '{') + brack_lev++; + else if (*xp == '}') + brack_lev--; + + if (brack_lev == 0) + break; + xp++; + } + if (*xp == '}') { + char varname[200]; + char *val; + + strncpy(varname,cp+2, xp-cp-2); + varname[xp-cp-2] = 0; + cp = xp; + val = find_var(varname); + if (val) { + char *z = val; + while (*z) + *ep++ = *z++; + } + else { + *ep++ = '5'; /* why not */ + *ep++ = '5'; + *ep++ = '5'; + } + } + else { + printf("Unterminated variable reference at line %d\n", global_lineno); + *ep++ = *cp; + } + } + else if (*cp == '\\') { + /* braindead simple elim of backslash */ + cp++; + *ep++ = *cp; + } + else + *ep++ = *cp; + } + *ep++ = 0; + + /* now, run the test */ + result = ast_expr(evalbuf, s, sizeof(s),NULL); + if (result) { + sprintf(error_report,"line %d, evaluation of $[ %s ] result: %s\n", global_lineno, evalbuf, s); + return 1; + } else { + sprintf(error_report,"line %d, evaluation of $[ %s ] result: ****SYNTAX ERROR****\n", global_lineno, evalbuf); + return 1; + } +} + + +void parse_file(const char *fname); + +void parse_file(const char *fname) +{ + FILE *f = fopen(fname,"r"); + FILE *l = fopen("expr2_log","w"); + int c1; + char last_char= 0; + char buffer[30000]; /* I sure hope no expr gets this big! */ + + if (!f) { + fprintf(stderr,"Couldn't open %s for reading... need an extensions.conf file to parse!\n",fname); + exit(20); + } + if (!l) { + fprintf(stderr,"Couldn't open 'expr2_log' file for writing... please fix and re-run!\n"); + exit(21); + } + + global_lineno = 1; + + while ((c1 = fgetc(f)) != EOF) { + if (c1 == '\n') + global_lineno++; + else if (c1 == '[') { + if (last_char == '$') { + /* bingo, an expr */ + int bracklev = 1; + int bufcount = 0; + int retval; + char error_report[30000]; + + while ((c1 = fgetc(f)) != EOF) { + if (c1 == '[') + bracklev++; + else if (c1 == ']') + bracklev--; + if (c1 == '\n') { + fprintf(l, "ERROR-- A newline in an expression? Weird! ...at line %d\n", global_lineno); + fclose(f); + fclose(l); + printf("--- ERROR --- A newline in the middle of an expression at line %d!\n", global_lineno); + } + + if (bracklev == 0) + break; + buffer[bufcount++] = c1; + } + if (c1 == EOF) { + fprintf(l, "ERROR-- End of File Reached in the middle of an Expr at line %d\n", global_lineno); + fclose(f); + fclose(l); + printf("--- ERROR --- EOF reached in middle of an expression at line %d!\n", global_lineno); + exit(22); + } + + buffer[bufcount] = 0; + /* update stats */ + global_expr_tot_size += bufcount; + global_expr_count++; + if (bufcount > global_expr_max_size) + global_expr_max_size = bufcount; + + retval = check_expr(buffer, error_report); /* check_expr should bump the warning counter */ + if (retval != 0) { + /* print error report */ + printf("Warning(s) at line %d, expression: $[%s]; see expr2_log file for details\n", + global_lineno, buffer); + fprintf(l, "%s", error_report); + } + else { + printf("OK -- $[%s] at line %d\n", buffer, global_lineno); + global_OK_count++; + } + error_report[0] = 0; + retval = check_eval(buffer, error_report); + fprintf(l, "%s", error_report); + } + } + last_char = c1; + } + printf("Summary:\n Expressions detected: %d\n Expressions OK: %d\n Total # Warnings: %d\n Longest Expr: %d chars\n Ave expr len: %d chars\n", + global_expr_count, + global_OK_count, + global_warn_count, + global_expr_max_size, + (global_expr_count) ? global_expr_tot_size/global_expr_count : 0); + + fclose(f); + fclose(l); +} + + +int main(int argc,char **argv) +{ + int argc1; + char *eq; + + if (argc < 2) { + printf("check_expr -- a program to look thru extensions.conf files for $[...] expressions,\n"); + printf(" and run them thru the parser, looking for problems\n"); + printf("Hey-- give me a path to an extensions.conf file!\n"); + printf(" You can also follow the file path with a series of variable decls,\n"); + printf(" of the form, varname=value, each separated from the next by spaces.\n"); + printf(" (this might allow you to avoid division by zero messages, check that math\n"); + printf(" is being done correctly, etc.)\n"); + printf(" Note that messages about operators not being surrounded by spaces is merely to alert\n"); + printf(" you to possible problems where you might be expecting those operators as part of a string.\n"); + printf(" (to include operators in a string, wrap with double quotes!)\n"); + + exit(19); + } + global_varlist = 0; + for (argc1=2;argc1 < argc; argc1++) { + if ((eq = strchr(argv[argc1],'='))) { + *eq = 0; + set_var(argv[argc1],eq+1); + } + } + + /* parse command args for x=y and set varz */ + + parse_file(argv[1]); + return 0; +} diff --git a/trunk/utils/clicompat.c b/trunk/utils/clicompat.c new file mode 100644 index 000000000..33f90fae1 --- /dev/null +++ b/trunk/utils/clicompat.c @@ -0,0 +1,16 @@ +/* + * Stubs for some cli functions used by the test routines. + * $Revision$ + */ +void ast_cli(int fd, const char *fmt, ...); +void ast_cli(int fd, const char *fmt, ...) +{ +} + +struct ast_cli_entry; + +int ast_cli_register_multiple(struct ast_cli_entry *e, int len); +int ast_cli_register_multiple(struct ast_cli_entry *e, int len) +{ + return 0; +} diff --git a/trunk/utils/conf2ael.c b/trunk/utils/conf2ael.c new file mode 100644 index 000000000..fbb8aa3b6 --- /dev/null +++ b/trunk/utils/conf2ael.c @@ -0,0 +1,692 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2007, Digium, Inc. + * + * Steve Murphy <murf@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* + * + * Reverse compile extensions.conf code into prototype AEL code + * + */ + +/*** MODULEINFO + <depend>res_ael_share</depend> + ***/ + +#include "asterisk.h" +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/paths.h" /* CONFIG_DIR */ +#include <locale.h> +#include <ctype.h> +#if !defined(SOLARIS) && !defined(__CYGWIN__) +#include <err.h> +#endif +#include <regex.h> + +#include "asterisk.h" +#include "asterisk/pbx.h" +#include "asterisk/ast_expr.h" +#include "asterisk/channel.h" +#include "asterisk/chanvars.h" +#include "asterisk/module.h" +#include "asterisk/app.h" +#include "asterisk/config.h" +#include "asterisk/options.h" +#include "asterisk/callerid.h" +#include "asterisk/ael_structs.h" +#include "asterisk/devicestate.h" +#include "asterisk/stringfields.h" +#include "asterisk/pval.h" +#include "asterisk/extconf.h" + +const char *ast_config_AST_CONFIG_DIR = "/etc/asterisk"; /* placeholder */ + +void get_start_stop(unsigned int *word, int bitsperword, int totalbits, int *start, int *end); +int all_bits_set(unsigned int *word, int bitsperword, int totalbits); +extern char *days[]; +extern char *months[]; + +char *config = "extensions.conf"; + +/* +static char *registrar = "conf2ael"; +static char userscontext[AST_MAX_EXTENSION] = "default"; +static int static_config = 0; +static int write_protect_config = 1; +static int autofallthrough_config = 0; +static int clearglobalvars_config = 0; +char ast_config_AST_SYSTEM_NAME[20] = ""; */ + +/*! Go no deeper than this through includes (not counting loops) */ +#define AST_PBX_MAX_STACK 128 +/* static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function); */ +//extern char ast_config_AST_CONFIG_DIR[PATH_MAX]; + +void ast_register_file_version(const char *file, const char *version) +{ +} + +void ast_unregister_file_version(const char *file) +{ +} +int ast_add_profile(const char *x, uint64_t scale) { return 0;} + +/* Our own version of ast_log, since the expr parser uses it. -- stolen from utils/check_expr.c */ +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6))); + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + + printf("LOG: lev:%d file:%s line:%d func: %s ", + level, file, line, function); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +/* stolen from pbx.c */ +struct ast_context; +struct ast_app; +#ifdef LOW_MEMORY +#define EXT_DATA_SIZE 256 +#else +#define EXT_DATA_SIZE 8192 +#endif + +#define SWITCH_DATA_LENGTH 256 + +#define VAR_BUF_SIZE 4096 + +#define VAR_NORMAL 1 +#define VAR_SOFTTRAN 2 +#define VAR_HARDTRAN 3 + +#define BACKGROUND_SKIP (1 << 0) +#define BACKGROUND_NOANSWER (1 << 1) +#define BACKGROUND_MATCHEXTEN (1 << 2) +#define BACKGROUND_PLAYBACK (1 << 3) + +/*! + \brief ast_exten: An extension + The dialplan is saved as a linked list with each context + having it's own linked list of extensions - one item per + priority. +*/ +struct ast_exten { + char *exten; /*!< Extension name */ + int matchcid; /*!< Match caller id ? */ + const char *cidmatch; /*!< Caller id to match for this extension */ + int priority; /*!< Priority */ + const char *label; /*!< Label */ + struct ast_context *parent; /*!< The context this extension belongs to */ + const char *app; /*!< Application to execute */ + struct ast_app *cached_app; /*!< Cached location of application */ + void *data; /*!< Data to use (arguments) */ + void (*datad)(void *); /*!< Data destructor */ + struct ast_exten *peer; /*!< Next higher priority with our extension */ + const char *registrar; /*!< Registrar */ + struct ast_exten *next; /*!< Extension with a greater ID */ + char stuff[0]; +}; + + +/*! \brief ast_include: include= support in extensions.conf */ +struct ast_include { + const char *name; + const char *rname; /*!< Context to include */ + const char *registrar; /*!< Registrar */ + int hastime; /*!< If time construct exists */ + struct ast_timing timing; /*!< time construct */ + struct ast_include *next; /*!< Link them together */ + char stuff[0]; +}; + +/*! \brief ast_sw: Switch statement in extensions.conf */ +struct ast_sw { + char *name; + const char *registrar; /*!< Registrar */ + char *data; /*!< Data load */ + int eval; + AST_LIST_ENTRY(ast_sw) list; + char *tmpdata; + char stuff[0]; +}; + +/*! \brief ast_ignorepat: Ignore patterns in dial plan */ +struct ast_ignorepat { + const char *registrar; + struct ast_ignorepat *next; + const char pattern[0]; +}; + +/*! \brief ast_context: An extension context */ +struct ast_context { + ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */ + struct ast_exten *root; /*!< The root of the list of extensions */ + struct ast_context *next; /*!< Link them together */ + struct ast_include *includes; /*!< Include other contexts */ + struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */ + const char *registrar; /*!< Registrar */ + AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */ + ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */ + char name[0]; /*!< Name of the context */ +}; + + +/*! \brief ast_app: A registered application */ +struct ast_app { + int (*execute)(struct ast_channel *chan, void *data); + const char *synopsis; /*!< Synopsis text for 'show applications' */ + const char *description; /*!< Description (help text) for 'show application <name>' */ + AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */ + struct module *module; /*!< Module this app belongs to */ + char name[0]; /*!< Name of the application */ +}; + +/*! \brief ast_state_cb: An extension state notify register item */ +struct ast_state_cb { + int id; + void *data; + ast_state_cb_type callback; + struct ast_state_cb *next; +}; + +/*! \brief Structure for dial plan hints + + \note Hints are pointers from an extension in the dialplan to one or + more devices (tech/name) + - See \ref AstExtState +*/ +struct ast_hint { + struct ast_exten *exten; /*!< Extension */ + int laststate; /*!< Last known state */ + struct ast_state_cb *callbacks; /*!< Callback list for this extension */ + AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */ +}; + +struct store_hint { + char *context; + char *exten; + struct ast_state_cb *callbacks; + int laststate; + AST_LIST_ENTRY(store_hint) list; + char data[1]; +}; + +AST_LIST_HEAD(store_hints, store_hint); + +static const struct cfextension_states { + int extension_state; + const char * const text; +} extension_states[] = { + { AST_EXTENSION_NOT_INUSE, "Idle" }, + { AST_EXTENSION_INUSE, "InUse" }, + { AST_EXTENSION_BUSY, "Busy" }, + { AST_EXTENSION_UNAVAILABLE, "Unavailable" }, + { AST_EXTENSION_RINGING, "Ringing" }, + { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" }, + { AST_EXTENSION_ONHOLD, "Hold" }, + { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" } +}; +#define STATUS_NO_CONTEXT 1 +#define STATUS_NO_EXTENSION 2 +#define STATUS_NO_PRIORITY 3 +#define STATUS_NO_LABEL 4 +#define STATUS_SUCCESS 5 + +extern struct ast_context *local_contexts; +extern struct ast_context *contexts; + + +struct ast_custom_function *ast_custom_function_find(const char *name); + + +struct ast_custom_function *ast_custom_function_find(const char *name) +{ + return 0; /* in "standalone" mode, functions are just not avail */ +} + + +struct profile_entry { + const char *name; + uint64_t scale; /* if non-zero, values are scaled by this */ + int64_t mark; + int64_t value; + int64_t events; +}; + +struct profile_data { + int entries; + int max_size; + struct profile_entry e[0]; +}; + +static int bit_at(unsigned int *word, int bitsperword, int bitnum) +{ + return word[bitnum/bitsperword] & (1 << (bitnum % bitsperword)); +} + +void get_start_stop(unsigned int *word, int bitsperword, int totalbits, int *start, int *end) +{ + int i; + int thisbit, thatbit = bit_at(word, bitsperword, totalbits-1); + + for (i=0; i<totalbits; i++) { + thisbit = bit_at(word, bitsperword, i); + + if (thisbit != thatbit ) { + if (thisbit) { + *start = i; + } else { + *end = i; + } + } + thatbit = thisbit; + } +} + +int all_bits_set(unsigned int *word, int bitsperword, int totalbits ) +{ + + int i, total=totalbits/bitsperword,bitmask = 0; + + for (i=0; i<bitsperword; i++) + { + bitmask |= (1 << i); + } + + for (i=0; i<total; i++) + { + if (word[i] != bitmask) + return 0; + } + return 1; +} + + +int main(int argc, char **argv) +{ + struct ast_context *tmp; + struct ast_exten *e, *eroot; + pval *tree, *tmptree, *sws; + struct ast_include *tmpi; + struct ast_sw *sw = 0; + struct ast_ignorepat *ipi; + pval *incl=0; + int localdir = 0, i; + + tree = 0; + tmptree = 0; + + /* process the command line args */ + for (i=1; i<argc; i++) + { + if (strcmp(argv[i],"-d")==0) + localdir =1; + } + + /* 3 simple steps: */ + /* 1. read in the extensions.conf config file + * 2. traverse, and build an AEL tree + * 3. Output the AEL tree into a file + */ + printf("WARNING: This is an EXTREMELY preliminary version of a program\n"); + printf(" that will someday hopefully do a thoughful and intelligent\n"); + printf(" job of transforming your extensions.conf file into an\n"); + printf(" extensions.ael file.\n"); + printf(" This version has absolutely no intelligence, and pretty\n"); + printf(" much just does a direct conversion\n"); + printf(" The result will most likely need careful attention to\n"); + printf(" finish the job!!!!!\n"); + + if (!localdir) + printf(" (You could use -d the use the extensions.conf in the current directory!)\n"); + + strcpy((char *)ast_config_AST_CONFIG_DIR,"/etc/asterisk"); + + printf("Loading %s/%s...\n", ast_config_AST_CONFIG_DIR, config); + + if (!localdir) + localized_use_conf_dir(); + localized_pbx_load_module(); + + printf("... Done!\n"); + + tmp = 0; + while ((tmp = localized_walk_contexts(tmp)) ) { + printf("Context: %s\n", tmp->name); + } + printf("=========\n"); + tmp = 0; + while ((tmp = localized_walk_contexts(tmp)) ) { + /* printf("Context: %s\n", tmp->name); */ + tmptree = pvalCreateNode(PV_CONTEXT); + if (!tree) + tree = tmptree; + else + pvalTopLevAddObject(tree, tmptree); + + pvalContextSetName(tmptree, ast_strdup(tmp->name)); + + if (tmp->includes) { + incl = pvalCreateNode(PV_INCLUDES); + pvalContextAddStatement(tmptree, incl); + for (tmpi = tmp->includes; tmpi; ) { /* includes */ + if (strchr(tmpi->name,'|')==0) { + if (tmpi->hastime) + { + char timerange[15]; + char dowrange[10]; + char domrange[10]; + char monrange[10]; + int startbit=0, endbit=0; + + if (all_bits_set(tmpi->timing.minmask, 30, 720)) + strcpy(timerange, "*"); + else { + int hr, min; + char tbuf[20]; + get_start_stop(tmpi->timing.minmask, 30, 720, &startbit, &endbit); + hr = startbit/30; + min = (startbit % 30) * 2; + sprintf(tbuf,"%02d:%02d", hr, min); + strcpy(timerange, tbuf); + hr = endbit/30; + min = (endbit % 30) * 2; + sprintf(tbuf,"%02d:%02d", hr, min); + strcat(timerange,"-"); + strcat(timerange,tbuf); + } + + if (all_bits_set(&tmpi->timing.dowmask, 7, 7)) + strcpy(dowrange, "*"); + else { + get_start_stop(&tmpi->timing.dowmask, 7, 7, &startbit, &endbit); + strcpy(dowrange, days[startbit]); + strcat(dowrange,"-"); + strcat(dowrange, days[endbit]); + } + + if (all_bits_set(&tmpi->timing.monthmask, 12, 12)) + strcpy(monrange, "*"); + else { + get_start_stop(&tmpi->timing.monthmask, 12, 12, &startbit, &endbit); + strcpy(monrange, months[startbit]); + strcat(monrange,"-"); + strcat(monrange, months[endbit]); + } + + if (all_bits_set(&tmpi->timing.daymask, 31, 31)) + strcpy(domrange, "*"); + else { + char tbuf[20]; + get_start_stop(&tmpi->timing.daymask, 31, 31, &startbit, &endbit); + sprintf(tbuf,"%d", startbit); + strcpy(domrange, tbuf); + strcat(domrange,"-"); + sprintf(tbuf,"%d", endbit); + strcat(domrange, tbuf); + } + /* now all 4 fields are set; what do we do? */ + pvalIncludesAddIncludeWithTimeConstraints(incl, strdup(tmpi->name), strdup(timerange), strdup(domrange), strdup(dowrange), strdup(monrange)); + + } else { + pvalIncludesAddInclude(incl, strdup(tmpi->name)); + } + } else { /* it appears the timing constraint info is tacked onto the name, carve it up and divvy it out */ + char *dow,*dom,*mon; + char *all = strdup(tmpi->name); + char *hr = strchr(all,'|'); + if (hr) { + *hr++ = 0; + dow = strchr(hr,'|'); + if (dow) { + *dow++ = 0; + dom = strchr(dow,'|'); + if (dom) { + *dom++ = 0; + mon = strchr(dom,'|'); + if (mon) { + *mon++ = 0; + /* now all 4 fields are set; what do we do? */ + pvalIncludesAddIncludeWithTimeConstraints(incl, all, hr, dow, dom, mon); + /* the original data is always best to keep (no 2-min rounding) */ + } else { + ast_log(LOG_ERROR,"No month spec attached to include!\n"); + } + } else { + ast_log(LOG_ERROR,"No day of month spec attached to include!\n"); + } + } else { + ast_log(LOG_ERROR,"No day of week spec attached to include!\n"); + } + } + } + tmpi = tmpi->next; + } + } + for (ipi = tmp->ignorepats; ipi; ) { /* ignorepats */ + incl = pvalCreateNode(PV_IGNOREPAT); + pvalIgnorePatSetPattern(incl,(char *)ipi->pattern); + pvalContextAddStatement(tmptree, incl); + ipi = ipi->next; + } + eroot=0; + while ( (eroot = localized_walk_context_extensions(tmp, eroot)) ) { + pval *exten = pvalCreateNode(PV_EXTENSION); + pvalContextAddStatement(tmptree, exten); + pvalExtenSetName(exten, ast_strdup(eroot->exten)); + + if (eroot->peer) { + pval *block = pvalCreateNode(PV_STATEMENTBLOCK); + pvalExtenSetStatement(exten, block); + + e = 0; + while ( (e = localized_walk_extension_priorities(eroot, e)) ) { + + pval *statemnt = pvalCreateNode(PV_APPLICATION_CALL); + pval *args = pvalCreateNode(PV_WORD); + + /* printf(" %s(%s)\n", e->app, (char*)e->data); */ + + pvalAppCallSetAppName(statemnt, ast_strdup(e->app)); + pvalWordSetString(args, ast_strdup(e->data)); + pvalAppCallAddArg(statemnt, args); + + pvalStatementBlockAddStatement(block, statemnt); + } + } else if (eroot->priority == -1) { + + pval *statemnt = pvalCreateNode(PV_APPLICATION_CALL); + pval *args = pvalCreateNode(PV_WORD); + + /* printf("Mike, we have a hint on exten %s with data %s\n", eroot->exten, eroot->app); */ + + pvalAppCallSetAppName(statemnt, "NoOp"); + pvalWordSetString(args, ast_strdup(eroot->app)); + + + pvalExtenSetStatement(exten, statemnt); + pvalExtenSetHints(exten, ast_strdup(eroot->app)); + } else { + + pval *statemnt = pvalCreateNode(PV_APPLICATION_CALL); + pval *args = pvalCreateNode(PV_WORD); + + /* printf(" %s (%s)\n", eroot->app, (char *)eroot->data); */ + + pvalAppCallSetAppName(statemnt, ast_strdup(eroot->app)); + pvalWordSetString(args, ast_strdup(eroot->data)); + + + pvalAppCallAddArg(statemnt, args); + pvalExtenSetStatement(exten, statemnt); + } + + /* printf(" extension: %s\n", eroot->exten); */ + } + if (AST_LIST_FIRST(&tmp->alts)) { + sws = pvalCreateNode(PV_SWITCHES); + pvalContextAddStatement(tmptree,sws); + + sw = 0; + while ((sw = localized_walk_context_switches(tmp,sw)) ) { + pvalSwitchesAddSwitch(sws, ast_strdup(sw->name)); + } + } + } + printf("Generating aelout.ael file...\n"); + + ael2_print("aelout.ael", tree); + + printf("...Done!\n"); + return 0; +} + + +/* ==================================== for linking internal stuff to external stuff */ + +int pbx_builtin_setvar(struct ast_channel *chan, void *data) +{ + return localized_pbx_builtin_setvar(chan, data); +} + +void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count); +void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count) +{ + if (cp1 && *cp1) + strncpy(cp2,cp1,AST_MAX_EXTENSION); /* Right now, this routine is ONLY being called for + a possible var substitution on extension names, + so....! */ + else + *cp2 = 0; +} + +int ast_add_extension2(struct ast_context *con, + int replace, const char *extension, int priority, const char *label, const char *callerid, + const char *application, void *data, void (*datad)(void *), + const char *registrar) +{ + return localized_add_extension2(con, replace, extension, priority, label, callerid, application, data, datad, registrar); +} + +int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar) +{ + + return localized_context_add_ignorepat2(con, value, registrar); +} + +int ast_context_add_switch2(struct ast_context *con, const char *value, + const char *data, int eval, const char *registrar) +{ + + return localized_context_add_switch2(con, value, data, eval, registrar); +} + +int ast_context_add_include2(struct ast_context *con, const char *value, + const char *registrar) +{ + + return localized_context_add_include2(con, value,registrar); +} + +struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar) +{ + printf("Creating context %s, registrar=%s\n", name, registrar); + + return localized_context_create(extcontexts, name, registrar); +} + +struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar) +{ + printf("find/Creating context %s, registrar=%s\n", name, registrar); + + return localized_context_create(extcontexts, name, registrar); +} + +void ast_cli_register_multiple(void); + +void ast_cli_register_multiple(void) +{ +} + +void ast_module_register(const struct ast_module_info *x) +{ +} + +void ast_module_unregister(const struct ast_module_info *x) +{ +} + +void ast_cli_unregister_multiple(void); + +void ast_cli_unregister_multiple(void) +{ +} + +struct ast_context *ast_walk_contexts(struct ast_context *con); +struct ast_context *ast_walk_contexts(struct ast_context *con) +{ + return localized_walk_contexts(con); +} + +void ast_context_destroy(struct ast_context *con, const char *registrar); + +void ast_context_destroy(struct ast_context *con, const char *registrar) +{ + return localized_context_destroy(con, registrar); +} + +int ast_context_verify_includes(struct ast_context *con); + +int ast_context_verify_includes(struct ast_context *con) +{ + return localized_context_verify_includes(con); +} + +void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar); + +void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar) +{ + localized_merge_contexts_and_delete(extcontexts, registrar); +} + +struct ast_exten *pbx_find_extension(struct ast_channel *chan, + struct ast_context *bypass, + struct pbx_find_info *q, + const char *context, + const char *exten, + int priority, + const char *label, + const char *callerid, + enum ext_match_t action); + +struct ast_exten *pbx_find_extension(struct ast_channel *chan, + struct ast_context *bypass, + struct pbx_find_info *q, + const char *context, + const char *exten, + int priority, + const char *label, + const char *callerid, + enum ext_match_t action) +{ + return localized_find_extension(bypass, q, context, exten, priority, label, callerid, action); +} + diff --git a/trunk/utils/expr2.testinput b/trunk/utils/expr2.testinput new file mode 100644 index 000000000..9e1532b27 --- /dev/null +++ b/trunk/utils/expr2.testinput @@ -0,0 +1,126 @@ +2 + 2 + 2 + 2 + +2 - 4 +4 - 2 +-4 - -2 +4 + 2 * 8 +(4 + 2) * 8 +4 + (2 * 8) +4 + (2 * 8) ? 3 :: 6 +4 + 8 / 2 +4 + 8 / 3 +(4+8) / 3 +4 + 8 % 3 +4 + 9 % 3 +(4+9) %3 +(4+8) %3 +(4+9) %3 +(4+8) %3 +(4+9) % 3 +(4+8) % 3 +(4+9) % 3 +(4+8) % 3 +(4+9)% 3 +(4+8)% 3 +(4+9)% 3 +(4+8)% 3 +4 & 4 +0 & 4 +0 & 0 +2 | 0 +2 | 4 +0 | 0 +!0 | 0 +!4 | 0 +4 | !0 +!4 | !0 +3 < 4 +4 < 3 +3 > 4 +4 > 3 +3 = 3 +3 = 4 +3 != 3 +3 != 4 +3 >= 4 +3 >= 3 +4 >= 3 +3 <= 4 +4 <= 3 +4 <= 4 +3 > 4 & 4 < 3 +4 > 3 & 3 < 4 +x = x +y = x +x != y +x != x +"Something interesting" =~ interesting +"Something interesting" =~ Something +"Something interesting" : Something +"Something interesting" : interesting +"Something interesting" =~ "interesting" +"Something interesting" =~ "Something" +"Something interesting" : "Something" +"Something interesting" : "interesting" +"Something interesting" =~ (interesting) +"Something interesting" =~ (Something) +"Something interesting" : (Something) +"Something interesting" : (interesting) +"Something interesting" =~ "\(interesting\)" +"Something interesting" =~ "\(Something\)" +"Something interesting" : "\(Something\)" +"Something interesting" : "\(interesting\)" +"011043567857575" : "011\(..\)" +"9011043567857575" : "011\(..\)" +"011043567857575" =~ "011\(..\)" +"9011043567857575" =~ "011\(..\)" +"Something interesting" =~ (interesting) +"Something interesting" =~ (Something) +"Something interesting" : (Something) +"Something interesting" : (interesting) +"Something interesting" =~ "(interesting)" +"Something interesting" =~ "(Something)" +"Something interesting" : "(Something)" +"Something interesting" : "(interesting)" +"011043567857575" : "011(..)" +"9011043567857575" : "011(..)" +"011043567857575" =~ "011(..)" +"9011043567857575" =~ "011(..)" +3 +something +043 +2.1+4.2 +1.500003+1.4999999999999898989898989898989898989889898 +1/4 +2.3 + COS(3.141592653) +REMAINDER(13,3) +2.3 + SIN(3.1415823) +TAN(45) + 2.3 +POW(10.0,4.0) +SQRT(4) +SQRT(2) +FLOOR(2.4) +FLOOR(2.6) +CEIL(2.4) +CEIL(2.6) +ROUND(2.4) +ROUND(2.5) +ROUND(2.6) +RINT(2.4) +RINT(2.5) +RINT(2.6) +TRUNC(2.4) +TRUNC(2.5) +TRUNC(2.6) +EXP(1.0) +EXP2(1.0) +LOG(10) +LOG2(10) +LOG10(10) +ATAN2(4,5) +ACOS(12) +ASIN(1) +ATAN(10) +SQRT(2)*SQRT(2) +MATH(3*9) diff --git a/trunk/utils/extconf.c b/trunk/utils/extconf.c new file mode 100644 index 000000000..c5b9fbdea --- /dev/null +++ b/trunk/utils/extconf.c @@ -0,0 +1,6211 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006, Digium, Inc. + * + * Steve Murphy <murf@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + + +/* + * + * A condensation of the pbx_config stuff, to read into exensions.conf, and provide an interface to the data there, + * for operations outside of asterisk. A huge, awful hack. + * + */ + +#include "asterisk/compat.h" +#include "asterisk/paths.h" /* we use AST_CONFIG_DIR */ + +#include <errno.h> +#include <time.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <stdarg.h> +#include <string.h> +#include <locale.h> +#include <ctype.h> +#if !defined(SOLARIS) && !defined(__CYGWIN__) +#include <err.h> +#endif +#include <regex.h> +#include <limits.h> +#include <pthread.h> +#include <netdb.h> +#include <sys/param.h> +#define ASINCLUDE_GLOB 1 +#ifdef AST_INCLUDE_GLOB +#if defined(__Darwin__) || defined(__CYGWIN__) +#define GLOB_ABORTED GLOB_ABEND +#endif +# include <glob.h> +#endif + +#define AST_API_MODULE 1 /* gimme the inline defs! */ +struct ast_channel +{ + char x; /* basically empty! */ +}; + + + +#include "asterisk/inline_api.h" +#include "asterisk/endian.h" +#include "asterisk/ast_expr.h" +#include "asterisk/ael_structs.h" +#include "asterisk/pval.h" + +/* logger.h */ +#define EVENTLOG "event_log" +#define QUEUELOG "queue_log" + +#define DEBUG_M(a) { \ + a; \ +} + +#define VERBOSE_PREFIX_1 " " +#define VERBOSE_PREFIX_2 " == " +#define VERBOSE_PREFIX_3 " -- " +#define VERBOSE_PREFIX_4 " > " + +/* IN CONFLICT: void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) + __attribute__ ((format (printf, 5, 6))); */ + +static void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6))); + + +void ast_backtrace(void); + +void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...) + __attribute__ ((format (printf, 5, 6))); + +/* IN CONFLICT: void ast_verbose(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); */ + +int ast_register_verbose(void (*verboser)(const char *string)); +int ast_unregister_verbose(void (*verboser)(const char *string)); + +void ast_console_puts(const char *string); + +void ast_console_puts_mutable(const char *string); +void ast_console_toggle_mute(int fd); + +#define _A_ __FILE__, __LINE__, __PRETTY_FUNCTION__ + +#ifdef LOG_DEBUG +#undef LOG_DEBUG +#endif +#define __LOG_DEBUG 0 +#define LOG_DEBUG __LOG_DEBUG, _A_ + +#ifdef LOG_EVENT +#undef LOG_EVENT +#endif +#define __LOG_EVENT 1 +#define LOG_EVENT __LOG_EVENT, _A_ + +#ifdef LOG_NOTICE +#undef LOG_NOTICE +#endif +#define __LOG_NOTICE 2 +#define LOG_NOTICE __LOG_NOTICE, _A_ + +#ifdef LOG_WARNING +#undef LOG_WARNING +#endif +#define __LOG_WARNING 3 +#define LOG_WARNING __LOG_WARNING, _A_ + +#ifdef LOG_ERROR +#undef LOG_ERROR +#endif +#define __LOG_ERROR 4 +#define LOG_ERROR __LOG_ERROR, _A_ + +#ifdef LOG_VERBOSE +#undef LOG_VERBOSE +#endif +#define __LOG_VERBOSE 5 +#define LOG_VERBOSE __LOG_VERBOSE, _A_ + +#ifdef LOG_DTMF +#undef LOG_DTMF +#endif +#define __LOG_DTMF 6 +#define LOG_DTMF __LOG_DTMF, _A_ + +/* from utils.h */ + +static unsigned int __unsigned_int_flags_dummy; + +struct ast_flags { /* stolen from utils.h */ + unsigned int flags; +}; +#define ast_test_flag(p,flag) ({ \ + typeof ((p)->flags) __p = (p)->flags; \ + typeof (__unsigned_int_flags_dummy) __x = 0; \ + (void) (&__p == &__x); \ + ((p)->flags & (flag)); \ + }) + +#define ast_set2_flag(p,value,flag) do { \ + typeof ((p)->flags) __p = (p)->flags; \ + typeof (__unsigned_int_flags_dummy) __x = 0; \ + (void) (&__p == &__x); \ + if (value) \ + (p)->flags |= (flag); \ + else \ + (p)->flags &= ~(flag); \ + } while (0) + + +#ifdef __AST_DEBUG_MALLOC +static void ast_free(void *ptr) attribute_unused; +static void ast_free(void *ptr) +{ + free(ptr); +} +#else +#define ast_free free +#endif + +#ifndef __AST_DEBUG_MALLOC + +#define MALLOC_FAILURE_MSG \ + ast_log(LOG_ERROR, "Memory Allocation Failure in function %s at line %d of %s\n", func, lineno, file); +/*! + * \brief A wrapper for malloc() + * + * ast_malloc() is a wrapper for malloc() that will generate an Asterisk log + * message in the case that the allocation fails. + * + * The argument and return value are the same as malloc() + */ +#define ast_malloc(len) \ + _ast_malloc((len), __FILE__, __LINE__, __PRETTY_FUNCTION__) + +AST_INLINE_API( +void * attribute_malloc _ast_malloc(size_t len, const char *file, int lineno, const char *func), +{ + void *p; + + if (!(p = malloc(len))) + MALLOC_FAILURE_MSG; + + return p; +} +) + +/*! + * \brief A wrapper for calloc() + * + * ast_calloc() is a wrapper for calloc() that will generate an Asterisk log + * message in the case that the allocation fails. + * + * The arguments and return value are the same as calloc() + */ +#define ast_calloc(num, len) \ + _ast_calloc((num), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__) + +AST_INLINE_API( +void * attribute_malloc _ast_calloc(size_t num, size_t len, const char *file, int lineno, const char *func), +{ + void *p; + + if (!(p = calloc(num, len))) + MALLOC_FAILURE_MSG; + + return p; +} +) + +/*! + * \brief A wrapper for calloc() for use in cache pools + * + * ast_calloc_cache() is a wrapper for calloc() that will generate an Asterisk log + * message in the case that the allocation fails. When memory debugging is in use, + * the memory allocated by this function will be marked as 'cache' so it can be + * distinguished from normal memory allocations. + * + * The arguments and return value are the same as calloc() + */ +#define ast_calloc_cache(num, len) \ + _ast_calloc((num), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__) + +/*! + * \brief A wrapper for realloc() + * + * ast_realloc() is a wrapper for realloc() that will generate an Asterisk log + * message in the case that the allocation fails. + * + * The arguments and return value are the same as realloc() + */ +#define ast_realloc(p, len) \ + _ast_realloc((p), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__) + +AST_INLINE_API( +void * attribute_malloc _ast_realloc(void *p, size_t len, const char *file, int lineno, const char *func), +{ + void *newp; + + if (!(newp = realloc(p, len))) + MALLOC_FAILURE_MSG; + + return newp; +} +) + +/*! + * \brief A wrapper for strdup() + * + * ast_strdup() is a wrapper for strdup() that will generate an Asterisk log + * message in the case that the allocation fails. + * + * ast_strdup(), unlike strdup(), can safely accept a NULL argument. If a NULL + * argument is provided, ast_strdup will return NULL without generating any + * kind of error log message. + * + * The argument and return value are the same as strdup() + */ +#define ast_strdup(str) \ + _ast_strdup((str), __FILE__, __LINE__, __PRETTY_FUNCTION__) + +AST_INLINE_API( +char * attribute_malloc _ast_strdup(const char *str, const char *file, int lineno, const char *func), +{ + char *newstr = NULL; + + if (str) { + if (!(newstr = strdup(str))) + MALLOC_FAILURE_MSG; + } + + return newstr; +} +) + +/*! + * \brief A wrapper for strndup() + * + * ast_strndup() is a wrapper for strndup() that will generate an Asterisk log + * message in the case that the allocation fails. + * + * ast_strndup(), unlike strndup(), can safely accept a NULL argument for the + * string to duplicate. If a NULL argument is provided, ast_strdup will return + * NULL without generating any kind of error log message. + * + * The arguments and return value are the same as strndup() + */ +#define ast_strndup(str, len) \ + _ast_strndup((str), (len), __FILE__, __LINE__, __PRETTY_FUNCTION__) + +AST_INLINE_API( +char * attribute_malloc _ast_strndup(const char *str, size_t len, const char *file, int lineno, const char *func), +{ + char *newstr = NULL; + + if (str) { + if (!(newstr = strndup(str, len))) + MALLOC_FAILURE_MSG; + } + + return newstr; +} +) + +/*! + * \brief A wrapper for asprintf() + * + * ast_asprintf() is a wrapper for asprintf() that will generate an Asterisk log + * message in the case that the allocation fails. + * + * The arguments and return value are the same as asprintf() + */ +#define ast_asprintf(ret, fmt, ...) \ + _ast_asprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, fmt, __VA_ARGS__) + +AST_INLINE_API( +int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...), +{ + int res; + va_list ap; + + va_start(ap, fmt); + if ((res = vasprintf(ret, fmt, ap)) == -1) + MALLOC_FAILURE_MSG; + va_end(ap); + + return res; +} +) + +/*! + * \brief A wrapper for vasprintf() + * + * ast_vasprintf() is a wrapper for vasprintf() that will generate an Asterisk log + * message in the case that the allocation fails. + * + * The arguments and return value are the same as vasprintf() + */ +#define ast_vasprintf(ret, fmt, ap) \ + _ast_vasprintf((ret), __FILE__, __LINE__, __PRETTY_FUNCTION__, (fmt), (ap)) + +AST_INLINE_API( +int _ast_vasprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, va_list ap), +{ + int res; + + if ((res = vasprintf(ret, fmt, ap)) == -1) + MALLOC_FAILURE_MSG; + + return res; +} +) + +#else + +/* If astmm is in use, let it handle these. Otherwise, it will report that + all allocations are coming from this header file */ + +#define ast_malloc(a) malloc(a) +#define ast_calloc(a,b) calloc(a,b) +#define ast_realloc(a,b) realloc(a,b) +#define ast_strdup(a) strdup(a) +#define ast_strndup(a,b) strndup(a,b) +#define ast_asprintf(a,b,c) asprintf(a,b,c) +#define ast_vasprintf(a,b,c) vasprintf(a,b,c) + +#endif /* AST_DEBUG_MALLOC */ + +#if !defined(ast_strdupa) && defined(__GNUC__) +/*! + \brief duplicate a string in memory from the stack + \param s The string to duplicate + + This macro will duplicate the given string. It returns a pointer to the stack + allocatted memory for the new string. +*/ +#define ast_strdupa(s) \ + (__extension__ \ + ({ \ + const char *__old = (s); \ + size_t __len = strlen(__old) + 1; \ + char *__new = __builtin_alloca(__len); \ + memcpy (__new, __old, __len); \ + __new; \ + })) +#endif + + +/* from config.c */ + +#define MAX_NESTED_COMMENTS 128 +#define COMMENT_START ";--" +#define COMMENT_END "--;" +#define COMMENT_META ';' +#define COMMENT_TAG '-' + +static char *extconfig_conf = "extconfig.conf"; + +/*! Growable string buffer */ +static char *comment_buffer; /*!< this will be a comment collector.*/ +static int comment_buffer_size; /*!< the amount of storage so far alloc'd for the comment_buffer */ + +static char *lline_buffer; /*!< A buffer for stuff behind the ; */ +static int lline_buffer_size; + +#define CB_INCR 250 + +struct ast_comment { + struct ast_comment *next; + char cmt[0]; +}; + +static void CB_INIT(void) +{ + if (!comment_buffer) { + comment_buffer = ast_malloc(CB_INCR); + if (!comment_buffer) + return; + comment_buffer[0] = 0; + comment_buffer_size = CB_INCR; + lline_buffer = ast_malloc(CB_INCR); + if (!lline_buffer) + return; + lline_buffer[0] = 0; + lline_buffer_size = CB_INCR; + } else { + comment_buffer[0] = 0; + lline_buffer[0] = 0; + } +} + +static void CB_ADD(char *str) +{ + int rem = comment_buffer_size - strlen(comment_buffer) - 1; + int siz = strlen(str); + if (rem < siz+1) { + comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + siz + 1); + if (!comment_buffer) + return; + comment_buffer_size += CB_INCR+siz+1; + } + strcat(comment_buffer,str); +} + +static void CB_ADD_LEN(char *str, int len) +{ + int cbl = strlen(comment_buffer) + 1; + int rem = comment_buffer_size - cbl; + if (rem < len+1) { + comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + len + 1); + if (!comment_buffer) + return; + comment_buffer_size += CB_INCR+len+1; + } + strncat(comment_buffer,str,len); + comment_buffer[cbl+len-1] = 0; +} + +static void LLB_ADD(char *str) +{ + int rem = lline_buffer_size - strlen(lline_buffer) - 1; + int siz = strlen(str); + if (rem < siz+1) { + lline_buffer = ast_realloc(lline_buffer, lline_buffer_size + CB_INCR + siz + 1); + if (!lline_buffer) + return; + lline_buffer_size += CB_INCR + siz + 1; + } + strcat(lline_buffer,str); +} + +static void CB_RESET(void ) +{ + comment_buffer[0] = 0; + lline_buffer[0] = 0; +} + +/*! \brief Keep track of how many threads are currently trying to wait*() on + * a child process */ +static unsigned int safe_system_level = 0; +static void *safe_system_prev_handler; + +/*! \brief NULL handler so we can collect the child exit status */ +static void null_sig_handler(int signal) +{ + +} + +void ast_replace_sigchld(void); + +void ast_replace_sigchld(void) +{ + unsigned int level; + + level = safe_system_level++; + + /* only replace the handler if it has not already been done */ + if (level == 0) + safe_system_prev_handler = signal(SIGCHLD, null_sig_handler); + +} + +void ast_unreplace_sigchld(void); + +void ast_unreplace_sigchld(void) +{ + unsigned int level; + + level = --safe_system_level; + + /* only restore the handler if we are the last one */ + if (level == 0) + signal(SIGCHLD, safe_system_prev_handler); + +} + +int ast_safe_system(const char *s); + +int ast_safe_system(const char *s) +{ + pid_t pid; +#ifdef HAVE_WORKING_FORK + int x; +#endif + int res; + struct rusage rusage; + int status; + +#if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK) + ast_replace_sigchld(); + +#ifdef HAVE_WORKING_FORK + pid = fork(); +#else + pid = vfork(); +#endif + + if (pid == 0) { +#ifdef HAVE_WORKING_FORK + /* Close file descriptors and launch system command */ + for (x = STDERR_FILENO + 1; x < 4096; x++) + close(x); +#endif + execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL); + _exit(1); + } else if (pid > 0) { + for(;;) { + res = wait4(pid, &status, 0, &rusage); + if (res > -1) { + res = WIFEXITED(status) ? WEXITSTATUS(status) : -1; + break; + } else if (errno != EINTR) + break; + } + } else { + ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno)); + res = -1; + } + + ast_unreplace_sigchld(); +#else + res = -1; +#endif + + return res; +} + +static struct ast_comment *ALLOC_COMMENT(const char *buffer) +{ + struct ast_comment *x = ast_calloc(1,sizeof(struct ast_comment)+strlen(buffer)+1); + strcpy(x->cmt, buffer); + return x; +} + +static struct ast_config_map { + struct ast_config_map *next; + char *name; + char *driver; + char *database; + char *table; + char stuff[0]; +} *config_maps = NULL; + +static struct ast_config_engine *config_engine_list; + +#define MAX_INCLUDE_LEVEL 10 + + +struct ast_category { + char name[80]; + int ignored; /*!< do not let user of the config see this category */ + int include_level; + char *file; /*!< the file name from whence this declaration was read */ + int lineno; + struct ast_comment *precomments; + struct ast_comment *sameline; + struct ast_variable *root; + struct ast_variable *last; + struct ast_category *next; +}; + +struct ast_config { + struct ast_category *root; + struct ast_category *last; + struct ast_category *current; + struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */ + int include_level; + int max_include_level; + struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */ +}; + +struct ast_config_include { + char *include_location_file; /*!< file name in which the include occurs */ + int include_location_lineno; /*!< lineno where include occurred */ + int exec; /*!< set to non-zero if itsa #exec statement */ + char *exec_file; /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */ + char *included_file; /*!< file name included */ + int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not, + we explode the instances and will include those-- so all entries will be unique */ + int output; /*!< a flag to indicate if the inclusion has been output */ + struct ast_config_include *next; /*!< ptr to next inclusion in the list */ +}; + +typedef struct ast_config *config_load_func(const char *database, const char *table, const char *configfile, struct ast_config *config, int withcomments, const char *suggested_include_file); +typedef struct ast_variable *realtime_var_get(const char *database, const char *table, va_list ap); +typedef struct ast_config *realtime_multi_get(const char *database, const char *table, va_list ap); +typedef int realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap); + +/*! \brief Configuration engine structure, used to define realtime drivers */ +struct ast_config_engine { + char *name; + config_load_func *load_func; + realtime_var_get *realtime_func; + realtime_multi_get *realtime_multi_func; + realtime_update *update_func; + struct ast_config_engine *next; +}; + +static struct ast_config_engine *config_engine_list; + +/* taken from strings.h */ + +static force_inline int ast_strlen_zero(const char *s) +{ + return (!s || (*s == '\0')); +} + +#define S_OR(a, b) (!ast_strlen_zero(a) ? (a) : (b)) + +AST_INLINE_API( +void ast_copy_string(char *dst, const char *src, size_t size), +{ + while (*src && size) { + *dst++ = *src++; + size--; + } + if (__builtin_expect(!size, 0)) + dst--; + *dst = '\0'; +} +) + +AST_INLINE_API( +char *ast_skip_blanks(const char *str), +{ + while (*str && *str < 33) + str++; + return (char *)str; +} +) + +/*! + \brief Trims trailing whitespace characters from a string. + \param ast_trim_blanks function being used + \param str the input string + \return a pointer to the modified string + */ +AST_INLINE_API( +char *ast_trim_blanks(char *str), +{ + char *work = str; + + if (work) { + work += strlen(work) - 1; + /* It's tempting to only want to erase after we exit this loop, + but since ast_trim_blanks *could* receive a constant string + (which we presumably wouldn't have to touch), we shouldn't + actually set anything unless we must, and it's easier just + to set each position to \0 than to keep track of a variable + for it */ + while ((work >= str) && *work < 33) + *(work--) = '\0'; + } + return str; +} +) + +/*! + \brief Strip leading/trailing whitespace from a string. + \param s The string to be stripped (will be modified). + \return The stripped string. + + This functions strips all leading and trailing whitespace + characters from the input string, and returns a pointer to + the resulting string. The string is modified in place. +*/ +AST_INLINE_API( +char *ast_strip(char *s), +{ + s = ast_skip_blanks(s); + if (s) + ast_trim_blanks(s); + return s; +} +) + + +/* from config.h */ + +struct ast_variable { + char *name; + char *value; + char *file; + int lineno; + int object; /*!< 0 for variable, 1 for object */ + int blanklines; /*!< Number of blanklines following entry */ + struct ast_comment *precomments; + struct ast_comment *sameline; + struct ast_variable *next; + char stuff[0]; +}; + +static const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable); +static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_include_file); + +struct ast_config *localized_config_load_with_comments(const char *filename); +static char *ast_category_browse(struct ast_config *config, const char *prev); +static struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category); +static void ast_variables_destroy(struct ast_variable *v); +static void ast_config_destroy(struct ast_config *cfg); +static struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size); +static struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file); +void localized_ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file); + +static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename); + +static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename) +{ + struct ast_variable *variable; + int name_len = strlen(name) + 1; + + if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + strlen(filename) + 1 + sizeof(*variable)))) { + variable->name = variable->stuff; + variable->value = variable->stuff + name_len; + variable->file = variable->value + strlen(value) + 1; + strcpy(variable->name,name); + strcpy(variable->value,value); + strcpy(variable->file,filename); + } + + return variable; +} + +static struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size) +{ + /* a file should be included ONCE. Otherwise, if one of the instances is changed, + then all be changed. -- how do we know to include it? -- Handling modified + instances is possible, I'd have + to create a new master for each instance. */ + struct ast_config_include *inc; + + inc = ast_include_find(conf, included_file); + if (inc) + { + inc->inclusion_count++; + snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count); + ast_log(LOG_WARNING,"'%s', line %d: Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name); + } else + *real_included_file_name = 0; + + inc = ast_calloc(1,sizeof(struct ast_config_include)); + inc->include_location_file = ast_strdup(from_file); + inc->include_location_lineno = from_lineno; + if (!ast_strlen_zero(real_included_file_name)) + inc->included_file = ast_strdup(real_included_file_name); + else + inc->included_file = ast_strdup(included_file); + + inc->exec = is_exec; + if (is_exec) + inc->exec_file = ast_strdup(exec_file); + + /* attach this new struct to the conf struct */ + inc->next = conf->includes; + conf->includes = inc; + + return inc; +} + +void localized_ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file) +{ + struct ast_config_include *incl; + struct ast_category *cat; + struct ast_variable *v; + + int from_len = strlen(from_file); + int to_len = strlen(to_file); + + if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */ + return; + + /* the manager code allows you to read in one config file, then + write it back out under a different name. But, the new arrangement + ties output lines to the file name. So, before you try to write + the config file to disk, better riffle thru the data and make sure + the file names are changed. + */ + /* file names are on categories, includes (of course), and on variables. So, + traverse all this and swap names */ + + for (incl = conf->includes; incl; incl=incl->next) { + if (strcmp(incl->include_location_file,from_file) == 0) { + if (from_len >= to_len) + strcpy(incl->include_location_file, to_file); + else { + free(incl->include_location_file); + incl->include_location_file = strdup(to_file); + } + } + } + for (cat = conf->root; cat; cat = cat->next) { + if (strcmp(cat->file,from_file) == 0) { + if (from_len >= to_len) + strcpy(cat->file, to_file); + else { + free(cat->file); + cat->file = strdup(to_file); + } + } + for (v = cat->root; v; v = v->next) { + if (strcmp(v->file,from_file) == 0) { + if (from_len >= to_len) + strcpy(v->file, to_file); + else { + free(v->file); + v->file = strdup(to_file); + } + } + } + } +} + +static struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file) +{ + struct ast_config_include *x; + for (x=conf->includes;x;x=x->next) + { + if (strcmp(x->included_file,included_file) == 0) + return x; + } + return 0; +} + + +static void ast_variable_append(struct ast_category *category, struct ast_variable *variable); + +static void ast_variable_append(struct ast_category *category, struct ast_variable *variable) +{ + if (!variable) + return; + if (category->last) + category->last->next = variable; + else + category->root = variable; + category->last = variable; + while (category->last->next) + category->last = category->last->next; +} + +static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored); + +static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored) +{ + struct ast_category *cat; + + /* try exact match first, then case-insensitive match */ + for (cat = config->root; cat; cat = cat->next) { + if (cat->name == category_name && (ignored || !cat->ignored)) + return cat; + } + + for (cat = config->root; cat; cat = cat->next) { + if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored)) + return cat; + } + + return NULL; +} + +static struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name) +{ + return category_get(config, category_name, 0); +} + +static struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category) +{ + struct ast_category *cat = NULL; + + if (category && config->last_browse && (config->last_browse->name == category)) + cat = config->last_browse; + else + cat = ast_category_get(config, category); + + return (cat) ? cat->root : NULL; +} + +static const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable) +{ + struct ast_variable *v; + + if (category) { + for (v = ast_variable_browse(config, category); v; v = v->next) { + if (!strcasecmp(variable, v->name)) + return v->value; + } + } else { + struct ast_category *cat; + + for (cat = config->root; cat; cat = cat->next) + for (v = cat->root; v; v = v->next) + if (!strcasecmp(variable, v->name)) + return v->value; + } + + return NULL; +} + +static struct ast_variable *variable_clone(const struct ast_variable *old) +{ + struct ast_variable *new = ast_variable_new(old->name, old->value, old->file); + + if (new) { + new->lineno = old->lineno; + new->object = old->object; + new->blanklines = old->blanklines; + /* TODO: clone comments? */ + } + + return new; +} + +static void ast_variables_destroy(struct ast_variable *v) +{ + struct ast_variable *vn; + + while (v) { + vn = v; + v = v->next; + free(vn); + } +} + +static void ast_includes_destroy(struct ast_config_include *incls) +{ + struct ast_config_include *incl,*inclnext; + + for (incl=incls; incl; incl = inclnext) { + inclnext = incl->next; + if (incl->include_location_file) + free(incl->include_location_file); + if (incl->exec_file) + free(incl->exec_file); + if (incl->included_file) + free(incl->included_file); + free(incl); + } +} + +static void ast_config_destroy(struct ast_config *cfg) +{ + struct ast_category *cat, *catn; + + if (!cfg) + return; + + ast_includes_destroy(cfg->includes); + + cat = cfg->root; + while (cat) { + ast_variables_destroy(cat->root); + catn = cat; + cat = cat->next; + free(catn); + } + free(cfg); +} + + +/* options.h declars ast_options extern; I need it static? */ + +#define AST_CACHE_DIR_LEN 512 +#define AST_FILENAME_MAX 80 + +/*! \ingroup main_options */ +enum ast_option_flags { + /*! Allow \#exec in config files */ + AST_OPT_FLAG_EXEC_INCLUDES = (1 << 0), + /*! Do not fork() */ + AST_OPT_FLAG_NO_FORK = (1 << 1), + /*! Keep quiet */ + AST_OPT_FLAG_QUIET = (1 << 2), + /*! Console mode */ + AST_OPT_FLAG_CONSOLE = (1 << 3), + /*! Run in realtime Linux priority */ + AST_OPT_FLAG_HIGH_PRIORITY = (1 << 4), + /*! Initialize keys for RSA authentication */ + AST_OPT_FLAG_INIT_KEYS = (1 << 5), + /*! Remote console */ + AST_OPT_FLAG_REMOTE = (1 << 6), + /*! Execute an asterisk CLI command upon startup */ + AST_OPT_FLAG_EXEC = (1 << 7), + /*! Don't use termcap colors */ + AST_OPT_FLAG_NO_COLOR = (1 << 8), + /*! Are we fully started yet? */ + AST_OPT_FLAG_FULLY_BOOTED = (1 << 9), + /*! Trascode via signed linear */ + AST_OPT_FLAG_TRANSCODE_VIA_SLIN = (1 << 10), + /*! Enable priority jumping in applications */ + AST_OPT_FLAG_PRIORITY_JUMPING = (1 << 11), + /*! Dump core on a seg fault */ + AST_OPT_FLAG_DUMP_CORE = (1 << 12), + /*! Cache sound files */ + AST_OPT_FLAG_CACHE_RECORD_FILES = (1 << 13), + /*! Display timestamp in CLI verbose output */ + AST_OPT_FLAG_TIMESTAMP = (1 << 14), + /*! Override config */ + AST_OPT_FLAG_OVERRIDE_CONFIG = (1 << 15), + /*! Reconnect */ + AST_OPT_FLAG_RECONNECT = (1 << 16), + /*! Transmit Silence during Record() */ + AST_OPT_FLAG_TRANSMIT_SILENCE = (1 << 17), + /*! Suppress some warnings */ + AST_OPT_FLAG_DONT_WARN = (1 << 18), + /*! End CDRs before the 'h' extension */ + AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN = (1 << 19), + /*! Use Zaptel Timing for generators if available */ + AST_OPT_FLAG_INTERNAL_TIMING = (1 << 20), + /*! Always fork, even if verbose or debug settings are non-zero */ + AST_OPT_FLAG_ALWAYS_FORK = (1 << 21), + /*! Disable log/verbose output to remote consoles */ + AST_OPT_FLAG_MUTE = (1 << 22) +}; + +/*! These are the options that set by default when Asterisk starts */ +#define AST_DEFAULT_OPTIONS AST_OPT_FLAG_TRANSCODE_VIA_SLIN + +#define ast_opt_exec_includes ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES) +#define ast_opt_no_fork ast_test_flag(&ast_options, AST_OPT_FLAG_NO_FORK) +#define ast_opt_quiet ast_test_flag(&ast_options, AST_OPT_FLAG_QUIET) +#define ast_opt_console ast_test_flag(&ast_options, AST_OPT_FLAG_CONSOLE) +#define ast_opt_high_priority ast_test_flag(&ast_options, AST_OPT_FLAG_HIGH_PRIORITY) +#define ast_opt_init_keys ast_test_flag(&ast_options, AST_OPT_FLAG_INIT_KEYS) +#define ast_opt_remote ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE) +#define ast_opt_exec ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC) +#define ast_opt_no_color ast_test_flag(&ast_options, AST_OPT_FLAG_NO_COLOR) +#define ast_fully_booted ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED) +#define ast_opt_transcode_via_slin ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSCODE_VIA_SLIN) +#define ast_opt_priority_jumping ast_test_flag(&ast_options, AST_OPT_FLAG_PRIORITY_JUMPING) +#define ast_opt_dump_core ast_test_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE) +#define ast_opt_cache_record_files ast_test_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES) +#define ast_opt_timestamp ast_test_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP) +#define ast_opt_override_config ast_test_flag(&ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG) +#define ast_opt_reconnect ast_test_flag(&ast_options, AST_OPT_FLAG_RECONNECT) +#define ast_opt_transmit_silence ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE) +#define ast_opt_dont_warn ast_test_flag(&ast_options, AST_OPT_FLAG_DONT_WARN) +#define ast_opt_end_cdr_before_h_exten ast_test_flag(&ast_options, AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN) +#define ast_opt_internal_timing ast_test_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING) +#define ast_opt_always_fork ast_test_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK) +#define ast_opt_mute ast_test_flag(&ast_options, AST_OPT_FLAG_MUTE) + +/* IN CONFLICT: extern int option_verbose; */ +/* IN CONFLICT: extern int option_debug; */ /*!< Debugging */ +extern int option_maxcalls; /*!< Maximum number of simultaneous channels */ +extern double option_maxload; +extern char defaultlanguage[]; + +extern time_t ast_startuptime; +extern time_t ast_lastreloadtime; +extern pid_t ast_mainpid; + +extern char record_cache_dir[AST_CACHE_DIR_LEN]; +extern char debug_filename[AST_FILENAME_MAX]; + +extern int ast_language_is_prefix; + + + +/* lock.h */ + +#ifndef HAVE_MTX_PROFILE +#define __MTX_PROF(a) return pthread_mutex_lock((a)) +#else +#define __MTX_PROF(a) do { \ + int i; \ + /* profile only non-blocking events */ \ + ast_mark(mtx_prof, 1); \ + i = pthread_mutex_trylock((a)); \ + ast_mark(mtx_prof, 0); \ + if (!i) \ + return i; \ + else \ + return pthread_mutex_lock((a)); \ + } while (0) +#endif /* HAVE_MTX_PROFILE */ + +#define AST_PTHREADT_NULL (pthread_t) -1 +#define AST_PTHREADT_STOP (pthread_t) -2 + +#if defined(SOLARIS) || defined(BSD) +#define AST_MUTEX_INIT_W_CONSTRUCTORS +#endif /* SOLARIS || BSD */ + +/* Asterisk REQUIRES recursive (not error checking) mutexes + and will not run without them. */ +#if defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) && defined(PTHREAD_MUTEX_RECURSIVE_NP) +#define PTHREAD_MUTEX_INIT_VALUE PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP +#define AST_MUTEX_KIND PTHREAD_MUTEX_RECURSIVE_NP +#else +#define PTHREAD_MUTEX_INIT_VALUE PTHREAD_MUTEX_INITIALIZER +#define AST_MUTEX_KIND PTHREAD_MUTEX_RECURSIVE +#endif /* PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP */ + +#ifdef DEBUG_THREADS + +#define __ast_mutex_logger(...) do { if (canlog) ast_log(LOG_ERROR, __VA_ARGS__); else fprintf(stderr, __VA_ARGS__); } while (0) + +#ifdef THREAD_CRASH +#define DO_THREAD_CRASH do { *((int *)(0)) = 1; } while(0) +#else +#define DO_THREAD_CRASH do { } while (0) +#endif + +#define AST_MUTEX_INIT_VALUE { PTHREAD_MUTEX_INIT_VALUE, { NULL }, { 0 }, 0, { NULL }, { 0 } } + +#define AST_MAX_REENTRANCY 10 + +struct ast_mutex_info { + pthread_mutex_t mutex; + /*! Track which thread holds this lock */ + unsigned int track:1; + const char *file[AST_MAX_REENTRANCY]; + int lineno[AST_MAX_REENTRANCY]; + int reentrancy; + const char *func[AST_MAX_REENTRANCY]; + pthread_t thread[AST_MAX_REENTRANCY]; +}; + +typedef struct ast_mutex_info ast_mutex_t; + +typedef pthread_cond_t ast_cond_t; + +static pthread_mutex_t empty_mutex; + +static void __attribute__((constructor)) init_empty_mutex(void) +{ + memset(&empty_mutex, 0, sizeof(empty_mutex)); +} + +static inline int __ast_pthread_mutex_init_attr(const char *filename, int lineno, const char *func, + const char *mutex_name, ast_mutex_t *t, + pthread_mutexattr_t *attr) +{ +#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS + int canlog = strcmp(filename, "logger.c"); + + if ((t->mutex) != ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) { + if ((t->mutex) != (empty_mutex)) { + __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is already initialized.\n", + filename, lineno, func, mutex_name); + __ast_mutex_logger("%s line %d (%s): Error: previously initialization of mutex '%s'.\n", + t->file[0], t->lineno[0], t->func[0], mutex_name); + DO_THREAD_CRASH; + return 0; + } + } +#endif + + t->file[0] = filename; + t->lineno[0] = lineno; + t->func[0] = func; + t->thread[0] = 0; + t->reentrancy = 0; + + return pthread_mutex_init(&t->mutex, attr); +} + +static inline int __ast_pthread_mutex_init(const char *filename, int lineno, const char *func, + const char *mutex_name, ast_mutex_t *t) +{ + static pthread_mutexattr_t attr; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, AST_MUTEX_KIND); + + return __ast_pthread_mutex_init_attr(filename, lineno, func, mutex_name, t, &attr); +} +#define ast_mutex_init(pmutex) __ast_pthread_mutex_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #pmutex, pmutex) + +static inline int __ast_pthread_mutex_destroy(const char *filename, int lineno, const char *func, + const char *mutex_name, ast_mutex_t *t) +{ + int res; + int canlog = strcmp(filename, "logger.c"); + +#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS + if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) { + __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n", + filename, lineno, func, mutex_name); + } +#endif + + res = pthread_mutex_trylock(&t->mutex); + switch (res) { + case 0: + pthread_mutex_unlock(&t->mutex); + break; + case EINVAL: + __ast_mutex_logger("%s line %d (%s): Error: attempt to destroy invalid mutex '%s'.\n", + filename, lineno, func, mutex_name); + break; + case EBUSY: + __ast_mutex_logger("%s line %d (%s): Error: attempt to destroy locked mutex '%s'.\n", + filename, lineno, func, mutex_name); + __ast_mutex_logger("%s line %d (%s): Error: '%s' was locked here.\n", + t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name); + break; + } + + if ((res = pthread_mutex_destroy(&t->mutex))) + __ast_mutex_logger("%s line %d (%s): Error destroying mutex: %s\n", + filename, lineno, func, strerror(res)); +#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP + else + t->mutex = PTHREAD_MUTEX_INIT_VALUE; +#endif + t->file[0] = filename; + t->lineno[0] = lineno; + t->func[0] = func; + + return res; +} + +static inline int __ast_pthread_mutex_lock(const char *filename, int lineno, const char *func, + const char* mutex_name, ast_mutex_t *t) +{ + int res; + int canlog = strcmp(filename, "logger.c"); + +#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) + if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) { + __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n", + filename, lineno, func, mutex_name); + ast_mutex_init(t); + } +#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */ + +#ifdef DETECT_DEADLOCKS + { + time_t seconds = time(NULL); + time_t current; + do { +#ifdef HAVE_MTX_PROFILE + ast_mark(mtx_prof, 1); +#endif + res = pthread_mutex_trylock(&t->mutex); +#ifdef HAVE_MTX_PROFILE + ast_mark(mtx_prof, 0); +#endif + if (res == EBUSY) { + current = time(NULL); + if ((current - seconds) && (!((current - seconds) % 5))) { + __ast_mutex_logger("%s line %d (%s): Deadlock? waited %d sec for mutex '%s'?\n", + filename, lineno, func, (int)(current - seconds), mutex_name); + __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n", + t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], + t->func[t->reentrancy-1], mutex_name); + } + usleep(200); + } + } while (res == EBUSY); + } +#else +#ifdef HAVE_MTX_PROFILE + ast_mark(mtx_prof, 1); + res = pthread_mutex_trylock(&t->mutex); + ast_mark(mtx_prof, 0); + if (res) +#endif + res = pthread_mutex_lock(&t->mutex); +#endif /* DETECT_DEADLOCKS */ + + if (!res) { + if (t->reentrancy < AST_MAX_REENTRANCY) { + t->file[t->reentrancy] = filename; + t->lineno[t->reentrancy] = lineno; + t->func[t->reentrancy] = func; + t->thread[t->reentrancy] = pthread_self(); + t->reentrancy++; + } else { + __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n", + filename, lineno, func, mutex_name); + } + } else { + __ast_mutex_logger("%s line %d (%s): Error obtaining mutex: %s\n", + filename, lineno, func, strerror(errno)); + DO_THREAD_CRASH; + } + + return res; +} + +static inline int __ast_pthread_mutex_trylock(const char *filename, int lineno, const char *func, + const char* mutex_name, ast_mutex_t *t) +{ + int res; + int canlog = strcmp(filename, "logger.c"); + +#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) + if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) { + __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n", + filename, lineno, func, mutex_name); + ast_mutex_init(t); + } +#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */ + + if (!(res = pthread_mutex_trylock(&t->mutex))) { + if (t->reentrancy < AST_MAX_REENTRANCY) { + t->file[t->reentrancy] = filename; + t->lineno[t->reentrancy] = lineno; + t->func[t->reentrancy] = func; + t->thread[t->reentrancy] = pthread_self(); + t->reentrancy++; + } else { + __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n", + filename, lineno, func, mutex_name); + } + } else { + __ast_mutex_logger("%s line %d (%s): Warning: '%s' was locked here.\n", + t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name); + } + + return res; +} + +static inline int __ast_pthread_mutex_unlock(const char *filename, int lineno, const char *func, + const char *mutex_name, ast_mutex_t *t) +{ + int res; + int canlog = strcmp(filename, "logger.c"); + +#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS + if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) { + __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n", + filename, lineno, func, mutex_name); + } +#endif + + if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) { + __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n", + filename, lineno, func, mutex_name); + __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n", + t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name); + DO_THREAD_CRASH; + } + + if (--t->reentrancy < 0) { + __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n", + filename, lineno, func, mutex_name); + t->reentrancy = 0; + } + + if (t->reentrancy < AST_MAX_REENTRANCY) { + t->file[t->reentrancy] = NULL; + t->lineno[t->reentrancy] = 0; + t->func[t->reentrancy] = NULL; + t->thread[t->reentrancy] = 0; + } + + if ((res = pthread_mutex_unlock(&t->mutex))) { + __ast_mutex_logger("%s line %d (%s): Error releasing mutex: %s\n", + filename, lineno, func, strerror(res)); + DO_THREAD_CRASH; + } + + return res; +} + +static inline int __ast_cond_init(const char *filename, int lineno, const char *func, + const char *cond_name, ast_cond_t *cond, pthread_condattr_t *cond_attr) +{ + return pthread_cond_init(cond, cond_attr); +} + +static inline int __ast_cond_signal(const char *filename, int lineno, const char *func, + const char *cond_name, ast_cond_t *cond) +{ + return pthread_cond_signal(cond); +} + +static inline int __ast_cond_broadcast(const char *filename, int lineno, const char *func, + const char *cond_name, ast_cond_t *cond) +{ + return pthread_cond_broadcast(cond); +} + +static inline int __ast_cond_destroy(const char *filename, int lineno, const char *func, + const char *cond_name, ast_cond_t *cond) +{ + return pthread_cond_destroy(cond); +} + +static inline int __ast_cond_wait(const char *filename, int lineno, const char *func, + const char *cond_name, const char *mutex_name, + ast_cond_t *cond, ast_mutex_t *t) +{ + int res; + int canlog = strcmp(filename, "logger.c"); + +#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS + if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) { + __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n", + filename, lineno, func, mutex_name); + } +#endif + + if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) { + __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n", + filename, lineno, func, mutex_name); + __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n", + t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name); + DO_THREAD_CRASH; + } + + if (--t->reentrancy < 0) { + __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n", + filename, lineno, func, mutex_name); + t->reentrancy = 0; + } + + if (t->reentrancy < AST_MAX_REENTRANCY) { + t->file[t->reentrancy] = NULL; + t->lineno[t->reentrancy] = 0; + t->func[t->reentrancy] = NULL; + t->thread[t->reentrancy] = 0; + } + + if ((res = pthread_cond_wait(cond, &t->mutex))) { + __ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n", + filename, lineno, func, strerror(res)); + DO_THREAD_CRASH; + } else { + if (t->reentrancy < AST_MAX_REENTRANCY) { + t->file[t->reentrancy] = filename; + t->lineno[t->reentrancy] = lineno; + t->func[t->reentrancy] = func; + t->thread[t->reentrancy] = pthread_self(); + t->reentrancy++; + } else { + __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n", + filename, lineno, func, mutex_name); + } + } + + return res; +} + +static inline int __ast_cond_timedwait(const char *filename, int lineno, const char *func, + const char *cond_name, const char *mutex_name, ast_cond_t *cond, + ast_mutex_t *t, const struct timespec *abstime) +{ + int res; + int canlog = strcmp(filename, "logger.c"); + +#ifdef AST_MUTEX_INIT_W_CONSTRUCTORS + if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) { + __ast_mutex_logger("%s line %d (%s): Error: mutex '%s' is uninitialized.\n", + filename, lineno, func, mutex_name); + } +#endif + + if (t->reentrancy && (t->thread[t->reentrancy-1] != pthread_self())) { + __ast_mutex_logger("%s line %d (%s): attempted unlock mutex '%s' without owning it!\n", + filename, lineno, func, mutex_name); + __ast_mutex_logger("%s line %d (%s): '%s' was locked here.\n", + t->file[t->reentrancy-1], t->lineno[t->reentrancy-1], t->func[t->reentrancy-1], mutex_name); + DO_THREAD_CRASH; + } + + if (--t->reentrancy < 0) { + __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n", + filename, lineno, func, mutex_name); + t->reentrancy = 0; + } + + if (t->reentrancy < AST_MAX_REENTRANCY) { + t->file[t->reentrancy] = NULL; + t->lineno[t->reentrancy] = 0; + t->func[t->reentrancy] = NULL; + t->thread[t->reentrancy] = 0; + } + + if ((res = pthread_cond_timedwait(cond, &t->mutex, abstime)) && (res != ETIMEDOUT)) { + __ast_mutex_logger("%s line %d (%s): Error waiting on condition mutex '%s'\n", + filename, lineno, func, strerror(res)); + DO_THREAD_CRASH; + } else { + if (t->reentrancy < AST_MAX_REENTRANCY) { + t->file[t->reentrancy] = filename; + t->lineno[t->reentrancy] = lineno; + t->func[t->reentrancy] = func; + t->thread[t->reentrancy] = pthread_self(); + t->reentrancy++; + } else { + __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n", + filename, lineno, func, mutex_name); + } + } + + return res; +} + +#define ast_mutex_destroy(a) __ast_pthread_mutex_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a) +#define ast_mutex_lock(a) __ast_pthread_mutex_lock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a) +#define ast_mutex_unlock(a) __ast_pthread_mutex_unlock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a) +#define ast_mutex_trylock(a) __ast_pthread_mutex_trylock(__FILE__, __LINE__, __PRETTY_FUNCTION__, #a, a) +#define ast_cond_init(cond, attr) __ast_cond_init(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond, attr) +#define ast_cond_destroy(cond) __ast_cond_destroy(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond) +#define ast_cond_signal(cond) __ast_cond_signal(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond) +#define ast_cond_broadcast(cond) __ast_cond_broadcast(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, cond) +#define ast_cond_wait(cond, mutex) __ast_cond_wait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex) +#define ast_cond_timedwait(cond, mutex, time) __ast_cond_timedwait(__FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #mutex, cond, mutex, time) + +#else /* !DEBUG_THREADS */ + + +typedef pthread_mutex_t ast_mutex_t; + +#define AST_MUTEX_INIT_VALUE ((ast_mutex_t) PTHREAD_MUTEX_INIT_VALUE) + +static inline int ast_mutex_init(ast_mutex_t *pmutex) +{ + pthread_mutexattr_t attr; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, AST_MUTEX_KIND); + + return pthread_mutex_init(pmutex, &attr); +} + +#define ast_pthread_mutex_init(pmutex,a) pthread_mutex_init(pmutex,a) + +static inline int ast_mutex_unlock(ast_mutex_t *pmutex) +{ + return pthread_mutex_unlock(pmutex); +} + +static inline int ast_mutex_destroy(ast_mutex_t *pmutex) +{ + return pthread_mutex_destroy(pmutex); +} + +static inline int ast_mutex_lock(ast_mutex_t *pmutex) +{ + __MTX_PROF(pmutex); +} + +static inline int ast_mutex_trylock(ast_mutex_t *pmutex) +{ + return pthread_mutex_trylock(pmutex); +} + +typedef pthread_cond_t ast_cond_t; + +static inline int ast_cond_init(ast_cond_t *cond, pthread_condattr_t *cond_attr) +{ + return pthread_cond_init(cond, cond_attr); +} + +static inline int ast_cond_signal(ast_cond_t *cond) +{ + return pthread_cond_signal(cond); +} + +static inline int ast_cond_broadcast(ast_cond_t *cond) +{ + return pthread_cond_broadcast(cond); +} + +static inline int ast_cond_destroy(ast_cond_t *cond) +{ + return pthread_cond_destroy(cond); +} + +static inline int ast_cond_wait(ast_cond_t *cond, ast_mutex_t *t) +{ + return pthread_cond_wait(cond, t); +} + +static inline int ast_cond_timedwait(ast_cond_t *cond, ast_mutex_t *t, const struct timespec *abstime) +{ + return pthread_cond_timedwait(cond, t, abstime); +} + +#endif /* !DEBUG_THREADS */ + +#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) +/* If AST_MUTEX_INIT_W_CONSTRUCTORS is defined, use file scope + constructors/destructors to create/destroy mutexes. */ +#define __AST_MUTEX_DEFINE(scope, mutex) \ + scope ast_mutex_t mutex = AST_MUTEX_INIT_VALUE; \ +static void __attribute__ ((constructor)) init_##mutex(void) \ +{ \ + ast_mutex_init(&mutex); \ +} \ +static void __attribute__ ((destructor)) fini_##mutex(void) \ +{ \ + ast_mutex_destroy(&mutex); \ +} +#else /* !AST_MUTEX_INIT_W_CONSTRUCTORS */ +/* By default, use static initialization of mutexes. */ +#define __AST_MUTEX_DEFINE(scope, mutex) \ + scope ast_mutex_t mutex = AST_MUTEX_INIT_VALUE +#endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */ + +#define pthread_mutex_t use_ast_mutex_t_instead_of_pthread_mutex_t +#define pthread_mutex_lock use_ast_mutex_lock_instead_of_pthread_mutex_lock +#define pthread_mutex_unlock use_ast_mutex_unlock_instead_of_pthread_mutex_unlock +#define pthread_mutex_trylock use_ast_mutex_trylock_instead_of_pthread_mutex_trylock +#define pthread_mutex_init use_ast_mutex_init_instead_of_pthread_mutex_init +#define pthread_mutex_destroy use_ast_mutex_destroy_instead_of_pthread_mutex_destroy +#define pthread_cond_t use_ast_cond_t_instead_of_pthread_cond_t +#define pthread_cond_init use_ast_cond_init_instead_of_pthread_cond_init +#define pthread_cond_destroy use_ast_cond_destroy_instead_of_pthread_cond_destroy +#define pthread_cond_signal use_ast_cond_signal_instead_of_pthread_cond_signal +#define pthread_cond_broadcast use_ast_cond_broadcast_instead_of_pthread_cond_broadcast +#define pthread_cond_wait use_ast_cond_wait_instead_of_pthread_cond_wait +#define pthread_cond_timedwait use_ast_cond_timedwait_instead_of_pthread_cond_timedwait + +#define AST_MUTEX_DEFINE_STATIC(mutex) __AST_MUTEX_DEFINE(static, mutex) + +#define AST_MUTEX_INITIALIZER __use_AST_MUTEX_DEFINE_STATIC_rather_than_AST_MUTEX_INITIALIZER__ + +#define gethostbyname __gethostbyname__is__not__reentrant__use__ast_gethostbyname__instead__ + +#ifndef __linux__ +#define pthread_create __use_ast_pthread_create_instead__ +#endif + +typedef pthread_rwlock_t ast_rwlock_t; + +static inline int ast_rwlock_init(ast_rwlock_t *prwlock) +{ + pthread_rwlockattr_t attr; + + pthread_rwlockattr_init(&attr); + +#ifdef HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NP + pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP); +#endif + + return pthread_rwlock_init(prwlock, &attr); +} + +static inline int ast_rwlock_destroy(ast_rwlock_t *prwlock) +{ + return pthread_rwlock_destroy(prwlock); +} + +static inline int ast_rwlock_unlock(ast_rwlock_t *prwlock) +{ + return pthread_rwlock_unlock(prwlock); +} + +static inline int ast_rwlock_rdlock(ast_rwlock_t *prwlock) +{ + return pthread_rwlock_rdlock(prwlock); +} + +static inline int ast_rwlock_tryrdlock(ast_rwlock_t *prwlock) +{ + return pthread_rwlock_tryrdlock(prwlock); +} + +static inline int ast_rwlock_wrlock(ast_rwlock_t *prwlock) +{ + return pthread_rwlock_wrlock(prwlock); +} + +static inline int ast_rwlock_trywrlock(ast_rwlock_t *prwlock) +{ + return pthread_rwlock_trywrlock(prwlock); +} + +/* Statically declared read/write locks */ + +#ifndef HAVE_PTHREAD_RWLOCK_INITIALIZER +#define __AST_RWLOCK_DEFINE(scope, rwlock) \ + scope ast_rwlock_t rwlock; \ +static void __attribute__ ((constructor)) init_##rwlock(void) \ +{ \ + ast_rwlock_init(&rwlock); \ +} \ +static void __attribute__ ((destructor)) fini_##rwlock(void) \ +{ \ + ast_rwlock_destroy(&rwlock); \ +} +#else +#define AST_RWLOCK_INIT_VALUE PTHREAD_RWLOCK_INITIALIZER +#define __AST_RWLOCK_DEFINE(scope, rwlock) \ + scope ast_rwlock_t rwlock = AST_RWLOCK_INIT_VALUE +#endif + +#define AST_RWLOCK_DEFINE_STATIC(rwlock) __AST_RWLOCK_DEFINE(static, rwlock) + +/* + * Initial support for atomic instructions. + * For platforms that have it, use the native cpu instruction to + * implement them. For other platforms, resort to a 'slow' version + * (defined in utils.c) that protects the atomic instruction with + * a single lock. + * The slow versions is always available, for testing purposes, + * as ast_atomic_fetchadd_int_slow() + */ + +#if defined(HAVE_OSX_ATOMICS) +#include "libkern/OSAtomic.h" +#endif + +/*! \brief Atomically add v to *p and return * the previous value of *p. + * This can be used to handle reference counts, and the return value + * can be used to generate unique identifiers. + */ + +#if defined(HAVE_GCC_ATOMICS) +AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v), +{ + return __sync_fetch_and_add(p, v); +}) +#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 4) +AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v), +{ + return OSAtomicAdd32(v, (int32_t *) p); +}) +#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 8) +AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v), +{ + return OSAtomicAdd64(v, (int64_t *) p); +#elif defined (__i386__) || defined(__x86_64__) +AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v), +{ + __asm __volatile ( + " lock xaddl %0, %1 ; " + : "+r" (v), /* 0 (result) */ + "=m" (*p) /* 1 */ + : "m" (*p)); /* 2 */ + return (v); +}) +#else +static int ast_atomic_fetchadd_int_slow(volatile int *p, int v) +{ + int ret; + ret = *p; + *p += v; + return ret; +} +AST_INLINE_API(int ast_atomic_fetchadd_int(volatile int *p, int v), +{ + return ast_atomic_fetchadd_int_slow(p, v); +}) +#endif + +/*! \brief decrement *p by 1 and return true if the variable has reached 0. + * Useful e.g. to check if a refcount has reached 0. + */ +#if defined(HAVE_GCC_ATOMICS) +AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p), +{ + return __sync_sub_and_fetch(p, 1) == 0; +}) +#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 4) +AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p), +{ + return OSAtomicAdd32( -1, (int32_t *) p) == 0; +}) +#elif defined(HAVE_OSX_ATOMICS) && (SIZEOF_INT == 8) +AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p), +{ + return OSAtomicAdd64( -1, (int64_t *) p) == 0; +#else +AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p), +{ + int a = ast_atomic_fetchadd_int(p, -1); + return a == 1; /* true if the value is 0 now (so it was 1 previously) */ +}) +#endif + +#ifndef DEBUG_CHANNEL_LOCKS +/*! \brief Lock a channel. If DEBUG_CHANNEL_LOCKS is defined + in the Makefile, print relevant output for debugging */ +#define ast_channel_lock(x) ast_mutex_lock(&x->lock) +/*! \brief Unlock a channel. If DEBUG_CHANNEL_LOCKS is defined + in the Makefile, print relevant output for debugging */ +#define ast_channel_unlock(x) ast_mutex_unlock(&x->lock) +/*! \brief Try locking a channel. If DEBUG_CHANNEL_LOCKS is defined + in the Makefile, print relevant output for debugging */ +#define ast_channel_trylock(x) ast_mutex_trylock(&x->lock) +#else + +/*! \brief Lock AST channel (and print debugging output) +\note You need to enable DEBUG_CHANNEL_LOCKS for this function */ +int ast_channel_lock(struct ast_channel *chan); + +/*! \brief Unlock AST channel (and print debugging output) +\note You need to enable DEBUG_CHANNEL_LOCKS for this function +*/ +int ast_channel_unlock(struct ast_channel *chan); + +/*! \brief Lock AST channel (and print debugging output) +\note You need to enable DEBUG_CHANNEL_LOCKS for this function */ +int ast_channel_trylock(struct ast_channel *chan); +#endif + + +/* linkedlists.h */ + +#define AST_LIST_LOCK(head) \ + ast_mutex_lock(&(head)->lock) + +/*! + \brief Write locks a list. + \param head This is a pointer to the list head structure + + This macro attempts to place an exclusive write lock in the + list head structure pointed to by head. + Returns non-zero on success, 0 on failure +*/ +#define AST_RWLIST_WRLOCK(head) \ + ast_rwlock_wrlock(&(head)->lock) + +/*! + \brief Read locks a list. + \param head This is a pointer to the list head structure + + This macro attempts to place a read lock in the + list head structure pointed to by head. + Returns non-zero on success, 0 on failure +*/ +#define AST_RWLIST_RDLOCK(head) \ + ast_rwlock_rdlock(&(head)->lock) + +/*! + \brief Locks a list, without blocking if the list is locked. + \param head This is a pointer to the list head structure + + This macro attempts to place an exclusive lock in the + list head structure pointed to by head. + Returns non-zero on success, 0 on failure +*/ +#define AST_LIST_TRYLOCK(head) \ + ast_mutex_trylock(&(head)->lock) + +/*! + \brief Write locks a list, without blocking if the list is locked. + \param head This is a pointer to the list head structure + + This macro attempts to place an exclusive write lock in the + list head structure pointed to by head. + Returns non-zero on success, 0 on failure +*/ +#define AST_RWLIST_TRYWRLOCK(head) \ + ast_rwlock_trywrlock(&(head)->lock) + +/*! + \brief Read locks a list, without blocking if the list is locked. + \param head This is a pointer to the list head structure + + This macro attempts to place a read lock in the + list head structure pointed to by head. + Returns non-zero on success, 0 on failure +*/ +#define AST_RWLIST_TRYRDLOCK(head) \ + ast_rwlock_tryrdlock(&(head)->lock) + +/*! + \brief Attempts to unlock a list. + \param head This is a pointer to the list head structure + + This macro attempts to remove an exclusive lock from the + list head structure pointed to by head. If the list + was not locked by this thread, this macro has no effect. +*/ +#define AST_LIST_UNLOCK(head) \ + ast_mutex_unlock(&(head)->lock) + +/*! + \brief Attempts to unlock a read/write based list. + \param head This is a pointer to the list head structure + + This macro attempts to remove a read or write lock from the + list head structure pointed to by head. If the list + was not locked by this thread, this macro has no effect. +*/ +#define AST_RWLIST_UNLOCK(head) \ + ast_rwlock_unlock(&(head)->lock) + +/*! + \brief Defines a structure to be used to hold a list of specified type. + \param name This will be the name of the defined structure. + \param type This is the type of each list entry. + + This macro creates a structure definition that can be used + to hold a list of the entries of type \a type. It does not actually + declare (allocate) a structure; to do that, either follow this + macro with the desired name of the instance you wish to declare, + or use the specified \a name to declare instances elsewhere. + + Example usage: + \code + static AST_LIST_HEAD(entry_list, entry) entries; + \endcode + + This would define \c struct \c entry_list, and declare an instance of it named + \a entries, all intended to hold a list of type \c struct \c entry. +*/ +#define AST_LIST_HEAD(name, type) \ +struct name { \ + struct type *first; \ + struct type *last; \ + ast_mutex_t lock; \ +} + +/*! + \brief Defines a structure to be used to hold a read/write list of specified type. + \param name This will be the name of the defined structure. + \param type This is the type of each list entry. + + This macro creates a structure definition that can be used + to hold a list of the entries of type \a type. It does not actually + declare (allocate) a structure; to do that, either follow this + macro with the desired name of the instance you wish to declare, + or use the specified \a name to declare instances elsewhere. + + Example usage: + \code + static AST_RWLIST_HEAD(entry_list, entry) entries; + \endcode + + This would define \c struct \c entry_list, and declare an instance of it named + \a entries, all intended to hold a list of type \c struct \c entry. +*/ +#define AST_RWLIST_HEAD(name, type) \ +struct name { \ + struct type *first; \ + struct type *last; \ + ast_rwlock_t lock; \ +} + +/*! + \brief Defines a structure to be used to hold a list of specified type (with no lock). + \param name This will be the name of the defined structure. + \param type This is the type of each list entry. + + This macro creates a structure definition that can be used + to hold a list of the entries of type \a type. It does not actually + declare (allocate) a structure; to do that, either follow this + macro with the desired name of the instance you wish to declare, + or use the specified \a name to declare instances elsewhere. + + Example usage: + \code + static AST_LIST_HEAD_NOLOCK(entry_list, entry) entries; + \endcode + + This would define \c struct \c entry_list, and declare an instance of it named + \a entries, all intended to hold a list of type \c struct \c entry. +*/ +#define AST_LIST_HEAD_NOLOCK(name, type) \ +struct name { \ + struct type *first; \ + struct type *last; \ +} + +/*! + \brief Defines initial values for a declaration of AST_LIST_HEAD +*/ +#define AST_LIST_HEAD_INIT_VALUE { \ + .first = NULL, \ + .last = NULL, \ + .lock = AST_MUTEX_INIT_VALUE, \ + } + +/*! + \brief Defines initial values for a declaration of AST_RWLIST_HEAD +*/ +#define AST_RWLIST_HEAD_INIT_VALUE { \ + .first = NULL, \ + .last = NULL, \ + .lock = AST_RWLOCK_INIT_VALUE, \ + } + +/*! + \brief Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK +*/ +#define AST_LIST_HEAD_NOLOCK_INIT_VALUE { \ + .first = NULL, \ + .last = NULL, \ + } + +/*! + \brief Defines a structure to be used to hold a list of specified type, statically initialized. + \param name This will be the name of the defined structure. + \param type This is the type of each list entry. + + This macro creates a structure definition that can be used + to hold a list of the entries of type \a type, and allocates an instance + of it, initialized to be empty. + + Example usage: + \code + static AST_LIST_HEAD_STATIC(entry_list, entry); + \endcode + + This would define \c struct \c entry_list, intended to hold a list of + type \c struct \c entry. +*/ +#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) +#define AST_LIST_HEAD_STATIC(name, type) \ +struct name { \ + struct type *first; \ + struct type *last; \ + ast_mutex_t lock; \ +} name; \ +static void __attribute__ ((constructor)) init_##name(void) \ +{ \ + AST_LIST_HEAD_INIT(&name); \ +} \ +static void __attribute__ ((destructor)) fini_##name(void) \ +{ \ + AST_LIST_HEAD_DESTROY(&name); \ +} \ +struct __dummy_##name +#else +#define AST_LIST_HEAD_STATIC(name, type) \ +struct name { \ + struct type *first; \ + struct type *last; \ + ast_mutex_t lock; \ +} name = AST_LIST_HEAD_INIT_VALUE +#endif + +/*! + \brief Defines a structure to be used to hold a read/write list of specified type, statically initialized. + \param name This will be the name of the defined structure. + \param type This is the type of each list entry. + + This macro creates a structure definition that can be used + to hold a list of the entries of type \a type, and allocates an instance + of it, initialized to be empty. + + Example usage: + \code + static AST_RWLIST_HEAD_STATIC(entry_list, entry); + \endcode + + This would define \c struct \c entry_list, intended to hold a list of + type \c struct \c entry. +*/ +#ifndef AST_RWLOCK_INIT_VALUE +#define AST_RWLIST_HEAD_STATIC(name, type) \ +struct name { \ + struct type *first; \ + struct type *last; \ + ast_rwlock_t lock; \ +} name; \ +static void __attribute__ ((constructor)) init_##name(void) \ +{ \ + AST_RWLIST_HEAD_INIT(&name); \ +} \ +static void __attribute__ ((destructor)) fini_##name(void) \ +{ \ + AST_RWLIST_HEAD_DESTROY(&name); \ +} \ +struct __dummy_##name +#else +#define AST_RWLIST_HEAD_STATIC(name, type) \ +struct name { \ + struct type *first; \ + struct type *last; \ + ast_rwlock_t lock; \ +} name = AST_RWLIST_HEAD_INIT_VALUE +#endif + +/*! + \brief Defines a structure to be used to hold a list of specified type, statically initialized. + + This is the same as AST_LIST_HEAD_STATIC, except without the lock included. +*/ +#define AST_LIST_HEAD_NOLOCK_STATIC(name, type) \ +struct name { \ + struct type *first; \ + struct type *last; \ +} name = AST_LIST_HEAD_NOLOCK_INIT_VALUE + +/*! + \brief Initializes a list head structure with a specified first entry. + \param head This is a pointer to the list head structure + \param entry pointer to the list entry that will become the head of the list + + This macro initializes a list head structure by setting the head + entry to the supplied value and recreating the embedded lock. +*/ +#define AST_LIST_HEAD_SET(head, entry) do { \ + (head)->first = (entry); \ + (head)->last = (entry); \ + ast_mutex_init(&(head)->lock); \ +} while (0) + +/*! + \brief Initializes an rwlist head structure with a specified first entry. + \param head This is a pointer to the list head structure + \param entry pointer to the list entry that will become the head of the list + + This macro initializes a list head structure by setting the head + entry to the supplied value and recreating the embedded lock. +*/ +#define AST_RWLIST_HEAD_SET(head, entry) do { \ + (head)->first = (entry); \ + (head)->last = (entry); \ + ast_rwlock_init(&(head)->lock); \ +} while (0) + +/*! + \brief Initializes a list head structure with a specified first entry. + \param head This is a pointer to the list head structure + \param entry pointer to the list entry that will become the head of the list + + This macro initializes a list head structure by setting the head + entry to the supplied value. +*/ +#define AST_LIST_HEAD_SET_NOLOCK(head, entry) do { \ + (head)->first = (entry); \ + (head)->last = (entry); \ +} while (0) + +/*! + \brief Declare a forward link structure inside a list entry. + \param type This is the type of each list entry. + + This macro declares a structure to be used to link list entries together. + It must be used inside the definition of the structure named in + \a type, as follows: + + \code + struct list_entry { + ... + AST_LIST_ENTRY(list_entry) list; + } + \endcode + + The field name \a list here is arbitrary, and can be anything you wish. +*/ +#define AST_LIST_ENTRY(type) \ +struct { \ + struct type *next; \ +} + +#define AST_RWLIST_ENTRY AST_LIST_ENTRY + +/*! + \brief Returns the first entry contained in a list. + \param head This is a pointer to the list head structure + */ +#define AST_LIST_FIRST(head) ((head)->first) + +#define AST_RWLIST_FIRST AST_LIST_FIRST + +/*! + \brief Returns the last entry contained in a list. + \param head This is a pointer to the list head structure + */ +#define AST_LIST_LAST(head) ((head)->last) + +#define AST_RWLIST_LAST AST_LIST_LAST + +/*! + \brief Returns the next entry in the list after the given entry. + \param elm This is a pointer to the current entry. + \param field This is the name of the field (declared using AST_LIST_ENTRY()) + used to link entries of this list together. +*/ +#define AST_LIST_NEXT(elm, field) ((elm)->field.next) + +#define AST_RWLIST_NEXT AST_LIST_NEXT + +/*! + \brief Checks whether the specified list contains any entries. + \param head This is a pointer to the list head structure + + Returns non-zero if the list has entries, zero if not. + */ +#define AST_LIST_EMPTY(head) (AST_LIST_FIRST(head) == NULL) + +#define AST_RWLIST_EMPTY AST_LIST_EMPTY + +/*! + \brief Loops over (traverses) the entries in a list. + \param head This is a pointer to the list head structure + \param var This is the name of the variable that will hold a pointer to the + current list entry on each iteration. It must be declared before calling + this macro. + \param field This is the name of the field (declared using AST_LIST_ENTRY()) + used to link entries of this list together. + + This macro is use to loop over (traverse) the entries in a list. It uses a + \a for loop, and supplies the enclosed code with a pointer to each list + entry as it loops. It is typically used as follows: + \code + static AST_LIST_HEAD(entry_list, list_entry) entries; + ... + struct list_entry { + ... + AST_LIST_ENTRY(list_entry) list; + } + ... + struct list_entry *current; + ... + AST_LIST_TRAVERSE(&entries, current, list) { + (do something with current here) + } + \endcode + \warning If you modify the forward-link pointer contained in the \a current entry while + inside the loop, the behavior will be unpredictable. At a minimum, the following + macros will modify the forward-link pointer, and should not be used inside + AST_LIST_TRAVERSE() against the entry pointed to by the \a current pointer without + careful consideration of their consequences: + \li AST_LIST_NEXT() (when used as an lvalue) + \li AST_LIST_INSERT_AFTER() + \li AST_LIST_INSERT_HEAD() + \li AST_LIST_INSERT_TAIL() +*/ +#define AST_LIST_TRAVERSE(head,var,field) \ + for((var) = (head)->first; (var); (var) = (var)->field.next) + +#define AST_RWLIST_TRAVERSE AST_LIST_TRAVERSE + +/*! + \brief Loops safely over (traverses) the entries in a list. + \param head This is a pointer to the list head structure + \param var This is the name of the variable that will hold a pointer to the + current list entry on each iteration. It must be declared before calling + this macro. + \param field This is the name of the field (declared using AST_LIST_ENTRY()) + used to link entries of this list together. + + This macro is used to safely loop over (traverse) the entries in a list. It + uses a \a for loop, and supplies the enclosed code with a pointer to each list + entry as it loops. It is typically used as follows: + + \code + static AST_LIST_HEAD(entry_list, list_entry) entries; + ... + struct list_entry { + ... + AST_LIST_ENTRY(list_entry) list; + } + ... + struct list_entry *current; + ... + AST_LIST_TRAVERSE_SAFE_BEGIN(&entries, current, list) { + (do something with current here) + } + AST_LIST_TRAVERSE_SAFE_END; + \endcode + + It differs from AST_LIST_TRAVERSE() in that the code inside the loop can modify + (or even free, after calling AST_LIST_REMOVE_CURRENT()) the entry pointed to by + the \a current pointer without affecting the loop traversal. +*/ +#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field) { \ + typeof((head)->first) __list_next; \ + typeof((head)->first) __list_prev = NULL; \ + typeof((head)->first) __new_prev = NULL; \ + for ((var) = (head)->first, __new_prev = (var), \ + __list_next = (var) ? (var)->field.next : NULL; \ + (var); \ + __list_prev = __new_prev, (var) = __list_next, \ + __new_prev = (var), \ + __list_next = (var) ? (var)->field.next : NULL \ + ) + +#define AST_RWLIST_TRAVERSE_SAFE_BEGIN AST_LIST_TRAVERSE_SAFE_BEGIN + +/*! + \brief Removes the \a current entry from a list during a traversal. + \param head This is a pointer to the list head structure + \param field This is the name of the field (declared using AST_LIST_ENTRY()) + used to link entries of this list together. + + \note This macro can \b only be used inside an AST_LIST_TRAVERSE_SAFE_BEGIN() + block; it is used to unlink the current entry from the list without affecting + the list traversal (and without having to re-traverse the list to modify the + previous entry, if any). + */ +#define AST_LIST_REMOVE_CURRENT(head, field) \ + __new_prev->field.next = NULL; \ + __new_prev = __list_prev; \ + if (__list_prev) \ + __list_prev->field.next = __list_next; \ + else \ + (head)->first = __list_next; \ + if (!__list_next) \ + (head)->last = __list_prev; + +#define AST_RWLIST_REMOVE_CURRENT AST_LIST_REMOVE_CURRENT + +/*! + \brief Inserts a list entry before the current entry during a traversal. + \param head This is a pointer to the list head structure + \param elm This is a pointer to the entry to be inserted. + \param field This is the name of the field (declared using AST_LIST_ENTRY()) + used to link entries of this list together. + + \note This macro can \b only be used inside an AST_LIST_TRAVERSE_SAFE_BEGIN() + block. + */ +#define AST_LIST_INSERT_BEFORE_CURRENT(head, elm, field) do { \ + if (__list_prev) { \ + (elm)->field.next = __list_prev->field.next; \ + __list_prev->field.next = elm; \ + } else { \ + (elm)->field.next = (head)->first; \ + (head)->first = (elm); \ + } \ + __new_prev = (elm); \ +} while (0) + +#define AST_RWLIST_INSERT_BEFORE_CURRENT AST_LIST_INSERT_BEFORE_CURRENT + +/*! + \brief Closes a safe loop traversal block. + */ +#define AST_LIST_TRAVERSE_SAFE_END } + +#define AST_RWLIST_TRAVERSE_SAFE_END AST_LIST_TRAVERSE_SAFE_END + +/*! + \brief Initializes a list head structure. + \param head This is a pointer to the list head structure + + This macro initializes a list head structure by setting the head + entry to \a NULL (empty list) and recreating the embedded lock. +*/ +#define AST_LIST_HEAD_INIT(head) { \ + (head)->first = NULL; \ + (head)->last = NULL; \ + ast_mutex_init(&(head)->lock); \ +} + +/*! + \brief Initializes an rwlist head structure. + \param head This is a pointer to the list head structure + + This macro initializes a list head structure by setting the head + entry to \a NULL (empty list) and recreating the embedded lock. +*/ +#define AST_RWLIST_HEAD_INIT(head) { \ + (head)->first = NULL; \ + (head)->last = NULL; \ + ast_rwlock_init(&(head)->lock); \ +} + +/*! + \brief Destroys a list head structure. + \param head This is a pointer to the list head structure + + This macro destroys a list head structure by setting the head + entry to \a NULL (empty list) and destroying the embedded lock. + It does not free the structure from memory. +*/ +#define AST_LIST_HEAD_DESTROY(head) { \ + (head)->first = NULL; \ + (head)->last = NULL; \ + ast_mutex_destroy(&(head)->lock); \ +} + +/*! + \brief Destroys an rwlist head structure. + \param head This is a pointer to the list head structure + + This macro destroys a list head structure by setting the head + entry to \a NULL (empty list) and destroying the embedded lock. + It does not free the structure from memory. +*/ +#define AST_RWLIST_HEAD_DESTROY(head) { \ + (head)->first = NULL; \ + (head)->last = NULL; \ + ast_rwlock_destroy(&(head)->lock); \ +} + +/*! + \brief Initializes a list head structure. + \param head This is a pointer to the list head structure + + This macro initializes a list head structure by setting the head + entry to \a NULL (empty list). There is no embedded lock handling + with this macro. +*/ +#define AST_LIST_HEAD_INIT_NOLOCK(head) { \ + (head)->first = NULL; \ + (head)->last = NULL; \ +} + +/*! + \brief Inserts a list entry after a given entry. + \param head This is a pointer to the list head structure + \param listelm This is a pointer to the entry after which the new entry should + be inserted. + \param elm This is a pointer to the entry to be inserted. + \param field This is the name of the field (declared using AST_LIST_ENTRY()) + used to link entries of this list together. + */ +#define AST_LIST_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.next = (listelm)->field.next; \ + (listelm)->field.next = (elm); \ + if ((head)->last == (listelm)) \ + (head)->last = (elm); \ +} while (0) + +#define AST_RWLIST_INSERT_AFTER AST_LIST_INSERT_AFTER + +/*! + \brief Inserts a list entry at the head of a list. + \param head This is a pointer to the list head structure + \param elm This is a pointer to the entry to be inserted. + \param field This is the name of the field (declared using AST_LIST_ENTRY()) + used to link entries of this list together. + */ +#define AST_LIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.next = (head)->first; \ + (head)->first = (elm); \ + if (!(head)->last) \ + (head)->last = (elm); \ +} while (0) + +#define AST_RWLIST_INSERT_HEAD AST_LIST_INSERT_HEAD + +/*! + \brief Appends a list entry to the tail of a list. + \param head This is a pointer to the list head structure + \param elm This is a pointer to the entry to be appended. + \param field This is the name of the field (declared using AST_LIST_ENTRY()) + used to link entries of this list together. + + Note: The link field in the appended entry is \b not modified, so if it is + actually the head of a list itself, the entire list will be appended + temporarily (until the next AST_LIST_INSERT_TAIL is performed). + */ +#define AST_LIST_INSERT_TAIL(head, elm, field) do { \ + if (!(head)->first) { \ + (head)->first = (elm); \ + (head)->last = (elm); \ + } else { \ + (head)->last->field.next = (elm); \ + (head)->last = (elm); \ + } \ +} while (0) + +#define AST_RWLIST_INSERT_TAIL AST_LIST_INSERT_TAIL + +/*! + \brief Appends a whole list to the tail of a list. + \param head This is a pointer to the list head structure + \param list This is a pointer to the list to be appended. + \param field This is the name of the field (declared using AST_LIST_ENTRY()) + used to link entries of this list together. + */ +#define AST_LIST_APPEND_LIST(head, list, field) do { \ + if (!(head)->first) { \ + (head)->first = (list)->first; \ + (head)->last = (list)->last; \ + } else { \ + (head)->last->field.next = (list)->first; \ + (head)->last = (list)->last; \ + } \ +} while (0) + +#define AST_RWLIST_APPEND_LIST AST_LIST_APPEND_LIST + +/*! + \brief Removes and returns the head entry from a list. + \param head This is a pointer to the list head structure + \param field This is the name of the field (declared using AST_LIST_ENTRY()) + used to link entries of this list together. + + Removes the head entry from the list, and returns a pointer to it. + This macro is safe to call on an empty list. + */ +#define AST_LIST_REMOVE_HEAD(head, field) ({ \ + typeof((head)->first) cur = (head)->first; \ + if (cur) { \ + (head)->first = cur->field.next; \ + cur->field.next = NULL; \ + if ((head)->last == cur) \ + (head)->last = NULL; \ + } \ + cur; \ + }) + +#define AST_RWLIST_REMOVE_HEAD AST_LIST_REMOVE_HEAD + +/*! + \brief Removes a specific entry from a list. + \param head This is a pointer to the list head structure + \param elm This is a pointer to the entry to be removed. + \param field This is the name of the field (declared using AST_LIST_ENTRY()) + used to link entries of this list together. + \warning The removed entry is \b not freed nor modified in any way. + */ +#define AST_LIST_REMOVE(head, elm, field) do { \ + if ((head)->first == (elm)) { \ + (head)->first = (elm)->field.next; \ + if ((head)->last == (elm)) \ + (head)->last = NULL; \ + } else { \ + typeof(elm) curelm = (head)->first; \ + while (curelm && (curelm->field.next != (elm))) \ + curelm = curelm->field.next; \ + if (curelm) { \ + curelm->field.next = (elm)->field.next; \ + if ((head)->last == (elm)) \ + (head)->last = curelm; \ + } \ + } \ + (elm)->field.next = NULL; \ +} while (0) + +#define AST_RWLIST_REMOVE AST_LIST_REMOVE + +/* chanvars.h */ + +struct ast_var_t { + AST_LIST_ENTRY(ast_var_t) entries; + char *value; + char name[0]; +}; + +AST_LIST_HEAD_NOLOCK(varshead, ast_var_t); + +AST_RWLOCK_DEFINE_STATIC(globalslock); +static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE; + + +/* IN CONFLICT: struct ast_var_t *ast_var_assign(const char *name, const char *value); */ + +static struct ast_var_t *ast_var_assign(const char *name, const char *value); + +static void ast_var_delete(struct ast_var_t *var); + +/*from channel.h */ +#define AST_MAX_EXTENSION 80 /*!< Max length of an extension */ + + +/* from pbx.h */ +#define PRIORITY_HINT -1 /*!< Special Priority for a hint */ + +enum ast_extension_states { + AST_EXTENSION_REMOVED = -2, /*!< Extension removed */ + AST_EXTENSION_DEACTIVATED = -1, /*!< Extension hint removed */ + AST_EXTENSION_NOT_INUSE = 0, /*!< No device INUSE or BUSY */ + AST_EXTENSION_INUSE = 1 << 0, /*!< One or more devices INUSE */ + AST_EXTENSION_BUSY = 1 << 1, /*!< All devices BUSY */ + AST_EXTENSION_UNAVAILABLE = 1 << 2, /*!< All devices UNAVAILABLE/UNREGISTERED */ + AST_EXTENSION_RINGING = 1 << 3, /*!< All devices RINGING */ + AST_EXTENSION_ONHOLD = 1 << 4, /*!< All devices ONHOLD */ +}; + +struct ast_custom_function { + const char *name; /*!< Name */ + const char *synopsis; /*!< Short description for "show functions" */ + const char *desc; /*!< Help text that explains it all */ + const char *syntax; /*!< Syntax description */ + int (*read)(struct ast_channel *, const char *, char *, char *, size_t); /*!< Read function, if read is supported */ + int (*write)(struct ast_channel *, const char *, char *, const char *); /*!< Write function, if write is supported */ + AST_RWLIST_ENTRY(ast_custom_function) acflist; +}; + +typedef int (ast_switch_f)(struct ast_channel *chan, const char *context, + const char *exten, int priority, const char *callerid, const char *data); + +struct ast_switch { + AST_LIST_ENTRY(ast_switch) list; + const char *name; /*!< Name of the switch */ + const char *description; /*!< Description of the switch */ + + ast_switch_f *exists; + ast_switch_f *canmatch; + ast_switch_f *exec; + ast_switch_f *matchmore; +}; + + +static char *config = "extensions.conf"; +static char *registrar = "conf2ael"; +static char userscontext[AST_MAX_EXTENSION] = "default"; +static int static_config = 0; +static int write_protect_config = 1; +static int autofallthrough_config = 0; +static int clearglobalvars_config = 0; +/*! Go no deeper than this through includes (not counting loops) */ +#define AST_PBX_MAX_STACK 128 +static void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count); + + +/* stolen from callerid.c */ + +/*! \brief Clean up phone string + * remove '(', ' ', ')', non-trailing '.', and '-' not in square brackets. + * Basically, remove anything that could be invalid in a pattern. + */ +static void ast_shrink_phone_number(char *n) +{ + int x, y=0; + int bracketed = 0; + + for (x=0; n[x]; x++) { + switch(n[x]) { + case '[': + bracketed++; + n[y++] = n[x]; + break; + case ']': + bracketed--; + n[y++] = n[x]; + break; + case '-': + if (bracketed) + n[y++] = n[x]; + break; + case '.': + if (!n[x+1]) + n[y++] = n[x]; + break; + default: + if (!strchr("()", n[x])) + n[y++] = n[x]; + } + } + n[y] = '\0'; +} + + +/* stolen from chanvars.c */ + +static const char *ast_var_name(const struct ast_var_t *var) +{ + const char *name; + + if (var == NULL || (name = var->name) == NULL) + return NULL; + /* Return the name without the initial underscores */ + if (name[0] == '_') { + name++; + if (name[0] == '_') + name++; + } + return name; +} + + +/* stolen from asterisk.c */ + +static struct ast_flags ast_options = { AST_DEFAULT_OPTIONS }; +static int option_verbose = 0; /*!< Verbosity level */ +static int option_debug = 0; /*!< Debug level */ + + +/* experiment 1: see if it's easier just to use existing config code + * to read in the extensions.conf file. In this scenario, + I have to rip/copy code from other modules, because they + are staticly declared as-is. A solution would be to move + the ripped code to another location and make them available + to other modules and standalones */ + +/* Our own version of ast_log, since the expr parser uses it. -- stolen from utils/check_expr.c */ + +static void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + + printf("LOG: lev:%d file:%s line:%d func: %s ", + level, file, line, function); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +static void ast_verbose(const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + + printf("VERBOSE: "); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +/* stolen from main/utils.c */ +static char *ast_process_quotes_and_slashes(char *start, char find, char replace_with) +{ + char *dataPut = start; + int inEscape = 0; + int inQuotes = 0; + + for (; *start; start++) { + if (inEscape) { + *dataPut++ = *start; /* Always goes verbatim */ + inEscape = 0; + } else { + if (*start == '\\') { + inEscape = 1; /* Do not copy \ into the data */ + } else if (*start == '\'') { + inQuotes = 1 - inQuotes; /* Do not copy ' into the data */ + } else { + /* Replace , with |, unless in quotes */ + *dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start); + } + } + } + if (start != dataPut) + *dataPut = 0; + return dataPut; +} + +static int ast_true(const char *s) +{ + if (ast_strlen_zero(s)) + return 0; + + /* Determine if this is a true value */ + if (!strcasecmp(s, "yes") || + !strcasecmp(s, "true") || + !strcasecmp(s, "y") || + !strcasecmp(s, "t") || + !strcasecmp(s, "1") || + !strcasecmp(s, "on")) + return -1; + + return 0; +} + +/* stolen from pbx.c */ +#define VAR_BUF_SIZE 4096 + +#define VAR_NORMAL 1 +#define VAR_SOFTTRAN 2 +#define VAR_HARDTRAN 3 + +#define BACKGROUND_SKIP (1 << 0) +#define BACKGROUND_NOANSWER (1 << 1) +#define BACKGROUND_MATCHEXTEN (1 << 2) +#define BACKGROUND_PLAYBACK (1 << 3) + +/*! + \brief ast_exten: An extension + The dialplan is saved as a linked list with each context + having it's own linked list of extensions - one item per + priority. +*/ +struct ast_exten { + char *exten; /*!< Extension name */ + int matchcid; /*!< Match caller id ? */ + const char *cidmatch; /*!< Caller id to match for this extension */ + int priority; /*!< Priority */ + const char *label; /*!< Label */ + struct ast_context *parent; /*!< The context this extension belongs to */ + const char *app; /*!< Application to execute */ + struct ast_app *cached_app; /*!< Cached location of application */ + void *data; /*!< Data to use (arguments) */ + void (*datad)(void *); /*!< Data destructor */ + struct ast_exten *peer; /*!< Next higher priority with our extension */ + const char *registrar; /*!< Registrar */ + struct ast_exten *next; /*!< Extension with a greater ID */ + char stuff[0]; +}; +/* from pbx.h */ +typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data); +struct ast_timing { + int hastime; /*!< If time construct exists */ + unsigned int monthmask; /*!< Mask for month */ + unsigned int daymask; /*!< Mask for date */ + unsigned int dowmask; /*!< Mask for day of week (mon-sun) */ + unsigned int minmask[24]; /*!< Mask for minute */ +}; +/* end of pbx.h */ +/*! \brief ast_include: include= support in extensions.conf */ +struct ast_include { + const char *name; + const char *rname; /*!< Context to include */ + const char *registrar; /*!< Registrar */ + int hastime; /*!< If time construct exists */ + struct ast_timing timing; /*!< time construct */ + struct ast_include *next; /*!< Link them together */ + char stuff[0]; +}; + +/*! \brief ast_sw: Switch statement in extensions.conf */ +struct ast_sw { + char *name; + const char *registrar; /*!< Registrar */ + char *data; /*!< Data load */ + int eval; + AST_LIST_ENTRY(ast_sw) list; + char *tmpdata; + char stuff[0]; +}; + +/*! \brief ast_ignorepat: Ignore patterns in dial plan */ +struct ast_ignorepat { + const char *registrar; + struct ast_ignorepat *next; + const char pattern[0]; +}; + +/*! \brief ast_context: An extension context */ +struct ast_context { + ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */ + struct ast_exten *root; /*!< The root of the list of extensions */ + struct ast_context *next; /*!< Link them together */ + struct ast_include *includes; /*!< Include other contexts */ + struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */ + const char *registrar; /*!< Registrar */ + AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */ + ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */ + char name[0]; /*!< Name of the context */ +}; + + +/*! \brief ast_app: A registered application */ +struct ast_app { + int (*execute)(struct ast_channel *chan, void *data); + const char *synopsis; /*!< Synopsis text for 'show applications' */ + const char *description; /*!< Description (help text) for 'show application <name>' */ + AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */ + void *module; /*!< Module this app belongs to */ + char name[0]; /*!< Name of the application */ +}; + + +/*! \brief ast_state_cb: An extension state notify register item */ +struct ast_state_cb { + int id; + void *data; + ast_state_cb_type callback; + struct ast_state_cb *next; +}; + +/*! \brief Structure for dial plan hints + + \note Hints are pointers from an extension in the dialplan to one or + more devices (tech/name) + - See \ref AstExtState +*/ +struct ast_hint { + struct ast_exten *exten; /*!< Extension */ + int laststate; /*!< Last known state */ + struct ast_state_cb *callbacks; /*!< Callback list for this extension */ + AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */ +}; + +struct store_hint { + char *context; + char *exten; + struct ast_state_cb *callbacks; + int laststate; + AST_LIST_ENTRY(store_hint) list; + char data[1]; +}; + +AST_LIST_HEAD(store_hints, store_hint); + +static const struct cfextension_states { + int extension_state; + const char * const text; +} extension_states[] = { + { AST_EXTENSION_NOT_INUSE, "Idle" }, + { AST_EXTENSION_INUSE, "InUse" }, + { AST_EXTENSION_BUSY, "Busy" }, + { AST_EXTENSION_UNAVAILABLE, "Unavailable" }, + { AST_EXTENSION_RINGING, "Ringing" }, + { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" }, + { AST_EXTENSION_ONHOLD, "Hold" }, + { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" } +}; +#define STATUS_NO_CONTEXT 1 +#define STATUS_NO_EXTENSION 2 +#define STATUS_NO_PRIORITY 3 +#define STATUS_NO_LABEL 4 +#define STATUS_SUCCESS 5 + + +#if defined ( __i386__) && (defined(__FreeBSD__) || defined(linux)) +#if defined(__FreeBSD__) +#include <machine/cpufunc.h> +#elif defined(linux) +static __inline uint64_t +rdtsc(void) +{ + uint64_t rv; + + __asm __volatile(".byte 0x0f, 0x31" : "=A" (rv)); + return (rv); +} +#endif +#else /* supply a dummy function on other platforms */ +static __inline uint64_t +rdtsc(void) +{ + return 0; +} +#endif + + +static struct ast_var_t *ast_var_assign(const char *name, const char *value) +{ + struct ast_var_t *var; + int name_len = strlen(name) + 1; + int value_len = strlen(value) + 1; + + if (!(var = ast_calloc(sizeof(*var) + name_len + value_len, sizeof(char)))) { + return NULL; + } + + ast_copy_string(var->name, name, name_len); + var->value = var->name + name_len; + ast_copy_string(var->value, value, value_len); + + return var; +} + +static void ast_var_delete(struct ast_var_t *var) +{ + if (var) + free(var); +} + + +/* chopped this one off at the knees! */ +static int ast_func_write(struct ast_channel *chan, const char *function, const char *value) +{ + + /* ast_log(LOG_ERROR, "Function %s not registered\n", function); we are not interested in the details here */ + + return -1; +} + +static unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen) +{ + int argc; + char *scan; + int paren = 0, quote = 0; + + if (!buf || !array || !arraylen) + return 0; + + memset(array, 0, arraylen * sizeof(*array)); + + scan = buf; + + for (argc = 0; *scan && (argc < arraylen - 1); argc++) { + array[argc] = scan; + for (; *scan; scan++) { + if (*scan == '(') + paren++; + else if (*scan == ')') { + if (paren) + paren--; + } else if (*scan == '"' && delim != '"') { + quote = quote ? 0 : 1; + /* Remove quote character from argument */ + memmove(scan, scan + 1, strlen(scan)); + scan--; + } else if (*scan == '\\') { + /* Literal character, don't parse */ + memmove(scan, scan + 1, strlen(scan)); + } else if ((*scan == delim) && !paren && !quote) { + *scan++ = '\0'; + break; + } + } + } + + if (*scan) + array[argc++] = scan; + + return argc; +} + +static void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value) +{ + struct ast_var_t *newvariable; + struct varshead *headp; + const char *nametail = name; + + /* XXX may need locking on the channel ? */ + if (name[strlen(name)-1] == ')') { + char *function = ast_strdupa(name); + + ast_func_write(chan, function, value); + return; + } + + headp = &globals; + + /* For comparison purposes, we have to strip leading underscores */ + if (*nametail == '_') { + nametail++; + if (*nametail == '_') + nametail++; + } + + AST_LIST_TRAVERSE (headp, newvariable, entries) { + if (strcasecmp(ast_var_name(newvariable), nametail) == 0) { + /* there is already such a variable, delete it */ + AST_LIST_REMOVE(headp, newvariable, entries); + ast_var_delete(newvariable); + break; + } + } + + if (value) { + if ((option_verbose > 1) && (headp == &globals)) + ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value); + newvariable = ast_var_assign(name, value); + AST_LIST_INSERT_HEAD(headp, newvariable, entries); + } + +} + +static int pbx_builtin_setvar(struct ast_channel *chan, void *data) +{ + char *name, *value, *mydata; + int argc; + char *argv[24]; /* this will only support a maximum of 24 variables being set in a single operation */ + int global = 0; + int x; + + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "Set requires at least one variable name/value pair.\n"); + return 0; + } + + mydata = ast_strdupa(data); + argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0])); + + /* check for a trailing flags argument */ + if ((argc > 1) && !strchr(argv[argc-1], '=')) { + argc--; + if (strchr(argv[argc], 'g')) + global = 1; + } + + for (x = 0; x < argc; x++) { + name = argv[x]; + if ((value = strchr(name, '='))) { + *value++ = '\0'; + pbx_builtin_setvar_helper((global) ? NULL : chan, name, value); + } else + ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name); + } + + return(0); +} + +int localized_pbx_builtin_setvar(struct ast_channel *chan, void *data); + +int localized_pbx_builtin_setvar(struct ast_channel *chan, void *data) +{ + return pbx_builtin_setvar(chan, data); +} + + +/*! \brief Helper for get_range. + * return the index of the matching entry, starting from 1. + * If names is not supplied, try numeric values. + */ + +static int lookup_name(const char *s, char *const names[], int max) +{ + int i; + + if (names) { + for (i = 0; names[i]; i++) { + if (!strcasecmp(s, names[i])) + return i+1; + } + } else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) { + return i; + } + return 0; /* error return */ +} + +/*! \brief helper function to return a range up to max (7, 12, 31 respectively). + * names, if supplied, is an array of names that should be mapped to numbers. + */ +static unsigned get_range(char *src, int max, char *const names[], const char *msg) +{ + int s, e; /* start and ending position */ + unsigned int mask = 0; + + /* Check for whole range */ + if (ast_strlen_zero(src) || !strcmp(src, "*")) { + s = 0; + e = max - 1; + } else { + /* Get start and ending position */ + char *c = strchr(src, '-'); + if (c) + *c++ = '\0'; + /* Find the start */ + s = lookup_name(src, names, max); + if (!s) { + ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src); + return 0; + } + s--; + if (c) { /* find end of range */ + e = lookup_name(c, names, max); + if (!e) { + ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c); + return 0; + } + e--; + } else + e = s; + } + /* Fill the mask. Remember that ranges are cyclic */ + mask = 1 << e; /* initialize with last element */ + while (s != e) { + if (s >= max) { + s = 0; + mask |= (1 << s); + } else { + mask |= (1 << s); + s++; + } + } + return mask; +} + +/*! \brief store a bitmask of valid times, one bit each 2 minute */ +static void get_timerange(struct ast_timing *i, char *times) +{ + char *e; + int x; + int s1, s2; + int e1, e2; + /* int cth, ctm; */ + + /* start disabling all times, fill the fields with 0's, as they may contain garbage */ + memset(i->minmask, 0, sizeof(i->minmask)); + + /* 2-minutes per bit, since the mask has only 32 bits :( */ + /* Star is all times */ + if (ast_strlen_zero(times) || !strcmp(times, "*")) { + for (x=0; x<24; x++) + i->minmask[x] = 0x3fffffff; /* 30 bits */ + return; + } + /* Otherwise expect a range */ + e = strchr(times, '-'); + if (!e) { + ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n"); + return; + } + *e++ = '\0'; + /* XXX why skip non digits ? */ + while (*e && !isdigit(*e)) + e++; + if (!*e) { + ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n"); + return; + } + if (sscanf(times, "%d:%d", &s1, &s2) != 2) { + ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times); + return; + } + if (sscanf(e, "%d:%d", &e1, &e2) != 2) { + ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e); + return; + } + /* XXX this needs to be optimized */ +#if 1 + s1 = s1 * 30 + s2/2; + if ((s1 < 0) || (s1 >= 24*30)) { + ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times); + return; + } + e1 = e1 * 30 + e2/2; + if ((e1 < 0) || (e1 >= 24*30)) { + ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e); + return; + } + /* Go through the time and enable each appropriate bit */ + for (x=s1;x != e1;x = (x + 1) % (24 * 30)) { + i->minmask[x/30] |= (1 << (x % 30)); + } + /* Do the last one */ + i->minmask[x/30] |= (1 << (x % 30)); +#else + for (cth=0; cth<24; cth++) { + /* Initialize masks to blank */ + i->minmask[cth] = 0; + for (ctm=0; ctm<30; ctm++) { + if ( + /* First hour with more than one hour */ + (((cth == s1) && (ctm >= s2)) && + ((cth < e1))) + /* Only one hour */ + || (((cth == s1) && (ctm >= s2)) && + ((cth == e1) && (ctm <= e2))) + /* In between first and last hours (more than 2 hours) */ + || ((cth > s1) && + (cth < e1)) + /* Last hour with more than one hour */ + || ((cth > s1) && + ((cth == e1) && (ctm <= e2))) + ) + i->minmask[cth] |= (1 << (ctm / 2)); + } + } +#endif + /* All done */ + return; +} + +static void null_datad(void *foo) +{ +} + +/*! \brief Find realtime engine for realtime family */ +static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz) +{ + struct ast_config_engine *eng, *ret = NULL; + struct ast_config_map *map; + + + for (map = config_maps; map; map = map->next) { + if (!strcasecmp(family, map->name)) { + if (database) + ast_copy_string(database, map->database, dbsiz); + if (table) + ast_copy_string(table, map->table ? map->table : family, tabsiz); + break; + } + } + + /* Check if the required driver (engine) exist */ + if (map) { + for (eng = config_engine_list; !ret && eng; eng = eng->next) { + if (!strcasecmp(eng->name, map->driver)) + ret = eng; + } + } + + + /* if we found a mapping, but the engine is not available, then issue a warning */ + if (map && !ret) + ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver); + + return ret; +} + +struct ast_category *ast_config_get_current_category(const struct ast_config *cfg); + +struct ast_category *ast_config_get_current_category(const struct ast_config *cfg) +{ + return cfg->current; +} + +static struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno); + +static struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno) +{ + struct ast_category *category; + + if ((category = ast_calloc(1, sizeof(*category)))) + ast_copy_string(category->name, name, sizeof(category->name)); + category->file = strdup(in_file); + category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */ + return category; +} + +struct ast_category *localized_category_get(const struct ast_config *config, const char *category_name); + +struct ast_category *localized_category_get(const struct ast_config *config, const char *category_name) +{ + return category_get(config, category_name, 0); +} + +static void move_variables(struct ast_category *old, struct ast_category *new) +{ + struct ast_variable *var = old->root; + old->root = NULL; +#if 1 + /* we can just move the entire list in a single op */ + ast_variable_append(new, var); +#else + while (var) { + struct ast_variable *next = var->next; + var->next = NULL; + ast_variable_append(new, var); + var = next; + } +#endif +} + +static void inherit_category(struct ast_category *new, const struct ast_category *base) +{ + struct ast_variable *var; + + for (var = base->root; var; var = var->next) + ast_variable_append(new, variable_clone(var)); +} + +static void ast_category_append(struct ast_config *config, struct ast_category *category); + +static void ast_category_append(struct ast_config *config, struct ast_category *category) +{ + if (config->last) + config->last->next = category; + else + config->root = category; + config->last = category; + config->current = category; +} + +static void ast_category_destroy(struct ast_category *cat); + +static void ast_category_destroy(struct ast_category *cat) +{ + ast_variables_destroy(cat->root); + if (cat->file) + free(cat->file); + + free(cat); +} + +static struct ast_config_engine text_file_engine = { + .name = "text", + .load_func = config_text_file_load, +}; + + +static struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_incl_file); + +static struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_incl_file) +{ + char db[256]; + char table[256]; + struct ast_config_engine *loader = &text_file_engine; + struct ast_config *result; + + if (cfg->include_level == cfg->max_include_level) { + ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level); + return NULL; + } + + cfg->include_level++; + /* silence is golden! + ast_log(LOG_WARNING, "internal loading file %s level=%d\n", filename, cfg->include_level); + */ + + if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) { + struct ast_config_engine *eng; + + eng = find_engine(filename, db, sizeof(db), table, sizeof(table)); + + + if (eng && eng->load_func) { + loader = eng; + } else { + eng = find_engine("global", db, sizeof(db), table, sizeof(table)); + if (eng && eng->load_func) + loader = eng; + } + } + + result = loader->load_func(db, table, filename, cfg, withcomments, suggested_incl_file); + /* silence is golden + ast_log(LOG_WARNING, "finished internal loading file %s level=%d\n", filename, cfg->include_level); + */ + + if (result) + result->include_level--; + + return result; +} + + +static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, int withcomments, const char *suggested_include_file) +{ + char *c; + char *cur = buf; + struct ast_variable *v; + char cmd[512], exec_file[512]; + int object, do_exec, do_include; + + /* Actually parse the entry */ + if (cur[0] == '[') { + struct ast_category *newcat = NULL; + char *catname; + + /* A category header */ + c = strchr(cur, ']'); + if (!c) { + ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile); + return -1; + } + *c++ = '\0'; + cur++; + if (*c++ != '(') + c = NULL; + catname = cur; + if (!(*cat = newcat = ast_category_new(catname, ast_strlen_zero(suggested_include_file)?configfile:suggested_include_file, lineno))) { + return -1; + } + (*cat)->lineno = lineno; + + /* add comments */ + if (withcomments && comment_buffer && comment_buffer[0] ) { + newcat->precomments = ALLOC_COMMENT(comment_buffer); + } + if (withcomments && lline_buffer && lline_buffer[0] ) { + newcat->sameline = ALLOC_COMMENT(lline_buffer); + } + if( withcomments ) + CB_RESET(); + + /* If there are options or categories to inherit from, process them now */ + if (c) { + if (!(cur = strchr(c, ')'))) { + ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile); + return -1; + } + *cur = '\0'; + while ((cur = strsep(&c, ","))) { + if (!strcasecmp(cur, "!")) { + (*cat)->ignored = 1; + } else if (!strcasecmp(cur, "+")) { + *cat = category_get(cfg, catname, 1); + if (!*cat) { + ast_config_destroy(cfg); + if (newcat) + ast_category_destroy(newcat); + ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile); + return -1; + } + if (newcat) { + move_variables(newcat, *cat); + ast_category_destroy(newcat); + newcat = NULL; + } + } else { + struct ast_category *base; + + base = category_get(cfg, cur, 1); + if (!base) { + ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile); + return -1; + } + inherit_category(*cat, base); + } + } + } + if (newcat) + ast_category_append(cfg, *cat); + } else if (cur[0] == '#') { + /* A directive */ + cur++; + c = cur; + while(*c && (*c > 32)) c++; + if (*c) { + *c = '\0'; + /* Find real argument */ + c = ast_skip_blanks(c + 1); + if (!*c) + c = NULL; + } else + c = NULL; + do_include = !strcasecmp(cur, "include"); + if(!do_include) + do_exec = !strcasecmp(cur, "exec"); + else + do_exec = 0; + if (do_exec && !ast_opt_exec_includes) { + ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n"); + do_exec = 0; + } + if (do_include || do_exec) { + if (c) { + char *cur2; + char real_inclusion_name[256]; + struct ast_config_include *inclu; + + /* Strip off leading and trailing "'s and <>'s */ + while((*c == '<') || (*c == '>') || (*c == '\"')) c++; + /* Get rid of leading mess */ + cur = c; + cur2 = cur; + while (!ast_strlen_zero(cur)) { + c = cur + strlen(cur) - 1; + if ((*c == '>') || (*c == '<') || (*c == '\"')) + *c = '\0'; + else + break; + } + /* #exec </path/to/executable> + We create a tmp file, then we #include it, then we delete it. */ + if (do_exec) { + snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self()); + snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file); + ast_safe_system(cmd); + cur = exec_file; + } else + exec_file[0] = '\0'; + /* A #include */ + /* ast_log(LOG_WARNING, "Reading in included file %s withcomments=%d\n", cur, withcomments); */ + + /* record this inclusion */ + inclu = ast_include_new(cfg, configfile, cur, do_exec, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name)); + + do_include = ast_config_internal_load(cur, cfg, withcomments, real_inclusion_name) ? 1 : 0; + if(!ast_strlen_zero(exec_file)) + unlink(exec_file); + if(!do_include) + return 0; + /* ast_log(LOG_WARNING, "Done reading in included file %s withcomments=%d\n", cur, withcomments); */ + + } else { + ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", + do_exec ? "exec" : "include", + do_exec ? "/path/to/executable" : "filename", + lineno, + configfile); + } + } + else + ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile); + } else { + /* Just a line (variable = value) */ + if (!*cat) { + ast_log(LOG_WARNING, + "parse error: No category context for line %d of %s\n", lineno, configfile); + return -1; + } + c = strchr(cur, '='); + if (c) { + *c = 0; + c++; + /* Ignore > in => */ + if (*c== '>') { + object = 1; + c++; + } else + object = 0; + if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), configfile))) { + v->lineno = lineno; + v->object = object; + /* Put and reset comments */ + v->blanklines = 0; + ast_variable_append(*cat, v); + /* add comments */ + if (withcomments && comment_buffer && comment_buffer[0] ) { + v->precomments = ALLOC_COMMENT(comment_buffer); + } + if (withcomments && lline_buffer && lline_buffer[0] ) { + v->sameline = ALLOC_COMMENT(lline_buffer); + } + if( withcomments ) + CB_RESET(); + + } else { + return -1; + } + } else { + ast_log(LOG_WARNING, "EXTENSIONS.CONF: No '=' (equal sign) in line %d of %s\n", lineno, configfile); + } + } + return 0; +} + +static int use_local_dir = 1; + +void localized_use_local_dir(void); +void localized_use_conf_dir(void); + +void localized_use_local_dir(void) +{ + use_local_dir = 1; +} + +void localized_use_conf_dir(void) +{ + use_local_dir = 0; +} + + +static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_include_file) +{ + char fn[256]; + char buf[8192]; + char *new_buf, *comment_p, *process_buf; + FILE *f; + int lineno=0; + int comment = 0, nest[MAX_NESTED_COMMENTS]; + struct ast_category *cat = NULL; + int count = 0; + struct stat statbuf; + + cat = ast_config_get_current_category(cfg); + + if (filename[0] == '/') { + ast_copy_string(fn, filename, sizeof(fn)); + } else { + if (use_local_dir) + snprintf(fn, sizeof(fn), "./%s", filename); + else + snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename); + } + + if (withcomments && cfg && cfg->include_level < 2 ) { + CB_INIT(); + } + +#ifdef AST_INCLUDE_GLOB + { + int glob_ret; + glob_t globbuf; + + globbuf.gl_offs = 0; /* initialize it to silence gcc */ +#ifdef SOLARIS + glob_ret = glob(fn, GLOB_NOCHECK, NULL, &globbuf); +#else + glob_ret = glob(fn, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf); +#endif + if (glob_ret == GLOB_NOSPACE) + ast_log(LOG_WARNING, + "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn); + else if (glob_ret == GLOB_ABORTED) + ast_log(LOG_WARNING, + "Glob Expansion of pattern '%s' failed: Read error\n", fn); + else { + /* loop over expanded files */ + int i; + for (i=0; i<globbuf.gl_pathc; i++) { + ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn)); +#endif + do { + if (stat(fn, &statbuf)) + continue; + + if (!S_ISREG(statbuf.st_mode)) { + ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn); + continue; + } + if (option_verbose > 1) { + ast_verbose(VERBOSE_PREFIX_2 "Parsing '%s': ", fn); + fflush(stdout); + } + if (!(f = fopen(fn, "r"))) { + if (option_debug) + ast_log(LOG_DEBUG, "No file to parse: %s\n", fn); + if (option_verbose > 1) + ast_verbose( "Not found (%s)\n", strerror(errno)); + continue; + } + count++; + if (option_debug) + ast_log(LOG_DEBUG, "Parsing %s\n", fn); + if (option_verbose > 1) + ast_verbose("Found\n"); + while(!feof(f)) { + lineno++; + if (fgets(buf, sizeof(buf), f)) { + if ( withcomments ) { + CB_ADD(lline_buffer); /* add the current lline buffer to the comment buffer */ + lline_buffer[0] = 0; /* erase the lline buffer */ + } + + new_buf = buf; + if (comment) + process_buf = NULL; + else + process_buf = buf; + + while ((comment_p = strchr(new_buf, COMMENT_META))) { + if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) { + /* Yuck, gotta memmove */ + memmove(comment_p - 1, comment_p, strlen(comment_p) + 1); + new_buf = comment_p; + } else if(comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) { + /* Meta-Comment start detected ";--" */ + if (comment < MAX_NESTED_COMMENTS) { + *comment_p = '\0'; + new_buf = comment_p + 3; + comment++; + nest[comment-1] = lineno; + } else { + ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS); + } + } else if ((comment_p >= new_buf + 2) && + (*(comment_p - 1) == COMMENT_TAG) && + (*(comment_p - 2) == COMMENT_TAG)) { + /* Meta-Comment end detected */ + comment--; + new_buf = comment_p + 1; + if (!comment) { + /* Back to non-comment now */ + if (process_buf) { + /* Actually have to move what's left over the top, then continue */ + char *oldptr; + oldptr = process_buf + strlen(process_buf); + if ( withcomments ) { + CB_ADD(";"); + CB_ADD_LEN(oldptr+1,new_buf-oldptr-1); + } + + memmove(oldptr, new_buf, strlen(new_buf) + 1); + new_buf = oldptr; + } else + process_buf = new_buf; + } + } else { + if (!comment) { + /* If ; is found, and we are not nested in a comment, + we immediately stop all comment processing */ + if ( withcomments ) { + LLB_ADD(comment_p); + } + *comment_p = '\0'; + new_buf = comment_p; + } else + new_buf = comment_p + 1; + } + } + if( withcomments && comment && !process_buf ) + { + CB_ADD(buf); /* the whole line is a comment, store it */ + } + + if (process_buf) { + char *buf = ast_strip(process_buf); + if (!ast_strlen_zero(buf)) { + if (process_text_line(cfg, &cat, buf, lineno, filename, withcomments, suggested_include_file)) { + cfg = NULL; + break; + } + } + } + } + } + fclose(f); + } while(0); + if (comment) { + ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment]); + } +#ifdef AST_INCLUDE_GLOB + if (!cfg) + break; + } + globfree(&globbuf); + } + } +#endif + if (cfg && cfg->include_level == 1 && withcomments && comment_buffer) { + if (comment_buffer) { + free(comment_buffer); + free(lline_buffer); + comment_buffer=0; + lline_buffer=0; + comment_buffer_size=0; + lline_buffer_size=0; + } + } + if (count == 0) + return NULL; + + return cfg; +} + + +static struct ast_config *ast_config_new(void) ; + +static struct ast_config *ast_config_new(void) +{ + struct ast_config *config; + + if ((config = ast_calloc(1, sizeof(*config)))) + config->max_include_level = MAX_INCLUDE_LEVEL; + return config; +} + +struct ast_config *localized_config_load(const char *filename); + +struct ast_config *localized_config_load(const char *filename) +{ + struct ast_config *cfg; + struct ast_config *result; + + cfg = ast_config_new(); + if (!cfg) + return NULL; + + result = ast_config_internal_load(filename, cfg, 0, ""); + if (!result) + ast_config_destroy(cfg); + + return result; +} + +struct ast_config *localized_config_load_with_comments(const char *filename); + +struct ast_config *localized_config_load_with_comments(const char *filename) +{ + struct ast_config *cfg; + struct ast_config *result; + + cfg = ast_config_new(); + if (!cfg) + return NULL; + + result = ast_config_internal_load(filename, cfg, 1, ""); + if (!result) + ast_config_destroy(cfg); + + return result; +} + +static struct ast_category *next_available_category(struct ast_category *cat) +{ + for (; cat && cat->ignored; cat = cat->next); + + return cat; +} + +static char *ast_category_browse(struct ast_config *config, const char *prev) +{ + struct ast_category *cat = NULL; + + if (prev && config->last_browse && (config->last_browse->name == prev)) + cat = config->last_browse->next; + else if (!prev && config->root) + cat = config->root; + else if (prev) { + for (cat = config->root; cat; cat = cat->next) { + if (cat->name == prev) { + cat = cat->next; + break; + } + } + if (!cat) { + for (cat = config->root; cat; cat = cat->next) { + if (!strcasecmp(cat->name, prev)) { + cat = cat->next; + break; + } + } + } + } + + if (cat) + cat = next_available_category(cat); + + config->last_browse = cat; + return (cat) ? cat->name : NULL; +} + + + +void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat); + +void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat) +{ + /* cast below is just to silence compiler warning about dropping "const" */ + cfg->current = (struct ast_category *) cat; +} + +/* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine + which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be + recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may + be shocked and mystified as to why things are not showing up in the files! + + Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name + and line number are stored for each include, plus the name of the file included, so that these statements may be + included in the output files on a file_save operation. + + The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines + are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine + the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation, + and a header gets added. + + vars and category heads are output in the order they are stored in the config file. So, if the software + shuffles these at all, then the placement of #include directives might get a little mixed up, because the + file/lineno data probably won't get changed. + +*/ + +static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator) +{ + char date[256]=""; + time_t t; + time(&t); + ast_copy_string(date, ctime(&t), sizeof(date)); + + fprintf(f1, ";!\n"); + fprintf(f1, ";! Automatically generated configuration file\n"); + if (strcmp(configfile, fn)) + fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn); + else + fprintf(f1, ";! Filename: %s\n", configfile); + fprintf(f1, ";! Generator: %s\n", generator); + fprintf(f1, ";! Creation Date: %s", date); + fprintf(f1, ";!\n"); +} + +static void set_fn(char *fn, int fn_size, const char *file, const char *configfile) +{ + if (!file || file[0] == 0) { + if (configfile[0] == '/') + ast_copy_string(fn, configfile, fn_size); + else + snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile); + } else if (file[0] == '/') + ast_copy_string(fn, file, fn_size); + else + snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file); +} + +int localized_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator); + +int localized_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator) +{ + FILE *f; + char fn[256]; + struct ast_variable *var; + struct ast_category *cat; + struct ast_comment *cmt; + struct ast_config_include *incl; + int blanklines = 0; + + /* reset all the output flags, in case this isn't our first time saving this data */ + + for (incl=cfg->includes; incl; incl = incl->next) + incl->output = 0; + + /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions) + are all truncated to zero bytes and have that nice header*/ + + for (incl=cfg->includes; incl; incl = incl->next) + { + if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/ + FILE *f1; + + set_fn(fn, sizeof(fn), incl->included_file, configfile); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */ + f1 = fopen(fn,"w"); + if (f1) { + gen_header(f1, configfile, fn, generator); + fclose(f1); /* this should zero out the file */ + } else { + ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno)); + } + } + } + + set_fn(fn, sizeof(fn), 0, configfile); /* just set fn to absolute ver of configfile */ +#ifdef __CYGWIN__ + if ((f = fopen(fn, "w+"))) { +#else + if ((f = fopen(fn, "w"))) { +#endif + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "Saving '%s': ", fn); + + gen_header(f, configfile, fn, generator); + cat = cfg->root; + fclose(f); + + /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */ + /* since each var, cat, and associated comments can come from any file, we have to be + mobile, and open each file, print, and close it on an entry-by-entry basis */ + + while(cat) { + set_fn(fn, sizeof(fn), cat->file, configfile); + f = fopen(fn, "a"); + if (!f) + { + ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno)); + return -1; + } + + /* dump any includes that happen before this category header */ + for (incl=cfg->includes; incl; incl = incl->next) { + if (strcmp(incl->include_location_file, cat->file) == 0){ + if (cat->lineno > incl->include_location_lineno && !incl->output) { + if (incl->exec) + fprintf(f,"#exec \"%s\"\n", incl->exec_file); + else + fprintf(f,"#include \"%s\"\n", incl->included_file); + incl->output = 1; + } + } + } + + /* Dump section with any appropriate comment */ + for (cmt = cat->precomments; cmt; cmt=cmt->next) { + if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!') + fprintf(f,"%s", cmt->cmt); + } + if (!cat->precomments) + fprintf(f,"\n"); + fprintf(f, "[%s]", cat->name); + for(cmt = cat->sameline; cmt; cmt=cmt->next) { + fprintf(f,"%s", cmt->cmt); + } + if (!cat->sameline) + fprintf(f,"\n"); + fclose(f); + + var = cat->root; + while(var) { + set_fn(fn, sizeof(fn), var->file, configfile); + f = fopen(fn, "a"); + if (!f) + { + ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno)); + return -1; + } + + /* dump any includes that happen before this category header */ + for (incl=cfg->includes; incl; incl = incl->next) { + if (strcmp(incl->include_location_file, var->file) == 0){ + if (var->lineno > incl->include_location_lineno && !incl->output) { + if (incl->exec) + fprintf(f,"#exec \"%s\"\n", incl->exec_file); + else + fprintf(f,"#include \"%s\"\n", incl->included_file); + incl->output = 1; + } + } + } + + for (cmt = var->precomments; cmt; cmt=cmt->next) { + if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!') + fprintf(f,"%s", cmt->cmt); + } + if (var->sameline) + fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt); + else + fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value); + if (var->blanklines) { + blanklines = var->blanklines; + while (blanklines--) + fprintf(f, "\n"); + } + + fclose(f); + + + var = var->next; + } + cat = cat->next; + } + if ((option_verbose > 1) && !option_debug) + ast_verbose("Saved\n"); + } else { + if (option_debug) + ast_log(LOG_DEBUG, "Unable to open for writing: %s\n", fn); + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "Unable to write (%s)", strerror(errno)); + return -1; + } + + /* Now, for files with trailing #include/#exec statements, + we have to make sure every entry is output */ + + for (incl=cfg->includes; incl; incl = incl->next) { + if (!incl->output) { + /* open the respective file */ + set_fn(fn, sizeof(fn), incl->include_location_file, configfile); + f = fopen(fn, "a"); + if (!f) + { + ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno)); + return -1; + } + + /* output the respective include */ + if (incl->exec) + fprintf(f,"#exec \"%s\"\n", incl->exec_file); + else + fprintf(f,"#include \"%s\"\n", incl->included_file); + fclose(f); + incl->output = 1; + } + } + + return 0; +} + +/* ================ the Line ======================================== + above this line, you have what you need to load a config file, + and below it, you have what you need to process the extensions.conf + file into the context/exten/prio stuff. They are both in one file + to make things simpler */ + +static struct ast_context *local_contexts = NULL; +static struct ast_context *contexts = NULL; +struct ast_context; +struct ast_app; +#ifdef LOW_MEMORY +#define EXT_DATA_SIZE 256 +#else +#define EXT_DATA_SIZE 8192 +#endif +/*! + * When looking up extensions, we can have different requests + * identified by the 'action' argument, as follows. + * Note that the coding is such that the low 4 bits are the + * third argument to extension_match_core. + */ +enum ext_match_t { + E_MATCHMORE = 0x00, /* extension can match but only with more 'digits' */ + E_CANMATCH = 0x01, /* extension can match with or without more 'digits' */ + E_MATCH = 0x02, /* extension is an exact match */ + E_MATCH_MASK = 0x03, /* mask for the argument to extension_match_core() */ + E_SPAWN = 0x12, /* want to spawn an extension. Requires exact match */ + E_FINDLABEL = 0x22 /* returns the priority for a given label. Requires exact match */ +}; + +#ifdef NOT_ANYMORE +static AST_RWLIST_HEAD_STATIC(switches, ast_switch); +#endif + +#define SWITCH_DATA_LENGTH 256 + +static const char *ast_get_extension_app(struct ast_exten *e) +{ + return e ? e->app : NULL; +} + +static const char *ast_get_extension_name(struct ast_exten *exten) +{ + return exten ? exten->exten : NULL; +} + +static AST_RWLIST_HEAD_STATIC(hints, ast_hint); + +/*! \brief ast_change_hint: Change hint for an extension */ +static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne) +{ + struct ast_hint *hint; + int res = -1; + + AST_RWLIST_TRAVERSE(&hints, hint, list) { + if (hint->exten == oe) { + hint->exten = ne; + res = 0; + break; + } + } + + return res; +} + +/*! \brief ast_add_hint: Add hint to hint list, check initial extension state */ +static int ast_add_hint(struct ast_exten *e) +{ + struct ast_hint *hint; + + if (!e) + return -1; + + + /* Search if hint exists, do nothing */ + AST_RWLIST_TRAVERSE(&hints, hint, list) { + if (hint->exten == e) { + if (option_debug > 1) + ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e)); + return -1; + } + } + + if (option_debug > 1) + ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e)); + + if (!(hint = ast_calloc(1, sizeof(*hint)))) { + return -1; + } + /* Initialize and insert new item at the top */ + hint->exten = e; + AST_RWLIST_INSERT_HEAD(&hints, hint, list); + + return 0; +} + +/*! \brief add the extension in the priority chain. + * returns 0 on success, -1 on failure + */ +static int add_pri(struct ast_context *con, struct ast_exten *tmp, + struct ast_exten *el, struct ast_exten *e, int replace) +{ + struct ast_exten *ep; + + for (ep = NULL; e ; ep = e, e = e->peer) { + if (e->priority >= tmp->priority) + break; + } + if (!e) { /* go at the end, and ep is surely set because the list is not empty */ + ep->peer = tmp; + return 0; /* success */ + } + if (e->priority == tmp->priority) { + /* Can't have something exactly the same. Is this a + replacement? If so, replace, otherwise, bonk. */ + if (!replace) { + ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name); + tmp->datad(tmp->data); + free(tmp); + return -1; + } + /* we are replacing e, so copy the link fields and then update + * whoever pointed to e to point to us + */ + tmp->next = e->next; /* not meaningful if we are not first in the peer list */ + tmp->peer = e->peer; /* always meaningful */ + if (ep) /* We're in the peer list, just insert ourselves */ + ep->peer = tmp; + else if (el) /* We're the first extension. Take over e's functions */ + el->next = tmp; + else /* We're the very first extension. */ + con->root = tmp; + if (tmp->priority == PRIORITY_HINT) + ast_change_hint(e,tmp); + /* Destroy the old one */ + e->datad(e->data); + free(e); + } else { /* Slip ourselves in just before e */ + tmp->peer = e; + tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */ + if (ep) /* Easy enough, we're just in the peer list */ + ep->peer = tmp; + else { /* we are the first in some peer list, so link in the ext list */ + if (el) + el->next = tmp; /* in the middle... */ + else + con->root = tmp; /* ... or at the head */ + e->next = NULL; /* e is no more at the head, so e->next must be reset */ + } + /* And immediately return success. */ + if (tmp->priority == PRIORITY_HINT) + ast_add_hint(tmp); + } + return 0; +} + +/*! \brief ast_remove_hint: Remove hint from extension */ +static int ast_remove_hint(struct ast_exten *e) +{ + /* Cleanup the Notifys if hint is removed */ + struct ast_hint *hint; + struct ast_state_cb *cblist, *cbprev; + int res = -1; + + if (!e) + return -1; + + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) { + if (hint->exten == e) { + cbprev = NULL; + cblist = hint->callbacks; + while (cblist) { + /* Notify with -1 and remove all callbacks */ + cbprev = cblist; + cblist = cblist->next; + free(cbprev); + } + hint->callbacks = NULL; + AST_RWLIST_REMOVE_CURRENT(&hints, list); + free(hint); + res = 0; + break; + } + } + AST_RWLIST_TRAVERSE_SAFE_END + + return res; +} + +static void destroy_exten(struct ast_exten *e) +{ + if (e->priority == PRIORITY_HINT) + ast_remove_hint(e); + + if (e->datad) + e->datad(e->data); + free(e); +} + +char *days[] = +{ + "sun", + "mon", + "tue", + "wed", + "thu", + "fri", + "sat", + NULL, +}; + +char *months[] = +{ + "jan", + "feb", + "mar", + "apr", + "may", + "jun", + "jul", + "aug", + "sep", + "oct", + "nov", + "dec", + NULL, +}; + +static int ast_build_timing(struct ast_timing *i, const char *info_in) +{ + char info_save[256]; + char *info; + + /* Check for empty just in case */ + if (ast_strlen_zero(info_in)) + return 0; + /* make a copy just in case we were passed a static string */ + ast_copy_string(info_save, info_in, sizeof(info_save)); + info = info_save; + /* Assume everything except time */ + i->monthmask = 0xfff; /* 12 bits */ + i->daymask = 0x7fffffffU; /* 31 bits */ + i->dowmask = 0x7f; /* 7 bits */ + /* on each call, use strsep() to move info to the next argument */ + get_timerange(i, strsep(&info, "|")); + if (info) + i->dowmask = get_range(strsep(&info, "|"), 7, days, "day of week"); + if (info) + i->daymask = get_range(strsep(&info, "|"), 31, NULL, "day"); + if (info) + i->monthmask = get_range(strsep(&info, "|"), 12, months, "month"); + return 1; +} + +/*! + * \brief helper functions to sort extensions and patterns in the desired way, + * so that more specific patterns appear first. + * + * ext_cmp1 compares individual characters (or sets of), returning + * an int where bits 0-7 are the ASCII code of the first char in the set, + * while bit 8-15 are the cardinality of the set minus 1. + * This way more specific patterns (smaller cardinality) appear first. + * Wildcards have a special value, so that we can directly compare them to + * sets by subtracting the two values. In particular: + * 0x000xx one character, xx + * 0x0yyxx yy character set starting with xx + * 0x10000 '.' (one or more of anything) + * 0x20000 '!' (zero or more of anything) + * 0x30000 NUL (end of string) + * 0x40000 error in set. + * The pointer to the string is advanced according to needs. + * NOTES: + * 1. the empty set is equivalent to NUL. + * 2. given that a full set has always 0 as the first element, + * we could encode the special cases as 0xffXX where XX + * is 1, 2, 3, 4 as used above. + */ +static int ext_cmp1(const char **p) +{ + uint32_t chars[8]; + int c, cmin = 0xff, count = 0; + const char *end; + + /* load, sign extend and advance pointer until we find + * a valid character. + */ + while ( (c = *(*p)++) && (c == ' ' || c == '-') ) + ; /* ignore some characters */ + + /* always return unless we have a set of chars */ + switch (c) { + default: /* ordinary character */ + return 0x0000 | (c & 0xff); + + case 'N': /* 2..9 */ + return 0x0700 | '2' ; + + case 'X': /* 0..9 */ + return 0x0900 | '0'; + + case 'Z': /* 1..9 */ + return 0x0800 | '1'; + + case '.': /* wildcard */ + return 0x10000; + + case '!': /* earlymatch */ + return 0x20000; /* less specific than NULL */ + + case '\0': /* empty string */ + *p = NULL; + return 0x30000; + + case '[': /* pattern */ + break; + } + /* locate end of set */ + end = strchr(*p, ']'); + + if (end == NULL) { + ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n"); + return 0x40000; /* XXX make this entry go last... */ + } + + bzero(chars, sizeof(chars)); /* clear all chars in the set */ + for (; *p < end ; (*p)++) { + unsigned char c1, c2; /* first-last char in range */ + c1 = (unsigned char)((*p)[0]); + if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */ + c2 = (unsigned char)((*p)[2]); + *p += 2; /* skip a total of 3 chars */ + } else /* individual character */ + c2 = c1; + if (c1 < cmin) + cmin = c1; + for (; c1 <= c2; c1++) { + uint32_t mask = 1 << (c1 % 32); + if ( (chars[ c1 / 32 ] & mask) == 0) + count += 0x100; + chars[ c1 / 32 ] |= mask; + } + } + (*p)++; + return count == 0 ? 0x30000 : (count | cmin); +} + +/*! + * \brief the full routine to compare extensions in rules. + */ +static int ext_cmp(const char *a, const char *b) +{ + /* make sure non-patterns come first. + * If a is not a pattern, it either comes first or + * we use strcmp to compare the strings. + */ + int ret = 0; + + if (a[0] != '_') + return (b[0] == '_') ? -1 : strcmp(a, b); + + /* Now we know a is a pattern; if b is not, a comes first */ + if (b[0] != '_') + return 1; +#if 0 /* old mode for ext matching */ + return strcmp(a, b); +#endif + /* ok we need full pattern sorting routine */ + while (!ret && a && b) + ret = ext_cmp1(&a) - ext_cmp1(&b); + if (ret == 0) + return 0; + else + return (ret > 0) ? 1 : -1; +} + +/*! \brief copy a string skipping whitespace */ +static int ext_strncpy(char *dst, const char *src, int len) +{ + int count=0; + + while (*src && (count < len - 1)) { + switch(*src) { + case ' ': + /* otherwise exten => [a-b],1,... doesn't work */ + /* case '-': */ + /* Ignore */ + break; + default: + *dst = *src; + dst++; + } + src++; + count++; + } + *dst = '\0'; + + return count; +} + +/* + * Wrapper around _extension_match_core() to do performance measurement + * using the profiling code. + */ +static int ast_check_timing(const struct ast_timing *i) +{ + struct tm tm; + time_t t = time(NULL); + + localtime_r(&t,&tm); + + /* If it's not the right month, return */ + if (!(i->monthmask & (1 << tm.tm_mon))) + return 0; + + /* If it's not that time of the month.... */ + /* Warning, tm_mday has range 1..31! */ + if (!(i->daymask & (1 << (tm.tm_mday-1)))) + return 0; + + /* If it's not the right day of the week */ + if (!(i->dowmask & (1 << tm.tm_wday))) + return 0; + + /* Sanity check the hour just to be safe */ + if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) { + ast_log(LOG_WARNING, "Insane time...\n"); + return 0; + } + + /* Now the tough part, we calculate if it fits + in the right time based on min/hour */ + if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2)))) + return 0; + + /* If we got this far, then we're good */ + return 1; +} + +#ifdef NOT_ANYMORE +static struct ast_switch *pbx_findswitch(const char *sw) +{ + struct ast_switch *asw; + + AST_RWLIST_TRAVERSE(&switches, asw, list) { + if (!strcasecmp(asw->name, sw)) + break; + } + + return asw; +} +#endif + + +static struct ast_context *ast_walk_contexts(struct ast_context *con); + +static struct ast_context *ast_walk_contexts(struct ast_context *con) +{ + return con ? con->next : contexts; +} + +struct ast_context *localized_walk_contexts(struct ast_context *con); +struct ast_context *localized_walk_contexts(struct ast_context *con) +{ + return ast_walk_contexts(con); +} + + + +static struct ast_exten *ast_walk_context_extensions(struct ast_context *con, + struct ast_exten *exten); + +static struct ast_exten *ast_walk_context_extensions(struct ast_context *con, + struct ast_exten *exten) +{ + if (!exten) + return con ? con->root : NULL; + else + return exten->next; +} + +struct ast_exten *localized_walk_context_extensions(struct ast_context *con, + struct ast_exten *exten); +struct ast_exten *localized_walk_context_extensions(struct ast_context *con, + struct ast_exten *exten) +{ + return ast_walk_context_extensions(con,exten); +} + + +static struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten, + struct ast_exten *priority); + +static struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten, + struct ast_exten *priority) +{ + return priority ? priority->peer : exten; +} + +struct ast_exten *localized_walk_extension_priorities(struct ast_exten *exten, + struct ast_exten *priority); +struct ast_exten *localized_walk_extension_priorities(struct ast_exten *exten, + struct ast_exten *priority) +{ + return ast_walk_extension_priorities(exten, priority); +} + + + +static struct ast_include *ast_walk_context_includes(struct ast_context *con, + struct ast_include *inc); + +static struct ast_include *ast_walk_context_includes(struct ast_context *con, + struct ast_include *inc) +{ + if (!inc) + return con ? con->includes : NULL; + else + return inc->next; +} + +struct ast_include *localized_walk_context_includes(struct ast_context *con, + struct ast_include *inc); +struct ast_include *localized_walk_context_includes(struct ast_context *con, + struct ast_include *inc) +{ + return ast_walk_context_includes(con, inc); +} + + +static struct ast_sw *ast_walk_context_switches(struct ast_context *con, + struct ast_sw *sw); + +static struct ast_sw *ast_walk_context_switches(struct ast_context *con, + struct ast_sw *sw) +{ + if (!sw) + return con ? AST_LIST_FIRST(&con->alts) : NULL; + else + return AST_LIST_NEXT(sw, list); +} + +struct ast_sw *localized_walk_context_switches(struct ast_context *con, + struct ast_sw *sw); +struct ast_sw *localized_walk_context_switches(struct ast_context *con, + struct ast_sw *sw) +{ + return ast_walk_context_switches(con, sw); +} + + +static struct ast_context *ast_context_find(const char *name); + +static struct ast_context *ast_context_find(const char *name) +{ + struct ast_context *tmp = NULL; + while ( (tmp = ast_walk_contexts(tmp)) ) { + if (!name || !strcasecmp(name, tmp->name)) + break; + } + return tmp; +} + +/* request and result for pbx_find_extension */ +struct pbx_find_info { +#if 0 + const char *context; + const char *exten; + int priority; +#endif + + char *incstack[AST_PBX_MAX_STACK]; /* filled during the search */ + int stacklen; /* modified during the search */ + int status; /* set on return */ + struct ast_switch *swo; /* set on return */ + const char *data; /* set on return */ + const char *foundcontext; /* set on return */ +}; + +/* + * Internal function for ast_extension_{match|close} + * return 0 on no-match, 1 on match, 2 on early match. + * mode is as follows: + * E_MATCH success only on exact match + * E_MATCHMORE success only on partial match (i.e. leftover digits in pattern) + * E_CANMATCH either of the above. + */ + +static int _extension_match_core(const char *pattern, const char *data, enum ext_match_t mode) +{ + mode &= E_MATCH_MASK; /* only consider the relevant bits */ + + if ( (mode == E_MATCH) && (pattern[0] == '_') && (strcasecmp(pattern,data)==0) ) /* note: if this test is left out, then _x. will not match _x. !!! */ + return 1; + + if (pattern[0] != '_') { /* not a pattern, try exact or partial match */ + int ld = strlen(data), lp = strlen(pattern); + + if (lp < ld) /* pattern too short, cannot match */ + return 0; + /* depending on the mode, accept full or partial match or both */ + if (mode == E_MATCH) + return !strcmp(pattern, data); /* 1 on match, 0 on fail */ + if (ld == 0 || !strncasecmp(pattern, data, ld)) /* partial or full match */ + return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */ + else + return 0; + } + pattern++; /* skip leading _ */ + /* + * XXX below we stop at '/' which is a separator for the CID info. However we should + * not store '/' in the pattern at all. When we insure it, we can remove the checks. + */ + while (*data && *pattern && *pattern != '/') { + const char *end; + + if (*data == '-') { /* skip '-' in data (just a separator) */ + data++; + continue; + } + switch (toupper(*pattern)) { + case '[': /* a range */ + end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */ + if (end == NULL) { + ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n"); + return 0; /* unconditional failure */ + } + for (pattern++; pattern != end; pattern++) { + if (pattern+2 < end && pattern[1] == '-') { /* this is a range */ + if (*data >= pattern[0] && *data <= pattern[2]) + break; /* match found */ + else { + pattern += 2; /* skip a total of 3 chars */ + continue; + } + } else if (*data == pattern[0]) + break; /* match found */ + } + if (pattern == end) + return 0; + pattern = end; /* skip and continue */ + break; + case 'N': + if (*data < '2' || *data > '9') + return 0; + break; + case 'X': + if (*data < '0' || *data > '9') + return 0; + break; + case 'Z': + if (*data < '1' || *data > '9') + return 0; + break; + case '.': /* Must match, even with more digits */ + return 1; + case '!': /* Early match */ + return 2; + case ' ': + case '-': /* Ignore these in patterns */ + data--; /* compensate the final data++ */ + break; + default: + if (*data != *pattern) + return 0; + } + data++; + pattern++; + } + if (*data) /* data longer than pattern, no match */ + return 0; + /* + * match so far, but ran off the end of the data. + * Depending on what is next, determine match or not. + */ + if (*pattern == '\0' || *pattern == '/') /* exact match */ + return (mode == E_MATCHMORE) ? 0 : 1; /* this is a failure for E_MATCHMORE */ + else if (*pattern == '!') /* early match */ + return 2; + else /* partial match */ + return (mode == E_MATCH) ? 0 : 1; /* this is a failure for E_MATCH */ +} + +static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode) +{ + int i; + i = _extension_match_core(pattern, data, mode); + return i; +} + +static int ast_extension_match(const char *pattern, const char *data); + +static int ast_extension_match(const char *pattern, const char *data) +{ + return extension_match_core(pattern, data, E_MATCH); +} + +static int matchcid(const char *cidpattern, const char *callerid) +{ + /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so + failing to get a number should count as a match, otherwise not */ + + if (ast_strlen_zero(callerid)) + return ast_strlen_zero(cidpattern) ? 1 : 0; + + return ast_extension_match(cidpattern, callerid); +} + +static inline int include_valid(struct ast_include *i) +{ + if (!i->hastime) + return 1; + + return ast_check_timing(&(i->timing)); +} + + + +static struct ast_exten *pbx_find_extension(struct ast_channel *chan, + struct ast_context *bypass, + struct pbx_find_info *q, + const char *context, + const char *exten, + int priority, + const char *label, + const char *callerid, + enum ext_match_t action); + + +static struct ast_exten *pbx_find_extension(struct ast_channel *chan, + struct ast_context *bypass, + struct pbx_find_info *q, + const char *context, + const char *exten, + int priority, + const char *label, + const char *callerid, + enum ext_match_t action) +{ + int x; + struct ast_context *tmp; + struct ast_exten *e, *eroot; + struct ast_include *i; + + /* Initialize status if appropriate */ + if (q->stacklen == 0) { + q->status = STATUS_NO_CONTEXT; + q->swo = NULL; + q->data = NULL; + q->foundcontext = NULL; + } else if (q->stacklen >= AST_PBX_MAX_STACK) { + ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n"); + return NULL; + } + /* Check first to see if we've already been checked */ + for (x = 0; x < q->stacklen; x++) { + if (!strcasecmp(q->incstack[x], context)) + return NULL; + } + if (bypass) /* bypass means we only look there */ + tmp = bypass; + else { /* look in contexts */ + tmp = NULL; + while ((tmp = ast_walk_contexts(tmp)) ) { + if (!strcmp(tmp->name, context)) + break; + } + if (!tmp) + return NULL; + } + if (q->status < STATUS_NO_EXTENSION) + q->status = STATUS_NO_EXTENSION; + + /* scan the list trying to match extension and CID */ + eroot = NULL; + while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) { + int match = extension_match_core(eroot->exten, exten, action); + /* 0 on fail, 1 on match, 2 on earlymatch */ + + if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid))) + continue; /* keep trying */ + if (match == 2 && action == E_MATCHMORE) { + /* We match an extension ending in '!'. + * The decision in this case is final and is NULL (no match). + */ + return NULL; + } + /* found entry, now look for the right priority */ + if (q->status < STATUS_NO_PRIORITY) + q->status = STATUS_NO_PRIORITY; + e = NULL; + while ( (e = ast_walk_extension_priorities(eroot, e)) ) { + /* Match label or priority */ + if (action == E_FINDLABEL) { + if (q->status < STATUS_NO_LABEL) + q->status = STATUS_NO_LABEL; + if (label && e->label && !strcmp(label, e->label)) + break; /* found it */ + } else if (e->priority == priority) { + break; /* found it */ + } /* else keep searching */ + } + if (e) { /* found a valid match */ + q->status = STATUS_SUCCESS; + q->foundcontext = context; + return e; + } + } +#ifdef NOT_RIGHT_NOW + /* Check alternative switches??? */ + AST_LIST_TRAVERSE(&tmp->alts, sw, list) { + struct ast_switch *asw = pbx_findswitch(sw->name); + ast_switch_f *aswf = NULL; + char *datap; + + if (!asw) { + ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name); + continue; + } + /* No need to Substitute variables now; we shouldn't be here if there's any */ + + /* equivalent of extension_match_core() at the switch level */ + if (action == E_CANMATCH) + aswf = asw->canmatch; + else if (action == E_MATCHMORE) + aswf = asw->matchmore; + else /* action == E_MATCH */ + aswf = asw->exists; + datap = sw->eval ? sw->tmpdata : sw->data; + res = !aswf ? 0 : aswf(chan, context, exten, priority, callerid, datap); + if (res) { /* Got a match */ + q->swo = asw; + q->data = datap; + q->foundcontext = context; + /* XXX keep status = STATUS_NO_CONTEXT ? */ + return NULL; + } + } +#endif + q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */ + /* Now try any includes we have in this context */ + for (i = tmp->includes; i; i = i->next) { + if (include_valid(i)) { + if ((e = pbx_find_extension(NULL, bypass, q, i->rname, exten, priority, label, callerid, action))) + return e; + if (q->swo) + return NULL; + } + } + return NULL; +} + +struct ast_exten *localized_find_extension(struct ast_context *bypass, + struct pbx_find_info *q, + const char *context, + const char *exten, + int priority, + const char *label, + const char *callerid, + enum ext_match_t action); + +struct ast_exten *localized_find_extension(struct ast_context *bypass, + struct pbx_find_info *q, + const char *context, + const char *exten, + int priority, + const char *label, + const char *callerid, + enum ext_match_t action) +{ + return pbx_find_extension(NULL, bypass, q, context, exten, priority, label, callerid, action); +} + + +static struct ast_context *contexts; +AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */ + +static const char *ast_get_context_name(struct ast_context *con); + +static const char *ast_get_context_name(struct ast_context *con) +{ + return con ? con->name : NULL; +} + +/* + * errno values + * ENOMEM - out of memory + * EBUSY - can't lock + * EEXIST - already included + * EINVAL - there is no existence of context for inclusion + */ +static int ast_context_add_include2(struct ast_context *con, const char *value, + const char *registrar); + +static int ast_context_add_include2(struct ast_context *con, const char *value, + const char *registrar) +{ + struct ast_include *new_include; + char *c; + struct ast_include *i, *il = NULL; /* include, include_last */ + int length; + char *p; + + length = sizeof(struct ast_include); + length += 2 * (strlen(value) + 1); + + /* allocate new include structure ... */ + if (!(new_include = ast_calloc(1, length))) + return -1; + /* Fill in this structure. Use 'p' for assignments, as the fields + * in the structure are 'const char *' + */ + p = new_include->stuff; + new_include->name = p; + strcpy(p, value); + p += strlen(value) + 1; + new_include->rname = p; + strcpy(p, value); + /* Strip off timing info, and process if it is there */ + if ( (c = strchr(p, '|')) ) { + *c++ = '\0'; + new_include->hastime = ast_build_timing(&(new_include->timing), c); + } + new_include->next = NULL; + new_include->registrar = registrar; + + + /* ... go to last include and check if context is already included too... */ + for (i = con->includes; i; i = i->next) { + if (!strcasecmp(i->name, new_include->name)) { + free(new_include); + errno = EEXIST; + return -1; + } + il = i; + } + + /* ... include new context into context list, unlock, return */ + if (il) + il->next = new_include; + else + con->includes = new_include; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con)); + + return 0; +} + +int localized_context_add_include2(struct ast_context *con, const char *value, + const char *registrar); +int localized_context_add_include2(struct ast_context *con, const char *value, + const char *registrar) +{ + return ast_context_add_include2(con, value, registrar); +} + + + +static int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar); + +static int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar) +{ + struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL; + int length; + length = sizeof(struct ast_ignorepat); + length += strlen(value) + 1; + if (!(ignorepat = ast_calloc(1, length))) + return -1; + /* The cast to char * is because we need to write the initial value. + * The field is not supposed to be modified otherwise + */ + strcpy((char *)ignorepat->pattern, value); + ignorepat->next = NULL; + ignorepat->registrar = registrar; + for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) { + ignorepatl = ignorepatc; + if (!strcasecmp(ignorepatc->pattern, value)) { + /* Already there */ + errno = EEXIST; + return -1; + } + } + if (ignorepatl) + ignorepatl->next = ignorepat; + else + con->ignorepats = ignorepat; + return 0; + +} + +int localized_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar); + +int localized_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar) +{ + return ast_context_add_ignorepat2(con, value, registrar); +} + + +/* + * Lock context list functions ... + */ + +static int ast_wrlock_contexts(void) +{ + return ast_rwlock_wrlock(&conlock); +} + +static int ast_unlock_contexts(void) +{ + return ast_rwlock_unlock(&conlock); +} + +static int ast_wrlock_context(struct ast_context *con) +{ + return ast_rwlock_wrlock(&con->lock); +} + +static int ast_unlock_context(struct ast_context *con) +{ + return ast_rwlock_unlock(&con->lock); +} + +/* + * errno values + * ENOMEM - out of memory + * EBUSY - can't lock + * EEXIST - already included + * EINVAL - there is no existence of context for inclusion + */ +static int ast_context_add_switch2(struct ast_context *con, const char *value, + const char *data, int eval, const char *registrar); + +static int ast_context_add_switch2(struct ast_context *con, const char *value, + const char *data, int eval, const char *registrar) +{ + struct ast_sw *new_sw; + struct ast_sw *i; + int length; + char *p; + + length = sizeof(struct ast_sw); + length += strlen(value) + 1; + if (data) + length += strlen(data); + length++; + if (eval) { + /* Create buffer for evaluation of variables */ + length += SWITCH_DATA_LENGTH; + length++; + } + + /* allocate new sw structure ... */ + if (!(new_sw = ast_calloc(1, length))) + return -1; + /* ... fill in this structure ... */ + p = new_sw->stuff; + new_sw->name = p; + strcpy(new_sw->name, value); + p += strlen(value) + 1; + new_sw->data = p; + if (data) { + strcpy(new_sw->data, data); + p += strlen(data) + 1; + } else { + strcpy(new_sw->data, ""); + p++; + } + if (eval) + new_sw->tmpdata = p; + new_sw->eval = eval; + new_sw->registrar = registrar; + + /* ... go to last sw and check if context is already swd too... */ + AST_LIST_TRAVERSE(&con->alts, i, list) { + if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) { + free(new_sw); + errno = EEXIST; + return -1; + } + } + + /* ... sw new context into context list, unlock, return */ + AST_LIST_INSERT_TAIL(&con->alts, new_sw, list); + + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con)); + + return 0; +} + +int localized_context_add_switch2(struct ast_context *con, const char *value, + const char *data, int eval, const char *registrar); + +int localized_context_add_switch2(struct ast_context *con, const char *value, + const char *data, int eval, const char *registrar) +{ + return ast_context_add_switch2(con, value, data, eval, registrar); +} + +static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay) +{ + struct ast_context *tmp, **local_contexts; + int length = sizeof(struct ast_context) + strlen(name) + 1; + + if (!extcontexts) { + ast_wrlock_contexts(); + local_contexts = &contexts; + } else + local_contexts = extcontexts; + + for (tmp = *local_contexts; tmp; tmp = tmp->next) { + if (!strcasecmp(tmp->name, name)) { + if (!existsokay) { + ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name); + tmp = NULL; + } + if (!extcontexts) + ast_unlock_contexts(); + return tmp; + } + } + if ((tmp = ast_calloc(1, length))) { + ast_rwlock_init(&tmp->lock); + ast_mutex_init(&tmp->macrolock); + strcpy(tmp->name, name); + tmp->root = NULL; + tmp->registrar = registrar; + tmp->next = *local_contexts; + tmp->includes = NULL; + tmp->ignorepats = NULL; + *local_contexts = tmp; + if (option_debug) + ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name); + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name); + } + + if (!extcontexts) + ast_unlock_contexts(); + return tmp; +} + +/*! \brief + * Main interface to add extensions to the list for out context. + * + * We sort extensions in order of matching preference, so that we can + * stop the search as soon as we find a suitable match. + * This ordering also takes care of wildcards such as '.' (meaning + * "one or more of any character") and '!' (which is 'earlymatch', + * meaning "zero or more of any character" but also impacts the + * return value from CANMATCH and EARLYMATCH. + * + * The extension match rules defined in the devmeeting 2006.05.05 are + * quite simple: WE SELECT THE LONGEST MATCH. + * In detail, "longest" means the number of matched characters in + * the extension. In case of ties (e.g. _XXX and 333) in the length + * of a pattern, we give priority to entries with the smallest cardinality + * (e.g, [5-9] comes before [2-8] before the former has only 5 elements, + * while the latter has 7, etc. + * In case of same cardinality, the first element in the range counts. + * If we still have a tie, any final '!' will make this as a possibly + * less specific pattern. + * + * EBUSY - can't lock + * EEXIST - extension with the same priority exist and no replace is set + * + */ +static int ast_add_extension2(struct ast_context *con, + int replace, const char *extension, int priority, const char *label, const char *callerid, + const char *application, void *data, void (*datad)(void *), + const char *registrar) +{ + /* + * Sort extensions (or patterns) according to the rules indicated above. + * These are implemented by the function ext_cmp()). + * All priorities for the same ext/pattern/cid are kept in a list, + * using the 'peer' field as a link field.. + */ + struct ast_exten *tmp, *e, *el = NULL; + int res; + int length; + char *p; + + /* if we are adding a hint, and there are global variables, and the hint + contains variable references, then expand them --- NOT In this situation!!! + */ + + length = sizeof(struct ast_exten); + length += strlen(extension) + 1; + length += strlen(application) + 1; + if (label) + length += strlen(label) + 1; + if (callerid) + length += strlen(callerid) + 1; + else + length ++; /* just the '\0' */ + + /* Be optimistic: Build the extension structure first */ + if (datad == NULL) + datad = null_datad; + if (!(tmp = ast_calloc(1, length))) + return -1; + + /* use p as dst in assignments, as the fields are const char * */ + p = tmp->stuff; + if (label) { + tmp->label = p; + strcpy(p, label); + p += strlen(label) + 1; + } + tmp->exten = p; + p += ext_strncpy(p, extension, strlen(extension) + 1) + 1; + tmp->priority = priority; + tmp->cidmatch = p; /* but use p for assignments below */ + if (callerid) { + p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1; + tmp->matchcid = 1; + } else { + *p++ = '\0'; + tmp->matchcid = 0; + } + tmp->app = p; + strcpy(p, application); + tmp->parent = con; + tmp->data = data; + tmp->datad = datad; + tmp->registrar = registrar; + + res = 0; /* some compilers will think it is uninitialized otherwise */ + for (e = con->root; e; el = e, e = e->next) { /* scan the extension list */ + res = ext_cmp(e->exten, extension); + if (res == 0) { /* extension match, now look at cidmatch */ + if (!e->matchcid && !tmp->matchcid) + res = 0; + else if (tmp->matchcid && !e->matchcid) + res = 1; + else if (e->matchcid && !tmp->matchcid) + res = -1; + else + res = strcasecmp(e->cidmatch, tmp->cidmatch); + } + if (res >= 0) + break; + } + if (e && res == 0) { /* exact match, insert in the pri chain */ + res = add_pri(con, tmp, el, e, replace); + if (res < 0) { + errno = EEXIST; /* XXX do we care ? */ + return 0; /* XXX should we return -1 maybe ? */ + } + } else { + /* + * not an exact match, this is the first entry with this pattern, + * so insert in the main list right before 'e' (if any) + */ + tmp->next = e; + if (el) + el->next = tmp; + else + con->root = tmp; + if (tmp->priority == PRIORITY_HINT) + ast_add_hint(tmp); + } + if (option_debug) { + if (tmp->matchcid) { + ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n", + tmp->exten, tmp->priority, tmp->cidmatch, con->name); + } else { + ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n", + tmp->exten, tmp->priority, con->name); + } + } + if (option_verbose > 2) { + if (tmp->matchcid) { + ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n", + tmp->exten, tmp->priority, tmp->cidmatch, con->name); + } else { + ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n", + tmp->exten, tmp->priority, con->name); + } + } + return 0; +} + +int localized_add_extension2(struct ast_context *con, + int replace, const char *extension, int priority, const char *label, const char *callerid, + const char *application, void *data, void (*datad)(void *), + const char *registrar); + +int localized_add_extension2(struct ast_context *con, + int replace, const char *extension, int priority, const char *label, const char *callerid, + const char *application, void *data, void (*datad)(void *), + const char *registrar) +{ + return ast_add_extension2(con, replace, extension, priority, label, callerid, application, data, datad, registrar); +} + + + +/*! \brief The return value depends on the action: + * + * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match, + * and return 0 on failure, -1 on match; + * E_FINDLABEL maps the label to a priority, and returns + * the priority on success, ... XXX + * E_SPAWN, spawn an application, + * and return 0 on success, -1 on failure. + */ +static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con, + const char *context, const char *exten, int priority, + const char *label, const char *callerid, enum ext_match_t action) +{ + struct ast_exten *e; + int res; + struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */ + + int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE); + + e = pbx_find_extension(NULL, con, &q, context, exten, priority, label, callerid, action); + if (e) { + if (matching_action) { + return -1; /* success, we found it */ + } else if (action == E_FINDLABEL) { /* map the label to a priority */ + res = e->priority; + return res; /* the priority we were looking for */ + } else { /* spawn */ + + /* NOT!!!!! */ + return 0; + } + } else if (q.swo) { /* not found here, but in another switch */ + if (matching_action) + return -1; + else { + if (!q.swo->exec) { + ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name); + res = -1; + } + return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data); + } + } else { /* not found anywhere, see what happened */ + switch (q.status) { + case STATUS_NO_CONTEXT: + if (!matching_action) + ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context); + break; + case STATUS_NO_EXTENSION: + if (!matching_action) + ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context); + break; + case STATUS_NO_PRIORITY: + if (!matching_action) + ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context); + break; + case STATUS_NO_LABEL: + if (context) + ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context); + break; + default: + if (option_debug) + ast_log(LOG_DEBUG, "Shouldn't happen!\n"); + } + + return (matching_action) ? 0 : -1; + } +} + +static int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid); + +static int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid) +{ + return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL); +} + +static struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar) +{ + return __ast_context_create(extcontexts, name, registrar, 1); +} + +struct ast_context *localized_context_create(struct ast_context **extcontexts, const char *name, const char *registrar); + +struct ast_context *localized_context_create(struct ast_context **extcontexts, const char *name, const char *registrar) +{ + return __ast_context_create(extcontexts, name, registrar, 0); +} + + + +/* chopped this one off at the knees */ +static int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len) +{ + ast_log(LOG_ERROR, "Function %s not registered\n", function); + return -1; +} + +/*! \brief extract offset:length from variable name. + * Returns 1 if there is a offset:length part, which is + * trimmed off (values go into variables) + */ +static int parse_variable_name(char *var, int *offset, int *length, int *isfunc) +{ + int parens=0; + + *offset = 0; + *length = INT_MAX; + *isfunc = 0; + for (; *var; var++) { + if (*var == '(') { + (*isfunc)++; + parens++; + } else if (*var == ')') { + parens--; + } else if (*var == ':' && parens == 0) { + *var++ = '\0'; + sscanf(var, "%d:%d", offset, length); + return 1; /* offset:length valid */ + } + } + return 0; +} + +static const char *ast_var_value(const struct ast_var_t *var) +{ + return (var ? var->value : NULL); +} + +/*! \brief takes a substring. It is ok to call with value == workspace. + * + * offset < 0 means start from the end of the string and set the beginning + * to be that many characters back. + * length is the length of the substring. A value less than 0 means to leave + * that many off the end. + * Always return a copy in workspace. + */ +static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len) +{ + char *ret = workspace; + int lr; /* length of the input string after the copy */ + + ast_copy_string(workspace, value, workspace_len); /* always make a copy */ + + lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */ + + /* Quick check if no need to do anything */ + if (offset == 0 && length >= lr) /* take the whole string */ + return ret; + + if (offset < 0) { /* translate negative offset into positive ones */ + offset = lr + offset; + if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */ + offset = 0; + } + + /* too large offset result in empty string so we know what to return */ + if (offset >= lr) + return ret + lr; /* the final '\0' */ + + ret += offset; /* move to the start position */ + if (length >= 0 && length < lr - offset) /* truncate if necessary */ + ret[length] = '\0'; + else if (length < 0) { + if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */ + ret[lr + length - offset] = '\0'; + else + ret[0] = '\0'; + } + + return ret; +} + +/*! \brief Support for Asterisk built-in variables in the dialplan +\note See also + - \ref AstVar Channel variables + - \ref AstCauses The HANGUPCAUSE variable + */ +static void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp) +{ + const char not_found = '\0'; + char *tmpvar; + const char *s; /* the result */ + int offset, length; + int i, need_substring; + struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */ + + /* + * Make a copy of var because parse_variable_name() modifies the string. + * Then if called directly, we might need to run substring() on the result; + * remember this for later in 'need_substring', 'offset' and 'length' + */ + tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */ + need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */); + + /* + * Look first into predefined variables, then into variable lists. + * Variable 's' points to the result, according to the following rules: + * s == ¬_found (set at the beginning) means that we did not find a + * matching variable and need to look into more places. + * If s != ¬_found, s is a valid result string as follows: + * s = NULL if the variable does not have a value; + * you typically do this when looking for an unset predefined variable. + * s = workspace if the result has been assembled there; + * typically done when the result is built e.g. with an snprintf(), + * so we don't need to do an additional copy. + * s != workspace in case we have a string, that needs to be copied + * (the ast_copy_string is done once for all at the end). + * Typically done when the result is already available in some string. + */ + s = ¬_found; /* default value */ + if (s == ¬_found) { /* look for more */ + if (!strcmp(var, "EPOCH")) { + snprintf(workspace, workspacelen, "%u",(int)time(NULL)); + } + + s = workspace; + } + /* if not found, look into chanvars or global vars */ + for (i = 0; s == ¬_found && i < (sizeof(places) / sizeof(places[0])); i++) { + struct ast_var_t *variables; + if (!places[i]) + continue; + if (places[i] == &globals) + ast_rwlock_rdlock(&globalslock); + AST_LIST_TRAVERSE(places[i], variables, entries) { + if (strcasecmp(ast_var_name(variables), var)==0) { + s = ast_var_value(variables); + break; + } + } + if (places[i] == &globals) + ast_rwlock_unlock(&globalslock); + } + if (s == ¬_found || s == NULL) + *ret = NULL; + else { + if (s != workspace) + ast_copy_string(workspace, s, workspacelen); + *ret = workspace; + if (need_substring) + *ret = substring(*ret, offset, length, workspace, workspacelen); + } +} + +static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count) +{ + /* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be + zero-filled */ + char *cp4; + const char *tmp, *whereweare; + int length, offset, offset2, isfunction; + char *workspace = NULL; + char *ltmp = NULL, *var = NULL; + char *nextvar, *nextexp, *nextthing; + char *vars, *vare; + int pos, brackets, needsub, len; + + *cp2 = 0; /* just in case there's nothing to do */ + whereweare=tmp=cp1; + while (!ast_strlen_zero(whereweare) && count) { + /* Assume we're copying the whole remaining string */ + pos = strlen(whereweare); + nextvar = NULL; + nextexp = NULL; + nextthing = strchr(whereweare, '$'); + if (nextthing) { + switch (nextthing[1]) { + case '{': + nextvar = nextthing; + pos = nextvar - whereweare; + break; + case '[': + nextexp = nextthing; + pos = nextexp - whereweare; + break; + } + } + + if (pos) { + /* Can't copy more than 'count' bytes */ + if (pos > count) + pos = count; + + /* Copy that many bytes */ + memcpy(cp2, whereweare, pos); + + count -= pos; + cp2 += pos; + whereweare += pos; + *cp2 = 0; + } + + if (nextvar) { + /* We have a variable. Find the start and end, and determine + if we are going to have to recursively call ourselves on the + contents */ + vars = vare = nextvar + 2; + brackets = 1; + needsub = 0; + + /* Find the end of it */ + while (brackets && *vare) { + if ((vare[0] == '$') && (vare[1] == '{')) { + needsub++; + } else if (vare[0] == '{') { + brackets++; + } else if (vare[0] == '}') { + brackets--; + } else if ((vare[0] == '$') && (vare[1] == '[')) + needsub++; + vare++; + } + if (brackets) + ast_log(LOG_NOTICE, "Error in extension logic (missing '}' in '%s')\n", cp1); + len = vare - vars - 1; + + /* Skip totally over variable string */ + whereweare += (len + 3); + + if (!var) + var = alloca(VAR_BUF_SIZE); + + /* Store variable name (and truncate) */ + ast_copy_string(var, vars, len + 1); + + /* Substitute if necessary */ + if (needsub) { + if (!ltmp) + ltmp = alloca(VAR_BUF_SIZE); + + memset(ltmp, 0, VAR_BUF_SIZE); + pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1); + vars = ltmp; + } else { + vars = var; + } + + if (!workspace) + workspace = alloca(VAR_BUF_SIZE); + + workspace[0] = '\0'; + + parse_variable_name(vars, &offset, &offset2, &isfunction); + if (isfunction) { + /* Evaluate function */ + cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace; + if (option_debug) + ast_log(LOG_DEBUG, "Function result is '%s'\n", cp4 ? cp4 : "(null)"); + } else { + /* Retrieve variable value */ + pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp); + } + if (cp4) { + cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE); + + length = strlen(cp4); + if (length > count) + length = count; + memcpy(cp2, cp4, length); + count -= length; + cp2 += length; + *cp2 = 0; + } + } else if (nextexp) { + /* We have an expression. Find the start and end, and determine + if we are going to have to recursively call ourselves on the + contents */ + vars = vare = nextexp + 2; + brackets = 1; + needsub = 0; + + /* Find the end of it */ + while (brackets && *vare) { + if ((vare[0] == '$') && (vare[1] == '[')) { + needsub++; + brackets++; + vare++; + } else if (vare[0] == '[') { + brackets++; + } else if (vare[0] == ']') { + brackets--; + } else if ((vare[0] == '$') && (vare[1] == '{')) { + needsub++; + vare++; + } + vare++; + } + if (brackets) + ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n"); + len = vare - vars - 1; + + /* Skip totally over expression */ + whereweare += (len + 3); + + if (!var) + var = alloca(VAR_BUF_SIZE); + + /* Store variable name (and truncate) */ + ast_copy_string(var, vars, len + 1); + + /* Substitute if necessary */ + if (needsub) { + if (!ltmp) + ltmp = alloca(VAR_BUF_SIZE); + + memset(ltmp, 0, VAR_BUF_SIZE); + pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1); + vars = ltmp; + } else { + vars = var; + } + + length = ast_expr(vars, cp2, count, NULL); + + if (length) { + if (option_debug) + ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2); + count -= length; + cp2 += length; + *cp2 = 0; + } + } else + break; + } +} + +static void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count) +{ + pbx_substitute_variables_helper_full(c, NULL, cp1, cp2, count); +} + + +static int pbx_load_config(const char *config_file); + +static int pbx_load_config(const char *config_file) +{ + struct ast_config *cfg; + char *end; + char *label; + char realvalue[256]; + int lastpri = -2; + struct ast_context *con; + struct ast_variable *v; + const char *cxt; + const char *aft; + + cfg = localized_config_load(config_file); + if (!cfg) + return 0; + + /* Use existing config to populate the PBX table */ + static_config = ast_true(ast_variable_retrieve(cfg, "general", "static")); + write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect")); + if ((aft = ast_variable_retrieve(cfg, "general", "autofallthrough"))) + autofallthrough_config = ast_true(aft); + clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars")); + ast_set2_flag(&ast_options, ast_true(ast_variable_retrieve(cfg, "general", "priorityjumping")), AST_OPT_FLAG_PRIORITY_JUMPING); + + if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext"))) + ast_copy_string(userscontext, cxt, sizeof(userscontext)); + else + ast_copy_string(userscontext, "default", sizeof(userscontext)); + + for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) { + memset(realvalue, 0, sizeof(realvalue)); + pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); + pbx_builtin_setvar_helper(NULL, v->name, realvalue); + } + for (cxt = NULL; (cxt = ast_category_browse(cfg, cxt)); ) { + /* All categories but "general" or "globals" are considered contexts */ + if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals")) + continue; + con=ast_context_find_or_create(&local_contexts,cxt, registrar); + if (con == NULL) + continue; + + for (v = ast_variable_browse(cfg, cxt); v; v = v->next) { + if (!strcasecmp(v->name, "exten")) { + char *tc = ast_strdup(v->value); + if (tc) { + int ipri = -2; + char realext[256]=""; + char *plus, *firstp, *firstc; + char *pri, *appl, *data, *cidmatch; + char *stringp = tc; + char *ext = strsep(&stringp, ","); + if (!ext) + ext=""; + pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1); + cidmatch = strchr(realext, '/'); + if (cidmatch) { + *cidmatch++ = '\0'; + ast_shrink_phone_number(cidmatch); + } + pri = strsep(&stringp, ","); + if (!pri) + pri=""; + label = strchr(pri, '('); + if (label) { + *label++ = '\0'; + end = strchr(label, ')'); + if (end) + *end = '\0'; + else + ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno); + } + plus = strchr(pri, '+'); + if (plus) + *plus++ = '\0'; + if (!strcmp(pri,"hint")) + ipri=PRIORITY_HINT; + else if (!strcmp(pri, "next") || !strcmp(pri, "n")) { + if (lastpri > -2) + ipri = lastpri + 1; + else + ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n"); + } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) { + if (lastpri > -2) + ipri = lastpri; + else + ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n"); + } else if (sscanf(pri, "%d", &ipri) != 1 && + (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) { + ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno); + ipri = 0; + } + appl = S_OR(stringp, ""); + /* Find the first occurrence of either '(' or ',' */ + firstc = strchr(appl, ','); + firstp = strchr(appl, '('); + if (firstc && (!firstp || firstc < firstp)) { + /* comma found, no parenthesis */ + /* or both found, but comma found first */ + appl = strsep(&stringp, ","); + data = stringp; + } else if (!firstc && !firstp) { + /* Neither found */ + data = ""; + } else { + /* Final remaining case is parenthesis found first */ + appl = strsep(&stringp, "("); + data = stringp; + end = strrchr(data, ')'); + if ((end = strrchr(data, ')'))) { + *end = '\0'; + } else { + ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data); + } + ast_process_quotes_and_slashes(data, ',', '|'); + } + + if (!data) + data=""; + appl = ast_skip_blanks(appl); + if (ipri) { + if (plus) + ipri += atoi(plus); + lastpri = ipri; + if (!ast_opt_dont_warn && !strcmp(realext, "_.")) + ast_log(LOG_WARNING, "The use of '_.' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X.' instead at line %d\n", v->lineno); + if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free, registrar)) { + ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno); + } + } + free(tc); + } + } else if (!strcasecmp(v->name, "include")) { + memset(realvalue, 0, sizeof(realvalue)); + pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); + if (ast_context_add_include2(con, realvalue, registrar)) + ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt); + } else if (!strcasecmp(v->name, "ignorepat")) { + memset(realvalue, 0, sizeof(realvalue)); + pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); + if (ast_context_add_ignorepat2(con, realvalue, registrar)) + ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt); + } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) { + char *stringp= realvalue; + char *appl, *data; + + memset(realvalue, 0, sizeof(realvalue)); + if (!strcasecmp(v->name, "switch")) + pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1); + else + ast_copy_string(realvalue, v->value, sizeof(realvalue)); + appl = strsep(&stringp, "/"); + data = strsep(&stringp, ""); /* XXX what for ? */ + if (!data) + data = ""; + if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar)) + ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt); + } else { + ast_log(LOG_WARNING, "==!!== Unknown directive: %s at line %d -- IGNORING!!!\n", v->name, v->lineno); + } + } + } + ast_config_destroy(cfg); + return 1; +} + +static void __ast_context_destroy(struct ast_context *con, const char *registrar) +{ + struct ast_context *tmp, *tmpl=NULL; + struct ast_include *tmpi; + struct ast_sw *sw; + struct ast_exten *e, *el, *en; + struct ast_ignorepat *ipi; + + for (tmp = contexts; tmp; ) { + struct ast_context *next; /* next starting point */ + for (; tmp; tmpl = tmp, tmp = tmp->next) { + if (option_debug) + ast_log(LOG_DEBUG, "check ctx %s %s\n", tmp->name, tmp->registrar); + if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) && + (!con || !strcasecmp(tmp->name, con->name)) ) + break; /* found it */ + } + if (!tmp) /* not found, we are done */ + break; + ast_wrlock_context(tmp); + if (option_debug) + ast_log(LOG_DEBUG, "delete ctx %s %s\n", tmp->name, tmp->registrar); + next = tmp->next; + if (tmpl) + tmpl->next = next; + else + contexts = next; + /* Okay, now we're safe to let it go -- in a sense, we were + ready to let it go as soon as we locked it. */ + ast_unlock_context(tmp); + for (tmpi = tmp->includes; tmpi; ) { /* Free includes */ + struct ast_include *tmpil = tmpi; + tmpi = tmpi->next; + free(tmpil); + } + for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */ + struct ast_ignorepat *ipl = ipi; + ipi = ipi->next; + free(ipl); + } + while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list))) + free(sw); + for (e = tmp->root; e;) { + for (en = e->peer; en;) { + el = en; + en = en->peer; + destroy_exten(el); + } + el = e; + e = e->next; + destroy_exten(el); + } + ast_rwlock_destroy(&tmp->lock); + free(tmp); + /* if we have a specific match, we are done, otherwise continue */ + tmp = con ? NULL : next; + } +} + +void localized_context_destroy(struct ast_context *con, const char *registrar); + +void localized_context_destroy(struct ast_context *con, const char *registrar) +{ + ast_wrlock_contexts(); + __ast_context_destroy(con,registrar); + ast_unlock_contexts(); +} + + +static void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar) +{ + struct ast_context *tmp, *lasttmp = NULL; + + /* it is very important that this function hold the hint list lock _and_ the conlock + during its operation; not only do we need to ensure that the list of contexts + and extensions does not change, but also that no hint callbacks (watchers) are + added or removed during the merge/delete process + + in addition, the locks _must_ be taken in this order, because there are already + other code paths that use this order + */ + ast_wrlock_contexts(); + + tmp = *extcontexts; + if (registrar) { + /* XXX remove previous contexts from same registrar */ + if (option_debug) + ast_log(LOG_DEBUG, "must remove any reg %s\n", registrar); + __ast_context_destroy(NULL,registrar); + while (tmp) { + lasttmp = tmp; + tmp = tmp->next; + } + } else { + /* XXX remove contexts with the same name */ + while (tmp) { + ast_log(LOG_WARNING, "must remove %s reg %s\n", tmp->name, tmp->registrar); + __ast_context_destroy(tmp,tmp->registrar); + lasttmp = tmp; + tmp = tmp->next; + } + } + if (lasttmp) { + lasttmp->next = contexts; + contexts = *extcontexts; + *extcontexts = NULL; + } else + ast_log(LOG_WARNING, "Requested contexts didn't get merged\n"); + + ast_unlock_contexts(); + + return; +} + +void localized_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar); + +void localized_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar) +{ + ast_merge_contexts_and_delete(extcontexts, registrar); +} + +static int ast_context_verify_includes(struct ast_context *con) +{ + struct ast_include *inc = NULL; + int res = 0; + + while ( (inc = ast_walk_context_includes(con, inc)) ) + if (!ast_context_find(inc->rname)) { + res = -1; + if (strcasecmp(inc->rname,"parkedcalls")!=0) + ast_log(LOG_WARNING, "Context '%s' tries to include the nonexistent context '%s'\n", + ast_get_context_name(con), inc->rname); + } + return res; +} + +int localized_context_verify_includes(struct ast_context *con); + +int localized_context_verify_includes(struct ast_context *con) +{ + return ast_context_verify_includes(con); +} + +int localized_pbx_load_module(void); + +int localized_pbx_load_module(void) +{ + struct ast_context *con; + + if(!pbx_load_config(config)) + return -1 /* AST_MODULE_LOAD_DECLINE*/; + + /* pbx_load_users(); */ /* does this affect the dialplan? */ + + ast_merge_contexts_and_delete(&local_contexts, registrar); + + for (con = NULL; (con = ast_walk_contexts(con));) + ast_context_verify_includes(con); + + printf("=== Loading extensions.conf ===\n"); + con = 0; + while ((con = ast_walk_contexts(con)) ) { + printf("Context: %s\n", con->name); + } + printf("=========\n"); + + return 0; +} + diff --git a/trunk/utils/frame.c b/trunk/utils/frame.c new file mode 100644 index 000000000..7fdb1637d --- /dev/null +++ b/trunk/utils/frame.c @@ -0,0 +1,1034 @@ +/**************************************************************************** + * + * Programs for processing sound files in raw- or WAV-format. + * -- Useful functions for parsing command line options and + * issuing errors, warnings, and chit chat. + * + * Name: frame.c + * Version: see static char *standardversion, below. + * Author: Mark Roberts <mark@manumark.de> + * Michael Labuschke <michael@labuschke.de> sys_errlist fixes + * + ****************************************************************************/ +/**************************************************************************** + * These are useful functions that all DSP programs might find handy + ****************************************************************************/ + +#include <stdio.h> +#include <math.h> +#include <stdlib.h> /* for exit and malloc */ +#include <string.h> +#include <time.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include "frame.h" + +time_t stopwatch; /* will hold time at start of calculation */ +int samplefrequency; +unsigned short samplewidth; +unsigned short channels; +int wavout; /* TRUE iff out file should be a .WAV file */ +int iswav; /* TRUE iff in file was found to be a .WAV file */ +FILE *in, *out; +char *infilename, *outfilename; +int verboselevel; +char *version = ""; +char *usage = ""; +static int test_usage; + +static char *standardversion = "frame version 1.3, June 13th 2001"; +static char *standardusage = +"\nOptions common to all mark-dsp programs:\n" + +"-h \t\t create a WAV-header on output files.\n" +"-c#\t\t set number of channels to # (1 or 2). Default: like input.\n" +"-w#\t\t set number of bits per sample (width) to # (only 16)\n" +"-f#\t\t set sample frequency to #. Default: like input.\n" +"-V \t\t verbose: talk a lot.\n" +"-Q \t\t quiet: talk as little as possible.\n\n" +"In most cases, a filename of '-' means stdin or stdout.\n\n" +"Bug-reports: mark@manumark.de\n" +; + +/* ----------------------------------------------------------------------- + Writes the number of samples to result that are yet to be read from anyin. + Return values are TRUE on success, FALSE on failure. + -----------------------------------------------------------------------*/ +int getremainingfilelength( FILE *anyin, long *result) +{ + long i; + + i = ftell (anyin); + if (i == -1) return FALSE; + if (fseek (anyin, 0, SEEK_END) == -1) return FALSE; + *result = ftell (anyin); + if (*result == -1) return FALSE; + (*result) -= i; + (*result) /= samplewidth; + if (fseek (anyin, i, SEEK_SET) == -1) return FALSE; + return TRUE; +} + +/* ----------------------------------------------------------------------- + Read a .pk-header from 'anyin'. + -----------------------------------------------------------------------*/ +void readpkheader( FILE *anyin) +{ + unsigned short tempushort; + int tempint, i, x; + unsigned char blood[8]; + + for (i = 0; i < 11; i++) + { + fread( &tempint, 4, 1, anyin); + printf( "%d: %d, ", i, tempint); + } + printf( "\n"); + fread( blood, 1, 8, anyin); + for (i = 0; i < 8; i++) + printf( "%d ", blood[i]); + printf( "\n"); + for (i = 0; i < 8; i++) + { + for (x = 128; x > 0; x /= 2) + printf((blood[i] & x) == 0? "0 ":"1 "); + printf(i%4==3? "\n":"| "); + } + printf( "\n"); + for (i = 0; i < 2; i++) + { + fread( &tempint, 4, 1, anyin); + printf( "%d: %d, ", i, tempint); + } + printf( "\n"); + for (i = 0; i < 2; i++) + { + fread( &tempushort, 2, 1, anyin); + printf( "%d: %d, ", i, tempushort); + } + printf( "\n"); +} + + + +/* ----------------------------------------------------------------------- + Read a .WAV header from 'anyin'. See header for details. + -----------------------------------------------------------------------*/ +void readwavheader( FILE *anyin) +{ + unsigned int tempuint, sf; + unsigned short tempushort, cn; + char str[9]; + int nowav = FALSE; + + iswav = FALSE; + + if (ftell(anyin) == -1) /* If we cannot seek this file */ + { + nowav = TRUE; /* -> Pretend this is no wav-file */ + chat("File not seekable: not checking for WAV-header.\n"); + } + else + { + /* Expect four bytes "RIFF" and four bytes filelength */ + fread (str, 1, 8, anyin); /* 0 */ + str[4] = '\0'; + if (strcmp(str, "RIFF") != 0) nowav = TRUE; + /* Expect eight bytes "WAVEfmt " */ + fread (str, 1, 8, anyin); /* 8 */ + str[8] = '\0'; + if (strcmp(str, "WAVEfmt ") != 0) nowav = TRUE; + /* Expect length of fmt data, which should be 16 */ + fread (&tempuint, 4, 1, anyin); /* 16 */ + if (tempuint != 16) nowav = TRUE; + /* Expect format tag, which should be 1 for pcm */ + fread (&tempushort, 2, 1, anyin); /* 20 */ + if (tempushort != 1) + nowav = TRUE; + /* Expect number of channels */ + fread (&cn, 2, 1, anyin); /* 20 */ + if (cn != 1 && cn != 2) nowav = TRUE; + /* Read samplefrequency */ + fread (&sf, 4, 1, anyin); /* 24 */ + /* Read bytes per second: Should be samplefreq * channels * 2 */ + fread (&tempuint, 4, 1, anyin); /* 28 */ + if (tempuint != sf * cn * 2) nowav = TRUE; + /* read bytes per frame: Should be channels * 2 */ + fread (&tempushort, 2, 1, anyin); /* 32 */ + if (tempushort != cn * 2) nowav = TRUE; + /* Read bits per sample: Should be 16 */ + fread (&tempushort, 2, 1, anyin); /* 34 */ + if (tempushort != 16) nowav = TRUE; + fread (str, 4, 1, anyin); /* 36 */ + str[4] = '\0'; + if (strcmp(str, "data") != 0) nowav = TRUE; + fread (&tempuint, 4, 1, anyin); /* 40 */ + if (nowav) + { + fseek (anyin, 0, SEEK_SET); /* Back to beginning of file */ + chat("File has no WAV header.\n"); + } + else + { + samplefrequency = sf; + channels = cn; + chat ("Read WAV header: %d channels, samplefrequency %d.\n", + channels, samplefrequency); + iswav = TRUE; + } + } + return; +} + + + +/* ----------------------------------------------------------------------- + Write a .WAV header to 'out'. See header for details. + -----------------------------------------------------------------------*/ +void makewavheader( void) +{ + unsigned int tempuint, filelength; + unsigned short tempushort; + + /* If fseek fails, don't create the header. */ + if (fseek (out, 0, SEEK_END) != -1) + { + filelength = ftell (out); + chat ("filelength %d, ", filelength); + fseek (out, 0, SEEK_SET); + fwrite ("RIFF", 1, 4, out); /* 0 */ + tempuint = filelength - 8; fwrite (&tempuint, 4, 1, out); /* 4 */ + fwrite ("WAVEfmt ", 1, 8, out); /* 8 */ + /* length of fmt data 16 bytes */ + tempuint = 16; + fwrite (&tempuint, 4, 1, out); /* 16 */ + /* Format tag: 1 for pcm */ + tempushort = 1; + fwrite (&tempushort, 2, 1, out); /* 20 */ + chat ("%d channels\n", channels); + fwrite (&channels, 2, 1, out); + chat ("samplefrequency %d\n", samplefrequency); + fwrite (&samplefrequency, 4, 1, out); /* 24 */ + /* Bytes per second */ + tempuint = channels * samplefrequency * 2; + fwrite (&tempuint, 4, 1, out); /* 28 */ + /* Block align */ + tempushort = 2 * channels; + fwrite (&tempushort, 2, 1, out); /* 32 */ + /* Bits per sample */ + tempushort = 16; + fwrite (&tempushort, 2, 1, out); /* 34 */ + fwrite ("data", 4, 1, out); /* 36 */ + tempuint = filelength - 44; fwrite (&tempuint, 4, 1, out); /* 40 */ + } + return; +} + +/* ----------------------------------------------------------------------- + After all is read and done, inform the inclined user of the elapsed time + -----------------------------------------------------------------------*/ +static void statistics( void) +{ + int temp; + + temp = time(NULL) - stopwatch; + if (temp != 1) + { + inform ("\nTime: %d seconds\n", temp); + } + else + { + inform ("\nTime: 1 second\n"); + } + return; +} + + +/* ----------------------------------------------------------------------- + Start the stopwatch and make sure the user is informed at end of program. + -----------------------------------------------------------------------*/ +void startstopwatch(void) +{ + stopwatch = time(NULL); /* Remember time 'now' */ + atexit(statistics); /* Call function statistics() at exit. */ + + return; +} + +/* -------------------------------------------------------------------- + Tests the character 'coal' for being a command line option character, + momentarrily '-'. + -------------------------------------------------------------------- */ +int isoptionchar (char coal) +{ + return (coal =='-'); +} + +/* ----------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as a time and passed + to *result, where the result is meant to mean 'number of samples' in + that time. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -----------------------------------------------------------------------*/ +int parsetimearg( int argcount, char *args[], char *string, int *result) +{ + int i; + + if ((i = findoption( argcount, args, string)) > 0) + { + if (parsetime(args[i] + 1 + strlen( string), result)) + return TRUE; + argerrornum(args[i]+1, ME_NOTIME); + } + return FALSE; +} + +/* ----------------------------------------------------------------------- + The string argument is read as a time and passed + to *result, where the result is meant to mean 'number of samples' in + that time. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -----------------------------------------------------------------------*/ +int parsetime(char *string, int *result) +{ + int k; + double temp; + char m, s, end; + + k = sscanf(string, "%lf%c%c%c", &temp, &m, &s, &end); + switch (k) + { + case 0: case EOF: case 4: + return FALSE; + case 1: + *result = temp; + break; + case 2: + if (m == 's') + *result = temp * samplefrequency; + else + return FALSE; + break; + case 3: + if (m == 'm' && s == 's') + *result = temp * samplefrequency / 1000; + else if (m == 'H' && s == 'z') + *result = samplefrequency / temp; + else + return FALSE; + break; + default: + argerrornum(NULL, ME_THISCANTHAPPEN); + } + return TRUE; +} + +/* ----------------------------------------------------------------------- + The string argument is read as a frequency and passed + to *result, where the result is meant to mean 'number of samples' in + one cycle of that frequency. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -----------------------------------------------------------------------*/ +int parsefreq(char *string, double *result) +{ + int k; + double temp; + char m, s, end; + + k = sscanf(string, "%lf%c%c%c", &temp, &m, &s, &end); + switch (k) + { + case 0: case EOF: case 2: case 4: + return FALSE; + case 1: + *result = temp; + break; + case 3: + if (m == 'H' && s == 'z') + *result = samplefrequency / temp; + else + return FALSE; + break; + default: + argerrornum(NULL, ME_THISCANTHAPPEN); + } + return TRUE; +} + +char *parsefilearg( int argcount, char *args[]) +{ + int i; + char *result = NULL; + + for (i = 1; i < argcount; i++) + { + if (args[i][0] != '\0' && + (!isoptionchar (args[i][0]) || args[i][1] == '\0' )) + { + /*---------------------------------------------* + * The argument is a filename: * + * it is either no dash followed by something, * + * or it is a dash following by nothing. * + *---------------------------------------------*/ + result = malloc( strlen( args[i]) + 1); + if (result == NULL) + fatalperror( "Couldn't allocate memory for filename\n"); + strcpy( result, args[i]); + args[i][0] = '\0'; /* Mark as used up */ + break; + } + } + return result; +} + +int parseswitch( char *found, char *wanted) +{ + if (strncmp( found, wanted, strlen( wanted)) == 0) + { + if (found[strlen( wanted)] == '\0') + return TRUE; + else + argerrornum( found, ME_NOSWITCH); + } + return FALSE; +} + +int parseswitcharg( int argcount, char *args[], char *string) +{ + int i; + + if ((i = findoption( argcount, args, string)) > 0) + { + if (args[i][strlen( string) + 1] == '\0') + return TRUE; + else + argerrornum( args[i] + 1, ME_NOSWITCH); + } + return FALSE; +} + +int parseintarg( int argcount, char *args[], char *string, int *result) +{ + int i, temp; + char c; + + if ((i = findoption( argcount, args, string)) > 0) + { + switch (sscanf(args[i] + 1 + strlen( string), + "%d%c", &temp, &c)) + { + case 0: case EOF: case 2: + argerrornum(args[i]+1, ME_NOINT); + return FALSE; + case 1: + *result = temp; + break; + default: + say("frame.c: This can't happen\n"); + } + return TRUE; + } + else + { + return FALSE; + } +} + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as a double and + passed to *result. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -------------------------------------------------------------------- */ +int parsedoublearg( int argcount, char *args[], char *string, double *result) +{ + int i; + double temp; + char end; + + if ((i = findoption( argcount, args, string)) > 0) + { + switch (sscanf(args[i] + 1 + strlen( string), "%lf%c", &temp, &end)) + { + case 0: case EOF: case 2: + argerrornum(args[i]+1, ME_NODOUBLE); + return FALSE; + case 1: + *result = temp; + break; + default: + say("frame.c: This can't happen\n"); + } + return TRUE; + } + else + { + return FALSE; + } +} + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as a volume, i.e. + absolute, percent or db. The result is passed to *result. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -------------------------------------------------------------------- */ +int parsevolarg( int argcount, char *args[], char *string, double *result) +{ + double vol = 1.0; + char sbd, sbb, end; + int i, weird = FALSE; + + if ((i = findoption( argcount, args, string)) > 0) + { + switch (sscanf(args[i] + 1 + strlen( string), + "%lf%c%c%c", &vol, &sbd, &sbb, &end)) + { + case 0: case EOF: case 4: + weird = TRUE; + break; /* No number: error */ + case 1: + *result = vol; + break; + case 2: + if (sbd == '%') + *result = vol / 100; + else + weird = TRUE; /* One char but no percent: error */ + break; + case 3: + if (sbd =='d' && sbb == 'b') + *result = pow(2, vol / 6.02); + else + weird = TRUE; /* Two chars but not db: error */ + break; + default: + say("frame.c: This can't happen.\n"); + } + if (weird) + argerrornum( args[i] + 1, ME_NOVOL); + /* ("Weird option: couldn't parse volume '%s'\n", args[i]+2); */ + return !weird; + } + else + { + return FALSE; + } +} + + +/* -------------------------------------------------------------------- + Reads the specified string 's' and interprets it as a volume. The string + would be of the form 1.8 or 180% or 5db. + On success, the return value TRUE and *result is given result + (i.e. the relative volume, i.e. 1.8). On failure, FALSE is returned and + result is given value 1.0. + -------------------------------------------------------------------- */ +int parsevolume(char *s, double *result) +{ + int k; + char sbd, sbb, end; + + *result = 1.0; + k = sscanf(s, "%lf%c%c%c", result, &sbd, &sbb, &end); + switch (k) + { + case 0: + case EOF: + case 4: + return FALSE; + case 1: + break; + case 2: + if (sbd != '%') + return FALSE; + (*result) /=100; + break; + case 3: + if (sbd !='d' || sbb != 'b') + return FALSE; + (*result) = pow(2, (*result) / 6.02); + break; + default: + say("parsevolume: This can't happen (%d).\n", k); + } + return TRUE; +} + +/* -------------------------------------------------------------------- + Reports an error due to parsing the string 's' encountered on the + command line. + -------------------------------------------------------------------- */ +void argerror(char *s) +{ + error ("Error parsing command line. Unrecognized option:\n\t-%s\n", s); + fatalerror("\nTry --help for help.\n"); +} + +/* -------------------------------------------------------------------- + Reports an error due to parsing the string 's' encountered on the + command line. 'code' indicates the type of error. + -------------------------------------------------------------------- */ +void argerrornum(char *s, Errornum code) +{ + char *message; + + if (code == ME_TOOMANYFILES) + { + error("Too many files on command line: '%s'.\n", s); + } + else + { + if (s != NULL) + error ("Error parsing option -%s:\n\t", s); + switch( code) + { + case ME_NOINT: + message = "Integer expected"; + break; + case ME_NODOUBLE: + message = "Floating point number expected"; + break; + case ME_NOTIME: + message = "Time argument expected"; + break; + case ME_NOVOL: + message = "Volume argument expected"; + break; + case ME_NOSWITCH: + message = "Garbage after switch-type option"; + break; + case ME_HEADERONTEXTFILE: + message = "Option -h is not useful for text-output"; + break; + case ME_NOINFILE: + message = "No input file specified"; + break; + case ME_NOOUTFILE: + message = "No output file specified"; + break; + case ME_NOIOFILE: + message = "No input/output file specified"; + break; + case ME_NOSTDIN: + message = "Standard in not supported here"; + break; + case ME_NOSTDOUT: + message = "Standard out not supported here"; + break; + case ME_NOSTDIO: + message = "Standard in/out not supported here"; + break; + case ME_NOTENOUGHFILES: + message = "Not enough files specified"; + break; + case ME_THISCANTHAPPEN: + fatalerror("\nThis can't happen. Report this as a bug\n"); + /* fatalerror does not return */ + default: + error("Error code %d not implemented. Fix me!\n", code); + message = "Error message not implemented. Fix me!"; + } + error("%s\n", message); + } + fatalerror("\nTry --help for help.\n"); +} + +/* -------------------------------------------------------------------- + Reports an error due to parsing the string 's' encountered on the + command line. 'message' explains the type of error. + -------------------------------------------------------------------- */ +void argerrortxt(char *s, char *message) +{ + if (s != NULL) + error ("Error parsing option -%s:\n\t", s); + else + error ("Error parsing command line:\n\t"); + error ("%s\n", message); + fatalerror("\nTry --help for help.\n"); +} + +/* -------------------------------------------------------------------- + Check for any remaining arguments and complain about their existence + -------------------------------------------------------------------- */ +void checknoargs( int argcount, char *args[]) +{ + int i, errorcount = 0; + + for (i = 1; i < argcount; i++) + { + if (args[i][0] != '\0') /* An unused argument! */ + { + errorcount++; + if (errorcount == 1) + error("The following arguments were not recognized:\n"); + error("\t%s\n", args[i]); + } + } + if (errorcount > 0) /* Errors are fatal */ + fatalerror("\nTry --help for help.\n"); + + return; /* No errors? Return. */ +} + +/* -------------------------------------------------------------------- + Parses the command line arguments as represented by the function + arguments. Sets the global variables 'in', 'out', 'samplefrequency' + and 'samplewidth' accordingly. Also verboselevel. + The files 'in' and 'out' are even opened according to 'fileswitch'. + See headerfile for details + -------------------------------------------------------------------- */ +void parseargs( int argcount, char *args[], int fileswitch) +{ + char *filename; + int tempint; + + if ((fileswitch & 1) != 0) /* If getting infile */ + in = NULL; + if ((fileswitch & 4) != 0) /* If getting outfile */ + out = NULL; + wavout = FALSE; + verboselevel = 5; + samplefrequency = DEFAULTFREQ; + samplewidth = 2; + channels = 1; + + /*-----------------------------------------------* + * First first check testcase, usage and version * + *-----------------------------------------------*/ + test_usage = parseswitcharg( argcount, args, "-test-usage"); + if (parseswitcharg( argcount, args, "-help")) + { + printf("%s%s", usage, standardusage); + exit(0); + } + if (parseswitcharg( argcount, args, "-version")) + { + printf("%s\n(%s)\n", version, standardversion); + exit(0); + } + /*--------------------------------------* + * Set verboselevel * + *--------------------------------------*/ + while (parseswitcharg( argcount, args, "V")) + verboselevel = 10; + while (parseswitcharg( argcount, args, "Q")) + verboselevel = 1; + /*-------------------------------------------------* + * Get filenames and open files * + *-------------------------------------------------*/ + if ((fileswitch & 1) != 0) /* Infile wanted */ + { + infilename = parsefilearg( argcount, args); + if (infilename == NULL) + argerrornum( NULL, ME_NOINFILE); + if (strcmp( infilename, "-") == 0) + { + infilename = "<stdin>"; + in = stdin; + if ((fileswitch & 2) != 0) /* Binfile wanted */ + readwavheader( in); + } + else + { + if ((fileswitch & 2) == 0) /* Textfile wanted */ + in = fopen(infilename, "rt"); + else /* Binfile wanted */ + if ((in = fopen(infilename, "rb")) != NULL) + readwavheader( in); + } + if (in == NULL) + fatalerror("Error opening input file '%s': %s\n", infilename,strerror(errno)); + else + inform("Using file '%s' as input\n", infilename); + } + if ((fileswitch & 4) != 0) /* Outfile wanted */ + { + outfilename = parsefilearg( argcount, args); + if (outfilename == NULL) + argerrornum( NULL, ME_NOOUTFILE); + if (strcmp( outfilename, "-") == 0) + { + outfilename = "<stdout>"; + out = stdout; + } + else + { + + if ((fileswitch & 8) == 0) /* Textfile wanted */ + out = fopen(outfilename, "wt"); + else /* Binfile wanted */ + out = fopen(outfilename, "wb"); + } + if (out == NULL) + fatalerror("Error opening output file '%s': %s\n", outfilename,strerror(errno)); + else + inform("Using file '%s' as output\n", outfilename); + } + if ((fileswitch & 32) != 0) /* In-/Outfile wanted */ + { + assert (in == NULL && out == NULL); + infilename = outfilename = parsefilearg( argcount, args); + if (outfilename == NULL) + argerrornum( NULL, ME_NOIOFILE); + if (strcmp( infilename, "-") == 0) + argerrornum( infilename, ME_NOSTDIN); + inform("Using file '%s' as input/output\n", outfilename); + in = out = fopen(outfilename, "r+"); + if (out == NULL) + fatalerror("Error opening input/output file '%s': %s\n", outfilename,strerror(errno)); + + readwavheader( in); + } + if ((fileswitch & 16) == 0) /* No additional files wanted */ + { + if ((filename = parsefilearg( argcount, args)) != NULL) + argerrornum( filename, ME_TOOMANYFILES); + } + + /*-------------------------------------------------* + * Set samplefrequency, width, wavout, + *-------------------------------------------------*/ + parseintarg( argcount, args, "f", &samplefrequency); + wavout = parseswitcharg( argcount, args, "h"); + if (parseintarg( argcount, args, "w", &tempint)) + { + if (tempint != 16) + argerrortxt(NULL, "Option -w is only valid " + "with value 16. Sorry."); + else + samplewidth = tempint; + } + if (parseintarg( argcount, args, "c", &tempint)) + { + if (tempint != 1 && tempint != 2) + argerrortxt(NULL, "Option -c is only valid " + "with values 1 or 2. Sorry."); + else + channels = tempint; + } + /*-------------------------------------------------* + * Create WAV-header on output if wanted. * + *-------------------------------------------------*/ + if (wavout) + switch (fileswitch & (12)) + { + case 4: /* User wants header on textfile */ + argerrornum( NULL, ME_HEADERONTEXTFILE); + case 12: /* User wants header on binfile */ + makewavheader(); + break; + case 0: /* User wants header, but there is no outfile */ + /* Problem: what about i/o-file, 32? You might want a header + on that? Better ignore this case. */ + break; + case 8: /* An application musn't ask for this */ + default: /* This can't happen */ + assert( FALSE); + } + return; +} + +/* -------------------------------------------------------------------- + Returns the index 'i' of the first argument that IS an option, and + which begins with the label 's'. If there is none, -1. + We also mark that option as done with, i.e. we cross it out. + -------------------------------------------------------------------- */ +int findoption( int argcount, char *args[], char *s) +{ + int i; + + if (test_usage) + printf("Checking for option -%s\n", s); + + for (i=1; i<argcount; i++) + { + if (isoptionchar (args[i][0]) && + strncmp( args[i] + 1, s, strlen( s)) == 0) + { + args[i][0] = '\0'; + return i; + } + } + return -1; +} + +/* -------------------------------------------------------------------- + Finishes off the .WAV header (if any) and exits correctly and formerly. + -------------------------------------------------------------------- */ +int myexit (int value) +{ + switch (value) + { + case 0: + if (wavout) + makewavheader(); /* Writes a fully informed .WAV header */ + chat ("Success!\n"); + break; + default: + chat ("Failure.\n"); + break; + } + exit (value); +} + +/* -------------------------------------------------------------------- + Reads the stated input file bufferwise, calls the function 'work' + with the proper values, and writes the result to the stated output file. + Return value: TRUE on success, FALSE otherwise. + -------------------------------------------------------------------- */ +int workloop( FILE *theinfile, FILE *theoutfile, + int (*work)( short *buffer, int length) ) +{ + short *buffer; + int length, nowlength; + + length = BUFFSIZE; + if ((buffer = malloc( sizeof(short) * length)) == NULL) + fatalperror (""); + while (TRUE) + { + nowlength = fread(buffer, sizeof(short), length, theinfile); + if (ferror( theinfile) != 0) + fatalperror("Error reading input file"); + if (nowlength == 0) /* Reached end of input file */ + break; + /* Call the routine that does the work */ + if (!work (buffer, nowlength)) /* On error, stop. */ + return FALSE; + fwrite(buffer, sizeof(short), nowlength, theoutfile); + if (ferror( theoutfile) != 0) + fatalperror("Error writing to output file"); + } + return TRUE; /* Input file done with, no errors. */ +} + +int chat( const char *format, ...) +{ + va_list ap; + int result = 0; + + if (verboselevel > 5) + { + va_start( ap, format); + result = vfprintf( stderr, format, ap); + va_end( ap); + } + return result; +} + + +int inform( const char *format, ...) +{ + va_list ap; + int result = 0; + + if (verboselevel > 1) + { + va_start( ap, format); + result = vfprintf( stderr, format, ap); + va_end( ap); + } + return result; +} + +int error( const char *format, ...) +{ + va_list ap; + int result; + + va_start( ap, format); + result = vfprintf( stderr, format, ap); + va_end( ap); + return result; +} + +void fatalerror( const char *format, ...) +{ + va_list ap; + + va_start( ap, format); + vfprintf( stderr, format, ap); + va_end( ap); + myexit(1); +} + +void fatalperror( const char *string) +{ + perror( string); + myexit( 1); +} + +int say( const char *format, ...) +{ + va_list ap; + int result; + + va_start( ap, format); + result = vfprintf( stdout, format, ap); + va_end( ap); + return result; +} + + +char *malloccopy( char *string) +{ + char *result; + + result = malloc( strlen( string) + 1); + if (result != NULL) + strcpy( result, string); + return result; +} + + +char *mallocconcat( char *one, char *two) +{ + char *result; + + result = malloc( strlen( one) + strlen( two) + 1); + if (result != NULL) + { + strcpy( result, one); + strcat( result, two); + } + return result; +} + +double double2db( double value) +{ + if (value < 0) + value = -value; + return 6.0 * log( value / 32767) / log( 2); +} + +void readawaysamples( FILE *in, size_t size) +{ + short *buffer; + int samplesread, count; + + buffer = malloc( sizeof( *buffer) * BUFFSIZE); + if (buffer == NULL) fatalperror("Couldn't allocate buffer"); + + while (size > 0) + { + if (size > BUFFSIZE) + count = BUFFSIZE; + else + count = size; + + samplesread = fread( buffer, sizeof(*buffer), count, in); + if (ferror( in) != 0) + fatalperror("Error reading input file"); + size -= samplesread; + } + free( buffer); +} + diff --git a/trunk/utils/frame.h b/trunk/utils/frame.h new file mode 100644 index 000000000..a07c605ec --- /dev/null +++ b/trunk/utils/frame.h @@ -0,0 +1,300 @@ +/**************************************************************************** + * + * Programs for processing sound files in raw- or WAV-format. + * -- Useful functions for parsing command line options and + * issuing errors, warnings, and chit chat. + * + * Name: frame.h + * Version: see frame.c + * Author: Mark Roberts <mark@manumark.de> + * + ****************************************************************************/ +/**************************************************************************** + * These are useful functions that all DSP programs might find handy + ****************************************************************************/ + +/* fileswitch for parseargs: + + The following are masks for several different ways of opening files. + -------------------------------------------------------------------- + Bit 0: Open infile? + Bit 1: Open infile as binary (as opposed to text) + Bit 2: Open outfile? + Bit 3: Open outfile as binary (as opposed to text) + Bit 4: Do not complain about too many file arguments + Bit 5: Open one file for input AND output, binary. +*/ +#define INTEXT (1+0) +#define INBIN (1+2) +#define OUTTEXT (4) +#define OUTBIN (4+8) +#define NOFILES (0) +#define NOCOMPLAIN (16) +#define IOBIN (32) + +#ifndef FALSE + #define FALSE (0==1) + #define TRUE (0==0) +#endif + +extern int samplefrequency; +extern unsigned short samplewidth; +extern unsigned short channels; +extern int wavout; /* TRUE iff out file is .WAV file */ +extern int iswav; /* TRUE iff in file was found to be a .WAV file */ +extern FILE *in, *out; +extern char *infilename, *outfilename; +extern int verboselevel; +extern char *version; /* String to be issued as version string. Should + be set by application. */ +extern char *usage; /* String to be issued as usage string. Should be + set by application. */ + +#define DEFAULTFREQ 44100 +#define BUFFSIZE 50000 /* How many samples to read in one go (preferred) */ +#define MINBUFFSIZE 5000 /* How many samples to read in one go (minimum) */ + +/************************************************* + * Types of errors handled by argerrornum() * + *************************************************/ +typedef enum +{ + ME_NOINT, + ME_NODOUBLE, + ME_NOTIME, + ME_NOVOL, + ME_NOSWITCH, + ME_TOOMANYFILES, + ME_HEADERONTEXTFILE, + ME_NOINFILE, + ME_NOOUTFILE, + ME_NOIOFILE, + ME_NOSTDIN, + ME_NOSTDOUT, + ME_NOSTDIO, + ME_NOTENOUGHFILES, + ME_THISCANTHAPPEN +} Errornum; + + +/* ----------------------------------------------------------------------- + Create memory and copy 'string', returning a pointer to the copy. + NULL is returned if malloc fails. + -----------------------------------------------------------------------*/ +extern char *malloccopy( char *string); + +/* ----------------------------------------------------------------------- + Start the stopwatch and make sure the user is informed at end of program. + -----------------------------------------------------------------------*/ +extern void startstopwatch(void); + +/* ----------------------------------------------------------------------- + Writes the number of samples to result that are yet to be read from anyin. + I.e. the number of remaining bytes is divided by the number of bytes per + sample value, but not by the number of channels. + Return values are TRUE on success, FALSE on failure. + -----------------------------------------------------------------------*/ +extern int getremainingfilelength( FILE *anyin, long *result); + +/* ----------------------------------------------------------------------- + Read a .pk-header from 'anyin' and printf the entries. + -----------------------------------------------------------------------*/ +void readpkheader( FILE *anyin); + +/* ----------------------------------------------------------------------- + Read a .WAV header from 'anyin'. + If it is recognised, the data is used. + Otherwise, we assume it's PCM-data and ignore the header. + The global variable 'iswav' is set on success, otherwise cleared. + -----------------------------------------------------------------------*/ +extern void readwavheader( FILE *anyin); + +/* ----------------------------------------------------------------------- + Write a .WAV header to 'out'. + The filepointer is placed at the end of 'out' before operation. + This should be called before any data is + written, and again, when ALL the data has been written. + First time, this positions the file pointer correctly; second time, the + missing data can be inserted that wasn't known the first time round. + -----------------------------------------------------------------------*/ +extern void makewavheader( void); + +/* -------------------------------------------------------------------- + Tests the character 'coal' for being a command line option character, + momentarrily '/' or '-'. + -------------------------------------------------------------------- */ +extern int isoptionchar (char coal); + +/* ----------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as a time and passed + to *result, where the result is meant to mean 'number of samples' in + that time. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -----------------------------------------------------------------------*/ +extern int parsetimearg( int argcount, char *args[], char *string, + int *result); + +/* ----------------------------------------------------------------------- + The string argument is read as a time and passed to *result, where + the result is meant to mean 'number of samples' in that time. On + failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -----------------------------------------------------------------------*/ +int parsetime(char *string, int *result); + +/* ----------------------------------------------------------------------- + The string argument is read as a frequency and passed + to *result, where the result is meant to mean 'number of samples' in + one cycle of that frequency. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -----------------------------------------------------------------------*/ +int parsefreq(char *string, double *result); + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for a switch -'string'. + return value is TRUE if one exists, FALSE otherwise. + If characters remain after the switch, a fatal error is issued. + -------------------------------------------------------------------- */ +extern int parseswitcharg( int argcount, char *args[], char *string); + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as an integer and + passed to &result. + On failure, &result is unchanged. + return value is TRUE on success, FALSE otherwise. + -------------------------------------------------------------------- */ +extern int parseintarg( int argcount, char *args[], char *string, + int *result); + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for a filename, i.e. anything + that does not start with the optionchar. The filename is copied to + newly allocated memory, a pointer to which is returned. + The argument is marked as used. Therefore repeated use of this function + will yield a complete list of filenames on the commandline. + If malloc() fails, the function does not return. + -------------------------------------------------------------------- */ +extern char *parsefilearg( int argcount, char *args[]); + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as a double and + passed to *result. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -------------------------------------------------------------------- */ +extern int parsedoublearg( int argcount, char *args[], char *string, + double *result); + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as a volume, i.e. + absolute, percent or db. The result is passed to *result. + On failure, *result is unchanged. + -------------------------------------------------------------------- */ +extern int parsevolarg( int argcount, char *args[], char *string, + double *result); + +/* -------------------------------------------------------------------- + Reads the specified string and interprets it as a volume. The string + would be of the form 1.8 or 180% or 5db. + On success, the return value is the relative volume, i.e. 1.8 + On failure, -1 is returned. + -------------------------------------------------------------------- */ +extern int parsevolume(char *s, double *result); + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for a switch -'string'. + return value is TRUE if one exists, FALSE otherwise. + If characters remain after the switch, a fatal error is issued. + -------------------------------------------------------------------- */ +extern int parseswitch( char *found, char *wanted); + +/* -------------------------------------------------------------------- + Reports an error due to parsing the string 's' encountered on the + command line. + -------------------------------------------------------------------- */ +extern void argerror(char *s); + +/* -------------------------------------------------------------------- + Reports an error due to parsing the string 's' encountered on the + command line. 'code' indicates the type of error. + -------------------------------------------------------------------- */ +extern void argerrornum(char *s, Errornum code); + +/* -------------------------------------------------------------------- + Reports an error due to parsing the string 's' encountered on the + command line. 'message' explains the type of error. + -------------------------------------------------------------------- */ +extern void argerrortxt(char *s, char *message); + +/* -------------------------------------------------------------------- + Check for any remaining arguments and complain about their existence. + If arguments are found, this function does not return. + -------------------------------------------------------------------- */ +extern void checknoargs( int argcount, char *args[]); + +/* -------------------------------------------------------------------- + Parses the command line arguments as represented by the function + arguments. Sets the global variables 'in', 'out', 'samplefrequency' + and 'samplewidth' accordingly. + According to 'fileswitch', in and out files are opened or not. See + above for an explanation of 'fileswitch'. + -------------------------------------------------------------------- */ +extern void parseargs( int argcount, char *args[], int fileswitch); + +/* -------------------------------------------------------------------- + Returns the index 'i' of the first argument that IS an option, and + which begins with the label 's'. If there is none, -1. + We also mark that option as done with, i.e. we cross it out. + -------------------------------------------------------------------- */ +extern int findoption( int argcount, char *args[], char *s); + +/* -------------------------------------------------------------------- + Finishes off the .WAV header (if any) and exits correctly and formerly. + -------------------------------------------------------------------- */ +extern int myexit (int value); + +/* -------------------------------------------------------------------- + Reads the stated input file bufferwise, calls the function 'work' + with the proper values, and writes the result to the stated output file. + Return value: TRUE on success, FALSE otherwise. + -------------------------------------------------------------------- */ +extern int workloop( FILE *theinfile, FILE *theoutfile, + int (*work)( short *buffer, int length) ); + +/* -------------------------------------------------------------------- + Five functions for printing to stderr. Depending on the level of verbose, + output may be supressed. fatalerror() is like error() but does not return. + fatalperror() is like the standard function perror() but does not return. + -------------------------------------------------------------------- */ +extern int chat( const char *format, ...); +extern int inform( const char *format, ...); +extern int error( const char *format, ...); +extern void fatalerror( const char *format, ...); +extern void fatalperror( const char *string); + +/* -------------------------------------------------------------------- + And one functions for printing to stdout. + -------------------------------------------------------------------- */ +extern int say( const char *format, ...); + +/* -------------------------------------------------------------------- + Allocate memory for it and return a pointer to a string made up of + the two argument strings. + -------------------------------------------------------------------- */ +extern char *mallocconcat( char *one, char *two); + +/* -------------------------------------------------------------------- + Convert a sample value to decibel. + -------------------------------------------------------------------- */ +extern double double2db( double value); + +/* -------------------------------------------------------------------- + Read 'size' samples from file 'in' and lose them. + -------------------------------------------------------------------- */ +extern void readawaysamples( FILE *in, size_t size); diff --git a/trunk/utils/hashtest.c b/trunk/utils/hashtest.c new file mode 100644 index 000000000..e1780791f --- /dev/null +++ b/trunk/utils/hashtest.c @@ -0,0 +1,364 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2007, Steve Murphy + * + * Steve Murphy <murf@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ +/*! \file + * + * \brief A program to thoroughly thrash a hash table, testing + * out locking safety, and making sure all functionality + * is functioning. Run with 5 or more threads to get that + * fully intense firestorm of activity. If your + * hash tables don't crash, lock up, or go weird, it must + * be good code! Even features some global counters + * that will get slightly behind because they aren't lock-protected. + * + * \author Steve Murphy <murf@digium.com> + */ + +#include "asterisk.h" +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <pthread.h> +#include <sys/stat.h> +#include <signal.h> +#include <errno.h> +#include "asterisk/lock.h" +#include "asterisk/hashtab.h" +#include "asterisk/channel.h" +#include "asterisk/utils.h" +#include "asterisk/module.h" +int testno = 1; + +/* stuff we need to make this work with the hashtab stuff */ + +int64_t ast_mark(int prof_id, int x) +{ + return 0; +} + +struct ht_element +{ + char *key; + char *val; +}; + +static int hashtab_compare_strings_nocase(const void *a, const void *b) +{ + const struct ht_element *ae = a, *be = b; + return ast_hashtab_compare_strings_nocase(ae->key, be->key); +} + +#if 0 +static int hashtab_compare_strings(const void *a, const void *b) +{ + const struct ht_element *ae = a, *be = b; + return ast_hashtab_compare_strings(ae->key, be->key); +} + +static unsigned int hashtab_hash_string(const void *obj) +{ + const struct ht_element *o = obj; + return ast_hashtab_hash_string((const void *)o->key); +} +#endif + +static unsigned int hashtab_hash_string_nocase(const void *obj) +{ + const struct ht_element *o = obj; + return ast_hashtab_hash_string_nocase((const void*)o->key); +} + +/* random numbers */ + +static int my_rand(int incl_low, int incl_high, unsigned int *seedp) +{ + if (incl_high == 0) + return 0; + + return incl_low + (rand_r(seedp) % incl_high); +} + + + + +/* the testing routines */ + +static int glob_highwater = 0; +struct ast_hashtab *glob_hashtab = 0; +unsigned int glob_seed = 0; +int els_removed = 0; +int els_added = 0; +int els_lookedup = 0; +int els_found = 0; +int els_traversals = 0; + +/* all the operations to perform on the hashtab */ + +static void add_element(void) +{ + char keybuf[100]; + struct ht_element *x = malloc(sizeof(struct ht_element)); + sprintf(keybuf,"key%08d", glob_highwater++); + x->key = strdup(keybuf); + x->val = strdup("interesting data"); + ast_hashtab_insert_immediate(glob_hashtab, x); + els_added++; +} + +static void traverse_elements(void) +{ + struct ht_element *el; + int c=0; + struct ast_hashtab_iter *it = ast_hashtab_start_write_traversal(glob_hashtab); +#ifdef DEBUG + printf("Traverse hashtab\n"); +#endif + while ((el = ast_hashtab_next(it))) { + c++; + } + ast_hashtab_end_traversal(it); + els_traversals++; /* unprotected, sometimes off, but, not really important, either */ +} + +static void * del_element(unsigned int *seedp) +{ + char keybuf[100]; + struct ht_element *el, lookup; + int x; + + /* pick a random element from 0 to highwater-1 */ + x = my_rand(0,glob_highwater-1,seedp); + sprintf(keybuf, "key%08d", x); +#ifdef DEBUG + printf("Removing %s", keybuf); +#endif + lookup.key = keybuf; + el = ast_hashtab_remove_object_via_lookup(glob_hashtab, &lookup); + + if (el) { +#ifdef DEBUG + printf("...YES (el=%x)\n", (unsigned long)el); +#endif + free(el->key); + free(el->val); + free(el); + els_removed++; + } else { +#ifdef DEBUG + printf("...NO.\n"); +#endif + return 0; + } + + + return el; +} + +static int lookup_element(unsigned int *seedp) +{ + char keybuf[100]; + struct ht_element *el, lookup; + int x; + + /* pick a random element from 0 to highwater-1 */ + x = my_rand(0,glob_highwater-1,seedp); + sprintf(keybuf, "key%08d", x); + lookup.key = keybuf; + el = ast_hashtab_lookup(glob_hashtab, &lookup); + els_lookedup++; + if (el) + els_found++; + if (el) + return 1; + return 0; +} + + +static void *hashtest(void *data) +{ + int my_els_removed = 0; + int my_els_added = 0; + int my_els_lookedup = 0; + int my_els_found = 0; + int my_els_traversals = 0; + int my_testno = testno++; + int its; + + /* data will be a random number == use as a seed for random numbers */ + unsigned long seed = (unsigned long)data; + + printf("hashtest thread created... test beginning\n"); + + /* main test routine-- a global hashtab exists, pound it like crazy */ + for(its=0;its<100000;its++) + { + void *seed2 = &seed; + int op = my_rand(0,100, seed2); + if (op<60) { + my_els_lookedup++; +#ifdef DEBUG + printf("%d[%d]: LOOKUP\n", my_testno, its); +#endif + if ((my_els_lookedup%1000)==0) { + printf("."); + fflush(stdout); + } + if (lookup_element(seed2)) + my_els_found++; + } else if (op < 61) { /* make this 61 and it'll take 15 minutes to run */ +#ifdef DEBUG + printf("%d[%d]: TRAVERSE\n", my_testno, its); +#endif + traverse_elements(); + my_els_traversals++; + + } else if (op < 80) { +#ifdef DEBUG + printf("%d[%d]: REMOVE\n", my_testno, its); +#endif + if (del_element(seed2)) + my_els_removed++; + } else { + my_els_added++; +#ifdef DEBUG + printf("%d[%d]: ADD\n", my_testno, its); +#endif + add_element(); + } + } + printf("\nhashtest thread %d exiting.... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n", + my_testno, my_els_found, my_els_lookedup, my_els_added, my_els_removed, my_els_traversals); + printf("\ntotals..................... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n", + els_found, els_lookedup, els_added, els_removed,els_traversals); + pthread_exit(0); +} + +static void run_hashtest(int numthr) +{ + pthread_t thr[numthr]; + void *thrres[numthr]; + int i, biggest, resize_cnt, numobjs, numbuckets; + + /* init a single global hashtab, then... */ + glob_hashtab = ast_hashtab_create(180000, hashtab_compare_strings_nocase, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_string_nocase, 1); + printf("starting with %d elements in the hashtable...\n", ast_hashtab_capacity(glob_hashtab)); + /* set a random seed */ + glob_seed = (unsigned int)time(0); + srand(glob_seed); + + /* create threads, each running hashtest */ + for(i=0;i<numthr;i++) + { + unsigned long z = rand(); + + printf("starting hashtest thread %d....\n",i+1); + if (ast_pthread_create(&thr[i], NULL, hashtest, (void*)z)) { + printf("Sorry, couldn't create thread #%d\n", i+1); + } + printf("hashtest thread spawned.... \n"); + } + /* collect threads, each running hashtest */ + for(i=0;i<numthr;i++) + { + printf("waiting for thread %d....\n", i+1); + if (pthread_join(thr[i], &thrres[i])) { + printf("Sorry, couldn't join thread #%d\n", i+1); + } + printf("hashtest thread %d done.... \n",i+1); + } + /* user has to kill/intr the process to stop the test? */ + ast_hashtab_get_stats(glob_hashtab, &biggest, &resize_cnt, &numobjs, &numbuckets); + printf("Some stats: longest bucket chain: %d; number of resizes: %d; number of objects: %d; capacity: %d\n", + biggest, resize_cnt, numobjs, numbuckets); +} + + +int main(int argc,char **argv) +{ + if (argc < 2 || argc > 2 || atoi(argv[1]) < 1) + { + printf("Usage: hashtest <number of threads>\n"); + exit(1); + } + + /* one arg == number of threads to create */ + run_hashtest(atoi(argv[1])); + + return 0; +} + +int ast_add_profile(const char *x, uint64_t scale) +{ + return 0; +} + +int ast_loader_register(int (*updater)(void)) +{ + return 1; +} + +int ast_loader_unregister(int (*updater)(void)) +{ + return 1; +} +void ast_module_register(const struct ast_module_info *x) +{ +} + +void ast_module_unregister(const struct ast_module_info *x) +{ +} + + +void ast_register_file_version(const char *file, const char *version) +{ +} + +void ast_unregister_file_version(const char *file) +{ + +} + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + printf("LOG: lev:%d file:%s line:%d func: %s ", + level, file, line, function); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +void ast_verbose(const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + + printf("VERBOSE: "); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +void ast_register_thread(char *name) +{ + +} + +void ast_unregister_thread(void *id) +{ +} diff --git a/trunk/utils/hashtest2.c b/trunk/utils/hashtest2.c new file mode 100644 index 000000000..0fe10ab75 --- /dev/null +++ b/trunk/utils/hashtest2.c @@ -0,0 +1,376 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2007, Steve Murphy + * + * Steve Murphy <murf@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ +/*! \file + * + * \brief A program to thoroughly thrash a hash table, testing + * out locking safety, and making sure all functionality + * is functioning. Run with 5 or more threads to get that + * fully intense firestorm of activity. If your + * hash tables don't crash, lock up, or go weird, it must + * be good code! Even features some global counters + * that will get slightly behind because they aren't lock-protected. + * + * \author Steve Murphy <murf@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <pthread.h> +#include <sys/stat.h> +#include <signal.h> + +#include "asterisk/lock.h" +#include "asterisk/astobj2.h" +#include "asterisk/channel.h" +#include "asterisk/utils.h" +#include "asterisk/module.h" + +int testno = 2; + +/* stuff we need to make this work with the astobj2 stuff */ + +int64_t ast_mark(int prof_id, int x) +{ + return 0; +} + +/* my OBJECT */ +struct ht_element +{ + char *key; + char *val; +}; + + +static int hash_string(const void *obj, const int flags) +{ + char *str = ((struct ht_element*)obj)->key; + int total; + + for (total=0; *str; str++) + { + unsigned int tmp = total; + total <<= 1; /* multiply by 2 */ + total += tmp; /* multiply by 3 */ + total <<= 2; /* multiply by 12 */ + total += tmp; /* multiply by 13 */ + + total += ((unsigned int)(*str)); + } + if (total < 0) + total = -total; + return total; +} + +static int hashtab_compare_strings(void *a, void *b, int flags) +{ + const struct ht_element *ae = a, *be = b; + return !strcmp(ae->key, be->key) ? CMP_MATCH : 0; +} + +/* random numbers */ + +static int my_rand(int incl_low, int incl_high, unsigned int *seedp) +{ + if (incl_high == 0) + return 0; + + return incl_low + (rand_r(seedp) % incl_high); +} + + + + +/* the testing routines */ + +static int glob_highwater = 0; +struct ao2_container *glob_hashtab = 0; +unsigned int glob_seed = 0; +int els_removed = 0; +int els_added = 0; +int els_lookedup = 0; +int els_found = 0; +int els_traversals = 0; + +/* all the operations to perform on the hashtab */ + +static void ht_destroy(void *obj) +{ + const struct ht_element *o = obj; + if (o->key) + free(o->key); + if (o->val) + free(o->val); +} + + +static void add_element(void) +{ + char keybuf[100]; + struct ht_element *x = ao2_alloc(sizeof(struct ht_element), ht_destroy); + sprintf(keybuf,"key%08d", glob_highwater++); + x->key = strdup(keybuf); + x->val = strdup("interesting data"); +#ifdef DEBUG + printf("+ %s\n", keybuf); +#endif + ao2_link(glob_hashtab, x); + + els_added++; /* unprotected, sometimes off, but, not really important, either */ +} + +static int do_nothing_cb(void *obj, void *arg, int flags) +{ + return 0; +} + +static void traverse_elements(void) +{ +#ifdef DEBUG + printf("Traverse hashtab\n"); +#endif + ao2_callback(glob_hashtab, OBJ_NODATA, do_nothing_cb, NULL); + els_traversals++; /* unprotected, sometimes off, but, not really important, either */ +} + +static void * del_element(unsigned int *seedp) +{ + char keybuf[100]; + struct ht_element *el, lookup; + int x; + + /* pick a random element from 0 to highwater-1 */ + x = my_rand(0,glob_highwater-1,seedp); + sprintf(keybuf, "key%08d", x); +#ifdef DEBUG + printf("- %s", keybuf); +#endif + lookup.key = keybuf; + el = ao2_find(glob_hashtab, &lookup, OBJ_POINTER); + if (el) { +#ifdef DEBUG + printf("...YES (el=%x)\n", (unsigned long)el); +#endif + ao2_unlink(glob_hashtab, el); /* mistakenly tried to use ao2_ref(c,-2) here to unlink. Bad Boy! */ + els_removed++; + } else { +#ifdef DEBUG + printf("...NO.\n"); +#endif + return 0; + } + return el; +} + +static int lookup_element(unsigned int *seedp) +{ + char keybuf[100]; + struct ht_element *el, lookup; + int x; + + /* pick a random element from 0 to highwater-1 */ + x = my_rand(0,glob_highwater-1,seedp); + sprintf(keybuf, "key%08d", x); + lookup.key = keybuf; + el = ao2_find(glob_hashtab, &lookup, OBJ_POINTER); + els_lookedup++; + if (el) { + els_found++; + ao2_ref(el, -1); /* toss out this ref, no longer needed */ + return 1; + } else { + return 0; + } +} + + +static void *hashtest(void *data) +{ + int my_els_removed = 0; + int my_els_added = 0; + int my_els_lookedup = 0; + int my_els_found = 0; + int my_els_traversals = 0; + int my_testno = testno++; + int its; + + /* data will be a random number == use as a seed for random numbers */ + unsigned long seed = (unsigned long)data; + printf("hashtest thread created... test beginning\n"); + + /* main test routine-- a global hashtab exists, pound it like crazy */ + for(its=0;its<100000;its++) + { + void *seed2 = &seed; + int op = my_rand(0,100, seed2); + if (op<60) { + my_els_lookedup++; +#ifdef DEBUG + printf("%d[%d]: LOOKUP\n", my_testno, its); +#endif + if ((my_els_lookedup%1000)==0) { + printf("."); + fflush(stdout); + } + if (lookup_element(seed2)) + my_els_found++; + } else if (op < 61) { /* make this 61 and it'll take 16 minutes to run */ +#ifdef DEBUG + printf("%d[%d]: TRAVERSE\n", my_testno, its); +#endif + traverse_elements(); + my_els_traversals++; + + } else if (op < 80) { +#ifdef DEBUG + printf("%d[%d]: REMOVE\n", my_testno, its); +#endif + if (del_element(seed2)) + my_els_removed++; + } else { + my_els_added++; +#ifdef DEBUG + printf("%d[%d]: ADD\n", my_testno, its); +#endif + add_element(); + } + } + printf("\nhashtest thread %d exiting.... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n", + my_testno, my_els_found, my_els_lookedup, my_els_added, my_els_removed, my_els_traversals); + printf("\ntotals..................... lookups=%d/%d, added=%d, removed=%d; traversals=%d\n", + els_found, els_lookedup, els_added, els_removed, els_traversals); + pthread_exit(0); + return NULL; +} + +static void run_hashtest(int numthr) +{ + pthread_t thr[numthr]; + void *thrres[numthr]; + int i; + + /* init a single global hashtab, then... */ + glob_hashtab = ao2_container_alloc(180000, hash_string, hashtab_compare_strings); + + /* set a random seed */ + glob_seed = (unsigned int)time(0); + srand(glob_seed); + + /* create threads, each running hashtest */ + for(i=0;i<numthr;i++) + { + unsigned long z = rand(); + + printf("starting hashtest thread %d....\n",i+1); + if (ast_pthread_create(&thr[i], NULL, hashtest, (void*)z)) { + printf("Sorry, couldn't create thread #%d\n", i+1); + } + printf("hashtest thread spawned.... \n"); + } + /* collect threads, each running hashtest */ + for(i=0;i<numthr;i++) + { + printf("waiting for thread %d....\n", i+1); + if (pthread_join(thr[i], &thrres[i])) { + printf("Sorry, couldn't join thread #%d\n", i+1); + } + printf("hashtest thread %d done.... \n",i+1); + } + /* user has to kill/intr the process to stop the test? */ +} + + +int main(int argc,char **argv) +{ + if (argc < 2 || argc > 2 || atoi(argv[1]) < 1) + { + printf("Usage: hashtest <number of threads>\n"); + exit(1); + } + + /* one arg == number of threads to create */ + run_hashtest(atoi(argv[1])); + + return 0; +} + + +int ast_add_profile(const char *x, uint64_t scale) +{ + return 0; +} + +int ast_loader_register(int (*updater)(void)) +{ + return 1; +} + +int ast_loader_unregister(int (*updater)(void)) +{ + return 1; +} +void ast_module_register(const struct ast_module_info *x) +{ +} + +void ast_module_unregister(const struct ast_module_info *x) +{ +} + + +void ast_register_file_version(const char *file, const char *version) +{ +} + +void ast_unregister_file_version(const char *file) +{ + +} + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + printf("LOG: lev:%d file:%s line:%d func: %s ", + level, file, line, function); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +void ast_verbose(const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + + printf("VERBOSE: "); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +void ast_register_thread(char *name) +{ + +} + +void ast_unregister_thread(void *id) +{ +} diff --git a/trunk/utils/muted.c b/trunk/utils/muted.c new file mode 100644 index 000000000..6a32b6d99 --- /dev/null +++ b/trunk/utils/muted.c @@ -0,0 +1,698 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * Updated for Mac OSX CoreAudio + * by Josh Roberson <josh@asteriasgi.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Mute Daemon + * + * \author Mark Spencer <markster@digium.com> + * + * Updated for Mac OSX CoreAudio + * \arg Josh Roberson <josh@asteriasgi.com> + * + * \note Specially written for Malcolm Davenport, but I think I'll use it too + * Connects to the Asterisk Manager Interface, AMI, and listens for events + * on certain devices. If a phone call is connected to one of the devices (phones) + * the local sound is muted to a lower volume during the call. + * + */ + +#ifdef __Darwin__ +#include <CoreAudio/AudioHardware.h> +#elif defined(__linux__) || defined(__FreeBSD__) +#include <sys/soundcard.h> +#endif +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <netdb.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +static char *config = "/etc/muted.conf"; + +static char host[256] = ""; +static char user[256] = ""; +static char pass[256] = ""; +static int smoothfade = 0; +static int mutelevel = 20; +static int muted = 0; +static int needfork = 1; +static int debug = 0; +static int stepsize = 3; +#ifndef __Darwin__ +static int mixchan = SOUND_MIXER_VOLUME; +#endif + +struct subchannel { + char *name; + struct subchannel *next; +}; + +static struct channel { + char *tech; + char *location; + struct channel *next; + struct subchannel *subs; +} *channels; + +static void add_channel(char *tech, char *location) +{ + struct channel *chan; + chan = malloc(sizeof(struct channel)); + if (chan) { + memset(chan, 0, sizeof(struct channel)); + if (!(chan->tech = strdup(tech))) { + free(chan); + return; + } + if (!(chan->location = strdup(location))) { + free(chan->tech); + free(chan); + return; + } + chan->next = channels; + channels = chan; + } + +} + +static int load_config(void) +{ + FILE *f; + char buf[1024]; + char *val; + char *val2; + int lineno=0; + int x; + f = fopen(config, "r"); + if (!f) { + fprintf(stderr, "Unable to open config file '%s': %s\n", config, strerror(errno)); + return -1; + } + while(!feof(f)) { + fgets(buf, sizeof(buf), f); + if (!feof(f)) { + lineno++; + val = strchr(buf, '#'); + if (val) *val = '\0'; + while(strlen(buf) && (buf[strlen(buf) - 1] < 33)) + buf[strlen(buf) - 1] = '\0'; + if (!strlen(buf)) + continue; + val = buf; + while(*val) { + if (*val < 33) + break; + val++; + } + if (*val) { + *val = '\0'; + val++; + while(*val && (*val < 33)) val++; + } + if (!strcasecmp(buf, "host")) { + if (val && strlen(val)) + strncpy(host, val, sizeof(host) - 1); + else + fprintf(stderr, "host needs an argument (the host) at line %d\n", lineno); + } else if (!strcasecmp(buf, "user")) { + if (val && strlen(val)) + strncpy(user, val, sizeof(user) - 1); + else + fprintf(stderr, "user needs an argument (the user) at line %d\n", lineno); + } else if (!strcasecmp(buf, "pass")) { + if (val && strlen(val)) + strncpy(pass, val, sizeof(pass) - 1); + else + fprintf(stderr, "pass needs an argument (the password) at line %d\n", lineno); + } else if (!strcasecmp(buf, "smoothfade")) { + smoothfade = 1; + } else if (!strcasecmp(buf, "mutelevel")) { + if (val && (sscanf(val, "%d", &x) == 1) && (x > -1) && (x < 101)) { + mutelevel = x; + } else + fprintf(stderr, "mutelevel must be a number from 0 (most muted) to 100 (no mute) at line %d\n", lineno); + } else if (!strcasecmp(buf, "channel")) { + if (val && strlen(val)) { + val2 = strchr(val, '/'); + if (val2) { + *val2 = '\0'; + val2++; + add_channel(val, val2); + } else + fprintf(stderr, "channel needs to be of the format Tech/Location at line %d\n", lineno); + } else + fprintf(stderr, "channel needs an argument (the channel) at line %d\n", lineno); + } else { + fprintf(stderr, "ignoring unknown keyword '%s'\n", buf); + } + } + } + fclose(f); + if (!strlen(host)) + fprintf(stderr, "no 'host' specification in config file\n"); + else if (!strlen(user)) + fprintf(stderr, "no 'user' specification in config file\n"); + else if (!channels) + fprintf(stderr, "no 'channel' specifications in config file\n"); + else + return 0; + return -1; +} + +static FILE *astf; +#ifndef __Darwin__ +static int mixfd; + +static int open_mixer(void) +{ + mixfd = open("/dev/mixer", O_RDWR); + if (mixfd < 0) { + fprintf(stderr, "Unable to open /dev/mixer: %s\n", strerror(errno)); + return -1; + } + return 0; +} +#endif /* !__Darwin */ + +/*! Connect to the asterisk manager interface */ +static int connect_asterisk(void) +{ + int sock; + struct hostent *hp; + char *ports; + int port = 5038; + struct sockaddr_in sin; + + ports = strchr(host, ':'); + if (ports) { + *ports = '\0'; + ports++; + if ((sscanf(ports, "%d", &port) != 1) || (port < 1) || (port > 65535)) { + fprintf(stderr, "'%s' is not a valid port number in the hostname\n", ports); + return -1; + } + } + hp = gethostbyname(host); + if (!hp) { + fprintf(stderr, "Can't find host '%s'\n", host); + return -1; + } + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + fprintf(stderr, "Failed to create socket: %s\n", strerror(errno)); + return -1; + } + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); + if (connect(sock, (struct sockaddr *)&sin, sizeof(sin))) { + fprintf(stderr, "Failed to connect to '%s' port '%d': %s\n", host, port, strerror(errno)); + close(sock); + return -1; + } + astf = fdopen(sock, "r+"); + if (!astf) { + fprintf(stderr, "fdopen failed: %s\n", strerror(errno)); + close(sock); + return -1; + } + return 0; +} + +static char *get_line(void) +{ + static char buf[1024]; + if (fgets(buf, sizeof(buf), astf)) { + while(strlen(buf) && (buf[strlen(buf) - 1] < 33)) + buf[strlen(buf) - 1] = '\0'; + return buf; + } else + return NULL; +} + +/*! Login to the asterisk manager interface */ +static int login_asterisk(void) +{ + char *welcome; + char *resp; + if (!(welcome = get_line())) { + fprintf(stderr, "disconnected (1)\n"); + return -1; + } + fprintf(astf, + "Action: Login\r\n" + "Username: %s\r\n" + "Secret: %s\r\n\r\n", user, pass); + if (!(welcome = get_line())) { + fprintf(stderr, "disconnected (2)\n"); + return -1; + } + if (strcasecmp(welcome, "Response: Success")) { + fprintf(stderr, "login failed ('%s')\n", welcome); + return -1; + } + /* Eat the rest of the event */ + while((resp = get_line()) && strlen(resp)); + if (!resp) { + fprintf(stderr, "disconnected (3)\n"); + return -1; + } + fprintf(astf, + "Action: Status\r\n\r\n"); + if (!(welcome = get_line())) { + fprintf(stderr, "disconnected (4)\n"); + return -1; + } + if (strcasecmp(welcome, "Response: Success")) { + fprintf(stderr, "status failed ('%s')\n", welcome); + return -1; + } + /* Eat the rest of the event */ + while((resp = get_line()) && strlen(resp)); + if (!resp) { + fprintf(stderr, "disconnected (5)\n"); + return -1; + } + return 0; +} + +static struct channel *find_channel(char *channel) +{ + char tmp[256] = ""; + char *s, *t; + struct channel *chan; + strncpy(tmp, channel, sizeof(tmp) - 1); + s = strchr(tmp, '/'); + if (s) { + *s = '\0'; + s++; + t = strrchr(s, '-'); + if (t) { + *t = '\0'; + } + if (debug) + printf("Searching for '%s' tech, '%s' location\n", tmp, s); + chan = channels; + while(chan) { + if (!strcasecmp(chan->tech, tmp) && !strcasecmp(chan->location, s)) { + if (debug) + printf("Found '%s'/'%s'\n", chan->tech, chan->location); + break; + } + chan = chan->next; + } + } else + chan = NULL; + return chan; +} + +#ifndef __Darwin__ +static int getvol(void) +{ + int vol; + + if (ioctl(mixfd, MIXER_READ(mixchan), &vol)) { +#else +static float getvol(void) +{ + float volumeL, volumeR, vol; + OSStatus err; + AudioDeviceID device; + UInt32 size; + UInt32 channels[2]; + + size = sizeof(device); + err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device); + size = sizeof(channels); + if (!err) + err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels); + size = sizeof(vol); + if (!err) + err = AudioDeviceGetProperty(device, channels[0], false, kAudioDevicePropertyVolumeScalar, &size, &volumeL); + if (!err) + err = AudioDeviceGetProperty(device, channels[1], false, kAudioDevicePropertyVolumeScalar, &size, &volumeR); + if (!err) + vol = (volumeL < volumeR) ? volumeR : volumeL; + else { +#endif + fprintf(stderr, "Unable to read mixer volume: %s\n", strerror(errno)); + return -1; + } + return vol; +} + +#ifndef __Darwin__ +static int setvol(int vol) +#else +static int setvol(float vol) +#endif +{ +#ifndef __Darwin__ + if (ioctl(mixfd, MIXER_WRITE(mixchan), &vol)) { +#else + float volumeL = vol; + float volumeR = vol; + OSStatus err; + AudioDeviceID device; + UInt32 size; + UInt32 channels[2]; + + size = sizeof(device); + err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device); + size = sizeof(channels); + err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels); + size = sizeof(vol); + if (!err) + err = AudioDeviceSetProperty(device, 0, channels[0], false, kAudioDevicePropertyVolumeScalar, size, &volumeL); + if (!err) + err = AudioDeviceSetProperty(device, 0, channels[1], false, kAudioDevicePropertyVolumeScalar, size, &volumeR); + if (err) { +#endif + + fprintf(stderr, "Unable to write mixer volume: %s\n", strerror(errno)); + return -1; + + } + return 0; +} + +#ifndef __Darwin__ +static int oldvol = 0; +static int mutevol = 0; +#else +static float oldvol = 0; +static float mutevol = 0; +#endif + +#ifndef __Darwin__ +static int mutedlevel(int orig, int mutelevel) +{ + int l = orig >> 8; + int r = orig & 0xff; + l = (float)(mutelevel) * (float)(l) / 100.0; + r = (float)(mutelevel) * (float)(r) / 100.0; + + return (l << 8) | r; +#else +static float mutedlevel(float orig, float mutelevel) +{ + float master = orig; + master = mutelevel * master / 100.0; + return master; +#endif + +} + +static void mute(void) +{ +#ifndef __Darwin__ + int vol; + int start; + int x; +#else + float vol; + float start = 1.0; + float x; +#endif + vol = getvol(); + oldvol = vol; + if (smoothfade) +#ifdef __Darwin__ + start = mutelevel; +#else + start = 100; + else + start = mutelevel; +#endif + for (x=start;x>=mutelevel;x-=stepsize) { + mutevol = mutedlevel(vol, x); + setvol(mutevol); + /* Wait 0.01 sec */ + usleep(10000); + } + mutevol = mutedlevel(vol, mutelevel); + setvol(mutevol); + if (debug) +#ifdef __Darwin__ + printf("Mute from '%f' to '%f'!\n", oldvol, mutevol); +#else + printf("Mute from '%04x' to '%04x'!\n", oldvol, mutevol); +#endif + muted = 1; +} + +static void unmute(void) +{ +#ifdef __Darwin__ + float vol; + float start; + float x; +#else + int vol; + int start; + int x; +#endif + vol = getvol(); + if (debug) +#ifdef __Darwin__ + printf("Unmute from '%f' (should be '%f') to '%f'!\n", vol, mutevol, oldvol); + mutevol = vol; + if (vol == mutevol) { +#else + printf("Unmute from '%04x' (should be '%04x') to '%04x'!\n", vol, mutevol, oldvol); + if ((int)vol == mutevol) { +#endif + if (smoothfade) + start = mutelevel; + else +#ifdef __Darwin__ + start = 1.0; +#else + start = 100; +#endif + for (x=start;x<100;x+=stepsize) { + mutevol = mutedlevel(oldvol, x); + setvol(mutevol); + /* Wait 0.01 sec */ + usleep(10000); + } + setvol(oldvol); + } else + printf("Whoops, it's already been changed!\n"); + muted = 0; +} + +static void check_mute(void) +{ + int offhook = 0; + struct channel *chan; + chan = channels; + while(chan) { + if (chan->subs) { + offhook++; + break; + } + chan = chan->next; + } + if (offhook && !muted) + mute(); + else if (!offhook && muted) + unmute(); +} + +static void delete_sub(struct channel *chan, char *name) +{ + struct subchannel *sub, *prev; + prev = NULL; + sub = chan->subs; + while(sub) { + if (!strcasecmp(sub->name, name)) { + if (prev) + prev->next = sub->next; + else + chan->subs = sub->next; + free(sub->name); + free(sub); + return; + } + prev = sub; + sub = sub->next; + } +} + +static void append_sub(struct channel *chan, char *name) +{ + struct subchannel *sub; + sub = chan->subs; + while(sub) { + if (!strcasecmp(sub->name, name)) + return; + sub = sub->next; + } + sub = malloc(sizeof(struct subchannel)); + if (sub) { + memset(sub, 0, sizeof(struct subchannel)); + if (!(sub->name = strdup(name))) { + free(sub); + return; + } + sub->next = chan->subs; + chan->subs = sub; + } +} + +static void hangup_chan(char *channel) +{ + struct channel *chan; + if (debug) + printf("Hangup '%s'\n", channel); + chan = find_channel(channel); + if (chan) + delete_sub(chan, channel); + check_mute(); +} + +static void offhook_chan(char *channel) +{ + struct channel *chan; + if (debug) + printf("Offhook '%s'\n", channel); + chan = find_channel(channel); + if (chan) + append_sub(chan, channel); + check_mute(); +} + +static int wait_event(void) +{ + char *resp; + char event[120]=""; + char channel[120]=""; + char oldname[120]=""; + char newname[120]=""; + + resp = get_line(); + if (!resp) { + fprintf(stderr, "disconnected (6)\n"); + return -1; + } + if (!strncasecmp(resp, "Event: ", strlen("Event: "))) { + strncpy(event, resp + strlen("Event: "), sizeof(event) - 1); + /* Consume the rest of the non-event */ + while((resp = get_line()) && strlen(resp)) { + if (!strncasecmp(resp, "Channel: ", strlen("Channel: "))) + strncpy(channel, resp + strlen("Channel: "), sizeof(channel) - 1); + if (!strncasecmp(resp, "Newname: ", strlen("Newname: "))) + strncpy(newname, resp + strlen("Newname: "), sizeof(newname) - 1); + if (!strncasecmp(resp, "Oldname: ", strlen("Oldname: "))) + strncpy(oldname, resp + strlen("Oldname: "), sizeof(oldname) - 1); + } + if (strlen(channel)) { + if (!strcasecmp(event, "Hangup")) + hangup_chan(channel); + else + offhook_chan(channel); + } + if (strlen(newname) && strlen(oldname)) { + if (!strcasecmp(event, "Rename")) { + hangup_chan(oldname); + offhook_chan(newname); + } + } + } else { + /* Consume the rest of the non-event */ + while((resp = get_line()) && strlen(resp)); + } + if (!resp) { + fprintf(stderr, "disconnected (7)\n"); + return -1; + } + return 0; +} + +static void usage(void) +{ + printf("Usage: muted [-f] [-d]\n" + " -f : Do not fork\n" + " -d : Debug (implies -f)\n"); +} + +int main(int argc, char *argv[]) +{ + int x; + while((x = getopt(argc, argv, "fhd")) > 0) { + switch(x) { + case 'd': + debug = 1; + needfork = 0; + break; + case 'f': + needfork = 0; + break; + case 'h': + /* Fall through */ + default: + usage(); + exit(1); + } + } + if (load_config()) + exit(1); +#ifndef __Darwin__ + if (open_mixer()) + exit(1); +#endif + if (connect_asterisk()) { +#ifndef __Darwin__ + close(mixfd); +#endif + exit(1); + } + if (login_asterisk()) { +#ifndef __Darwin__ + close(mixfd); +#endif + fclose(astf); + exit(1); + } + if (needfork) + daemon(0,0); + for(;;) { + if (wait_event()) { + fclose(astf); + while(connect_asterisk()) { + sleep(5); + } + if (login_asterisk()) { + fclose(astf); + exit(1); + } + } + } + exit(0); +} diff --git a/trunk/utils/smsq.c b/trunk/utils/smsq.c new file mode 100644 index 000000000..af8238c84 --- /dev/null +++ b/trunk/utils/smsq.c @@ -0,0 +1,771 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2004 - 2005 + * + * SMS queuing application for use with asterisk app_sms + * by Adrian Kennard + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" + +#include <popt.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> +#include <ctype.h> +#include <time.h> + +#ifdef SOLARIS +#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000 +#endif +#if !defined(POPT_ARGFLAG_SHOW_DEFAULT) +#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000 +#endif + + +/*! + * \brief reads next USC character from null terminated UTF-8 string and advanced pointer + * for non valid UTF-8 sequences. + * \return character as is Does \b NOT advance pointer for null termination +*/ +static int utf8decode (unsigned char **pp) +{ + unsigned char *p = *pp; + if (!*p) + return 0; /* null termination of string */ + (*pp)++; + if (*p < 0xC0) + return *p; /* ascii or continuation character */ + if (*p < 0xE0) + { + if (*p < 0xC2 || (p[1] & 0xC0) != 0x80) + return *p; /* not valid UTF-8 */ + (*pp)++; + return ((*p & 0x1F) << 6) + (p[1] & 0x3F); + } + if (*p < 0xF0) + { + if ((*p == 0xE0 && p[1] < 0xA0) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80) + return *p; /* not valid UTF-8 */ + (*pp) += 2; + return ((*p & 0x0F) << 12) + ((p[1] & 0x3F) << 6) + (p[2] & 0x3F); + } + if (*p < 0xF8) + { + if ((*p == 0xF0 && p[1] < 0x90) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80) + return *p; /* not valid UTF-8 */ + (*pp) += 3; + return ((*p & 0x07) << 18) + ((p[1] & 0x3F) << 12) + ((p[2] & 0x3F) << 6) + (p[3] & 0x3F); + } + if (*p < 0xFC) + { + if ((*p == 0xF8 && p[1] < 0x88) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80 + || (p[4] & 0xC0) != 0x80) + return *p; /* not valid UTF-8 */ + (*pp) += 4; + return ((*p & 0x03) << 24) + ((p[1] & 0x3F) << 18) + ((p[2] & 0x3F) << 12) + ((p[3] & 0x3F) << 6) + (p[4] & 0x3F); + } + if (*p < 0xFE) + { + if ((*p == 0xFC && p[1] < 0x84) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80 + || (p[4] & 0xC0) != 0x80 || (p[5] & 0xC0) != 0x80) + return *p; /* not valid UTF-8 */ + (*pp) += 5; + return ((*p & 0x01) << 30) + ((p[1] & 0x3F) << 24) + ((p[2] & 0x3F) << 18) + ((p[3] & 0x3F) << 12) + ((p[4] & 0x3F) << 6) + + (p[5] & 0x3F); + } + return *p; /* not sensible */ +} + +/*! + * \brief check for any queued messages in specific queue (queue="" means any queue) + * \param dir,queue,subaddress,channel,callerid,wait,delay,retries,concurrent + * \retval 0 if nothing queued + * \retval 1 if queued and outgoing set up OK + * \retval 2 of outgoing exists +*/ +static char txqcheck (char *dir, char *queue, char subaddress, char *channel, char *callerid, int wait, int delay, int retries, int concurrent) +{ + char ogname[100], + temp[100], + dirname[100], + *p=NULL; + FILE *f; + DIR *d; + int ql = strlen (queue), qfl = ql; + struct dirent *fn; + snprintf (dirname, sizeof(dirname), "sms/%s", dir); + d = opendir (dirname); + if (!d) + return 0; + while ((fn = readdir (d)) + && !(*fn->d_name != '.' + && ((!ql && (p = strchr (fn->d_name, '.'))) || (ql && !strncmp (fn->d_name, queue, ql) && fn->d_name[ql] == '.')))); + if (!fn) + { + closedir (d); + return 0; + } + if (!ql) + { /* not searching any specific queue, so use whatr we found as the queue */ + queue = fn->d_name; + qfl = ql = p - queue; + } + p = strchr (queue, '-'); + if (p && p < queue + ql) + { + ql = p - queue; + subaddress = p[1]; + } + snprintf (temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ()); + f = fopen (temp, "w"); + if (!f) + { + perror (temp); + closedir (d); + return 0; + } + fprintf (f, "Channel: "); + if (!channel) + fprintf (f, "Local/%.*s\n", ql, queue); + else + { + p = strchr (channel, '/'); + if (!p) + p = channel; + p = strchr (p, 'X'); + if (p) + fprintf (f, "%.*s%c%s\n", (int)(p - channel), channel, subaddress, p + 1); + else + fprintf (f, "%s\n", channel); + } + fprintf (f, "Callerid: SMS <"); + if (!callerid) + fprintf (f, "%.*s", ql, queue); + else + { + p = strchr (callerid, 'X'); + if (p) + fprintf (f, "%.*s%c%s", (int)(p - callerid), callerid, subaddress, p + 1); + else + fprintf (f, "%s", callerid); + } + fprintf (f, ">\n"); + fprintf (f, "Application: SMS\n"); + fprintf (f, "Data: %.*s", qfl, queue); + if (dir[1] == 't') + fprintf (f, "|s"); + fprintf (f, "\nMaxRetries: %d\n", retries); + fprintf (f, "RetryTime: %d\n", delay); + fprintf (f, "WaitTime: %d\n", wait); + fclose (f); + closedir (d); + { + int try = 0; + while (try < concurrent) + { + try++; + snprintf(ogname, sizeof(ogname), "outgoing/smsq.%s.%s.%d", dir, queue, try); + if (!link (temp, ogname)) + { /* queued OK */ + unlink (temp); + return 1; + } + } + } + /* failed to create call queue */ + unlink (temp); + return 2; +} + +/*! + * \brief Process received queue entries + * Run through a process, setting environment variables +*/ +static void rxqcheck (char *dir, char *queue, char *process) +{ + char *p; + void *pp = &p; + char dirname[100], + temp[100]; + DIR *d; + int ql = strlen (queue); + struct dirent *fn; + snprintf(temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ()); + snprintf(dirname, sizeof(dirname), "sms/%s", dir); + d = opendir (dirname); + if (!d) + return; + while ((fn = readdir (d))) + if ((*fn->d_name != '.' + && ((!ql && (p = strchr (fn->d_name, '.'))) || (ql && !strncmp (fn->d_name, queue, ql) && fn->d_name[ql] == '.')))) + { /* process file */ + char filename[1010]; + char line[1000]; + unsigned short ud[160]; + unsigned char udl = 0; + FILE *f; + snprintf (filename, sizeof(filename), "sms/%s/%s", dir, fn->d_name); + if (rename (filename, temp)) + continue; /* cannot access file */ + f = fopen (temp, "r"); + unlink (temp); + if (!f) + { + perror (temp); + continue; + } + unsetenv ("oa"); + unsetenv ("da"); + unsetenv ("scts"); + unsetenv ("pid"); + unsetenv ("dcs"); + unsetenv ("mr"); + unsetenv ("srr"); + unsetenv ("rp"); + unsetenv ("vp"); + unsetenv ("udh"); + unsetenv ("ud"); + unsetenv ("ude"); + unsetenv ("ud8"); + unsetenv ("ud16"); + unsetenv ("morx"); + unsetenv ("motx"); + unsetenv ("queue"); + if (*queue) + setenv ("queue", queue, 1); + setenv (dir, "", 1); + while (fgets (line, sizeof (line), f)) + { + for (p = line; *p && *p != '\n' && *p != '\r'; p++); + *p = 0; /* strip eoln */ + p = line; + if (!*p || *p == ';') + continue; /* blank line or comment, ignore */ + while (isalnum (*p)) + { + *p = tolower (*p); + p++; + } + while (isspace (*p)) + *p++ = 0; + if (*p == '=') + { /* = */ + *p++ = 0; + if (!strcmp (line, "oa") || !strcmp (line, "da") || !strcmp (line, "scts") || !strcmp (line, "pid") + || !strcmp (line, "dcs") || !strcmp (line, "mr") || !strcmp (line, "vp")) + setenv (line, p, 1); + else if ((!strcmp (line, "srr") || !strcmp (line, "rp")) && atoi (p)) + setenv (line, "", 1); + else if (!strcmp (line, "ud")) + { /* read the user data as UTF-8 */ + long v; + udl = 0; + while ((v = utf8decode (pp)) && udl < 160) + if (v && v <= 0xFFFF) + ud[udl++] = v; + } + } else if (*p == '#') + { + *p++ = 0; + if (*p == '#') + { /* ## */ + p++; + if (!strcmp (line, "udh")) + setenv (line, p, 1); + else if (!strcmp (line, "ud")) + { /* read user data UCS-2 */ + udl = 0; + while (*p && udl < 160) + { + if (isxdigit (*p) && isxdigit (p[1]) && isxdigit (p[2]) && isxdigit (p[3])) + { + ud[udl++] = + (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 12) + + (((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF)) << 8) + + (((isalpha (p[2]) ? 9 : 0) + (p[2] & 0xF)) << 4) + ((isalpha (p[3]) ? 9 : 0) + (p[3] & 0xF)); + p += 4; + } else + break; + } + } + } else + { /* # */ + if (!strcmp (line, "ud")) + { /* read user data UCS-1 */ + udl = 0; + while (*p && udl < 160) + { + if (isxdigit (*p) && isxdigit (p[1])) + { + ud[udl++] = (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF)); + p += 2; + } else + break; + } + } + } + } + } + fclose (f); + /* set up user data variables */ + { + char temp[481]; + int n, + p; + for (n = 0, p = 0; p < udl; p++) + { + unsigned short v = ud[p]; + if (v) + { + if (v < 0x80) + temp[n++] = v; + else if (v < 0x800) + { + temp[n++] = (0xC0 + (v >> 6)); + temp[n++] = (0x80 + (v & 0x3F)); + } else + { + temp[n++] = (0xE0 + (v >> 12)); + temp[n++] = (0x80 + ((v >> 6) & 0x3F)); + temp[n++] = (0x80 + (v & 0x3F)); + } + } + } + temp[n] = 0; + setenv ("ud", temp, 1); + for (n = 0, p = 0; p < udl; p++) + { + unsigned short v = ud[p]; + if (v < ' ' || v == '\\') + { + temp[n++] = '\\'; + if (v == '\\') + temp[n++] = '\\'; + else if (v == '\n') + temp[n++] = 'n'; + else if (v == '\r') + temp[n++] = 'r'; + else if (v == '\t') + temp[n++] = 't'; + else if (v == '\f') + temp[n++] = 'f'; + else + { + temp[n++] = '0' + (v >> 6); + temp[n++] = '0' + ((v >> 3) & 7); + temp[n++] = '0' + (v & 7); + } + } else if (v < 0x80) + temp[n++] = v; + else if (v < 0x800) + { + temp[n++] = (0xC0 + (v >> 6)); + temp[n++] = (0x80 + (v & 0x3F)); + } else + { + temp[n++] = (0xE0 + (v >> 12)); + temp[n++] = (0x80 + ((v >> 6) & 0x3F)); + temp[n++] = (0x80 + (v & 0x3F)); + } + } + temp[n] = 0; + setenv ("ude", temp, 1); + for (p = 0; p < udl && ud[p] < 0x100; p++); + if (p == udl) + { + for (n = 0, p = 0; p < udl; p++) + { + sprintf (temp + n, "%02X", ud[p]); + n += 2; + } + setenv ("ud8", temp, 1); + } + for (n = 0, p = 0; p < udl; p++) + { + sprintf (temp + n, "%04X", ud[p]); + n += 4; + } + setenv ("ud16", temp, 1); + } + /* run the command */ + system (process); + } + closedir (d); +} + +/* Main app */ +int +main (int argc, const char *argv[]) +{ + char c; + int mt = 0, + mo = 0, + tx = 0, + rx = 0, + nodial = 0, + nowait = 0, + concurrent = 1, + motxwait = 10, + motxdelay = 1, + motxretries = 10, + mttxwait = 10, + mttxdelay = 30, + mttxretries = 100, + mr = -1, + pid = -1, + dcs = -1, + srr = 0, + rp = 0, + vp = 0, + udl = 0, + utf8 = 0, + ucs1 = 0, + ucs2 = 0; + unsigned short ud[160]; + unsigned char *uds = 0, + *udh = 0; + char *da = 0, + *oa = 0, + *queue = "", + *udfile = 0, + *process = 0, + *spooldir = "/var/spool/asterisk", + *motxchannel = "Local/1709400X", + *motxcallerid = 0, + *mttxchannel = 0, + *mttxcallerid = "080058752X0", + *defaultsubaddress = "9", + subaddress = 0, + *scts = 0; + poptContext optCon; /* context for parsing command-line options */ + const struct poptOption optionsTable[] = { + {"queue", 'q', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &queue, 0, "Queue [inc sub address]", "number[-X]"}, + {"da", 'd', POPT_ARG_STRING, &da, 0, "Destination address", "number"}, + {"oa", 'o', POPT_ARG_STRING, &oa, 0, "Origination address", "number"}, + {"ud", 'm', POPT_ARG_STRING, &uds, 0, "Message", "text"}, + {"ud-file", 'f', POPT_ARG_STRING, &udfile, 0, "Message file", "filename"}, + {"UTF-8", 0, POPT_ARG_NONE, &utf8, 0, "File treated as null terminated UTF-8 (default)", 0}, + {"UCS-1", 0, POPT_ARG_NONE, &ucs1, 0, "File treated as UCS-1", 0}, + {"UCS-2", 0, POPT_ARG_NONE, &ucs2, 0, "File treated as UCS-2", 0}, + {"mt", 't', POPT_ARG_NONE, &mt, 0, "Mobile Terminated", 0}, + {"mo", 0, POPT_ARG_NONE, &mo, 0, "Mobile Originated", 0}, + {"tx", 0, POPT_ARG_NONE, &tx, 0, "Send message", 0}, + {"rx", 'r', POPT_ARG_NONE, &rx, 0, "Queue for receipt", 0}, + {"process", 'e', POPT_ARG_STRING, &process, 0, "Rx queue process command", "command"}, + {"no-dial", 'x', POPT_ARG_NONE, &nodial, 0, "Do not dial", 0}, + {"no-wait", 0, POPT_ARG_NONE, &nowait, 0, "Do not wait if already calling", 0}, + {"concurrent", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &concurrent, 0, "Number of concurrent calls to allow", "n"}, + {"motx-channel", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &motxchannel, 0, "Channel for motx calls", "channel"}, + {"motx-callerid", 0, POPT_ARG_STRING, &motxcallerid, 0, + "Caller ID for motx calls (default is queue name without sub address)", "number"}, + {"motx-wait", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxwait, 0, "Time to wait for motx call to answer", + "seconds"}, + {"motx-delay", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxdelay, 0, "Time between motx call retries", "seconds"}, + {"motx-retries", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxretries, 0, "Number of retries for motx call", "n"}, + {"mttx-channel", 0, POPT_ARG_STRING, &mttxchannel, 0, + "Channel for mttx calls (default is Local/ and queue name without sub address)", "channel"}, + {"mttx-callerid", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &mttxcallerid, 0, + "Caller ID for mttx calls (default is queue name without sub address)", "number"}, + {"mttx-wait", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxwait, 0, "Time to wait for mttx call to answer", + "seconds"}, + {"mttx-delay", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxdelay, 0, "Time between mttx call retries", "seconds"}, + {"mttx-retries", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxretries, 0, "Number of retries for mttx call", "n"}, + {"mr", 'n', POPT_ARG_INT, &mr, 0, "Message reference", "n"}, + {"pid", 'p', POPT_ARG_INT, &pid, 0, "Protocol ID", "n"}, + {"dcs", 'c', POPT_ARG_INT, &dcs, 0, "Data Coding Scheme", "n"}, + {"udh", 0, POPT_ARG_STRING, &udh, 0, "User data header", "hex"}, + {"srr", 0, POPT_ARG_NONE, &srr, 0, "Status Report Request", 0}, + {"rp", 0, POPT_ARG_NONE, &rp, 0, "Return Path request", 0}, + {"v", 0, POPT_ARG_INT, &vp, 0, "Validity Period", "seconds"}, + {"scts", 0, POPT_ARG_STRING, &scts, 0, "Timestamp", "YYYY-MM-SSTHH:MM:SS"}, + {"default-sub-address", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &defaultsubaddress, 0, "Default sub address", "X"}, + {"spool-dir", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &spooldir, 0, "Asterisk spool dir", "dirname"}, + POPT_AUTOHELP {NULL, 0, 0, NULL, 0} + }; + + optCon = poptGetContext (NULL, argc, argv, optionsTable, 0); + poptSetOtherOptionHelp (optCon, "<oa/da> <message>"); + + /* Now do options processing, get portname */ + if ((c = poptGetNextOpt (optCon)) < -1) + { + /* an error occurred during option processing */ + fprintf (stderr, "%s: %s\n", poptBadOption (optCon, POPT_BADOPTION_NOALIAS), poptStrerror (c)); + return 1; + } + if (!ucs1 && !ucs2) + utf8 = 1; + if (utf8 + ucs1 + ucs2 > 1) + { + fprintf (stderr, "Pick one of UTF-8, UCS-1 or UCS-2 only\n"); + return 1; + } + if (!udfile && (ucs1 || ucs2)) + { + fprintf (stderr, "Command line arguments always treated as UTF-8\n"); + return 1; + } + /* if (!where && poptPeekArg (optCon)) where = (char *) poptGetArg (optCon); */ + if (!mt && !mo && process) + mt = 1; + if (!mt && !mo && oa) + mt = 1; + if (!mt) + mo = 1; + if (mt && mo) + { + fprintf (stderr, "Cannot be --mt and --mo\n"); + return 1; + } + if (!rx && !tx && process) + rx = 1; + if (!rx) + tx = 1; + if (tx && rx) + { + fprintf (stderr, "Cannot be --tx and --rx\n"); + return 1; + } + if (rx) + nodial = 1; + if (uds && udfile) + { + fprintf (stderr, "Cannot have --ud and --ud-file\n"); + return 1; + } + if (mo && !da && poptPeekArg (optCon)) + da = (char *) poptGetArg (optCon); + if (mt && !oa && poptPeekArg (optCon)) + oa = (char *) poptGetArg (optCon); + if (tx && oa && mo) + { + fprintf (stderr, "--oa makes no sense with --mo as CLI is used (i.e. queue name)\n"); + return 1; + } + if (tx && da && mt) + { + fprintf (stderr, "--da makes no sense with --mt as called number is used (i.e. queue name)\n"); + return 1; + } + if (da && strlen (da) > 20) + { + fprintf (stderr, "--da too long\n"); + return 1; + } + if (oa && strlen (oa) > 20) + { + fprintf (stderr, "--oa too long\n"); + return 1; + } + if (queue && strlen (queue) > 20) + { + fprintf (stderr, "--queue name too long\n"); + return 1; + } + if (mo && scts) + { + fprintf (stderr, "scts is set my service centre\n"); + return 1; + } + if (uds) + { /* simple user data command line option in \UTF-8 */ + while (udl < 160 && *uds) + { + int v = utf8decode (&uds); + if (v > 0xFFFF) + { + fprintf (stderr, "Invalid character U+%X at %d\n", v, udl); + return 1; + } + ud[udl++] = v; + } + } + if (!uds && !udfile && poptPeekArg (optCon)) + { /* multiple command line arguments in UTF-8 */ + while (poptPeekArg (optCon) && udl < 160) + { + unsigned char *a = (unsigned char *) poptGetArg (optCon); + if (udl && udl < 160) + ud[udl++] = ' '; /* space between arguments */ + while (udl < 160 && *a) + { + int v = utf8decode (&a); + if (v > 0xFFFF) + { + fprintf (stderr, "Invalid character U+%X at %d\n", v, udl); + return 1; + } + ud[udl++] = v; + } + } + } + if (poptPeekArg (optCon)) + { + fprintf (stderr, "Unknown argument %s\n", poptGetArg (optCon)); + return 1; + } + if (udfile) + { /* get message from file */ + unsigned char dat[1204], + *p = dat, + *e; + int f, + n; + if (*udfile) + f = open (udfile, O_RDONLY); + else + f = fileno (stdin); + if (f < 0) + { + perror (udfile); + return 1; + } + n = read (f, dat, sizeof (dat)); + if (n < 0) + { + perror (udfile); + return 1; + } + if (*udfile) + close (f); + e = dat + n; + if (utf8) + { /* UTF-8 */ + while (p < e && udl < 160 && *p) + ud[udl++] = utf8decode (&p); + } else if (ucs1) + { /* UCS-1 */ + while (p < e && udl < 160) + ud[udl++] = *p++; + } else + { /* UCS-2 */ + while (p + 1 < e && udl < 160) + { + ud[udl++] = (*p << 8) + p[1]; + p += 2; + } + } + } + if (queue) + { + char *d = strrchr (queue, '-'); + if (d && d[1]) + subaddress = d[1]; + else + subaddress = *defaultsubaddress; + } + + if (chdir (spooldir)) + { + perror (spooldir); + return 1; + } + + if (oa || da) + { /* send message */ + char temp[100], + queuename[100], + *dir = (mo ? rx ? "sms/morx" : "sms/motx" : rx ? "sms/mtrx" : "sms/mttx"); + FILE *f; + snprintf (temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ()); + mkdir ("sms", 0777); /* ensure directory exists */ + mkdir (dir, 0777); /* ensure directory exists */ + snprintf (queuename, sizeof(queuename), "%s/%s.%ld-%d", dir, *queue ? queue : "0", (long)time (0), (int)getpid ()); + f = fopen (temp, "w"); + if (!f) + { + perror (temp); + return 1; + } + if (oa) + fprintf (f, "oa=%s\n", oa); + if (da) + fprintf (f, "da=%s\n", da); + if (scts) + fprintf (f, "scts=%s\n", scts); + if (pid >= 0) + fprintf (f, "pid=%d\n", pid); + if (dcs >= 0) + fprintf (f, "dcs=%d\n", dcs); + if (mr >= 0) + fprintf (f, "mr=%d\n", mr); + if (srr) + fprintf (f, "srr=1\n"); + if (rp) + fprintf (f, "rp=1\n"); + if (udh) + fprintf (f, "udh#%s\n", udh); + if (vp > 0) + fprintf (f, "vp=%d\n", vp); + if (udl) + { + int p; + for (p = 0; p < udl && ud[p] < 0x100; p++); + if (p == udl) + { + for (p = 0; p < udl && ud[p] < 0x80 && ud[p] >= 0x20; p++); + if (p == udl) + { /* use text */ + fprintf (f, "ud="); + for (p = 0; p < udl; p++) + fputc (ud[p], f); + } else + { /* use one byte hex */ + fprintf (f, "ud#"); + for (p = 0; p < udl; p++) + fprintf (f, "%02X", ud[p]); + } + } else + { /* use two byte hex */ + fprintf (f, "ud##"); + for (p = 0; p < udl; p++) + fprintf (f, "%04X", ud[p]); + } + fprintf (f, "\n"); + } + fclose (f); + if (rename (temp, queuename)) + { + perror (queuename); + unlink (temp); + return 1; + } + } + + if (!nodial && tx && !process) + { /* dial to send messages */ + char ret=0, + try = 3; + if (nowait) + try = 1; + while (try--) + { + if (mo) + ret = txqcheck ("motx", queue, subaddress, motxchannel, motxcallerid, motxwait, motxdelay, motxretries, concurrent); + else + ret = txqcheck ("mttx", queue, subaddress, mttxchannel, mttxcallerid, mttxwait, mttxdelay, mttxretries, concurrent); + if (ret < 2) + break; /* sent, or queued OK */ + if (try) + sleep (1); + } + if (ret == 2 && !nowait) + fprintf (stderr, "No call scheduled as already sending\n"); + } + if (process) + rxqcheck (mo ? rx ? "morx" : "motx" : rx ? "mtrx" : "mttx", queue, process); + + return 0; +} diff --git a/trunk/utils/stereorize.c b/trunk/utils/stereorize.c new file mode 100644 index 000000000..7d72cbdbf --- /dev/null +++ b/trunk/utils/stereorize.c @@ -0,0 +1,159 @@ +/**************************************************************************** + * + * Programs for processing sound files in raw- or WAV-format. + * -- Merge two mono WAV-files to one stereo WAV-file. + * + * Name: stereorize.c + * Version: 1.1 + * Author: Mark Roberts <mark@manumark.de> + * Michael Labuschke <michael@labuschke.de> + * + ****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <assert.h> +#include "frame.h" + +static char *Version = "stereorize 1.1, November 5th 2000"; +static char *Usage = +"Usage: stereorize [options] infile-left infile-right outfile\n\n" + +"Example:\n" +" stereorize left.wav right.wav stereo.wav -h\n\n" + +"Creates stereo.wav (with WAV-header, option -h) from data in mono files\n" +"left.wav and right.wav.\n" +; + +int main( int argcount, char *args[]) +{ + int i, k[2], maxk, stdin_in_use=FALSE; + short *leftsample, *rightsample, *stereosample; + FILE *channel[2]; + char *filename[2], *tempname; + + version = Version; + usage = Usage; + + channel[0] = NULL; + channel[1] = NULL; + + parseargs( argcount, args, NOFILES | NOCOMPLAIN); + + for (i = 0; i < 2; i++) + { + filename[i] = parsefilearg( argcount, args); + if (filename[i] == NULL) + argerrornum( NULL, ME_NOTENOUGHFILES); + if (strcmp (filename[i], "-") == 0) + { + if (stdin_in_use) + argerrortxt( filename[i] + 1, + "Cannot use <stdin> for both input files"); + filename[i] = "<stdin>"; + channel[i] = stdin; + stdin_in_use = TRUE; + } + else + { + channel[i] = fopen(filename[i], "rb"); + } + if (channel[i] == NULL) + fatalerror( "Error opening input file '%s': %s\n", filename[i],strerror(errno)); + else + inform("Using file '%s' as input\n", filename[i]); + } + for (i = 0; i < 2; i++) + { + assert ( channel[i] != NULL); + readwavheader( channel[i]); + if (iswav && channels != 1) + inform("Warning: '%s' is no mono file\n", filename[i]); + } + + outfilename = parsefilearg( argcount, args); + if (outfilename == NULL) argerrornum( NULL, ME_NOOUTFILE); + if (strcmp (outfilename, "-") == 0) + { + outfilename = "<stdout>"; + out = stdout; + } + else + { + out = fopen(outfilename, "wb"); + } + if (out == NULL) + fatalerror( "Error opening output file '%s': %s\n", outfilename,strerror(errno)); + else + inform("Using file '%s' as output\n", outfilename); + + if ((tempname = parsefilearg( argcount, args)) != NULL) + argerrornum( tempname, ME_TOOMANYFILES); + + checknoargs(argcount, args); /* Check that no arguments are left */ + + leftsample = malloc( sizeof(*leftsample) * BUFFSIZE); + rightsample = malloc( sizeof(*leftsample) * BUFFSIZE); + stereosample = malloc( sizeof(*leftsample) * 2 * BUFFSIZE); + if (leftsample == NULL || rightsample == NULL || stereosample == NULL) + fatalperror (""); + + channels = 2; /* Output files are stereo */ + if (wavout) + { + if ((strcmp(outfilename,"<stdout>")!=0) && (fseek( out, 0, SEEK_SET) != 0)) + fatalerror("Couldn't navigate output file '%s': %s\n",outfilename, strerror(errno)); + makewavheader(); + } + + startstopwatch(); + while (TRUE) + { + maxk = 0; + for (i = 0; i < 2; i++) + { + k[i] = fread(i==0? leftsample : rightsample, + sizeof(*leftsample), + BUFFSIZE, + channel[i]); + if (k[i] == -1) + fatalerror("Error reading file '%s': %s\n", filename[i],strerror(errno)); + if (k[i] > maxk) + maxk = k[i]; + } + if (maxk == 0) + myexit (0); + + /*-------------------------------------------------* + * First the left channel as far as it goes ... * + *-------------------------------------------------*/ + for (i = 0; i < k[0]; i++) + stereosample[2 * i] = leftsample[i]; + /*-------------------------------------------------* + * ... and fill up till the end of this buffer. * + *-------------------------------------------------*/ + for (; i < maxk; i++) + stereosample[2 * i] = 0; + + /*-------------------------------------------------* + * Next the right channel as far as it goes ... * + *-------------------------------------------------*/ + for (i = 0; i < k[1]; i++) + stereosample[2 * i + 1] = rightsample[i]; + /*-------------------------------------------------* + * ... and fill up till the end of this buffer. * + *-------------------------------------------------*/ + for (; i < maxk; i++) + stereosample[2 * i + 1] = 0; + + fwrite(stereosample, sizeof(*leftsample), 2 * maxk, out); + if (ferror( out) != 0) + fatalerror("Error writing to file '%s': %s\n", + outfilename, strerror(errno)); + } + /* That was an endless loop. This point is never reached. */ +} diff --git a/trunk/utils/streamplayer.c b/trunk/utils/streamplayer.c new file mode 100644 index 000000000..fb0d055a2 --- /dev/null +++ b/trunk/utils/streamplayer.c @@ -0,0 +1,121 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Russell Bryant <russell@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \author Russell Bryant <russell@digium.com> + * + * \brief A utility for reading from a raw TCP stream + * + * This application is intended for use when a raw TCP stream is desired to be + * used as a music on hold source for Asterisk. Some devices are capable of + * taking some kind of audio input and provide it as a raw TCP stream over the + * network, which is what inspired someone to fund this to be written. + * However, it would certainly be possible to write your own server application + * to provide music over a TCP stream from a centralized location. + * + * This application is quite simple. It just reads the data from the TCP + * stream and dumps it straight to stdout. Due to the way Asterisk handles + * music on hold sources, this application checks to make sure writing + * to stdout will not be a blocking operation before doing so. If so, the data + * is just thrown away. This ensures that the stream will continue to be + * serviced, even if Asterisk is not currently using the source. + * + * \todo Update this application to be able to connect to a stream via HTTP, + * since that is the #1 most requested feature, and it would be quite useful. + * A lot of people think that is what this is for and email me when it does + * not work. :) + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <netdb.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__) || defined(__CYGWIN__) +#include <netinet/in.h> +#endif +#include <sys/time.h> + + +int main(int argc, char *argv[]) +{ + struct sockaddr_in sin; + struct hostent *hp; + int s; + int res; + char buf[2048]; + fd_set wfds; + struct timeval tv; + + if (argc != 3) { + fprintf(stderr, "streamplayer -- A utility for reading from a raw TCP stream.\n"); + fprintf(stderr, "Written for use with Asterisk (http://www.asterisk.org)\n"); + fprintf(stderr, "Copyright (C) 2005 -- Russell Bryant -- Digium, Inc.\n\n"); + fprintf(stderr, "Usage: ./streamplayer <ip> <port>\n"); + exit(1); + } + + hp = gethostbyname(argv[1]); + if (!hp) { + fprintf(stderr, "Unable to lookup IP for host '%s'\n", argv[1]); + exit(1); + } + + memset(&sin, 0, sizeof(sin)); + + sin.sin_family = AF_INET; + sin.sin_port = htons(atoi(argv[2])); + memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); + + s = socket(AF_INET, SOCK_STREAM, 0); + + if (s < 0) { + fprintf(stderr, "Unable to allocate socket!\n"); + exit(1); + } + + res = connect(s, (struct sockaddr *)&sin, sizeof(sin)); + + if (res) { + fprintf(stderr, "Unable to connect to host!\n"); + close(s); + exit(1); + } + + while (1) { + res = read(s, buf, sizeof(buf)); + + if (res < 1) + break; + + memset(&tv, 0, sizeof(tv)); + FD_ZERO(&wfds); + FD_SET(1, &wfds); + + select(2, NULL, &wfds, NULL, &tv); + + if (FD_ISSET(1, &wfds)) + write(1, buf, res); + } + + close(s); + exit(res); +} |